|
- package merkletrie
-
- import (
- "fmt"
- "io"
-
- "gopkg.in/src-d/go-git.v4/utils/merkletrie/internal/frame"
- "gopkg.in/src-d/go-git.v4/utils/merkletrie/noder"
- )
-
- // Iter is an iterator for merkletries (only the trie part of the
- // merkletrie is relevant here, it does not use the Hasher interface).
- //
- // The iteration is performed in depth-first pre-order. Entries at each
- // depth are traversed in (case-sensitive) alphabetical order.
- //
- // This is the kind of traversal you will expect when listing ordinary
- // files and directories recursively, for example:
- //
- // Trie Traversal order
- // ---- ---------------
- // .
- // / | \ c
- // / | \ d/
- // d c z ===> d/a
- // / \ d/b
- // b a z
- //
- //
- // This iterator is somewhat especial as you can chose to skip whole
- // "directories" when iterating:
- //
- // - The Step method will iterate normally.
- //
- // - the Next method will not descend deeper into the tree.
- //
- // For example, if the iterator is at `d/`, the Step method will return
- // `d/a` while the Next would have returned `z` instead (skipping `d/`
- // and its descendants). The name of the these two methods are based on
- // the well known "next" and "step" operations, quite common in
- // debuggers, like gdb.
- //
- // The paths returned by the iterator will be relative, if the iterator
- // was created from a single node, or absolute, if the iterator was
- // created from the path to the node (the path will be prefixed to all
- // returned paths).
- type Iter struct {
- // Tells if the iteration has started.
- hasStarted bool
- // The top of this stack has the current node and its siblings. The
- // rest of the stack keeps the ancestors of the current node and
- // their corresponding siblings. The current element is always the
- // top element of the top frame.
- //
- // When "step"ping into a node, its children are pushed as a new
- // frame.
- //
- // When "next"ing pass a node, the current element is dropped by
- // popping the top frame.
- frameStack []*frame.Frame
- // The base path used to turn the relative paths used internally by
- // the iterator into absolute paths used by external applications.
- // For relative iterator this will be nil.
- base noder.Path
- }
-
- // NewIter returns a new relative iterator using the provider noder as
- // its unnamed root. When iterating, all returned paths will be
- // relative to node.
- func NewIter(n noder.Noder) (*Iter, error) {
- return newIter(n, nil)
- }
-
- // NewIterFromPath returns a new absolute iterator from the noder at the
- // end of the path p. When iterating, all returned paths will be
- // absolute, using the root of the path p as their root.
- func NewIterFromPath(p noder.Path) (*Iter, error) {
- return newIter(p, p) // Path implements Noder
- }
-
- func newIter(root noder.Noder, base noder.Path) (*Iter, error) {
- ret := &Iter{
- base: base,
- }
-
- if root == nil {
- return ret, nil
- }
-
- frame, err := frame.New(root)
- if err != nil {
- return nil, err
- }
- ret.push(frame)
-
- return ret, nil
- }
-
- func (iter *Iter) top() (*frame.Frame, bool) {
- if len(iter.frameStack) == 0 {
- return nil, false
- }
- top := len(iter.frameStack) - 1
-
- return iter.frameStack[top], true
- }
-
- func (iter *Iter) push(f *frame.Frame) {
- iter.frameStack = append(iter.frameStack, f)
- }
-
- const (
- doDescend = true
- dontDescend = false
- )
-
- // Next returns the path of the next node without descending deeper into
- // the trie and nil. If there are no more entries in the trie it
- // returns nil and io.EOF. In case of error, it will return nil and the
- // error.
- func (iter *Iter) Next() (noder.Path, error) {
- return iter.advance(dontDescend)
- }
-
- // Step returns the path to the next node in the trie, descending deeper
- // into it if needed, and nil. If there are no more nodes in the trie,
- // it returns nil and io.EOF. In case of error, it will return nil and
- // the error.
- func (iter *Iter) Step() (noder.Path, error) {
- return iter.advance(doDescend)
- }
-
- // Advances the iterator in the desired direction: descend or
- // dontDescend.
- //
- // Returns the new current element and a nil error on success. If there
- // are no more elements in the trie below the base, it returns nil, and
- // io.EOF. Returns nil and an error in case of errors.
- func (iter *Iter) advance(wantDescend bool) (noder.Path, error) {
- current, err := iter.current()
- if err != nil {
- return nil, err
- }
-
- // The first time we just return the current node.
- if !iter.hasStarted {
- iter.hasStarted = true
- return current, nil
- }
-
- // Advances means getting a next current node, either its first child or
- // its next sibling, depending if we must descend or not.
- numChildren, err := current.NumChildren()
- if err != nil {
- return nil, err
- }
-
- mustDescend := numChildren != 0 && wantDescend
- if mustDescend {
- // descend: add a new frame with the current's children.
- frame, err := frame.New(current)
- if err != nil {
- return nil, err
- }
- iter.push(frame)
- } else {
- // don't descend: just drop the current node
- iter.drop()
- }
-
- return iter.current()
- }
-
- // Returns the path to the current node, adding the base if there was
- // one, and a nil error. If there were no noders left, it returns nil
- // and io.EOF. If an error occurred, it returns nil and the error.
- func (iter *Iter) current() (noder.Path, error) {
- if topFrame, ok := iter.top(); !ok {
- return nil, io.EOF
- } else if _, ok := topFrame.First(); !ok {
- return nil, io.EOF
- }
-
- ret := make(noder.Path, 0, len(iter.base)+len(iter.frameStack))
-
- // concat the base...
- ret = append(ret, iter.base...)
- // ... and the current node and all its ancestors
- for i, f := range iter.frameStack {
- t, ok := f.First()
- if !ok {
- panic(fmt.Sprintf("frame %d is empty", i))
- }
- ret = append(ret, t)
- }
-
- return ret, nil
- }
-
- // removes the current node if any, and all the frames that become empty as a
- // consequence of this action.
- func (iter *Iter) drop() {
- frame, ok := iter.top()
- if !ok {
- return
- }
-
- frame.Drop()
- // if the frame is empty, remove it and its parent, recursively
- if frame.Len() == 0 {
- top := len(iter.frameStack) - 1
- iter.frameStack[top] = nil
- iter.frameStack = iter.frameStack[:top]
- iter.drop()
- }
- }
|