Skip to content

Commit

Permalink
trie: add start key to NodeIterator constructors
Browse files Browse the repository at this point in the history
The 'step' method is split into two parts, 'peek' and 'push'. peek
returns the next state but doesn't make it current.

The end of iteration was previously tracked by setting 'trie' to nil.
End of iteration is now tracked using the 'iteratorEnd' error, which is
slightly cleaner and requires less code.
  • Loading branch information
fjl committed Apr 25, 2017
1 parent a13e920 commit 4047cca
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 73 deletions.
4 changes: 2 additions & 2 deletions core/state/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (self *StateDB) RawDump() Dump {
Accounts: make(map[string]DumpAccount),
}

it := trie.NewIterator(self.trie.NodeIterator())
it := trie.NewIterator(self.trie.NodeIterator(nil))
for it.Next() {
addr := self.trie.GetKey(it.Key)
var data Account
Expand All @@ -62,7 +62,7 @@ func (self *StateDB) RawDump() Dump {
Code: common.Bytes2Hex(obj.Code(self.db)),
Storage: make(map[string]string),
}
storageIt := trie.NewIterator(obj.getTrie(self.db).NodeIterator())
storageIt := trie.NewIterator(obj.getTrie(self.db).NodeIterator(nil))
for storageIt.Next() {
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
}
Expand Down
4 changes: 2 additions & 2 deletions core/state/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (it *NodeIterator) step() error {
}
// Initialize the iterator if we've just started
if it.stateIt == nil {
it.stateIt = it.state.trie.NodeIterator()
it.stateIt = it.state.trie.NodeIterator(nil)
}
// If we had data nodes previously, we surely have at least state nodes
if it.dataIt != nil {
Expand Down Expand Up @@ -118,7 +118,7 @@ func (it *NodeIterator) step() error {
if err != nil {
return err
}
it.dataIt = dataTrie.NodeIterator()
it.dataIt = dataTrie.NodeIterator(nil)
if !it.dataIt.Next(true) {
it.dataIt = nil
}
Expand Down
2 changes: 1 addition & 1 deletion core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
cb(h, value)
}

it := trie.NewIterator(so.getTrie(db.db).NodeIterator())
it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
for it.Next() {
// ignore cached values
key := common.BytesToHash(db.trie.GetKey(it.Key))
Expand Down
127 changes: 76 additions & 51 deletions trie/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ package trie
import (
"bytes"
"container/heap"
"errors"

"github.com/ethereum/go-ethereum/common"
)

var iteratorEnd = errors.New("end of iteration")

// Iterator is a key-value trie iterator that traverses a Trie.
type Iterator struct {
nodeIt NodeIterator
Expand Down Expand Up @@ -79,25 +82,24 @@ type nodeIteratorState struct {
hash common.Hash // Hash of the node being iterated (nil if not standalone)
node node // Trie node being iterated
parent common.Hash // Hash of the first full ancestor node (nil if current is the root)
child int // Child to be processed next
index int // Child to be processed next
pathlen int // Length of the path to this node
}

type nodeIterator struct {
trie *Trie // Trie being iterated
stack []*nodeIteratorState // Hierarchy of trie nodes persisting the iteration state

err error // Failure set in case of an internal error in the iterator

path []byte // Path to the current node
err error // Failure set in case of an internal error in the iterator
path []byte // Path to the current node
}

// newNodeIterator creates an post-order trie iterator.
func newNodeIterator(trie *Trie) NodeIterator {
func newNodeIterator(trie *Trie, start []byte) NodeIterator {
if trie.Hash() == emptyState {
return new(nodeIterator)
}
return &nodeIterator{trie: trie}
it := &nodeIterator{trie: trie}
it.seek(start)
return it
}

// Hash returns the hash of the current node
Expand Down Expand Up @@ -147,6 +149,9 @@ func (it *nodeIterator) Path() []byte {

// Error returns the error set in case of an internal error in the iterator
func (it *nodeIterator) Error() error {
if it.err == iteratorEnd {
return nil
}
return it.err
}

Expand All @@ -155,111 +160,131 @@ func (it *nodeIterator) Error() error {
// sets the Error field to the encountered failure. If `descend` is false,
// skips iterating over any subnodes of the current node.
func (it *nodeIterator) Next(descend bool) bool {
// If the iterator failed previously, don't do anything
if it.err != nil {
return false
}
// Otherwise step forward with the iterator and report any errors
if err := it.step(descend); err != nil {
state, parentIndex, path, err := it.peek(descend)
if err != nil {
it.err = err
return false
}
return it.trie != nil
it.push(state, parentIndex, path)
return true
}

// step moves the iterator to the next node of the trie.
func (it *nodeIterator) step(descend bool) error {
if it.trie == nil {
// Abort if we reached the end of the iteration
return nil
func (it *nodeIterator) seek(prefix []byte) {
// The path we're looking for is the hex encoded key without terminator.
key := keybytesToHex(prefix)
key = key[:len(key)-1]
// Move forward until we're just before the closest match to key.
for {
state, parentIndex, path, err := it.peek(bytes.HasPrefix(key, it.path))
if err != nil || bytes.Compare(path, key) >= 0 {
it.err = err
return
}
it.push(state, parentIndex, path)
}
}

// peek creates the next state of the iterator.
func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, error) {
if len(it.stack) == 0 {
// Initialize the iterator if we've just started.
root := it.trie.Hash()
state := &nodeIteratorState{node: it.trie.root, child: -1}
state := &nodeIteratorState{node: it.trie.root, index: -1}
if root != emptyRoot {
state.hash = root
}
it.stack = append(it.stack, state)
return nil
return state, nil, nil, nil
}

if !descend {
// If we're skipping children, pop the current node first
it.path = it.path[:it.stack[len(it.stack)-1].pathlen]
it.stack = it.stack[:len(it.stack)-1]
it.pop()
}

// Continue iteration to the next child
outer:
for {
if len(it.stack) == 0 {
it.trie = nil
return nil
return nil, nil, nil, iteratorEnd
}
parent := it.stack[len(it.stack)-1]
ancestor := parent.hash
if (ancestor == common.Hash{}) {
ancestor = parent.parent
}
if node, ok := parent.node.(*fullNode); ok {
// Full node, iterate over children
for parent.child++; parent.child < len(node.Children); parent.child++ {
child := node.Children[parent.child]
// Full node, move to the first non-nil child.
for i := parent.index + 1; i < len(node.Children); i++ {
child := node.Children[i]
if child != nil {
hash, _ := child.cache()
it.stack = append(it.stack, &nodeIteratorState{
state := &nodeIteratorState{
hash: common.BytesToHash(hash),
node: child,
parent: ancestor,
child: -1,
index: -1,
pathlen: len(it.path),
})
it.path = append(it.path, byte(parent.child))
break outer
}
path := append(it.path, byte(i))
parent.index = i - 1
return state, &parent.index, path, nil
}
}
} else if node, ok := parent.node.(*shortNode); ok {
// Short node, return the pointer singleton child
if parent.child < 0 {
parent.child++
if parent.index < 0 {
hash, _ := node.Val.cache()
it.stack = append(it.stack, &nodeIteratorState{
state := &nodeIteratorState{
hash: common.BytesToHash(hash),
node: node.Val,
parent: ancestor,
child: -1,
index: -1,
pathlen: len(it.path),
})
}
var path []byte
if hasTerm(node.Key) {
it.path = append(it.path, node.Key[:len(node.Key)-1]...)
path = append(it.path, node.Key[:len(node.Key)-1]...)
} else {
it.path = append(it.path, node.Key...)
path = append(it.path, node.Key...)
}
break
return state, &parent.index, path, nil
}
} else if hash, ok := parent.node.(hashNode); ok {
// Hash node, resolve the hash child from the database
if parent.child < 0 {
parent.child++
if parent.index < 0 {
node, err := it.trie.resolveHash(hash, nil, nil)
if err != nil {
return err
return it.stack[len(it.stack)-1], &parent.index, it.path, err
}
it.stack = append(it.stack, &nodeIteratorState{
state := &nodeIteratorState{
hash: common.BytesToHash(hash),
node: node,
parent: ancestor,
child: -1,
index: -1,
pathlen: len(it.path),
})
break
}
return state, &parent.index, it.path, nil
}
}
it.path = it.path[:parent.pathlen]
it.stack = it.stack[:len(it.stack)-1]
// No more child nodes, move back up.
it.pop()
}
return nil
}

func (it *nodeIterator) push(state *nodeIteratorState, parentIndex *int, path []byte) {
it.path = path
it.stack = append(it.stack, state)
if parentIndex != nil {
*parentIndex += 1
}
}

func (it *nodeIterator) pop() {
parent := it.stack[len(it.stack)-1]
it.path = it.path[:parent.pathlen]
it.stack = it.stack[:len(it.stack)-1]
}

func compareNodes(a, b NodeIterator) int {
Expand Down
Loading

0 comments on commit 4047cca

Please sign in to comment.