Browse Source

First commit

pull/1842/head
Jae Kwon 9 years ago
commit
16372365c4
21 changed files with 1850 additions and 0 deletions
  1. +5
    -0
      array.go
  2. +15
    -0
      async.go
  3. +275
    -0
      bit_array.go
  4. +120
    -0
      bit_array_test.go
  5. +44
    -0
      byteslice.go
  6. +62
    -0
      cmap.go
  7. +84
    -0
      colors.go
  8. +45
    -0
      errors.go
  9. +103
    -0
      heap.go
  10. +55
    -0
      int.go
  11. +75
    -0
      io.go
  12. +157
    -0
      math.go
  13. +225
    -0
      os.go
  14. +145
    -0
      random.go
  15. +72
    -0
      repeat_timer.go
  16. +154
    -0
      service.go
  17. +24
    -0
      string.go
  18. +14
    -0
      test/assert.go
  19. +28
    -0
      test/mutate.go
  20. +57
    -0
      throttle_timer.go
  21. +91
    -0
      word.go

+ 5
- 0
array.go View File

@ -0,0 +1,5 @@
package common
func Arr(items ...interface{}) []interface{} {
return items
}

+ 15
- 0
async.go View File

@ -0,0 +1,15 @@
package common
import "sync"
func Parallel(tasks ...func()) {
var wg sync.WaitGroup
wg.Add(len(tasks))
for _, task := range tasks {
go func(task func()) {
task()
wg.Done()
}(task)
}
wg.Wait()
}

+ 275
- 0
bit_array.go View File

