diff --git a/blockchain/stake/internal/tickettreap/README.md b/blockchain/stake/internal/tickettreap/README.md new file mode 100644 index 00000000..147e78f3 --- /dev/null +++ b/blockchain/stake/internal/tickettreap/README.md @@ -0,0 +1,40 @@ +tickettreap +=========== + +[![Build Status](https://img.shields.io/travis/decred/dcrd.svg)] +(https://travis-ci.org/decred/dcrd) [![ISC License] +(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)] +(http://godoc.org/github.com/decred/dcrd/blockchain/stake/internal/tickettreap) + +Package tickettreap implements a treap data structure that is used to hold +live tickets ordered by their key along with some associated data using a +combination of binary search tree and heap semantics. It is a self-organizing +and randomized data structure that doesn't require complex operations to +maintain balance. Search, insert, and delete operations are all O(log n). +Both mutable and immutable variants are provided. + +The mutable variant is typically faster since it is able to simply update the +treap when modifications are made. However, a mutable treap is not safe for +concurrent access without careful use of locking by the caller and care must be +taken when iterating since it can change out from under the iterator. + +The immutable variant works by creating a new version of the treap for all +mutations by replacing modified nodes with new nodes that have updated values +while sharing all unmodified nodes with the previous version. This is extremely +useful in concurrent applications since the caller only has to atomically +replace the treap pointer with the newly returned version after performing any +mutations. All readers can simply use their existing pointer as a snapshot +since the treap it points to is immutable. This effectively provides O(1) +snapshot capability with efficient memory usage characteristics since the old +nodes only remain allocated until there are no longer any references to them. + +## Usage + +This package is only used internally in the stake code and as such is not +available for use outside of it. + +## License + +Package tickettreap is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/blockchain/stake/internal/tickettreap/benchmark_test.go b/blockchain/stake/internal/tickettreap/benchmark_test.go new file mode 100644 index 00000000..7b0af256 --- /dev/null +++ b/blockchain/stake/internal/tickettreap/benchmark_test.go @@ -0,0 +1,144 @@ +// Copyright (c) 2016 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package tickettreap + +import ( + "crypto/sha256" + "sync" + "testing" +) + +// numTicketKeys is the number of keys to generate for use in the benchmarks. +const numTicketKeys = 42500 + +var ( + // generatedTicketKeys is used to store ticket keys generated for use + // in the benchmarks so that they only need to be generatd once for all + // benchmarks that use them. + genTicketKeysLock sync.Mutex + generatedTicketKeys []Key +) + +// genTicketKeys generates and returns 'numTicketKeys' along with memoizing them +// so that future calls return the cached data. +func genTicketKeys() []Key { + // Return generated keys if already done. + genTicketKeysLock.Lock() + defer genTicketKeysLock.Unlock() + if generatedTicketKeys != nil { + return generatedTicketKeys + } + + // Generate the keys and cache them for future invocations. + ticketKeys := make([]Key, 0, numTicketKeys) + for i := 0; i < numTicketKeys; i++ { + key := Key(sha256.Sum256(serializeUint32(uint32(i)))) + ticketKeys = append(ticketKeys, key) + } + generatedTicketKeys = ticketKeys + return ticketKeys +} + +// BenchmarkMutableCopy benchmarks how long it takes to copy a mutable treap +// to another one when it contains 'numTicketKeys' entries. +func BenchmarkMutableCopy(b *testing.B) { + // Populate mutable treap with a bunch of key/value pairs. + testTreap := NewMutable() + ticketKeys := genTicketKeys() + for j := 0; j < len(ticketKeys); j++ { + hashBytes := ticketKeys[j] + value := &Value{Height: uint32(j)} + testTreap.Put(hashBytes, value) + } + + b.ReportAllocs() + b.ResetTimer() + + // Copying a mutable treap requires iterating all of the entries and + // populating them into a new treap all with a lock held for concurrency + // safety. + var mtx sync.RWMutex + for i := 0; i < b.N; i++ { + benchTreap := NewMutable() + mtx.Lock() + testTreap.ForEach(func(k Key, v *Value) bool { + benchTreap.Put(k, v) + return true + }) + mtx.Unlock() + } +} + +// BenchmarkImmutableCopy benchmarks how long it takes to copy an immutable +// treap to another one when it contains 'numTicketKeys' entries. +func BenchmarkImmutableCopy(b *testing.B) { + // Populate immutable treap with a bunch of key/value pairs. + testTreap := NewImmutable() + ticketKeys := genTicketKeys() + for j := 0; j < len(ticketKeys); j++ { + hashBytes := ticketKeys[j] + value := &Value{Height: uint32(j)} + testTreap = testTreap.Put(hashBytes, value) + } + + b.ReportAllocs() + b.ResetTimer() + + // Copying an immutable treap is just protecting access to the treap + // root and getting another reference to it. + var mtx sync.RWMutex + for i := 0; i < b.N; i++ { + mtx.RLock() + benchTreap := testTreap + mtx.RUnlock() + _ = benchTreap + } +} + +// BenchmarkMutableCopy benchmarks how long it takes to iterate a mutable treap +// when it contains 'numTicketKeys' entries. +func BenchmarkMutableIterate(b *testing.B) { + // Populate mutable treap with a bunch of key/value pairs. + testTreap := NewMutable() + ticketKeys := genTicketKeys() + for j := 0; j < len(ticketKeys); j++ { + hashBytes := ticketKeys[j] + value := &Value{Height: uint32(j)} + testTreap.Put(hashBytes, value) + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + testTreap.ForEach(func(k Key, v *Value) bool { + return true + }) + } + +} + +// BenchmarkImmutableIterate benchmarks how long it takes to iterate an +// immutable treap when it contains 'numTicketKeys' entries. +func BenchmarkImmutableIterate(b *testing.B) { + // Populate immutable treap with a bunch of key/value pairs. + testTreap := NewImmutable() + ticketKeys := genTicketKeys() + for j := 0; j < len(ticketKeys); j++ { + hashBytes := ticketKeys[j] + value := &Value{Height: uint32(j)} + testTreap = testTreap.Put(hashBytes, value) + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + testTreap.ForEach(func(k Key, v *Value) bool { + return true + }) + } + +} diff --git a/blockchain/stake/internal/tickettreap/common.go b/blockchain/stake/internal/tickettreap/common.go new file mode 100644 index 00000000..e855ec2e --- /dev/null +++ b/blockchain/stake/internal/tickettreap/common.go @@ -0,0 +1,176 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2016 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package tickettreap + +import ( + "math/rand" + "sync" + "time" + + "github.com/decred/dcrd/chaincfg/chainhash" +) + +const ( + // staticDepth is the size of the static array to use for keeping track + // of the parent stack during treap iteration. Since a treap has a very + // high probability that the tree height is logarithmic, it is + // exceedingly unlikely that the parent stack will ever exceed this size + // even for extremely large numbers of items. + staticDepth = 128 + + // nodeFieldsSize is the size the fields of each node takes excluding + // the contents of the key and value. It assumes 64-bit pointers so + // technically it is smaller on 32-bit platforms, but overestimating the + // size in that case is acceptable since it avoids the need to import + // unsafe. It consists of 8 bytes for each of the value, priority, + // left, and right fields (8*4). + nodeFieldsSize = 32 + + // nodeValueSize is the size of the fixed-size fields of a Value. + nodeValueSize = 4 +) + +// lockedSource is a rng source that is safe for concurrent access. +type lockedSource struct { + lock sync.Mutex + src rand.Source +} + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64. +// +// This function is safe for concurrent access. +// +// This is part of the implementation for the rand.Source interface. +func (r *lockedSource) Int63() int64 { + r.lock.Lock() + n := r.src.Int63() + r.lock.Unlock() + return n +} + +// Seed uses the provided seed value to initialize the generator to a +// deterministic state. +// +// This function is safe for concurrent access. +// +// This is part of the implementation for the rand.Source interface. +func (r *lockedSource) Seed(seed int64) { + r.lock.Lock() + r.src.Seed(seed) + r.lock.Unlock() +} + +var ( + // rng is a new random number generator that is local to the package. + rng = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())}) +) + +// Key defines the key used to add an associated value to the treap. +type Key chainhash.Hash + +// Value defines the information stored for a given key in the treap. +type Value struct { + // Height is the block height of the associated ticket. + Height uint32 +} + +// treapNode represents a node in the treap. +type treapNode struct { + key Key + value *Value + priority int + left *treapNode + right *treapNode +} + +// nodeSize returns the number of bytes the specified node occupies including +// the struct fields and the contents of the key and value. +func nodeSize(node *treapNode) uint64 { + return nodeFieldsSize + uint64(len(node.key)+nodeValueSize) +} + +// newTreapNode returns a new node from the given key, value, and priority. The +// node is not initially linked to any others. +func newTreapNode(key Key, value *Value, priority int) *treapNode { + return &treapNode{key: key, value: value, priority: priority} +} + +// parentStack represents a stack of parent treap nodes that are used during +// iteration. It consists of a static array for holding the parents and a +// dynamic overflow slice. It is extremely unlikely the overflow will ever be +// hit during normal operation, however, since a treap's height is +// probabilistic, the overflow case needs to be handled properly. This approach +// is used because it is much more efficient for the majority case than +// dynamically allocating heap space every time the treap is iterated. +type parentStack struct { + index int + items [staticDepth]*treapNode + overflow []*treapNode +} + +// Len returns the current number of items in the stack. +func (s *parentStack) Len() int { + return s.index +} + +// At returns the item n number of items from the top of the stack, where 0 is +// the topmost item, without removing it. It returns nil if n exceeds the +// number of items on the stack. +func (s *parentStack) At(n int) *treapNode { + index := s.index - n - 1 + if index < 0 { + return nil + } + + if index < staticDepth { + return s.items[index] + } + + return s.overflow[index-staticDepth] +} + +// Pop removes the top item from the stack. It returns nil if the stack is +// empty. +func (s *parentStack) Pop() *treapNode { + if s.index == 0 { + return nil + } + + s.index-- + if s.index < staticDepth { + node := s.items[s.index] + s.items[s.index] = nil + return node + } + + node := s.overflow[s.index-staticDepth] + s.overflow[s.index-staticDepth] = nil + return node +} + +// Push pushes the passed item onto the top of the stack. +func (s *parentStack) Push(node *treapNode) { + if s.index < staticDepth { + s.items[s.index] = node + s.index++ + return + } + + // This approach is used over append because reslicing the slice to pop + // the item causes the compiler to make unneeded allocations. Also, + // since the max number of items is related to the tree depth which + // requires expontentially more items to increase, only increase the cap + // one item at a time. This is more intelligent than the generic append + // expansion algorithm which often doubles the cap. + index := s.index - staticDepth + if index+1 > cap(s.overflow) { + overflow := make([]*treapNode, index+1) + copy(overflow, s.overflow) + s.overflow = overflow + } + s.overflow[index] = node + s.index++ +} diff --git a/blockchain/stake/internal/tickettreap/common_test.go b/blockchain/stake/internal/tickettreap/common_test.go new file mode 100644 index 00000000..d899fafe --- /dev/null +++ b/blockchain/stake/internal/tickettreap/common_test.go @@ -0,0 +1,128 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2016 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package tickettreap + +import ( + "encoding/binary" + "encoding/hex" + "reflect" + "testing" +) + +// fromHex converts the passed hex string into a byte slice and will panic if +// there is an error. This is only provided for the hard-coded constants so +// errors in the source code can be detected. It will only (and must only) be +// called for initialization purposes. +func fromHex(s string) []byte { + r, err := hex.DecodeString(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return r +} + +// serializeUint32 returns the big-endian encoding of the passed uint32. +func serializeUint32(ui uint32) []byte { + var ret [4]byte + binary.BigEndian.PutUint32(ret[:], ui) + return ret[:] +} + +// uint32ToKey converts the provided uint32 to a treap Key. +func uint32ToKey(ui uint32) Key { + var key Key + binary.BigEndian.PutUint32(key[len(key)-4:], ui) + return key +} + +// TestParentStack ensures the treapParentStack functionality works as intended. +func TestParentStack(t *testing.T) { + t.Parallel() + + tests := []struct { + numNodes int + }{ + {numNodes: 1}, + {numNodes: staticDepth}, + {numNodes: staticDepth + 1}, // Test dynamic code paths + } + +testLoop: + for i, test := range tests { + nodes := make([]*treapNode, 0, test.numNodes) + for j := 0; j < test.numNodes; j++ { + key := uint32ToKey(uint32(j)) + value := Value{Height: uint32(j)} + node := newTreapNode(key, &value, 0) + nodes = append(nodes, node) + } + + // Push all of the nodes onto the parent stack while testing + // various stack properties. + stack := &parentStack{} + for j, node := range nodes { + stack.Push(node) + + // Ensure the stack length is the expected value. + if stack.Len() != j+1 { + t.Errorf("Len #%d (%d): unexpected stack "+ + "length - got %d, want %d", i, j, + stack.Len(), j+1) + continue testLoop + } + + // Ensure the node at each index is the expected one. + for k := 0; k <= j; k++ { + atNode := stack.At(j - k) + if !reflect.DeepEqual(atNode, nodes[k]) { + t.Errorf("At #%d (%d): mismatched node "+ + "- got %v, want %v", i, j-k, + atNode, nodes[k]) + continue testLoop + } + } + } + + // Ensure each popped node is the expected one. + for j := 0; j < len(nodes); j++ { + node := stack.Pop() + expected := nodes[len(nodes)-j-1] + if !reflect.DeepEqual(node, expected) { + t.Errorf("At #%d (%d): mismatched node - "+ + "got %v, want %v", i, j, node, expected) + continue testLoop + } + } + + // Ensure the stack is now empty. + if stack.Len() != 0 { + t.Errorf("Len #%d: stack is not empty - got %d", i, + stack.Len()) + continue testLoop + } + + // Ensure attempting to retrieve a node at an index beyond the + // stack's length returns nil. + if node := stack.At(2); node != nil { + t.Errorf("At #%d: did not give back nil - got %v", i, + node) + continue testLoop + } + + // Ensure attempting to pop a node from an empty stack returns + // nil. + if node := stack.Pop(); node != nil { + t.Errorf("Pop #%d: did not give back nil - got %v", i, + node) + continue testLoop + } + } +} + +func init() { + // Force the same pseudo random numbers for each test run. + rng.Seed(0) +} diff --git a/blockchain/stake/internal/tickettreap/doc.go b/blockchain/stake/internal/tickettreap/doc.go new file mode 100644 index 00000000..1cd874bd --- /dev/null +++ b/blockchain/stake/internal/tickettreap/doc.go @@ -0,0 +1,29 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2016 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package tickettreap implements a treap data structure that is used to hold +live tickets ordered by their key along with some associated data using a +combination of binary search tree and heap semantics. It is a self-organizing +and randomized data structure that doesn't require complex operations to +maintain balance. Search, insert, and delete operations are all O(log n). +Both mutable and immutable variants are provided. + +The mutable variant is typically faster since it is able to simply update the +treap when modifications are made. However, a mutable treap is not safe for +concurrent access without careful use of locking by the caller and care must be +taken when iterating since it can change out from under the iterator. + +The immutable variant works by creating a new version of the treap for all +mutations by replacing modified nodes with new nodes that have updated values +while sharing all unmodified nodes with the previous version. This is extremely +useful in concurrent applications since the caller only has to atomically +replace the treap pointer with the newly returned version after performing any +mutations. All readers can simply use their existing pointer as a snapshot +since the treap it points to is immutable. This effectively provides O(1) +snapshot capability with efficient memory usage characteristics since the old +nodes only remain allocated until there are no longer any references to them. +*/ +package tickettreap diff --git a/blockchain/stake/internal/tickettreap/immutable.go b/blockchain/stake/internal/tickettreap/immutable.go new file mode 100644 index 00000000..82b69e02 --- /dev/null +++ b/blockchain/stake/internal/tickettreap/immutable.go @@ -0,0 +1,355 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2016 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package tickettreap + +import "bytes" + +// cloneTreapNode returns a shallow copy of the passed node. +func cloneTreapNode(node *treapNode) *treapNode { + return &treapNode{ + key: node.key, + value: node.value, + priority: node.priority, + left: node.left, + right: node.right, + } +} + +// Immutable represents a treap data structure which is used to hold ordered +// key/value pairs using a combination of binary search tree and heap semantics. +// It is a self-organizing and randomized data structure that doesn't require +// complex operations to maintain balance. Search, insert, and delete +// operations are all O(log n). In addition, it provides O(1) snapshots for +// multi-version concurrency control (MVCC). +// +// All operations which result in modifying the treap return a new version of +// the treap with only the modified nodes updated. All unmodified nodes are +// shared with the previous version. This is extremely useful in concurrent +// applications since the caller only has to atomically replace the treap +// pointer with the newly returned version after performing any mutations. All +// readers can simply use their existing pointer as a snapshot since the treap +// it points to is immutable. This effectively provides O(1) snapshot +// capability with efficient memory usage characteristics since the old nodes +// only remain allocated until there are no longer any references to them. +type Immutable struct { + root *treapNode + count int + + // totalSize is the best estimate of the total size of of all data in + // the treap including the keys, values, and node sizes. + totalSize uint64 +} + +// newImmutable returns a new immutable treap given the passed parameters. +func newImmutable(root *treapNode, count int, totalSize uint64) *Immutable { + return &Immutable{root: root, count: count, totalSize: totalSize} +} + +// Len returns the number of items stored in the treap. +func (t *Immutable) Len() int { + return t.count +} + +// Size returns a best estimate of the total number of bytes the treap is +// consuming including all of the fields used to represent the nodes as well as +// the size of the keys and values. Shared values are not detected, so the +// returned size assumes each value is pointing to different memory. +func (t *Immutable) Size() uint64 { + return t.totalSize +} + +// get returns the treap node that contains the passed key. It will return nil +// when the key does not exist. +func (t *Immutable) get(key Key) *treapNode { + for node := t.root; node != nil; { + // Traverse left or right depending on the result of the + // comparison. + compareResult := bytes.Compare(key[:], node.key[:]) + if compareResult < 0 { + node = node.left + continue + } + if compareResult > 0 { + node = node.right + continue + } + + // The key exists. + return node + } + + // A nil node was reached which means the key does not exist. + return nil +} + +// Has returns whether or not the passed key exists. +func (t *Immutable) Has(key Key) bool { + if node := t.get(key); node != nil { + return true + } + return false +} + +// Get returns the value for the passed key. The function will return nil when +// the key does not exist. +func (t *Immutable) Get(key Key) *Value { + if node := t.get(key); node != nil { + return node.value + } + return nil +} + +// Put inserts the passed key/value pair. Passing a nil value will result in a +// NOOP. +func (t *Immutable) Put(key Key, value *Value) *Immutable { + // Nothing to do if a nil value is passed. + if value == nil { + return t + } + + // The node is the root of the tree if there isn't already one. + if t.root == nil { + root := newTreapNode(key, value, rng.Int()) + return newImmutable(root, 1, nodeSize(root)) + } + + // Find the binary tree insertion point and construct a replaced list of + // parents while doing so. This is done because this is an immutable + // data structure so regardless of where in the treap the new key/value + // pair ends up, all ancestors up to and including the root need to be + // replaced. + // + // When the key matches an entry already in the treap, replace the node + // with a new one that has the new value set and return. + var parents parentStack + var compareResult int + for node := t.root; node != nil; { + // Clone the node and link its parent to it if needed. + nodeCopy := cloneTreapNode(node) + if oldParent := parents.At(0); oldParent != nil { + if oldParent.left == node { + oldParent.left = nodeCopy + } else { + oldParent.right = nodeCopy + } + } + parents.Push(nodeCopy) + + // Traverse left or right depending on the result of comparing + // the keys. + compareResult = bytes.Compare(key[:], node.key[:]) + if compareResult < 0 { + node = node.left + continue + } + if compareResult > 0 { + node = node.right + continue + } + + // The key already exists, so update its value. + nodeCopy.value = value + + // Return new immutable treap with the replaced node and + // ancestors up to and including the root of the tree. + newRoot := parents.At(parents.Len() - 1) + return newImmutable(newRoot, t.count, t.totalSize) + } + + // Link the new node into the binary tree in the correct position. + node := newTreapNode(key, value, rng.Int()) + parent := parents.At(0) + if compareResult < 0 { + parent.left = node + } else { + parent.right = node + } + + // Perform any rotations needed to maintain the min-heap and replace + // the ancestors up to and including the tree root. + newRoot := parents.At(parents.Len() - 1) + for parents.Len() > 0 { + // There is nothing left to do when the node's priority is + // greater than or equal to its parent's priority. + parent = parents.Pop() + if node.priority >= parent.priority { + break + } + + // Perform a right rotation if the node is on the left side or + // a left rotation if the node is on the right side. + if parent.left == node { + node.right, parent.left = parent, node.right + } else { + node.left, parent.right = parent, node.left + } + + // Either set the new root of the tree when there is no + // grandparent or relink the grandparent to the node based on + // which side the old parent the node is replacing was on. + grandparent := parents.At(0) + if grandparent == nil { + newRoot = node + } else if grandparent.left == parent { + grandparent.left = node + } else { + grandparent.right = node + } + } + + return newImmutable(newRoot, t.count+1, t.totalSize+nodeSize(node)) +} + +// Delete removes the passed key from the treap and returns the resulting treap +// if it exists. The original immutable treap is returned if the key does not +// exist. +func (t *Immutable) Delete(key Key) *Immutable { + // Find the node for the key while constructing a list of parents while + // doing so. + var parents parentStack + var delNode *treapNode + for node := t.root; node != nil; { + parents.Push(node) + + // Traverse left or right depending on the result of the + // comparison. + compareResult := bytes.Compare(key[:], node.key[:]) + if compareResult < 0 { + node = node.left + continue + } + if compareResult > 0 { + node = node.right + continue + } + + // The key exists. + delNode = node + break + } + + // There is nothing to do if the key does not exist. + if delNode == nil { + return t + } + + // When the only node in the tree is the root node and it is the one + // being deleted, there is nothing else to do besides removing it. + parent := parents.At(1) + if parent == nil && delNode.left == nil && delNode.right == nil { + return newImmutable(nil, 0, 0) + } + + // Construct a replaced list of parents and the node to delete itself. + // This is done because this is an immutable data structure and + // therefore all ancestors of the node that will be deleted, up to and + // including the root, need to be replaced. + var newParents parentStack + for i := parents.Len(); i > 0; i-- { + node := parents.At(i - 1) + nodeCopy := cloneTreapNode(node) + if oldParent := newParents.At(0); oldParent != nil { + if oldParent.left == node { + oldParent.left = nodeCopy + } else { + oldParent.right = nodeCopy + } + } + newParents.Push(nodeCopy) + } + delNode = newParents.Pop() + parent = newParents.At(0) + + // Perform rotations to move the node to delete to a leaf position while + // maintaining the min-heap while replacing the modified children. + var child *treapNode + newRoot := newParents.At(newParents.Len() - 1) + for delNode.left != nil || delNode.right != nil { + // Choose the child with the higher priority. + var isLeft bool + if delNode.left == nil { + child = delNode.right + } else if delNode.right == nil { + child = delNode.left + isLeft = true + } else if delNode.left.priority >= delNode.right.priority { + child = delNode.left + isLeft = true + } else { + child = delNode.right + } + + // Rotate left or right depending on which side the child node + // is on. This has the effect of moving the node to delete + // towards the bottom of the tree while maintaining the + // min-heap. + child = cloneTreapNode(child) + if isLeft { + child.right, delNode.left = delNode, child.right + } else { + child.left, delNode.right = delNode, child.left + } + + // Either set the new root of the tree when there is no + // grandparent or relink the grandparent to the node based on + // which side the old parent the node is replacing was on. + // + // Since the node to be deleted was just moved down a level, the + // new grandparent is now the current parent and the new parent + // is the current child. + if parent == nil { + newRoot = child + } else if parent.left == delNode { + parent.left = child + } else { + parent.right = child + } + + // The parent for the node to delete is now what was previously + // its child. + parent = child + } + + // Delete the node, which is now a leaf node, by disconnecting it from + // its parent. + if parent.right == delNode { + parent.right = nil + } else { + parent.left = nil + } + + return newImmutable(newRoot, t.count-1, t.totalSize-nodeSize(delNode)) +} + +// ForEach invokes the passed function with every key/value pair in the treap +// in ascending order. +func (t *Immutable) ForEach(fn func(k Key, v *Value) bool) { + // Add the root node and all children to the left of it to the list of + // nodes to traverse and loop until they, and all of their child nodes, + // have been traversed. + var parents parentStack + for node := t.root; node != nil; node = node.left { + parents.Push(node) + } + for parents.Len() > 0 { + node := parents.Pop() + if !fn(node.key, node.value) { + return + } + + // Extend the nodes to traverse by all children to the left of + // the current node's right child. + for node := node.right; node != nil; node = node.left { + parents.Push(node) + } + } +} + +// NewImmutable returns a new empty immutable treap ready for use. See the +// documentation for the Immutable structure for more details. +func NewImmutable() *Immutable { + return &Immutable{} +} diff --git a/blockchain/stake/internal/tickettreap/immutable_test.go b/blockchain/stake/internal/tickettreap/immutable_test.go new file mode 100644 index 00000000..ef257058 --- /dev/null +++ b/blockchain/stake/internal/tickettreap/immutable_test.go @@ -0,0 +1,501 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2016 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package tickettreap + +import ( + "bytes" + "crypto/sha256" + "reflect" + "testing" +) + +// TestImmutableEmpty ensures calling functions on an empty immutable treap +// works as expected. +func TestImmutableEmpty(t *testing.T) { + t.Parallel() + + // Ensure the treap length is the expected value. + testTreap := NewImmutable() + if gotLen := testTreap.Len(); gotLen != 0 { + t.Fatalf("Len: unexpected length - got %d, want %d", gotLen, 0) + } + + // Ensure the reported size is 0. + if gotSize := testTreap.Size(); gotSize != 0 { + t.Fatalf("Size: unexpected byte size - got %d, want 0", + gotSize) + } + + // Ensure there are no errors with requesting keys from an empty treap. + key := uint32ToKey(0) + if gotVal := testTreap.Has(key); gotVal != false { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get: unexpected result - got %v, want nil", gotVal) + } + + // Ensure there are no panics when deleting keys from an empty treap. + testTreap.Delete(key) + + // Ensure the number of keys iterated by ForEach on an empty treap is + // zero. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + numIterated++ + return true + }) + if numIterated != 0 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want 0", + numIterated) + } +} + +// TestImmutableSequential ensures that putting keys into an immutable treap in +// sequential order works as expected. +func TestImmutableSequential(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap = testTreap.Put(key, value) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !reflect.DeepEqual(gotVal, value) { + t.Fatalf("Get #%d: unexpected value - got %v, want %v", + i, gotVal, value) + } + + // Ensure the expected size is reported. + expectedSize += (nodeFieldsSize + uint64(len(key)) + nodeValueSize) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Ensure the all keys are iterated by ForEach in order. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + // Ensure the key is as expected. + wantKey := uint32ToKey(uint32(numIterated)) + if !bytes.Equal(k[:], wantKey[:]) { + t.Fatalf("ForEach #%d: unexpected key - got %x, want %x", + numIterated, k, wantKey) + } + + // Ensure the value is as expected. + wantValue := &Value{Height: uint32(numIterated)} + if !reflect.DeepEqual(v, wantValue) { + t.Fatalf("ForEach #%d: unexpected value - got %v, want %v", + numIterated, v, wantValue) + } + + numIterated++ + return true + }) + + // Ensure all items were iterated. + if numIterated != numItems { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + testTreap = testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %v, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestImmutableReverseSequential ensures that putting keys into an immutable +// treap in reverse sequential order works as expected. +func TestImmutableReverseSequential(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(numItems - i - 1)) + value := &Value{Height: uint32(numItems - i - 1)} + testTreap = testTreap.Put(key, value) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !reflect.DeepEqual(gotVal, value) { + t.Fatalf("Get #%d: unexpected value - got %v, want %v", + i, gotVal, value) + } + + // Ensure the expected size is reported. + expectedSize += (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Ensure the all keys are iterated by ForEach in order. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + // Ensure the key is as expected. + wantKey := uint32ToKey(uint32(numIterated)) + if !bytes.Equal(k[:], wantKey[:]) { + t.Fatalf("ForEach #%d: unexpected key - got %x, want %x", + numIterated, k, wantKey) + } + + // Ensure the value is as expected. + wantValue := &Value{Height: uint32(numIterated)} + if !reflect.DeepEqual(v, wantValue) { + t.Fatalf("ForEach #%d: unexpected value - got %v, want %v", + numIterated, v, wantValue) + } + + numIterated++ + return true + }) + + // Ensure all items were iterated. + if numIterated != numItems { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + // Intentionally use the reverse order they were inserted here. + key := uint32ToKey(uint32(i)) + testTreap = testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %v, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestImmutableUnordered ensures that putting keys into an immutable treap in +// no paritcular order works as expected. +func TestImmutableUnordered(t *testing.T) { + t.Parallel() + + // Insert a bunch of out-of-order keys while checking several of the + // treap functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + // Hash the serialized int to generate out-of-order keys. + key := Key(sha256.Sum256(serializeUint32(uint32(i)))) + value := &Value{Height: uint32(i)} + testTreap = testTreap.Put(key, value) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !reflect.DeepEqual(gotVal, value) { + t.Fatalf("Get #%d: unexpected value - got %v, want %v", + i, gotVal, value) + } + + // Ensure the expected size is reported. + expectedSize += nodeFieldsSize + uint64(len(key)) + 4 + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + // Hash the serialized int to generate out-of-order keys. + key := Key(sha256.Sum256(serializeUint32(uint32(i)))) + testTreap = testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %v, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestImmutableDuplicatePut ensures that putting a duplicate key into an +// immutable treap works as expected. +func TestImmutableDuplicatePut(t *testing.T) { + t.Parallel() + + expectedVal := &Value{Height: 10000} + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap = testTreap.Put(key, value) + expectedSize += nodeFieldsSize + uint64(len(key)) + 4 + + // Put a duplicate key with the the expected final value. + testTreap = testTreap.Put(key, expectedVal) + + // Ensure the key still exists and is the new value. + if gotVal := testTreap.Has(key); gotVal != true { + t.Fatalf("Has: unexpected result - got %v, want false", + gotVal) + } + if gotVal := testTreap.Get(key); !reflect.DeepEqual(gotVal, expectedVal) { + t.Fatalf("Get: unexpected result - got %v, want %v", + gotVal, expectedVal) + } + + // Ensure the expected size is reported. + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size: unexpected byte size - got %d, want %d", + gotSize, expectedSize) + } + } +} + +// TestImmutableNilValue ensures that putting a nil value into an immutable +// treap results in a NOOP. +func TestImmutableNilValue(t *testing.T) { + t.Parallel() + + key := uint32ToKey(0) + + // Put the key with a nil value. + testTreap := NewImmutable() + testTreap = testTreap.Put(key, nil) + + // Ensure the key does NOT exist. + if gotVal := testTreap.Has(key); gotVal == true { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get: unexpected result - got %v, want nil", gotVal) + } +} + +// TestImmutableForEachStopIterator ensures that returning false from the ForEach +// callback on an immutable treap stops iteration early. +func TestImmutableForEachStopIterator(t *testing.T) { + t.Parallel() + + // Insert a few keys. + numItems := 10 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap = testTreap.Put(key, value) + } + + // Ensure ForEach exits early on false return by caller. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + numIterated++ + if numIterated == numItems/2 { + return false + } + return true + }) + if numIterated != numItems/2 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems/2) + } +} + +// TestImmutableSnapshot ensures that immutable treaps are actually immutable by +// keeping a reference to the previous treap, performing a mutation, and then +// ensuring the referenced treap does not have the mutation applied. +func TestImmutableSnapshot(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewImmutable() + for i := 0; i < numItems; i++ { + treapSnap := testTreap + + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap = testTreap.Put(key, value) + + // Ensure the length of the treap snapshot is the expected + // value. + if gotLen := treapSnap.Len(); gotLen != i { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i) + } + + // Ensure the treap snapshot does not have the key. + if treapSnap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that doesn't exist in the treap snapshot and + // ensure it is nil. + if gotVal := treapSnap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %v, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + if gotSize := treapSnap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + expectedSize += (nodeFieldsSize + uint64(len(key)) + 4) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + treapSnap := testTreap + + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap = testTreap.Delete(key) + + // Ensure the length of the treap snapshot is the expected + // value. + if gotLen := treapSnap.Len(); gotLen != numItems-i { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i) + } + + // Ensure the treap snapshot still has the key. + if !treapSnap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap snapshot and ensure it is still + // the expected value. + if gotVal := treapSnap.Get(key); !reflect.DeepEqual(gotVal, value) { + t.Fatalf("Get #%d: unexpected value - got %v, want %v", + i, gotVal, value) + } + + // Ensure the expected size is reported. + if gotSize := treapSnap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + expectedSize -= (nodeFieldsSize + uint64(len(key)) + 4) + } +} diff --git a/blockchain/stake/internal/tickettreap/mutable.go b/blockchain/stake/internal/tickettreap/mutable.go new file mode 100644 index 00000000..62b312b3 --- /dev/null +++ b/blockchain/stake/internal/tickettreap/mutable.go @@ -0,0 +1,273 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2016 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package tickettreap + +import "bytes" + +// Mutable represents a treap data structure which is used to hold ordered +// key/value pairs using a combination of binary search tree and heap semantics. +// It is a self-organizing and randomized data structure that doesn't require +// complex operations to maintain balance. Search, insert, and delete +// operations are all O(log n). +type Mutable struct { + root *treapNode + count int + + // totalSize is the best estimate of the total size of of all data in + // the treap including the keys, values, and node sizes. + totalSize uint64 +} + +// Len returns the number of items stored in the treap. +func (t *Mutable) Len() int { + return t.count +} + +// Size returns a best estimate of the total number of bytes the treap is +// consuming including all of the fields used to represent the nodes as well as +// the size of the keys and values. Shared values are not detected, so the +// returned size assumes each value is pointing to different memory. +func (t *Mutable) Size() uint64 { + return t.totalSize +} + +// get returns the treap node that contains the passed key and its parent. When +// the found node is the root of the tree, the parent will be nil. When the key +// does not exist, both the node and the parent will be nil. +func (t *Mutable) get(key Key) (*treapNode, *treapNode) { + var parent *treapNode + for node := t.root; node != nil; { + // Traverse left or right depending on the result of the + // comparison. + compareResult := bytes.Compare(key[:], node.key[:]) + if compareResult < 0 { + parent = node + node = node.left + continue + } + if compareResult > 0 { + parent = node + node = node.right + continue + } + + // The key exists. + return node, parent + } + + // A nil node was reached which means the key does not exist. + return nil, nil +} + +// Has returns whether or not the passed key exists. +func (t *Mutable) Has(key Key) bool { + if node, _ := t.get(key); node != nil { + return true + } + return false +} + +// Get returns the value for the passed key. The function will return nil when +// the key does not exist. +func (t *Mutable) Get(key Key) *Value { + if node, _ := t.get(key); node != nil { + return node.value + } + return nil +} + +// relinkGrandparent relinks the node into the treap after it has been rotated +// by changing the passed grandparent's left or right pointer, depending on +// where the old parent was, to point at the passed node. Otherwise, when there +// is no grandparent, it means the node is now the root of the tree, so update +// it accordingly. +func (t *Mutable) relinkGrandparent(node, parent, grandparent *treapNode) { + // The node is now the root of the tree when there is no grandparent. + if grandparent == nil { + t.root = node + return + } + + // Relink the grandparent's left or right pointer based on which side + // the old parent was. + if grandparent.left == parent { + grandparent.left = node + } else { + grandparent.right = node + } +} + +// Put inserts the passed key/value pair. Passing a nil value will result in a +// NOOP. +func (t *Mutable) Put(key Key, value *Value) { + // Nothing to do if a nil value is passed. + if value == nil { + return + } + + // The node is the root of the tree if there isn't already one. + if t.root == nil { + node := newTreapNode(key, value, rng.Int()) + t.count = 1 + t.totalSize = nodeSize(node) + t.root = node + return + } + + // Find the binary tree insertion point and construct a list of parents + // while doing so. When the key matches an entry already in the treap, + // just update its value and return. + var parents parentStack + var compareResult int + for node := t.root; node != nil; { + parents.Push(node) + compareResult = bytes.Compare(key[:], node.key[:]) + if compareResult < 0 { + node = node.left + continue + } + if compareResult > 0 { + node = node.right + continue + } + + // The key already exists, so update its value. + node.value = value + return + } + + // Link the new node into the binary tree in the correct position. + node := newTreapNode(key, value, rng.Int()) + t.count++ + t.totalSize += nodeSize(node) + parent := parents.At(0) + if compareResult < 0 { + parent.left = node + } else { + parent.right = node + } + + // Perform any rotations needed to maintain the min-heap. + for parents.Len() > 0 { + // There is nothing left to do when the node's priority is + // greater than or equal to its parent's priority. + parent = parents.Pop() + if node.priority >= parent.priority { + break + } + + // Perform a right rotation if the node is on the left side or + // a left rotation if the node is on the right side. + if parent.left == node { + node.right, parent.left = parent, node.right + } else { + node.left, parent.right = parent, node.left + } + t.relinkGrandparent(node, parent, parents.At(0)) + } +} + +// Delete removes the passed key if it exists. +func (t *Mutable) Delete(key Key) { + // Find the node for the key along with its parent. There is nothing to + // do if the key does not exist. + node, parent := t.get(key) + if node == nil { + return + } + + // When the only node in the tree is the root node and it is the one + // being deleted, there is nothing else to do besides removing it. + if parent == nil && node.left == nil && node.right == nil { + t.root = nil + t.count = 0 + t.totalSize = 0 + return + } + + // Perform rotations to move the node to delete to a leaf position while + // maintaining the min-heap. + var isLeft bool + var child *treapNode + for node.left != nil || node.right != nil { + // Choose the child with the higher priority. + if node.left == nil { + child = node.right + isLeft = false + } else if node.right == nil { + child = node.left + isLeft = true + } else if node.left.priority >= node.right.priority { + child = node.left + isLeft = true + } else { + child = node.right + isLeft = false + } + + // Rotate left or right depending on which side the child node + // is on. This has the effect of moving the node to delete + // towards the bottom of the tree while maintaining the + // min-heap. + if isLeft { + child.right, node.left = node, child.right + } else { + child.left, node.right = node, child.left + } + t.relinkGrandparent(child, node, parent) + + // The parent for the node to delete is now what was previously + // its child. + parent = child + } + + // Delete the node, which is now a leaf node, by disconnecting it from + // its parent. + if parent.right == node { + parent.right = nil + } else { + parent.left = nil + } + t.count-- + t.totalSize -= nodeSize(node) +} + +// ForEach invokes the passed function with every key/value pair in the treap +// in ascending order. +func (t *Mutable) ForEach(fn func(k Key, v *Value) bool) { + // Add the root node and all children to the left of it to the list of + // nodes to traverse and loop until they, and all of their child nodes, + // have been traversed. + var parents parentStack + for node := t.root; node != nil; node = node.left { + parents.Push(node) + } + for parents.Len() > 0 { + node := parents.Pop() + if !fn(node.key, node.value) { + return + } + + // Extend the nodes to traverse by all children to the left of + // the current node's right child. + for node := node.right; node != nil; node = node.left { + parents.Push(node) + } + } +} + +// Reset efficiently removes all items in the treap. +func (t *Mutable) Reset() { + t.count = 0 + t.totalSize = 0 + t.root = nil +} + +// NewMutable returns a new empty mutable treap ready for use. See the +// documentation for the Mutable structure for more details. +func NewMutable() *Mutable { + return &Mutable{} +} diff --git a/blockchain/stake/internal/tickettreap/mutable_test.go b/blockchain/stake/internal/tickettreap/mutable_test.go new file mode 100644 index 00000000..faf252f4 --- /dev/null +++ b/blockchain/stake/internal/tickettreap/mutable_test.go @@ -0,0 +1,475 @@ +// Copyright (c) 2015-2016 The btcsuite developers +// Copyright (c) 2016 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package tickettreap + +import ( + "bytes" + "crypto/sha256" + "reflect" + "testing" +) + +// TestMutableEmpty ensures calling functions on an empty mutable treap works as +// expected. +func TestMutableEmpty(t *testing.T) { + t.Parallel() + + // Ensure the treap length is the expected value. + testTreap := NewMutable() + if gotLen := testTreap.Len(); gotLen != 0 { + t.Fatalf("Len: unexpected length - got %d, want %d", gotLen, 0) + } + + // Ensure the reported size is 0. + if gotSize := testTreap.Size(); gotSize != 0 { + t.Fatalf("Size: unexpected byte size - got %d, want 0", + gotSize) + } + + // Ensure there are no errors with requesting keys from an empty treap. + key := uint32ToKey(0) + if gotVal := testTreap.Has(key); gotVal != false { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get: unexpected result - got %v, want nil", gotVal) + } + + // Ensure there are no panics when deleting keys from an empty treap. + testTreap.Delete(key) + + // Ensure the number of keys iterated by ForEach on an empty treap is + // zero. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + numIterated++ + return true + }) + if numIterated != 0 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want 0", + numIterated) + } +} + +// TestMutableReset ensures that resetting an existing mutable treap works as +// expected. +func TestMutableReset(t *testing.T) { + t.Parallel() + + // Insert a few keys. + numItems := 10 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap.Put(key, value) + } + + // Reset it. + testTreap.Reset() + + // Ensure the treap length is now 0. + if gotLen := testTreap.Len(); gotLen != 0 { + t.Fatalf("Len: unexpected length - got %d, want %d", gotLen, 0) + } + + // Ensure the reported size is now 0. + if gotSize := testTreap.Size(); gotSize != 0 { + t.Fatalf("Size: unexpected byte size - got %d, want 0", + gotSize) + } + + // Ensure the treap no longer has any of the keys. + for i := 0; i < numItems; i++ { + // Ensure the treap no longer has the key. + key := uint32ToKey(uint32(i)) + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %v, want nil", + i, gotVal) + } + } + + // Ensure the number of keys iterated by ForEach is zero. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + numIterated++ + return true + }) + if numIterated != 0 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want 0", + numIterated) + } +} + +// TestMutableSequential ensures that putting keys into a mutable treap in +// sequential order works as expected. +func TestMutableSequential(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap.Put(key, value) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !reflect.DeepEqual(gotVal, value) { + t.Fatalf("Get #%d: unexpected value - got %v, want %v", + i, gotVal, value) + } + + // Ensure the expected size is reported. + expectedSize += (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Ensure the all keys are iterated by ForEach in order. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + // Ensure the key is as expected. + wantKey := uint32ToKey(uint32(numIterated)) + if !bytes.Equal(k[:], wantKey[:]) { + t.Fatalf("ForEach #%d: unexpected key - got %x, want %x", + numIterated, k, wantKey) + } + + // Ensure the value is as expected. + wantValue := &Value{Height: uint32(numIterated)} + if !reflect.DeepEqual(v, wantValue) { + t.Fatalf("ForEach #%d: unexpected value - got %v, want %v", + numIterated, v, wantValue) + } + + numIterated++ + return true + }) + + // Ensure all items were iterated. + if numIterated != numItems { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %v, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestMutableReverseSequential ensures that putting keys into a mutable treap +// in reverse sequential order works as expected. +func TestMutableReverseSequential(t *testing.T) { + t.Parallel() + + // Insert a bunch of sequential keys while checking several of the treap + // functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(numItems - i - 1)) + value := &Value{Height: uint32(numItems - i - 1)} + testTreap.Put(key, value) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !reflect.DeepEqual(gotVal, value) { + t.Fatalf("Get #%d: unexpected value - got %v, want %v", + i, gotVal, value) + } + + // Ensure the expected size is reported. + expectedSize += (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Ensure the all keys are iterated by ForEach in order. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + // Ensure the key is as expected. + wantKey := uint32ToKey(uint32(numIterated)) + if !bytes.Equal(k[:], wantKey[:]) { + t.Fatalf("ForEach #%d: unexpected key - got %x, want %x", + numIterated, k, wantKey) + } + + // Ensure the value is as expected. + wantValue := &Value{Height: uint32(numIterated)} + if !reflect.DeepEqual(v, wantValue) { + t.Fatalf("ForEach #%d: unexpected value - got %v, want %v", + numIterated, v, wantValue) + } + + numIterated++ + return true + }) + + // Ensure all items were iterated. + if numIterated != numItems { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems) + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + // Intentionally use the reverse order they were inserted here. + key := uint32ToKey(uint32(i)) + testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %v, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestMutableUnordered ensures that putting keys into a mutable treap in no +// paritcular order works as expected. +func TestMutableUnordered(t *testing.T) { + t.Parallel() + + // Insert a bunch of out-of-order keys while checking several of the + // treap functions work as expected. + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + // Hash the serialized int to generate out-of-order keys. + key := Key(sha256.Sum256(serializeUint32(uint32(i)))) + value := &Value{Height: uint32(i)} + testTreap.Put(key, value) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != i+1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, i+1) + } + + // Ensure the treap has the key. + if !testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is not in treap", i, key) + } + + // Get the key from the treap and ensure it is the expected + // value. + if gotVal := testTreap.Get(key); !reflect.DeepEqual(gotVal, value) { + t.Fatalf("Get #%d: unexpected value - got %v, want %v", + i, gotVal, value) + } + + // Ensure the expected size is reported. + expectedSize += nodeFieldsSize + uint64(len(key)) + 4 + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } + + // Delete the keys one-by-one while checking several of the treap + // functions work as expected. + for i := 0; i < numItems; i++ { + // Hash the serialized int to generate out-of-order keys. + key := Key(sha256.Sum256(serializeUint32(uint32(i)))) + testTreap.Delete(key) + + // Ensure the treap length is the expected value. + if gotLen := testTreap.Len(); gotLen != numItems-i-1 { + t.Fatalf("Len #%d: unexpected length - got %d, want %d", + i, gotLen, numItems-i-1) + } + + // Ensure the treap no longer has the key. + if testTreap.Has(key) { + t.Fatalf("Has #%d: key %q is in treap", i, key) + } + + // Get the key that no longer exists from the treap and ensure + // it is nil. + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get #%d: unexpected value - got %v, want nil", + i, gotVal) + } + + // Ensure the expected size is reported. + expectedSize -= (nodeFieldsSize + uint64(len(key)) + 4) + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size #%d: unexpected byte size - got %d, "+ + "want %d", i, gotSize, expectedSize) + } + } +} + +// TestMutableDuplicatePut ensures that putting a duplicate key into a mutable +// treap updates the existing value. +func TestMutableDuplicatePut(t *testing.T) { + t.Parallel() + + expectedVal := &Value{Height: 10000} + expectedSize := uint64(0) + numItems := 1000 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap.Put(key, value) + expectedSize += nodeFieldsSize + uint64(len(key)) + 4 + + // Put a duplicate key with the the expected final value. + testTreap.Put(key, expectedVal) + + // Ensure the key still exists and is the new value. + if gotVal := testTreap.Has(key); gotVal != true { + t.Fatalf("Has: unexpected result - got %v, want false", + gotVal) + } + if gotVal := testTreap.Get(key); !reflect.DeepEqual(gotVal, expectedVal) { + t.Fatalf("Get: unexpected result - got %v, want %v", + gotVal, expectedVal) + } + + // Ensure the expected size is reported. + if gotSize := testTreap.Size(); gotSize != expectedSize { + t.Fatalf("Size: unexpected byte size - got %d, want %d", + gotSize, expectedSize) + } + } +} + +// TestMutableNilValue ensures that putting a nil value into a mutable treap +// results in a NOOP. +func TestMutableNilValue(t *testing.T) { + t.Parallel() + + key := uint32ToKey(0) + + // Put the key with a nil value. + testTreap := NewMutable() + testTreap.Put(key, nil) + + // Ensure the key does NOT exist. + if gotVal := testTreap.Has(key); gotVal == true { + t.Fatalf("Has: unexpected result - got %v, want false", gotVal) + } + if gotVal := testTreap.Get(key); gotVal != nil { + t.Fatalf("Get: unexpected result - got %v, want nil", gotVal) + } +} + +// TestMutableForEachStopIterator ensures that returning false from the ForEach +// callback of a mutable treap stops iteration early. +func TestMutableForEachStopIterator(t *testing.T) { + t.Parallel() + + // Insert a few keys. + numItems := 10 + testTreap := NewMutable() + for i := 0; i < numItems; i++ { + key := uint32ToKey(uint32(i)) + value := &Value{Height: uint32(i)} + testTreap.Put(key, value) + } + + // Ensure ForEach exits early on false return by caller. + var numIterated int + testTreap.ForEach(func(k Key, v *Value) bool { + numIterated++ + if numIterated == numItems/2 { + return false + } + return true + }) + if numIterated != numItems/2 { + t.Fatalf("ForEach: unexpected iterate count - got %d, want %d", + numIterated, numItems/2) + } +}