aboutsummaryrefslogblamecommitdiffstats
path: root/difftree/internal/merkletrie/iter.go
blob: e17596650cfcca39f54481cdaf2e5eccf3dede04 (plain) (tree)






































































































































































                                                                            
package merkletrie

// Iter is a radix tree iterator that will traverse the trie in
// depth-first pre-order.  Entries are traversed in (case-sensitive)
// alphabetical order for each level.
//
// This is the kind of traversal you will expect when listing
// ordinary files and directories recursively, for example:
//
//             Trie           Traversal order
//             ----           ---------------
//              .
//            / | \           a
//           /  |  \          b
//          b   a   z   ===>  b/a
//         / \                b/c
//        c   a               z
//
//
// The Step method will return the next item, the Next method will do
// the same but without descending deeper into the tree (i.e. skipping
// the contents of "directories").
//
// The name of the type and its methods are based on the well known "next"
// and "step" operations, quite common in debuggers, like gdb.
type Iter struct {
	// tells if the iteration has started.
	hasStarted bool
	// Each level of the tree is represented as a frame, this stack
	// keeps track of the frames wrapping the current iterator position.
	// The iterator will "step" into a node by adding its frame to the
	// stack, or go to the next element at the same level by poping the
	// current frame.
	frameStack []*frame
}

// NewIter returns a new iterator for the trie with its root at n.
func NewIter(n Noder) *Iter {
	ret := &Iter{}
	ret.push(newFrame("", n))

	return ret
}

func (iter *Iter) top() (*frame, bool) {
	if len(iter.frameStack) == 0 {
		return nil, false
	}

	top := len(iter.frameStack) - 1

	return iter.frameStack[top], true
}

func (iter *Iter) pop() (*frame, bool) {
	if len(iter.frameStack) == 0 {
		return nil, false
	}

	top := len(iter.frameStack) - 1
	ret := iter.frameStack[top]
	iter.frameStack[top] = nil
	iter.frameStack = iter.frameStack[:top]

	return ret, true
}

func (iter *Iter) push(f *frame) {
	iter.frameStack = append(iter.frameStack, f)
}

const (
	descend     = true
	dontDescend = false
)

// Next returns the next node without descending deeper into the tree
// and true.  If there are no more entries it returns nil and false.
func (iter *Iter) Next() (Noder, bool) {
	return iter.advance(dontDescend)
}

// Step returns the next node in the tree, descending deeper into it if
// needed. If there are no more nodes in the tree, it returns nil and
// false.
func (iter *Iter) Step() (Noder, bool) {
	return iter.advance(descend)
}

// advances the iterator in whatever direction you want: descend or
// dontDescend.
func (iter *Iter) advance(mustDescend bool) (Noder, bool) {
	node, ok := iter.current()
	if !ok {
		return nil, false
	}

	// The first time we just return the current node.
	if !iter.hasStarted {
		iter.hasStarted = true
		return node, ok
	}
	// following advances will involve dropping already seen nodes
	// or getting into their children

	ignoreChildren := node.NumChildren() == 0 || !mustDescend
	if ignoreChildren {
		// if we must ignore the current node children, just drop
		// it and find the next one in the existing frames.
		_ = iter.drop()
		node, ok = iter.current()
		return node, ok
	}

	// if we must descend into the current's node children, drop the
	// parent and add a new frame with its children.
	_ = iter.drop()
	iter.push(newFrame(node.Key(), node))
	node, _ = iter.current()

	return node, true
}

// returns the current frame and the current node (i.e. the ones at the
// top of their respective stacks.
func (iter *Iter) current() (Noder, bool) {
	f, ok := iter.top()
	if !ok {
		return nil, false
	}

	n, ok := f.top()
	if !ok {
		return nil, false
	}

	return n, true
}

// removes the current node and all the frames that become empty as a
// consecuence of this action. It returns true if something was dropped,
// and false if there were no more nodes in the iterator.
func (iter *Iter) drop() bool {
	frame, ok := iter.top()
	if !ok {
		return false
	}

	_, ok = frame.pop()
	if !ok {
		return false
	}

	for { // remove empty frames
		if len(frame.stack) != 0 {
			break
		}

		_, _ = iter.pop()
		frame, ok = iter.top()
		if !ok {
			break
		}
	}

	return true
}