mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-16 14:41:21 +00:00
First commit
This commit is contained in:
5
array.go
Normal file
5
array.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
func Arr(items ...interface{}) []interface{} {
|
||||||
|
return items
|
||||||
|
}
|
15
async.go
Normal file
15
async.go
Normal 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
bit_array.go
Normal file
275
bit_array.go
Normal 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
bit_array_test.go
Normal file
120
bit_array_test.go
Normal 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
byteslice.go
Normal file
44
byteslice.go
Normal 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
cmap.go
Normal file
62
cmap.go
Normal 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
colors.go
Normal file
84
colors.go
Normal 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
errors.go
Normal file
45
errors.go
Normal 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
heap.go
Normal file
103
heap.go
Normal 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
int.go
Normal file
55
int.go
Normal 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
io.go
Normal file
75
io.go
Normal 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
math.go
Normal file
157
math.go
Normal 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
os.go
Normal file
225
os.go
Normal 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
random.go
Normal file
145
random.go
Normal 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
repeat_timer.go
Normal file
72
repeat_timer.go
Normal 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
service.go
Normal file
154
service.go
Normal 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
string.go
Normal file
24
string.go
Normal 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
test/assert.go
Normal file
14
test/assert.go
Normal 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
test/mutate.go
Normal file
28
test/mutate.go
Normal 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
throttle_timer.go
Normal file
57
throttle_timer.go
Normal 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
word.go
Normal file
91
word.go
Normal 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) }
|
Reference in New Issue
Block a user