mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-18 08:39:47 +01:00
217 lines
6.1 KiB
Go
217 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()
|
||
|
}
|
||
|
}
|