mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-18 23:51:21 +00:00
stack trace in p2p/connection panics, bitArray fix
This commit is contained in:
@ -1,8 +1,8 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -10,40 +10,43 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Not goroutine safe
|
// Not goroutine safe
|
||||||
type BitArray []uint64
|
type BitArray struct {
|
||||||
|
bits uint
|
||||||
|
elems []uint64
|
||||||
|
}
|
||||||
|
|
||||||
func NewBitArray(length uint) BitArray {
|
func NewBitArray(bits uint) BitArray {
|
||||||
return BitArray(make([]uint64, (length+63)/64))
|
return BitArray{bits, make([]uint64, (bits+63)/64)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadBitArray(r io.Reader, n *int64, err *error) BitArray {
|
func ReadBitArray(r io.Reader, n *int64, err *error) BitArray {
|
||||||
lengthTotal := ReadUInt32(r, n, err)
|
bits := ReadUVarInt(r, n, err)
|
||||||
lengthWritten := ReadUInt32(r, n, err)
|
elemsWritten := ReadUVarInt(r, n, err)
|
||||||
if *err != nil {
|
if *err != nil {
|
||||||
return nil
|
return BitArray{}
|
||||||
}
|
}
|
||||||
buf := make([]uint64, int(lengthTotal))
|
bA := NewBitArray(bits)
|
||||||
for i := uint32(0); i < lengthWritten; i++ {
|
for i := uint(0); i < elemsWritten; i++ {
|
||||||
buf[i] = ReadUInt64(r, n, err)
|
bA.elems[i] = ReadUInt64(r, n, err)
|
||||||
if err != nil {
|
if *err != nil {
|
||||||
return nil
|
return BitArray{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return BitArray(buf)
|
return bA
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bA BitArray) WriteTo(w io.Writer) (n int64, err error) {
|
func (bA BitArray) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
// Count the last element > 0.
|
// Count the last element > 0.
|
||||||
lastNonzeroIndex := -1
|
elemsToWrite := 0
|
||||||
for i, elem := range bA {
|
for i, elem := range bA.elems {
|
||||||
if elem > 0 {
|
if elem > 0 {
|
||||||
lastNonzeroIndex = i
|
elemsToWrite = i + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WriteUInt32(w, uint32(len(bA)), &n, &err)
|
WriteUVarInt(w, bA.bits, &n, &err)
|
||||||
WriteUInt32(w, uint32(lastNonzeroIndex+1), &n, &err)
|
WriteUVarInt(w, uint(elemsToWrite), &n, &err)
|
||||||
for i, elem := range bA {
|
for i, elem := range bA.elems {
|
||||||
if i > lastNonzeroIndex {
|
if i >= elemsToWrite {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
WriteUInt64(w, elem, &n, &err)
|
WriteUInt64(w, elem, &n, &err)
|
||||||
@ -51,44 +54,54 @@ func (bA BitArray) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: behavior is undefined if i >= bA.bits
|
||||||
func (bA BitArray) GetIndex(i uint) bool {
|
func (bA BitArray) GetIndex(i uint) bool {
|
||||||
return bA[i/64]&uint64(1<<(i%64)) > 0
|
return bA.elems[i/64]&uint64(1<<(i%64)) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: behavior is undefined if i >= bA.bits
|
||||||
func (bA BitArray) SetIndex(i uint, v bool) {
|
func (bA BitArray) SetIndex(i uint, v bool) {
|
||||||
if v {
|
if v {
|
||||||
bA[i/64] |= uint64(1 << (i % 64))
|
bA.elems[i/64] |= uint64(1 << (i % 64))
|
||||||
} else {
|
} else {
|
||||||
bA[i/64] &= ^uint64(1 << (i % 64))
|
bA.elems[i/64] &= ^uint64(1 << (i % 64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bA BitArray) Copy() BitArray {
|
func (bA BitArray) Copy() BitArray {
|
||||||
c := make([]uint64, len(bA))
|
c := make([]uint64, len(bA.elems))
|
||||||
copy(c, bA)
|
copy(c, bA.elems)
|
||||||
return BitArray(c)
|
return BitArray{bA.bits, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bA BitArray) copyBits(bits uint) BitArray {
|
||||||
|
c := make([]uint64, (bits+63)/64)
|
||||||
|
copy(c, bA.elems)
|
||||||
|
return BitArray{bits, c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a BitArray of larger bits size.
|
||||||
func (bA BitArray) Or(o BitArray) BitArray {
|
func (bA BitArray) Or(o BitArray) BitArray {
|
||||||
c := bA.Copy()
|
c := bA.copyBits(MaxUint(bA.bits, o.bits))
|
||||||
for i, _ := range c {
|
for i := 0; i < len(c.elems); i++ {
|
||||||
c[i] = o[i] | c[i]
|
c.elems[i] |= o.elems[i]
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a BitArray of smaller bit size.
|
||||||
func (bA BitArray) And(o BitArray) BitArray {
|
func (bA BitArray) And(o BitArray) BitArray {
|
||||||
c := bA.Copy()
|
c := bA.copyBits(MinUint(bA.bits, o.bits))
|
||||||
for i, _ := range c {
|
for i := 0; i < len(c.elems); i++ {
|
||||||
c[i] = o[i] & c[i]
|
c.elems[i] &= o.elems[i]
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bA BitArray) Not() BitArray {
|
func (bA BitArray) Not() BitArray {
|
||||||
c := bA.Copy()
|
c := bA.Copy()
|
||||||
for i, _ := range c {
|
for i := 0; i < len(c.elems); i++ {
|
||||||
c[i] = ^c[i]
|
c.elems[i] = ^c.elems[i]
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@ -97,64 +110,40 @@ func (bA BitArray) Sub(o BitArray) BitArray {
|
|||||||
return bA.And(o.Not())
|
return bA.And(o.Not())
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: returns counts or a longer int slice as necessary.
|
|
||||||
func (bA BitArray) AddToCounts(counts []int) []int {
|
|
||||||
for bytei := 0; bytei < len(bA); bytei++ {
|
|
||||||
for biti := 0; biti < 64; biti++ {
|
|
||||||
if (bA[bytei] & (1 << uint(biti))) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
index := 64*bytei + biti
|
|
||||||
if len(counts) <= index {
|
|
||||||
counts = append(counts, make([]int, (index-len(counts)+1))...)
|
|
||||||
}
|
|
||||||
counts[index]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return counts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bA BitArray) PickRandom() (int, bool) {
|
func (bA BitArray) PickRandom() (int, bool) {
|
||||||
randStart := rand.Intn(len(bA))
|
length := len(bA.elems)
|
||||||
for i := 0; i < len(bA); i++ {
|
randElemStart := rand.Intn(length)
|
||||||
bytei := ((i + randStart) % len(bA))
|
for i := 0; i < length; i++ {
|
||||||
if bA[bytei] > 0 {
|
elemIdx := ((i + randElemStart) % length)
|
||||||
|
if elemIdx < length-1 {
|
||||||
|
if bA.elems[elemIdx] > 0 {
|
||||||
randBitStart := rand.Intn(64)
|
randBitStart := rand.Intn(64)
|
||||||
for j := 0; j < 64; j++ {
|
for j := 0; j < 64; j++ {
|
||||||
biti := ((j + randBitStart) % 64)
|
bitIdx := ((j + randBitStart) % 64)
|
||||||
//fmt.Printf("%X %v %v %v\n", iHas, j, biti, randBitStart)
|
if (bA.elems[elemIdx] & (1 << uint(bitIdx))) > 0 {
|
||||||
if (bA[bytei] & (1 << uint(biti))) > 0 {
|
return 64*int(elemIdx) + int(bitIdx), true
|
||||||
return 64*int(bytei) + int(biti), true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic("should not happen")
|
panic("should not happen")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Special case for last elem, to ignore straggler bits
|
||||||
|
elemBits := int(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] & (1 << uint(bitIdx))) > 0 {
|
||||||
|
return 64*int(elemIdx) + int(bitIdx), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick an index from this BitArray that is 1 && whose count is lowest.
|
|
||||||
func (bA BitArray) PickRarest(counts []int) (rarest int, ok bool) {
|
|
||||||
smallestCount := math.MaxInt32
|
|
||||||
for bytei := 0; bytei < len(bA); bytei++ {
|
|
||||||
if bA[bytei] > 0 {
|
|
||||||
for biti := 0; biti < 64; biti++ {
|
|
||||||
if (bA[bytei] & (1 << uint(biti))) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
index := 64*bytei + biti
|
|
||||||
if counts[index] < smallestCount {
|
|
||||||
smallestCount = counts[index]
|
|
||||||
rarest = index
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("should not happen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bA BitArray) String() string {
|
func (bA BitArray) String() string {
|
||||||
return bA.StringWithIndent("")
|
return bA.StringWithIndent("")
|
||||||
}
|
}
|
||||||
@ -162,8 +151,8 @@ func (bA BitArray) String() string {
|
|||||||
func (bA BitArray) StringWithIndent(indent string) string {
|
func (bA BitArray) StringWithIndent(indent string) string {
|
||||||
lines := []string{}
|
lines := []string{}
|
||||||
bits := ""
|
bits := ""
|
||||||
for i := 0; i < len(bA)*64; i++ {
|
for i := uint(0); i < bA.bits; i++ {
|
||||||
if bA.GetIndex(uint(i)) {
|
if bA.GetIndex(i) {
|
||||||
bits += "X"
|
bits += "X"
|
||||||
} else {
|
} else {
|
||||||
bits += "_"
|
bits += "_"
|
||||||
@ -182,5 +171,5 @@ func (bA BitArray) StringWithIndent(indent string) string {
|
|||||||
if len(bits) > 0 {
|
if len(bits) > 0 {
|
||||||
lines = append(lines, bits)
|
lines = append(lines, bits)
|
||||||
}
|
}
|
||||||
return strings.Join(lines, indent)
|
return fmt.Sprintf("BA{%v:%v}", bA.bits, strings.Join(lines, indent))
|
||||||
}
|
}
|
||||||
|
135
common/bit_array_test.go
Normal file
135
common/bit_array_test.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func randBitArray(bits uint) (BitArray, []byte) {
|
||||||
|
src := RandBytes(int((bits + 7) / 8))
|
||||||
|
bA := NewBitArray(bits)
|
||||||
|
for i := uint(0); i < uint(len(src)); i++ {
|
||||||
|
for j := uint(0); j < 8; j++ {
|
||||||
|
if i*8+j >= bits {
|
||||||
|
return bA, src
|
||||||
|
}
|
||||||
|
setBit := src[i]&(1<<j) > 0
|
||||||
|
bA.SetIndex(i*8+j, setBit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bA, src
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadWriteEmptyBitarray(t *testing.T) {
|
||||||
|
bA1 := BitArray{}
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err := bA1.WriteTo(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to write empty bitarray")
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int64
|
||||||
|
bA2 := ReadBitArray(buf, &n, &err)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to read empty bitarray")
|
||||||
|
}
|
||||||
|
if bA2.bits != 0 {
|
||||||
|
t.Error("Expected to get bA2.bits 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadWriteBitarray(t *testing.T) {
|
||||||
|
|
||||||
|
// Make random bA1
|
||||||
|
bA1, testData := randBitArray(64*10 + 8) // not divisible by 64
|
||||||
|
|
||||||
|
// Write it
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err := bA1.WriteTo(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to write bitarray")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read it
|
||||||
|
var n int64
|
||||||
|
bA2 := ReadBitArray(buf, &n, &err)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to read bitarray")
|
||||||
|
}
|
||||||
|
testData2 := make([]byte, len(testData))
|
||||||
|
for i := uint(0); i < uint(len(testData)); i++ {
|
||||||
|
for j := uint(0); j < 8; j++ {
|
||||||
|
if bA2.GetIndex(i*8 + j) {
|
||||||
|
testData2[i] |= 1 << j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare testData
|
||||||
|
if !bytes.Equal(testData, testData2) {
|
||||||
|
t.Errorf("Not the same:\n%X\n%X", testData, testData2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 := uint(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 := uint(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 TestSub(t *testing.T) {
|
||||||
|
|
||||||
|
bA1, _ := randBitArray(31)
|
||||||
|
bA2, _ := randBitArray(51)
|
||||||
|
bA3 := bA1.Sub(bA2)
|
||||||
|
|
||||||
|
if bA3.bits != 31 {
|
||||||
|
t.Error("Expected min bits")
|
||||||
|
}
|
||||||
|
if len(bA3.elems) != len(bA1.elems) {
|
||||||
|
t.Error("Expected min elems length")
|
||||||
|
}
|
||||||
|
for i := uint(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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,3 +8,16 @@ import (
|
|||||||
func Errorf(s string, args ...interface{}) error {
|
func Errorf(s string, args ...interface{}) error {
|
||||||
return errors.New(fmt.Sprintf(s, args...))
|
return errors.New(fmt.Sprintf(s, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
@ -1,266 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import ()
|
|
||||||
|
|
||||||
// This immutable balanced binary tree happens to be an
|
|
||||||
// immutable AVL+ tree, adapted from tendermint/merkle.
|
|
||||||
// Unlike that one, this is in-memory, non-merkleized,
|
|
||||||
// and nodes can be nil to signify an empty tree.
|
|
||||||
type IBBSTree struct {
|
|
||||||
key uint64
|
|
||||||
value interface{}
|
|
||||||
size uint64
|
|
||||||
height uint8
|
|
||||||
left *IBBSTree
|
|
||||||
right *IBBSTree
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates an empty tree.
|
|
||||||
func NewIBBSTree() *IBBSTree {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a single tree node from key and value.
|
|
||||||
func NewIBBSTreeNode(key uint64, value interface{}) *IBBSTree {
|
|
||||||
return &IBBSTree{
|
|
||||||
key: key,
|
|
||||||
value: value,
|
|
||||||
size: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) Copy() *IBBSTree {
|
|
||||||
if self == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &IBBSTree{
|
|
||||||
key: self.key,
|
|
||||||
value: self.value,
|
|
||||||
size: self.size,
|
|
||||||
height: self.height,
|
|
||||||
left: self.left,
|
|
||||||
right: self.right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) Size() uint64 {
|
|
||||||
if self == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return self.size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) Height() uint8 {
|
|
||||||
if self == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return self.height
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) Has(key uint64) (has bool) {
|
|
||||||
if self == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if self.key == key {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if self.height == 0 {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
if key < self.key {
|
|
||||||
return self.left.Has(key)
|
|
||||||
} else {
|
|
||||||
return self.right.Has(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) Get(key uint64) (value interface{}) {
|
|
||||||
if self == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if self.height == 0 {
|
|
||||||
if self.key == key {
|
|
||||||
return self.value
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if key < self.key {
|
|
||||||
return self.left.Get(key)
|
|
||||||
} else {
|
|
||||||
return self.right.Get(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) Set(key uint64, value interface{}) (_ *IBBSTree, updated bool) {
|
|
||||||
if self == nil {
|
|
||||||
return NewIBBSTreeNode(key, value), false
|
|
||||||
}
|
|
||||||
if self.height == 0 {
|
|
||||||
if key < self.key {
|
|
||||||
return &IBBSTree{
|
|
||||||
key: self.key,
|
|
||||||
height: 1,
|
|
||||||
size: 2,
|
|
||||||
left: NewIBBSTreeNode(key, value),
|
|
||||||
right: self,
|
|
||||||
}, false
|
|
||||||
} else if self.key == key {
|
|
||||||
return NewIBBSTreeNode(key, value), true
|
|
||||||
} else {
|
|
||||||
return &IBBSTree{
|
|
||||||
key: key,
|
|
||||||
height: 1,
|
|
||||||
size: 2,
|
|
||||||
left: self,
|
|
||||||
right: NewIBBSTreeNode(key, value),
|
|
||||||
}, false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self = self.Copy()
|
|
||||||
if key < self.key {
|
|
||||||
self.left, updated = self.left.Set(key, value)
|
|
||||||
} else {
|
|
||||||
self.right, updated = self.right.Set(key, value)
|
|
||||||
}
|
|
||||||
if updated {
|
|
||||||
return self, updated
|
|
||||||
} else {
|
|
||||||
self.calcHeightAndSize()
|
|
||||||
return self.balance(), updated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) Remove(key uint64) (newSelf *IBBSTree, value interface{}, removed bool) {
|
|
||||||
newSelf, _, _, value, removed = self.remove(key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// newKey: new leftmost leaf key for tree after successfully removing 'key' if changed.
|
|
||||||
func (self *IBBSTree) remove(key uint64) (newSelf *IBBSTree, hasNewKey bool, newKey uint64, value interface{}, removed bool) {
|
|
||||||
if self == nil {
|
|
||||||
return nil, false, 0, nil, false
|
|
||||||
}
|
|
||||||
if self.height == 0 {
|
|
||||||
if self.key == key {
|
|
||||||
return nil, false, 0, self.value, true
|
|
||||||
} else {
|
|
||||||
return self, false, 0, nil, false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if key < self.key {
|
|
||||||
var newLeft *IBBSTree
|
|
||||||
newLeft, hasNewKey, newKey, value, removed = self.left.remove(key)
|
|
||||||
if !removed {
|
|
||||||
return self, false, 0, value, false
|
|
||||||
} else if newLeft == nil { // left node held value, was removed
|
|
||||||
return self.right, true, self.key, value, true
|
|
||||||
}
|
|
||||||
self = self.Copy()
|
|
||||||
self.left = newLeft
|
|
||||||
} else {
|
|
||||||
var newRight *IBBSTree
|
|
||||||
newRight, hasNewKey, newKey, value, removed = self.right.remove(key)
|
|
||||||
if !removed {
|
|
||||||
return self, false, 0, value, false
|
|
||||||
} else if newRight == nil { // right node held value, was removed
|
|
||||||
return self.left, false, 0, value, true
|
|
||||||
}
|
|
||||||
self = self.Copy()
|
|
||||||
self.right = newRight
|
|
||||||
if hasNewKey {
|
|
||||||
self.key = newKey
|
|
||||||
hasNewKey = false
|
|
||||||
newKey = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.calcHeightAndSize()
|
|
||||||
return self.balance(), hasNewKey, newKey, value, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) rotateRight() *IBBSTree {
|
|
||||||
self = self.Copy()
|
|
||||||
sl := self.left.Copy()
|
|
||||||
slr := sl.right
|
|
||||||
|
|
||||||
sl.right = self
|
|
||||||
self.left = slr
|
|
||||||
|
|
||||||
self.calcHeightAndSize()
|
|
||||||
sl.calcHeightAndSize()
|
|
||||||
|
|
||||||
return sl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) rotateLeft() *IBBSTree {
|
|
||||||
self = self.Copy()
|
|
||||||
sr := self.right.Copy()
|
|
||||||
srl := sr.left
|
|
||||||
|
|
||||||
sr.left = self
|
|
||||||
self.right = srl
|
|
||||||
|
|
||||||
self.calcHeightAndSize()
|
|
||||||
sr.calcHeightAndSize()
|
|
||||||
|
|
||||||
return sr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) calcHeightAndSize() {
|
|
||||||
self.height = MaxUint8(self.left.Height(), self.right.Height()) + 1
|
|
||||||
self.size = self.left.Size() + self.right.Size()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) calcBalance() int {
|
|
||||||
return int(self.left.Height()) - int(self.right.Height())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) balance() (newSelf *IBBSTree) {
|
|
||||||
balance := self.calcBalance()
|
|
||||||
if balance > 1 {
|
|
||||||
if self.left.calcBalance() >= 0 {
|
|
||||||
// Left Left Case
|
|
||||||
return self.rotateRight()
|
|
||||||
} else {
|
|
||||||
// Left Right Case
|
|
||||||
self = self.Copy()
|
|
||||||
self.left = self.left.rotateLeft()
|
|
||||||
//self.calcHeightAndSize()
|
|
||||||
return self.rotateRight()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if balance < -1 {
|
|
||||||
if self.right.calcBalance() <= 0 {
|
|
||||||
// Right Right Case
|
|
||||||
return self.rotateLeft()
|
|
||||||
} else {
|
|
||||||
// Right Left Case
|
|
||||||
self = self.Copy()
|
|
||||||
self.right = self.right.rotateRight()
|
|
||||||
//self.calcHeightAndSize()
|
|
||||||
return self.rotateLeft()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Nothing changed
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iteration stops when stop is returned.
|
|
||||||
func (self *IBBSTree) Iterate(cb func(uint64, interface{}) bool) bool {
|
|
||||||
if self == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if self.height == 0 {
|
|
||||||
return cb(self.key, self.value)
|
|
||||||
} else {
|
|
||||||
stop := self.left.Iterate(cb)
|
|
||||||
if stop {
|
|
||||||
return stop
|
|
||||||
}
|
|
||||||
return self.right.Iterate(cb)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,203 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// TODO: seed rand?
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IBBSTree) lmd() *IBBSTree {
|
|
||||||
if self.height == 0 {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
return self.left.lmd()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnit(t *testing.T) {
|
|
||||||
|
|
||||||
// Convenience for a new node
|
|
||||||
N := func(l, r interface{}) *IBBSTree {
|
|
||||||
var left, right *IBBSTree
|
|
||||||
if _, ok := l.(*IBBSTree); ok {
|
|
||||||
left = l.(*IBBSTree)
|
|
||||||
} else {
|
|
||||||
left = NewIBBSTreeNode(uint64(l.(int)), nil)
|
|
||||||
}
|
|
||||||
if _, ok := r.(*IBBSTree); ok {
|
|
||||||
right = r.(*IBBSTree)
|
|
||||||
} else {
|
|
||||||
right = NewIBBSTreeNode(uint64(r.(int)), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
n := &IBBSTree{
|
|
||||||
key: right.lmd().key,
|
|
||||||
left: left,
|
|
||||||
right: right,
|
|
||||||
}
|
|
||||||
n.calcHeightAndSize()
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convenience for simple printing of keys & tree structure
|
|
||||||
var P func(*IBBSTree) string
|
|
||||||
P = func(n *IBBSTree) string {
|
|
||||||
if n.height == 0 {
|
|
||||||
return fmt.Sprintf("%v", n.key)
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("(%v %v)", P(n.left), P(n.right))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expectSet := func(n *IBBSTree, i uint64, repr string) {
|
|
||||||
n2, updated := n.Set(i, nil)
|
|
||||||
// ensure node was added & structure is as expected.
|
|
||||||
if updated == true || P(n2) != repr {
|
|
||||||
t.Fatalf("Adding %v to %v:\nExpected %v\nUnexpectedly got %v updated:%v",
|
|
||||||
i, P(n), repr, P(n2), updated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expectRemove := func(n *IBBSTree, i uint64, repr string) {
|
|
||||||
n2, value, removed := n.Remove(i)
|
|
||||||
// ensure node was removed & structure is as expected.
|
|
||||||
if value != nil || P(n2) != repr || !removed {
|
|
||||||
t.Fatalf("Removing %v from %v:\nExpected %v\nUnexpectedly got %v value:%v err:%v",
|
|
||||||
i, P(n), repr, P(n2), value, removed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////// Test Set cases:
|
|
||||||
|
|
||||||
// Case 1:
|
|
||||||
n1 := N(4, 20)
|
|
||||||
|
|
||||||
expectSet(n1, 8, "((4 8) 20)")
|
|
||||||
expectSet(n1, 25, "(4 (20 25))")
|
|
||||||
|
|
||||||
n2 := N(4, N(20, 25))
|
|
||||||
|
|
||||||
expectSet(n2, 8, "((4 8) (20 25))")
|
|
||||||
expectSet(n2, 30, "((4 20) (25 30))")
|
|
||||||
|
|
||||||
n3 := N(N(1, 2), 6)
|
|
||||||
|
|
||||||
expectSet(n3, 4, "((1 2) (4 6))")
|
|
||||||
expectSet(n3, 8, "((1 2) (6 8))")
|
|
||||||
|
|
||||||
n4 := N(N(1, 2), N(N(5, 6), N(7, 9)))
|
|
||||||
|
|
||||||
expectSet(n4, 8, "(((1 2) (5 6)) ((7 8) 9))")
|
|
||||||
expectSet(n4, 10, "(((1 2) (5 6)) (7 (9 10)))")
|
|
||||||
|
|
||||||
//////// Test Remove cases:
|
|
||||||
|
|
||||||
n10 := N(N(1, 2), 3)
|
|
||||||
|
|
||||||
expectRemove(n10, 2, "(1 3)")
|
|
||||||
expectRemove(n10, 3, "(1 2)")
|
|
||||||
|
|
||||||
n11 := N(N(N(1, 2), 3), N(4, 5))
|
|
||||||
|
|
||||||
expectRemove(n11, 4, "((1 2) (3 5))")
|
|
||||||
expectRemove(n11, 3, "((1 2) (4 5))")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntegration(t *testing.T) {
|
|
||||||
|
|
||||||
type record struct {
|
|
||||||
key uint64
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
|
|
||||||
records := make([]*record, 400)
|
|
||||||
var tree *IBBSTree = NewIBBSTree()
|
|
||||||
var val interface{}
|
|
||||||
var removed bool
|
|
||||||
var updated bool
|
|
||||||
|
|
||||||
randomRecord := func() *record {
|
|
||||||
return &record{RandUInt64(), RandStr(20)}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range records {
|
|
||||||
r := randomRecord()
|
|
||||||
records[i] = r
|
|
||||||
//t.Log("New record", r)
|
|
||||||
//PrintIBBSTree(tree.root)
|
|
||||||
tree, updated = tree.Set(r.key, "")
|
|
||||||
if updated {
|
|
||||||
t.Error("should have not been updated")
|
|
||||||
}
|
|
||||||
tree, updated = tree.Set(r.key, r.value)
|
|
||||||
if !updated {
|
|
||||||
t.Error("should have been updated")
|
|
||||||
}
|
|
||||||
if tree.Size() != uint64(i+1) {
|
|
||||||
t.Error("size was wrong", tree.Size(), i+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range records {
|
|
||||||
if has := tree.Has(r.key); !has {
|
|
||||||
t.Error("Missing key", r.key)
|
|
||||||
}
|
|
||||||
if val := tree.Get(r.key); val.(string) != r.value {
|
|
||||||
t.Error("wrong value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, x := range records {
|
|
||||||
if tree, val, removed = tree.Remove(x.key); !removed {
|
|
||||||
t.Error("not removed")
|
|
||||||
} else if val.(string) != x.value {
|
|
||||||
t.Error("wrong value")
|
|
||||||
}
|
|
||||||
for _, r := range records[i+1:] {
|
|
||||||
if has := tree.Has(r.key); !has {
|
|
||||||
t.Error("Missing key", r.key)
|
|
||||||
}
|
|
||||||
val := tree.Get(r.key)
|
|
||||||
if val.(string) != r.value {
|
|
||||||
t.Error("wrong value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tree.Size() != uint64(len(records)-(i+1)) {
|
|
||||||
t.Error("size was wrong", tree.Size(), (len(records) - (i + 1)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkIBBSTree(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
|
|
||||||
type record struct {
|
|
||||||
key uint64
|
|
||||||
value interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
randomRecord := func() *record {
|
|
||||||
return &record{RandUInt64(), RandUInt64()}
|
|
||||||
}
|
|
||||||
|
|
||||||
t := NewIBBSTree()
|
|
||||||
for i := 0; i < 1000000; i++ {
|
|
||||||
r := randomRecord()
|
|
||||||
t, _ = t.Set(r.key, r.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("ok, starting")
|
|
||||||
|
|
||||||
runtime.GC()
|
|
||||||
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
r := randomRecord()
|
|
||||||
t, _ = t.Set(r.key, r.value)
|
|
||||||
t, _, _ = t.Remove(r.key)
|
|
||||||
}
|
|
||||||
}
|
|
@ -710,13 +710,13 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) error {
|
|||||||
// Reset the rest
|
// Reset the rest
|
||||||
ps.Proposal = false
|
ps.Proposal = false
|
||||||
ps.ProposalBlockHash = nil
|
ps.ProposalBlockHash = nil
|
||||||
ps.ProposalBlockBitArray = nil
|
ps.ProposalBlockBitArray = BitArray{}
|
||||||
ps.ProposalPOLHash = nil
|
ps.ProposalPOLHash = nil
|
||||||
ps.ProposalPOLBitArray = nil
|
ps.ProposalPOLBitArray = BitArray{}
|
||||||
ps.Prevotes = nil
|
ps.Prevotes = BitArray{}
|
||||||
ps.Precommits = nil
|
ps.Precommits = BitArray{}
|
||||||
if ps.Height != msg.Height {
|
if ps.Height != msg.Height {
|
||||||
ps.Commits = nil
|
ps.Commits = BitArray{}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
|
"runtime/debug"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -141,7 +142,9 @@ func (c *MConnection) flush() {
|
|||||||
// Catch panics, usually caused by remote disconnects.
|
// Catch panics, usually caused by remote disconnects.
|
||||||
func (c *MConnection) _recover() {
|
func (c *MConnection) _recover() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
c.stopForError(r)
|
stack := debug.Stack()
|
||||||
|
err := StackError{r, stack}
|
||||||
|
c.stopForError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,16 +377,16 @@ FOR_LOOP:
|
|||||||
}
|
}
|
||||||
break FOR_LOOP
|
break FOR_LOOP
|
||||||
}
|
}
|
||||||
channel := c.channels[pkt.ChannelId]
|
channel, ok := c.channelsIdx[pkt.ChannelId]
|
||||||
if channel == nil {
|
if !ok || channel == nil {
|
||||||
Panicf("Unknown channel %v", pkt.ChannelId)
|
Panicf("Unknown channel %X", pkt.ChannelId)
|
||||||
}
|
}
|
||||||
msgBytes := channel.recvPacket(pkt)
|
msgBytes := channel.recvPacket(pkt)
|
||||||
if msgBytes != nil {
|
if msgBytes != nil {
|
||||||
c.onReceive(pkt.ChannelId, msgBytes)
|
c.onReceive(pkt.ChannelId, msgBytes)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
Panicf("Unknown message type %v", pktType)
|
Panicf("Unknown message type %X", pktType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: shouldn't this go in the sendRoutine?
|
// TODO: shouldn't this go in the sendRoutine?
|
||||||
@ -550,7 +553,7 @@ func (p packet) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p packet) String() string {
|
func (p packet) String() string {
|
||||||
return fmt.Sprintf("%v:%X", p.ChannelId, p.Bytes)
|
return fmt.Sprintf("%X:%X", p.ChannelId, p.Bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readPacketSafe(r io.Reader) (pkt packet, n int64, err error) {
|
func readPacketSafe(r io.Reader) (pkt packet, n int64, err error) {
|
||||||
|
Reference in New Issue
Block a user