@ -0,0 +1,275 @@
package common
import (
"fmt"
"math/rand"
"strings"
"sync"
)
type BitArray struct {
mtx sync.Mutex
Bits int `json:"bits"` // NOTE: persisted via reflect, must be exported
Elems []uint64 `json:"elems"` // NOTE: persisted via reflect, must be exported
}
// There is no BitArray whose Size is 0. Use nil instead.
func NewBitArray(bits int) *BitArray {
if bits == 0 {
return nil
}
return &BitArray{
Bits: bits,
Elems: make([]uint64, (bits+63)/64),
}
}
func (bA *BitArray) Size() int {
if bA == nil {
return 0
}
return bA.Bits
}
// NOTE: behavior is undefined if i >= bA.Bits
func (bA *BitArray) GetIndex(i int) bool {
if bA == nil {
return false
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
return bA.getIndex(i)
}
func (bA *BitArray) getIndex(i int) bool {
if i >= bA.Bits {
return false
}
return bA.Elems[i/64]&(uint64(1)<<uint(i%64)) > 0
}
// NOTE: behavior is undefined if i >= bA.Bits
func (bA *BitArray) SetIndex(i int, v bool) bool {
if bA == nil {
return false
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
return bA.setIndex(i, v)
}
func (bA *BitArray) setIndex(i int, v bool) bool {
if i >= bA.Bits {
return false
}
if v {
bA.Elems[i/64] |= (uint64(1) << uint(i%64))
} else {
bA.Elems[i/64] &= ^(uint64(1) << uint(i%64))
}
return true
}
func (bA *BitArray) Copy() *BitArray {
if bA == nil {
return nil
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
return bA.copy()
}
func (bA *BitArray) copy() *BitArray {
c := make([]uint64, len(bA.Elems))
copy(c, bA.Elems)
return &BitArray{
Bits: bA.Bits,
Elems: c,
}
}
func (bA *BitArray) copyBits(bits int) *BitArray {
c := make([]uint64, (bits+63)/64)
copy(c, bA.Elems)
return &BitArray{
Bits: bits,
Elems: c,
}
}
// Returns a BitArray of larger bits size.
func (bA *BitArray) Or(o *BitArray) *BitArray {
if bA == nil {
o.Copy()
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
c := bA.copyBits(MaxInt(bA.Bits, o.Bits))
for i := 0; i < len(c.Elems); i++ {
c.Elems[i] |= o.Elems[i]
}
return c
}
// Returns a BitArray of smaller bit size.
func (bA *BitArray) And(o *BitArray) *BitArray {
if bA == nil {
return nil
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
return bA.and(o)
}
func (bA *BitArray) and(o *BitArray) *BitArray {
c := bA.copyBits(MinInt(bA.Bits, o.Bits))
for i := 0; i < len(c.Elems); i++ {
c.Elems[i] &= o.Elems[i]
}
return c
}
func (bA *BitArray) Not() *BitArray {
if bA == nil {
return nil // Degenerate
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
c := bA.copy()
for i := 0; i < len(c.Elems); i++ {
c.Elems[i] = ^c.Elems[i]
}
return c
}
func (bA *BitArray) Sub(o *BitArray) *BitArray {
if bA == nil {
return nil
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
if bA.Bits > o.Bits {
c := bA.copy()
for i := 0; i < len(o.Elems)-1; i++ {
c.Elems[i] &= ^c.Elems[i]
}
i := len(o.Elems) - 1
if i >= 0 {
for idx := i * 64; idx < o.Bits; idx++ {
// NOTE: each individual GetIndex() call to o is safe.
c.setIndex(idx, c.getIndex(idx) && !o.GetIndex(idx))
}
}
return c
} else {
return bA.and(o.Not()) // Note degenerate case where o == nil
}
}
func (bA *BitArray) IsFull() bool {
if bA == nil {
return true
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
// Check all elements except the last
for _, elem := range bA.Elems[:len(bA.Elems)-1] {
if (^elem) != 0 {
return false
}
}
// Check that the last element has (lastElemBits) 1's
lastElemBits := (bA.Bits+63)%64 + 1
lastElem := bA.Elems[len(bA.Elems)-1]
return (lastElem+1)&((uint64(1)<<uint(lastElemBits))-1) == 0
}
func (bA *BitArray) PickRandom() (int, bool) {
if bA == nil {
return 0, false
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
length := len(bA.Elems)
if length == 0 {
return 0, false
}
randElemStart := rand.Intn(length)
for i := 0; i < length; i++ {
elemIdx := ((i + randElemStart) % length)
if elemIdx < length-1 {
if bA.Elems[elemIdx] > 0 {
randBitStart := rand.Intn(64)
for j := 0; j < 64; j++ {
bitIdx := ((j + randBitStart) % 64)
if (bA.Elems[elemIdx] & (uint64(1) << uint(bitIdx))) > 0 {
return 64*elemIdx + bitIdx, true
}
}
PanicSanity("should not happen")
}
} else {
// Special case for last elem, to ignore straggler bits
elemBits := bA.Bits % 64
if elemBits == 0 {
elemBits = 64
}
randBitStart := rand.Intn(elemBits)
for j := 0; j < elemBits; j++ {
bitIdx := ((j + randBitStart) % elemBits)
if (bA.Elems[elemIdx] & (uint64(1) << uint(bitIdx))) > 0 {
return 64*elemIdx + bitIdx, true
}
}
}
}
return 0, false
}
func (bA *BitArray) String() string {
if bA == nil {
return "nil-BitArray"
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
return bA.stringIndented("")
}
func (bA *BitArray) StringIndented(indent string) string {
if bA == nil {
return "nil-BitArray"
}
bA.mtx.Lock()
defer bA.mtx.Unlock()
return bA.stringIndented(indent)
}
func (bA *BitArray) stringIndented(indent string) string {
lines := []string{}
bits := ""
for i := 0; i < bA.Bits; i++ {
if bA.getIndex(i) {
bits += "X"
} else {
bits += "_"
}
if i%100 == 99 {
lines = append(lines, bits)
bits = ""
}
if i%10 == 9 {
bits += " "
}
if i%50 == 49 {
bits += " "
}
}
if len(bits) > 0 {
lines = append(lines, bits)
}
return fmt.Sprintf("BA{%v:%v}", bA.Bits, strings.Join(lines, indent))
}

+ 120
- 0
bit_array_test.go View File

@ -0,0 +1,120 @@
package common
import (
"testing"
)
func randBitArray(bits int) (*BitArray, []byte) {
src := RandBytes((bits + 7) / 8)
bA := NewBitArray(bits)
for i := 0; i < len(src); i++ {
for j := 0; j < 8; j++ {
if i*8+j >= bits {
return bA, src
}
setBit := src[i]&(1<<uint(j)) > 0
bA.SetIndex(i*8+j, setBit)
}
}
return bA, src
}
func TestAnd(t *testing.T) {
bA1, _ := randBitArray(51)
bA2, _ := randBitArray(31)
bA3 := bA1.And(bA2)
if bA3.Bits != 31 {
t.Error("Expected min bits", bA3.Bits)
}
if len(bA3.Elems) != len(bA2.Elems) {
t.Error("Expected min elems length")
}
for i := 0; i < bA3.Bits; i++ {
expected := bA1.GetIndex(i) && bA2.GetIndex(i)
if bA3.GetIndex(i) != expected {
t.Error("Wrong bit from bA3", i, bA1.GetIndex(i), bA2.GetIndex(i), bA3.GetIndex(i))
}
}
}
func TestOr(t *testing.T) {
bA1, _ := randBitArray(51)
bA2, _ := randBitArray(31)
bA3 := bA1.Or(bA2)
if bA3.Bits != 51 {
t.Error("Expected max bits")
}
if len(bA3.Elems) != len(bA1.Elems) {
t.Error("Expected max elems length")
}
for i := 0; i < bA3.Bits; i++ {
expected := bA1.GetIndex(i) || bA2.GetIndex(i)
if bA3.GetIndex(i) != expected {
t.Error("Wrong bit from bA3", i, bA1.GetIndex(i), bA2.GetIndex(i), bA3.GetIndex(i))
}
}
}
func TestSub1(t *testing.T) {
bA1, _ := randBitArray(31)
bA2, _ := randBitArray(51)
bA3 := bA1.Sub(bA2)
if bA3.Bits != bA1.Bits {
t.Error("Expected bA1 bits")
}
if len(bA3.Elems) != len(bA1.Elems) {
t.Error("Expected bA1 elems length")
}
for i := 0; i < bA3.Bits; i++ {
expected := bA1.GetIndex(i)
if bA2.GetIndex(i) {
expected = false
}
if bA3.GetIndex(i) != expected {
t.Error("Wrong bit from bA3", i, bA1.GetIndex(i), bA2.GetIndex(i), bA3.GetIndex(i))
}
}
}
func TestSub2(t *testing.T) {
bA1, _ := randBitArray(51)
bA2, _ := randBitArray(31)
bA3 := bA1.Sub(bA2)
if bA3.Bits != bA1.Bits {
t.Error("Expected bA1 bits")
}
if len(bA3.Elems) != len(bA1.Elems) {
t.Error("Expected bA1 elems length")
}
for i := 0; i < bA3.Bits; i++ {
expected := bA1.GetIndex(i)
if i < bA2.Bits && bA2.GetIndex(i) {
expected = false
}
if bA3.GetIndex(i) != expected {
t.Error("Wrong bit from bA3")
}
}
}
func TestPickRandom(t *testing.T) {
for idx := 0; idx < 123; idx++ {
bA1 := NewBitArray(123)
bA1.SetIndex(idx, true)
index, ok := bA1.PickRandom()
if !ok {
t.Fatal("Expected to pick element but got none")
}
if index != idx {
t.Fatalf("Expected to pick element at %v but got wrong index", idx)
}
}
}

+ 44
- 0
byteslice.go View File

@ -0,0 +1,44 @@
package common
import (
"bytes"
)
func Fingerprint(slice []byte) []byte {
fingerprint := make([]byte, 6)
copy(fingerprint, slice)
return fingerprint
}
func IsZeros(slice []byte) bool {
for _, byt := range slice {
if byt != byte(0) {
return false
}
}
return true
}
func RightPadBytes(slice []byte, l int) []byte {
if l < len(slice) {
return slice
}
padded := make([]byte, l)
copy(padded[0:len(slice)], slice)
return padded
}
func LeftPadBytes(slice []byte, l int) []byte {
if l < len(slice) {
return slice
}
padded := make([]byte, l)
copy(padded[l-len(slice):], slice)
return padded
}
func TrimmedString(b []byte) string {
trimSet := string([]byte{0})
return string(bytes.TrimLeft(b, trimSet))
}

+ 62
- 0
cmap.go View File

@ -0,0 +1,62 @@
package common
import "sync"
// CMap is a goroutine-safe map
type CMap struct {
m map[string]interface{}
l sync.Mutex
}
func NewCMap() *CMap {
return &CMap{
m: make(map[string]interface{}, 0),
}
}
func (cm *CMap) Set(key string, value interface{}) {
cm.l.Lock()
defer cm.l.Unlock()
cm.m[key] = value
}
func (cm *CMap) Get(key string) interface{} {
cm.l.Lock()
defer cm.l.Unlock()
return cm.m[key]
}
func (cm *CMap) Has(key string) bool {
cm.l.Lock()
defer cm.l.Unlock()
_, ok := cm.m[key]
return ok
}
func (cm *CMap) Delete(key string) {
cm.l.Lock()
defer cm.l.Unlock()
delete(cm.m, key)
}
func (cm *CMap) Size() int {
cm.l.Lock()
defer cm.l.Unlock()
return len(cm.m)
}
func (cm *CMap) Clear() {
cm.l.Lock()
defer cm.l.Unlock()
cm.m = make(map[string]interface{}, 0)
}
func (cm *CMap) Values() []interface{} {
cm.l.Lock()
defer cm.l.Unlock()
items := []interface{}{}
for _, v := range cm.m {
items = append(items, v)
}
return items
}

+ 84
- 0
colors.go View File

@ -0,0 +1,84 @@
package common
import (
"fmt"
"strings"
)
const (
ANSIReset = "\x1b[0m"
ANSIBright = "\x1b[1m"
ANSIDim = "\x1b[2m"
ANSIUnderscore = "\x1b[4m"
ANSIBlink = "\x1b[5m"
ANSIReverse = "\x1b[7m"
ANSIHidden = "\x1b[8m"
ANSIFgBlack = "\x1b[30m"
ANSIFgRed = "\x1b[31m"
ANSIFgGreen = "\x1b[32m"
ANSIFgYellow = "\x1b[33m"
ANSIFgBlue = "\x1b[34m"
ANSIFgMagenta = "\x1b[35m"
ANSIFgCyan = "\x1b[36m"
ANSIFgWhite = "\x1b[37m"
ANSIBgBlack = "\x1b[40m"
ANSIBgRed = "\x1b[41m"
ANSIBgGreen = "\x1b[42m"
ANSIBgYellow = "\x1b[43m"
ANSIBgBlue = "\x1b[44m"
ANSIBgMagenta = "\x1b[45m"
ANSIBgCyan = "\x1b[46m"
ANSIBgWhite = "\x1b[47m"
)
// color the string s with color 'color'
// unless s is already colored
func treat(s string, color string) string {
if len(s) > 2 && s[:2] == "\x1b[" {
return s
} else {
return color + s + ANSIReset
}
}
func treatAll(color string, args ...interface{}) string {
var parts []string
for _, arg := range args {
parts = append(parts, treat(fmt.Sprintf("%v", arg), color))
}
return strings.Join(parts, "")
}
func Black(args ...interface{}) string {
return treatAll(ANSIFgBlack, args...)
}
func Red(args ...interface{}) string {
return treatAll(ANSIFgRed, args...)
}
func Green(args ...interface{}) string {
return treatAll(ANSIFgGreen, args...)
}
func Yellow(args ...interface{}) string {
return treatAll(ANSIFgYellow, args...)
}
func Blue(args ...interface{}) string {
return treatAll(ANSIFgBlue, args...)
}
func Magenta(args ...interface{}) string {
return treatAll(ANSIFgMagenta, args...)
}
func Cyan(args ...interface{}) string {
return treatAll(ANSIFgCyan, args...)
}
func White(args ...interface{}) string {
return treatAll(ANSIFgWhite, args...)
}

+ 45
- 0
errors.go View File

@ -0,0 +1,45 @@
package common
import (
"fmt"
)
type StackError struct {
Err interface{}
Stack []byte
}
func (se StackError) String() string {
return fmt.Sprintf("Error: %v\nStack: %s", se.Err, se.Stack)
}
func (se StackError) Error() string {
return se.String()
}
//--------------------------------------------------------------------------------------------------
// panic wrappers
// A panic resulting from a sanity check means there is a programmer error
// and some gaurantee is not satisfied.
func PanicSanity(v interface{}) {
panic(Fmt("Paniced on a Sanity Check: %v", v))
}
// A panic here means something has gone horribly wrong, in the form of data corruption or
// failure of the operating system. In a correct/healthy system, these should never fire.
// If they do, it's indicative of a much more serious problem.
func PanicCrisis(v interface{}) {
panic(Fmt("Paniced on a Crisis: %v", v))
}
// Indicates a failure of consensus. Someone was malicious or something has
// gone horribly wrong. These should really boot us into an "emergency-recover" mode
func PanicConsensus(v interface{}) {
panic(Fmt("Paniced on a Consensus Failure: %v", v))
}
// For those times when we're not sure if we should panic
func PanicQ(v interface{}) {
panic(Fmt("Paniced questionably: %v", v))
}

+ 103
- 0
heap.go View File

@ -0,0 +1,103 @@
package common
import (
"container/heap"
)
type Comparable interface {
Less(o interface{}) bool
}
//-----------------------------------------------------------------------------
/*
Example usage:
h := NewHeap()
h.Push(String("msg1"), 1)
h.Push(String("msg3"), 3)
h.Push(String("msg2"), 2)
fmt.Println(h.Pop())
fmt.Println(h.Pop())
fmt.Println(h.Pop())
*/
type Heap struct {
pq priorityQueue
}
func NewHeap() *Heap {
return &Heap{pq: make([]*pqItem, 0)}
}
func (h *Heap) Len() int64 {
return int64(len(h.pq))
}
func (h *Heap) Push(value interface{}, priority Comparable) {
heap.Push(&h.pq, &pqItem{value: value, priority: priority})
}
func (h *Heap) Peek() interface{} {
if len(h.pq) == 0 {
return nil
}
return h.pq[0].value
}
func (h *Heap) Update(value interface{}, priority Comparable) {
h.pq.Update(h.pq[0], value, priority)
}
func (h *Heap) Pop() interface{} {
item := heap.Pop(&h.pq).(*pqItem)
return item.value
}
//-----------------------------------------------------------------------------
///////////////////////
// From: http://golang.org/pkg/container/heap/#example__priorityQueue
type pqItem struct {
value interface{}
priority Comparable
index int
}
type priorityQueue []*pqItem
func (pq priorityQueue) Len() int { return len(pq) }
func (pq priorityQueue) Less(i, j int) bool {
return pq[i].priority.Less(pq[j].priority)
}
func (pq priorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *priorityQueue) Push(x interface{}) {
n := len(*pq)
item := x.(*pqItem)
item.index = n
*pq = append(*pq, item)
}
func (pq *priorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
item.index = -1 // for safety
*pq = old[0 : n-1]
return item
}
func (pq *priorityQueue) Update(item *pqItem, value interface{}, priority Comparable) {
item.value = value
item.priority = priority
heap.Fix(pq, item.index)
}

+ 55
- 0
int.go View File

@ -0,0 +1,55 @@
package common
import (
"encoding/binary"
"sort"
)
// Sort for []uint64
type Uint64Slice []uint64
func (p Uint64Slice) Len() int { return len(p) }
func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p Uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p Uint64Slice) Sort() { sort.Sort(p) }
func SearchUint64s(a []uint64, x uint64) int {
return sort.Search(len(a), func(i int) bool { return a[i] >= x })
}
func (p Uint64Slice) Search(x uint64) int { return SearchUint64s(p, x) }
//-----------------------------------------------------------------------------
func PutUint64LE(dest []byte, i uint64) {
binary.LittleEndian.PutUint64(dest, i)
}
func GetUint64LE(src []byte) uint64 {
return binary.LittleEndian.Uint64(src)
}
func PutUint64BE(dest []byte, i uint64) {
binary.BigEndian.PutUint64(dest, i)
}
func GetUint64BE(src []byte) uint64 {
return binary.BigEndian.Uint64(src)
}
func PutInt64LE(dest []byte, i int64) {
binary.LittleEndian.PutUint64(dest, uint64(i))
}
func GetInt64LE(src []byte) int64 {
return int64(binary.LittleEndian.Uint64(src))
}
func PutInt64BE(dest []byte, i int64) {
binary.BigEndian.PutUint64(dest, uint64(i))
}
func GetInt64BE(src []byte) int64 {
return int64(binary.BigEndian.Uint64(src))
}

+ 75
- 0
io.go View File

@ -0,0 +1,75 @@
package common
import (
"bytes"
"errors"
"io"
)
type PrefixedReader struct {
Prefix []byte
reader io.Reader
}
func NewPrefixedReader(prefix []byte, reader io.Reader) *PrefixedReader {
return &PrefixedReader{prefix, reader}
}
func (pr *PrefixedReader) Read(p []byte) (n int, err error) {
if len(pr.Prefix) > 0 {
read := copy(p, pr.Prefix)
pr.Prefix = pr.Prefix[read:]
return read, nil
} else {
return pr.reader.Read(p)
}
}
// NOTE: Not goroutine safe
type BufferCloser struct {
bytes.Buffer
Closed bool
}
func NewBufferCloser(buf []byte) *BufferCloser {
return &BufferCloser{
*bytes.NewBuffer(buf),
false,
}
}
func (bc *BufferCloser) Close() error {
if bc.Closed {
return errors.New("BufferCloser already closed")
}
bc.Closed = true
return nil
}
func (bc *BufferCloser) Write(p []byte) (n int, err error) {
if bc.Closed {
return 0, errors.New("Cannot write to closed BufferCloser")
}
return bc.Buffer.Write(p)
}
func (bc *BufferCloser) WriteByte(c byte) error {
if bc.Closed {
return errors.New("Cannot write to closed BufferCloser")
}
return bc.Buffer.WriteByte(c)
}
func (bc *BufferCloser) WriteRune(r rune) (n int, err error) {
if bc.Closed {
return 0, errors.New("Cannot write to closed BufferCloser")
}
return bc.Buffer.WriteRune(r)
}
func (bc *BufferCloser) WriteString(s string) (n int, err error) {
if bc.Closed {
return 0, errors.New("Cannot write to closed BufferCloser")
}
return bc.Buffer.WriteString(s)
}

+ 157
- 0
math.go View File

@ -0,0 +1,157 @@
package common
func MaxInt8(a, b int8) int8 {
if a > b {
return a
}
return b
}
func MaxUint8(a, b uint8) uint8 {
if a > b {
return a
}
return b
}
func MaxInt16(a, b int16) int16 {
if a > b {
return a
}
return b
}
func MaxUint16(a, b uint16) uint16 {
if a > b {
return a
}
return b
}
func MaxInt32(a, b int32) int32 {
if a > b {
return a
}
return b
}
func MaxUint32(a, b uint32) uint32 {
if a > b {
return a
}
return b
}
func MaxInt64(a, b int64) int64 {
if a > b {
return a
}
return b
}
func MaxUint64(a, b uint64) uint64 {
if a > b {
return a
}
return b
}
func MaxInt(a, b int) int {
if a > b {
return a
}
return b
}
func MaxUint(a, b uint) uint {
if a > b {
return a
}
return b
}
//-----------------------------------------------------------------------------
func MinInt8(a, b int8) int8 {
if a < b {
return a
}
return b
}
func MinUint8(a, b uint8) uint8 {
if a < b {
return a
}
return b
}
func MinInt16(a, b int16) int16 {
if a < b {
return a
}
return b
}
func MinUint16(a, b uint16) uint16 {
if a < b {
return a
}
return b
}
func MinInt32(a, b int32) int32 {
if a < b {
return a
}
return b
}
func MinUint32(a, b uint32) uint32 {
if a < b {
return a
}
return b
}
func MinInt64(a, b int64) int64 {
if a < b {
return a
}
return b
}
func MinUint64(a, b uint64) uint64 {
if a < b {
return a
}
return b
}
func MinInt(a, b int) int {
if a < b {
return a
}
return b
}
func MinUint(a, b uint) uint {
if a < b {
return a
}
return b
}
//-----------------------------------------------------------------------------
func ExpUint64(a, b uint64) uint64 {
accum := uint64(1)
for b > 0 {
if b&1 == 1 {
accum *= a
}
a *= a
b >>= 1
}
return accum
}

+ 225
- 0
os.go View File

@ -0,0 +1,225 @@
package common
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"os/signal"
"strings"
"sync"
"time"
)
var (
GoPath = os.Getenv("GOPATH")
)
func TrapSignal(cb func()) {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, os.Kill)
go func() {
for sig := range c {
fmt.Printf("captured %v, exiting...\n", sig)
if cb != nil {
cb()
}
os.Exit(1)
}
}()
select {}
}
func Exit(s string) {
fmt.Printf(s + "\n")
os.Exit(1)
}
func EnsureDir(dir string) error {
if _, err := os.Stat(dir); os.IsNotExist(err) {
err := os.MkdirAll(dir, 0700)
if err != nil {
return fmt.Errorf("Could not create directory %v. %v", dir, err)
}
}
return nil
}
func FileExists(filePath string) bool {
_, err := os.Stat(filePath)
return !os.IsNotExist(err)
}
func ReadFile(filePath string) ([]byte, error) {
return ioutil.ReadFile(filePath)
}
func MustReadFile(filePath string) []byte {
fileBytes, err := ioutil.ReadFile(filePath)
if err != nil {
Exit(Fmt("MustReadFile failed: %v", err))
return nil
}
return fileBytes
}
func WriteFile(filePath string, contents []byte) error {
err := ioutil.WriteFile(filePath, contents, 0600)
if err != nil {
return err
}
// fmt.Printf("File written to %v.\n", filePath)
return nil
}
func MustWriteFile(filePath string, contents []byte) {
err := WriteFile(filePath, contents)
if err != nil {
Exit(Fmt("MustWriteFile failed: %v", err))
}
}
// Writes to newBytes to filePath.
// Guaranteed not to lose *both* oldBytes and newBytes,
// (assuming that the OS is perfect)
func WriteFileAtomic(filePath string, newBytes []byte) error {
// If a file already exists there, copy to filePath+".bak" (overwrite anything)
if _, err := os.Stat(filePath); !os.IsNotExist(err) {
fileBytes, err := ioutil.ReadFile(filePath)
if err != nil {
return fmt.Errorf("Could not read file %v. %v", filePath, err)
}
err = ioutil.WriteFile(filePath+".bak", fileBytes, 0600)
if err != nil {
return fmt.Errorf("Could not write file %v. %v", filePath+".bak", err)
}
}
// Write newBytes to filePath.new
err := ioutil.WriteFile(filePath+".new", newBytes, 0600)
if err != nil {
return fmt.Errorf("Could not write file %v. %v", filePath+".new", err)
}
// Move filePath.new to filePath
err = os.Rename(filePath+".new", filePath)
return err
}
//--------------------------------------------------------------------------------
/* AutoFile usage
// Create/Append to ./autofile_test
af, err := OpenAutoFile("autofile_test")
if err != nil {
panic(err)
}
// Stream of writes.
// During this time, the file may be moved e.g. by logRotate.
for i := 0; i < 60; i++ {
af.Write([]byte(Fmt("LOOP(%v)", i)))
time.Sleep(time.Second)
}
// Close the AutoFile
err = af.Close()
if err != nil {
panic(err)
}
*/
const autoFileOpenDuration = 1000 * time.Millisecond
// Automatically closes and re-opens file for writing.
// This is useful for using a log file with the logrotate tool.
type AutoFile struct {
Path string
ticker *time.Ticker
mtx sync.Mutex
file *os.File
}
func OpenAutoFile(path string) (af *AutoFile, err error) {
af = &AutoFile{
Path: path,
ticker: time.NewTicker(autoFileOpenDuration),
}
if err = af.openFile(); err != nil {
return
}
go af.processTicks()
return
}
func (af *AutoFile) Close() error {
af.ticker.Stop()
af.mtx.Lock()
err := af.closeFile()
af.mtx.Unlock()
return err
}
func (af *AutoFile) processTicks() {
for {
_, ok := <-af.ticker.C
if !ok {
return // Done.
}
af.mtx.Lock()
af.closeFile()
af.mtx.Unlock()
}
}
func (af *AutoFile) closeFile() (err error) {
file := af.file
if file == nil {
return nil
}
af.file = nil
return file.Close()
}
func (af *AutoFile) Write(b []byte) (n int, err error) {
af.mtx.Lock()
defer af.mtx.Unlock()
if af.file == nil {
if err = af.openFile(); err != nil {
return
}
}
return af.file.Write(b)
}
func (af *AutoFile) openFile() error {
file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
return err
}
af.file = file
return nil
}
func Tempfile(prefix string) (*os.File, string) {
file, err := ioutil.TempFile("", prefix)
if err != nil {
PanicCrisis(err)
}
return file, file.Name()
}
func Prompt(prompt string, defaultValue string) (string, error) {
fmt.Print(prompt)
reader := bufio.NewReader(os.Stdin)
line, err := reader.ReadString('\n')
if err != nil {
return defaultValue, err
} else {
line = strings.TrimSpace(line)
if line == "" {
return defaultValue, nil
}
return line, nil
}
}

+ 145
- 0
random.go View File

@ -0,0 +1,145 @@
package common
import (
crand "crypto/rand"
"encoding/hex"
"math/rand"
"time"
)
const (
strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters
)
func init() {
// Seed math/rand with "secure" int64
b := CRandBytes(8)
var seed uint64
for i := 0; i < 8; i++ {
seed |= uint64(b[i])
seed <<= 8
}
rand.Seed(int64(seed))
}
// Constructs an alphanumeric string of given length.
func RandStr(length int) string {
chars := []byte{}
MAIN_LOOP:
for {
val := rand.Int63()
for i := 0; i < 10; i++ {
v := int(val & 0x3f) // rightmost 6 bits
if v >= 62 { // only 62 characters in strChars
val >>= 6
continue
} else {
chars = append(chars, strChars[v])
if len(chars) == length {
break MAIN_LOOP
}
val >>= 6
}
}
}
return string(chars)
}
func RandUint16() uint16 {
return uint16(rand.Uint32() & (1<<16 - 1))
}
func RandUint32() uint32 {
return rand.Uint32()
}
func RandUint64() uint64 {
return uint64(rand.Uint32())<<32 + uint64(rand.Uint32())
}
func RandUint() uint {
return uint(rand.Int())
}
func RandInt16() int16 {
return int16(rand.Uint32() & (1<<16 - 1))
}
func RandInt32() int32 {
return int32(rand.Uint32())
}
func RandInt64() int64 {
return int64(rand.Uint32())<<32 + int64(rand.Uint32())
}
func RandInt() int {
return rand.Int()
}
// Distributed pseudo-exponentially to test for various cases
func RandUint16Exp() uint16 {
bits := rand.Uint32() % 16
if bits == 0 {
return 0
}
n := uint16(1 << (bits - 1))
n += uint16(rand.Int31()) & ((1 << (bits - 1)) - 1)
return n
}
// Distributed pseudo-exponentially to test for various cases
func RandUint32Exp() uint32 {
bits := rand.Uint32() % 32
if bits == 0 {
return 0
}
n := uint32(1 << (bits - 1))
n += uint32(rand.Int31()) & ((1 << (bits - 1)) - 1)
return n
}
// Distributed pseudo-exponentially to test for various cases
func RandUint64Exp() uint64 {
bits := rand.Uint32() % 64
if bits == 0 {
return 0
}
n := uint64(1 << (bits - 1))
n += uint64(rand.Int63()) & ((1 << (bits - 1)) - 1)
return n
}
func RandFloat32() float32 {
return rand.Float32()
}
func RandTime() time.Time {
return time.Unix(int64(RandUint64Exp()), 0)
}
func RandBytes(n int) []byte {
bs := make([]byte, n)
for i := 0; i < n; i++ {
bs[i] = byte(rand.Intn(256))
}
return bs
}
//-----------------------------------------------------------------------------
// CRand* methods are crypto safe.
func CRandBytes(numBytes int) []byte {
b := make([]byte, numBytes)
_, err := crand.Read(b)
if err != nil {
PanicCrisis(err)
}
return b
}
// RandHex(24) gives 96 bits of randomness, strong enough for most purposes.
func CRandHex(numDigits int) string {
return hex.EncodeToString(CRandBytes(numDigits / 2))
}

+ 72
- 0
repeat_timer.go View File

@ -0,0 +1,72 @@
package common
import "time"
import "sync"
/*
RepeatTimer repeatedly sends a struct{}{} to .Ch after each "dur" period.
It's good for keeping connections alive.
A RepeatTimer must be Stop()'d or it will keep a goroutine alive.
*/
type RepeatTimer struct {
Ch chan time.Time
mtx sync.Mutex
name string
ticker *time.Ticker
quit chan struct{}
dur time.Duration
}
func NewRepeatTimer(name string, dur time.Duration) *RepeatTimer {
var t = &RepeatTimer{
Ch: make(chan time.Time),
ticker: time.NewTicker(dur),
quit: make(chan struct{}),
name: name,
dur: dur,
}
go t.fireRoutine(t.ticker)
return t
}
func (t *RepeatTimer) fireRoutine(ticker *time.Ticker) {
for {
select {
case t_ := <-ticker.C:
t.Ch <- t_
case <-t.quit:
return
}
}
}
// Wait the duration again before firing.
func (t *RepeatTimer) Reset() {
t.Stop()
t.mtx.Lock() // Lock
defer t.mtx.Unlock()
t.ticker = time.NewTicker(t.dur)
t.quit = make(chan struct{})
go t.fireRoutine(t.ticker)
}
// For ease of .Stop()'ing services before .Start()'ing them,
// we ignore .Stop()'s on nil RepeatTimers.
func (t *RepeatTimer) Stop() bool {
if t == nil {
return false
}
t.mtx.Lock() // Lock
defer t.mtx.Unlock()
exists := t.ticker != nil
if exists {
t.ticker.Stop()
t.ticker = nil
close(t.quit)
}
return exists
}

+ 154
- 0
service.go View File

@ -0,0 +1,154 @@
/*
Classical-inheritance-style service declarations.
Services can be started, then stopped.
Users can override the OnStart/OnStop methods.
These methods are guaranteed to be called at most once.
Caller must ensure that Start() and Stop() are not called concurrently.
It is ok to call Stop() without calling Start() first.
Services cannot be re-started unless otherwise documented.
Typical usage:
type FooService struct {
BaseService
// private fields
}
func NewFooService() *FooService {
fs := &FooService{
// init
}
fs.BaseService = *NewBaseService(log, "FooService", fs)
return fs
}
func (fs *FooService) OnStart() error {
fs.BaseService.OnStart() // Always call the overridden method.
// initialize private fields
// start subroutines, etc.
}
func (fs *FooService) OnStop() error {
fs.BaseService.OnStop() // Always call the overridden method.
// close/destroy private fields
// stop subroutines, etc.
}
*/
package common
import "sync/atomic"
import "github.com/tendermint/tendermint/Godeps/_workspace/src/github.com/tendermint/log15"
type Service interface {
Start() (bool, error)
OnStart() error
Stop() bool
OnStop()
IsRunning() bool
String() string
}
type BaseService struct {
log log15.Logger
name string
started uint32 // atomic
stopped uint32 // atomic
// The "subclass" of BaseService
impl Service
}
func NewBaseService(log log15.Logger, name string, impl Service) *BaseService {
return &BaseService{
log: log,
name: name,
impl: impl,
}
}
// Implements Servce
func (bs *BaseService) Start() (bool, error) {
if atomic.CompareAndSwapUint32(&bs.started, 0, 1) {
if atomic.LoadUint32(&bs.stopped) == 1 {
if bs.log != nil {
bs.log.Warn(Fmt("Not starting %v -- already stopped", bs.name), "impl", bs.impl)
}
return false, nil
} else {
if bs.log != nil {
bs.log.Notice(Fmt("Starting %v", bs.name), "impl", bs.impl)
}
}
err := bs.impl.OnStart()
return true, err
} else {
if bs.log != nil {
bs.log.Info(Fmt("Not starting %v -- already started", bs.name), "impl", bs.impl)
}
return false, nil
}
}
// Implements Service
func (bs *BaseService) OnStart() error { return nil }
// Implements Service
func (bs *BaseService) Stop() bool {
if atomic.CompareAndSwapUint32(&bs.stopped, 0, 1) {
if bs.log != nil {
bs.log.Notice(Fmt("Stopping %v", bs.name), "impl", bs.impl)
}
bs.impl.OnStop()
return true
} else {
if bs.log != nil {
bs.log.Notice(Fmt("Not stopping %v", bs.name), "impl", bs.impl)
}
return false
}
}
// Implements Service
func (bs *BaseService) OnStop() {}
// Implements Service
func (bs *BaseService) IsRunning() bool {
return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
}
// Implements Servce
func (bs *BaseService) String() string {
return bs.name
}
//----------------------------------------
type QuitService struct {
BaseService
Quit chan struct{}
}
func NewQuitService(log log15.Logger, name string, impl Service) *QuitService {
return &QuitService{
BaseService: *NewBaseService(log, name, impl),
Quit: nil,
}
}
// NOTE: when overriding OnStart, must call .QuitService.OnStart().
func (qs *QuitService) OnStart() error {
qs.Quit = make(chan struct{})
return nil
}
// NOTE: when overriding OnStop, must call .QuitService.OnStop().
func (qs *QuitService) OnStop() {
if qs.Quit != nil {
close(qs.Quit)
}
}

+ 24
- 0
string.go View File

@ -0,0 +1,24 @@
package common
import (
"fmt"
"strings"
)
var Fmt = fmt.Sprintf
func RightPadString(s string, totalLength int) string {
remaining := totalLength - len(s)
if remaining > 0 {
s = s + strings.Repeat(" ", remaining)
}
return s
}
func LeftPadString(s string, totalLength int) string {
remaining := totalLength - len(s)
if remaining > 0 {
s = strings.Repeat(" ", remaining) + s
}
return s
}

+ 14
- 0
test/assert.go View File

@ -0,0 +1,14 @@
package test
import (
"testing"
)
func AssertPanics(t *testing.T, msg string, f func()) {
defer func() {
if err := recover(); err == nil {
t.Errorf("Should have panic'd, but didn't: %v", msg)
}
}()
f()
}

+ 28
- 0
test/mutate.go View File

@ -0,0 +1,28 @@
package test
import (
. "github.com/tendermint/tendermint/common"
)
// Contract: !bytes.Equal(input, output) && len(input) >= len(output)
func MutateByteSlice(bytez []byte) []byte {
// If bytez is empty, panic
if len(bytez) == 0 {
panic("Cannot mutate an empty bytez")
}
// Copy bytez
mBytez := make([]byte, len(bytez))
copy(mBytez, bytez)
bytez = mBytez
// Try a random mutation
switch RandInt() % 2 {
case 0: // Mutate a single byte
bytez[RandInt()%len(bytez)] += byte(RandInt()%255 + 1)
case 1: // Remove an arbitrary byte
pos := RandInt() % len(bytez)
bytez = append(bytez[:pos], bytez[pos+1:]...)
}
return bytez
}

+ 57
- 0
throttle_timer.go View File

@ -0,0 +1,57 @@
package common
import (
"sync/atomic"
"time"
)
/*
ThrottleTimer fires an event at most "dur" after each .Set() call.
If a short burst of .Set() calls happens, ThrottleTimer fires once.
If a long continuous burst of .Set() calls happens, ThrottleTimer fires
at most once every "dur".
*/
type ThrottleTimer struct {
Name string
Ch chan struct{}
quit chan struct{}
dur time.Duration
timer *time.Timer
isSet uint32
}
func NewThrottleTimer(name string, dur time.Duration) *ThrottleTimer {
var ch = make(chan struct{})
var quit = make(chan struct{})
var t = &ThrottleTimer{Name: name, Ch: ch, dur: dur, quit: quit}
t.timer = time.AfterFunc(dur, t.fireRoutine)
t.timer.Stop()
return t
}
func (t *ThrottleTimer) fireRoutine() {
select {
case t.Ch <- struct{}{}:
atomic.StoreUint32(&t.isSet, 0)
case <-t.quit:
// do nothing
default:
t.timer.Reset(t.dur)
}
}
func (t *ThrottleTimer) Set() {
if atomic.CompareAndSwapUint32(&t.isSet, 0, 1) {
t.timer.Reset(t.dur)
}
}
// For ease of .Stop()'ing services before .Start()'ing them,
// we ignore .Stop()'s on nil ThrottleTimers
func (t *ThrottleTimer) Stop() bool {
if t == nil {
return false
}
close(t.quit)
return t.timer.Stop()
}

+ 91
- 0
word.go View File

@ -0,0 +1,91 @@
package common
import (
"bytes"
"sort"
)
var (
Zero256 = Word256{0}
One256 = Word256{1}
)
type Word256 [32]byte
func (w Word256) String() string { return string(w[:]) }
func (w Word256) TrimmedString() string { return TrimmedString(w.Bytes()) }
func (w Word256) Copy() Word256 { return w }
func (w Word256) Bytes() []byte { return w[:] } // copied.
func (w Word256) Prefix(n int) []byte { return w[:n] }
func (w Word256) Postfix(n int) []byte { return w[32-n:] }
func (w Word256) IsZero() bool {
accum := byte(0)
for _, byt := range w {
accum |= byt
}
return accum == 0
}
func (w Word256) Compare(other Word256) int {
return bytes.Compare(w[:], other[:])
}
func Uint64ToWord256(i uint64) Word256 {
buf := [8]byte{}
PutUint64BE(buf[:], i)
return LeftPadWord256(buf[:])
}
func Int64ToWord256(i int64) Word256 {
buf := [8]byte{}
PutInt64BE(buf[:], i)
return LeftPadWord256(buf[:])
}
func RightPadWord256(bz []byte) (word Word256) {
copy(word[:], bz)
return
}
func LeftPadWord256(bz []byte) (word Word256) {
copy(word[32-len(bz):], bz)
return
}
func Uint64FromWord256(word Word256) uint64 {
buf := word.Postfix(8)
return GetUint64BE(buf)
}
func Int64FromWord256(word Word256) int64 {
buf := word.Postfix(8)
return GetInt64BE(buf)
}
//-------------------------------------
type Tuple256 struct {
First Word256
Second Word256
}
func (tuple Tuple256) Compare(other Tuple256) int {
firstCompare := tuple.First.Compare(other.First)
if firstCompare == 0 {
return tuple.Second.Compare(other.Second)
} else {
return firstCompare
}
}
func Tuple256Split(t Tuple256) (Word256, Word256) {
return t.First, t.Second
}
type Tuple256Slice []Tuple256
func (p Tuple256Slice) Len() int { return len(p) }
func (p Tuple256Slice) Less(i, j int) bool {
return p[i].Compare(p[j]) < 0
}
func (p Tuple256Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p Tuple256Slice) Sort() { sort.Sort(p) }

Loading…
Cancel
Save