1
0
Fork 0
mirror of https://github.com/Luzifer/ansible-role-version.git synced 2024-12-24 11:31:21 +00:00
ansible-role-version/vendor/gopkg.in/src-d/go-git.v4/utils/merkletrie/iter.go
Knut Ahlers 209b813c5b
Update dependencies
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2018-03-19 18:16:10 +01:00

216 lines
6.1 KiB
Go

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()
}
}