mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-19 05:22:06 +02:00
Bump deps
``` * use go 1.17 semantics in go.mod * pin tonistiigi/fsutil dep to match docker/docker's dep go get -u ./... go mod vendor ```
This commit is contained in:
-3
@@ -1,3 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- tip
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# UNRELEASED
|
||||
|
||||
# 1.3.0 (September 17th, 2020)
|
||||
|
||||
FEATURES
|
||||
|
||||
* Add reverse tree traversal [[GH-30](https://github.com/hashicorp/go-immutable-radix/pull/30)]
|
||||
|
||||
# 1.2.0 (March 18th, 2020)
|
||||
|
||||
FEATURES
|
||||
|
||||
* Adds a `Clone` method to `Txn` allowing transactions to be split either into two independently mutable trees. [[GH-26](https://github.com/hashicorp/go-immutable-radix/pull/26)]
|
||||
|
||||
# 1.1.0 (May 22nd, 2019)
|
||||
|
||||
FEATURES
|
||||
|
||||
* Add `SeekLowerBound` to allow for range scans. [[GH-24](https://github.com/hashicorp/go-immutable-radix/pull/24)]
|
||||
|
||||
# 1.0.0 (August 30th, 2018)
|
||||
|
||||
* go mod adopted
|
||||
+26
-1
@@ -1,4 +1,4 @@
|
||||
go-immutable-radix [](https://travis-ci.org/hashicorp/go-immutable-radix)
|
||||
go-immutable-radix [](https://circleci.com/gh/hashicorp/go-immutable-radix/tree/master)
|
||||
=========
|
||||
|
||||
Provides the `iradix` package that implements an immutable [radix tree](http://en.wikipedia.org/wiki/Radix_tree).
|
||||
@@ -39,3 +39,28 @@ if string(m) != "foo" {
|
||||
}
|
||||
```
|
||||
|
||||
Here is an example of performing a range scan of the keys.
|
||||
|
||||
```go
|
||||
// Create a tree
|
||||
r := iradix.New()
|
||||
r, _, _ = r.Insert([]byte("001"), 1)
|
||||
r, _, _ = r.Insert([]byte("002"), 2)
|
||||
r, _, _ = r.Insert([]byte("005"), 5)
|
||||
r, _, _ = r.Insert([]byte("010"), 10)
|
||||
r, _, _ = r.Insert([]byte("100"), 10)
|
||||
|
||||
// Range scan over the keys that sort lexicographically between [003, 050)
|
||||
it := r.Root().Iterator()
|
||||
it.SeekLowerBound([]byte("003"))
|
||||
for key, _, ok := it.Next(); ok; key, _, ok = it.Next() {
|
||||
if key >= "050" {
|
||||
break
|
||||
}
|
||||
fmt.Println(key)
|
||||
}
|
||||
// Output:
|
||||
// 005
|
||||
// 010
|
||||
```
|
||||
|
||||
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
module github.com/hashicorp/go-immutable-radix
|
||||
|
||||
require (
|
||||
github.com/hashicorp/go-uuid v1.0.0
|
||||
github.com/hashicorp/golang-lru v0.5.0
|
||||
)
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
+14
@@ -86,6 +86,20 @@ func (t *Tree) Txn() *Txn {
|
||||
return txn
|
||||
}
|
||||
|
||||
// Clone makes an independent copy of the transaction. The new transaction
|
||||
// does not track any nodes and has TrackMutate turned off. The cloned transaction will contain any uncommitted writes in the original transaction but further mutations to either will be independent and result in different radix trees on Commit. A cloned transaction may be passed to another goroutine and mutated there independently however each transaction may only be mutated in a single thread.
|
||||
func (t *Txn) Clone() *Txn {
|
||||
// reset the writable node cache to avoid leaking future writes into the clone
|
||||
t.writable = nil
|
||||
|
||||
txn := &Txn{
|
||||
root: t.root,
|
||||
snap: t.snap,
|
||||
size: t.size,
|
||||
}
|
||||
return txn
|
||||
}
|
||||
|
||||
// TrackMutate can be used to toggle if mutations are tracked. If this is enabled
|
||||
// then notifications will be issued for affected internal nodes and leaves when
|
||||
// the transaction is committed.
|
||||
|
||||
+117
-3
@@ -1,6 +1,8 @@
|
||||
package iradix
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// Iterator is used to iterate over a set of nodes
|
||||
// in pre-order
|
||||
@@ -18,7 +20,7 @@ func (i *Iterator) SeekPrefixWatch(prefix []byte) (watch <-chan struct{}) {
|
||||
watch = n.mutateCh
|
||||
search := prefix
|
||||
for {
|
||||
// Check for key exhaution
|
||||
// Check for key exhaustion
|
||||
if len(search) == 0 {
|
||||
i.node = n
|
||||
return
|
||||
@@ -53,12 +55,124 @@ func (i *Iterator) SeekPrefix(prefix []byte) {
|
||||
i.SeekPrefixWatch(prefix)
|
||||
}
|
||||
|
||||
func (i *Iterator) recurseMin(n *Node) *Node {
|
||||
// Traverse to the minimum child
|
||||
if n.leaf != nil {
|
||||
return n
|
||||
}
|
||||
nEdges := len(n.edges)
|
||||
if nEdges > 1 {
|
||||
// Add all the other edges to the stack (the min node will be added as
|
||||
// we recurse)
|
||||
i.stack = append(i.stack, n.edges[1:])
|
||||
}
|
||||
if nEdges > 0 {
|
||||
return i.recurseMin(n.edges[0].node)
|
||||
}
|
||||
// Shouldn't be possible
|
||||
return nil
|
||||
}
|
||||
|
||||
// SeekLowerBound is used to seek the iterator to the smallest key that is
|
||||
// greater or equal to the given key. There is no watch variant as it's hard to
|
||||
// predict based on the radix structure which node(s) changes might affect the
|
||||
// result.
|
||||
func (i *Iterator) SeekLowerBound(key []byte) {
|
||||
// Wipe the stack. Unlike Prefix iteration, we need to build the stack as we
|
||||
// go because we need only a subset of edges of many nodes in the path to the
|
||||
// leaf with the lower bound. Note that the iterator will still recurse into
|
||||
// children that we don't traverse on the way to the reverse lower bound as it
|
||||
// walks the stack.
|
||||
i.stack = []edges{}
|
||||
// i.node starts off in the common case as pointing to the root node of the
|
||||
// tree. By the time we return we have either found a lower bound and setup
|
||||
// the stack to traverse all larger keys, or we have not and the stack and
|
||||
// node should both be nil to prevent the iterator from assuming it is just
|
||||
// iterating the whole tree from the root node. Either way this needs to end
|
||||
// up as nil so just set it here.
|
||||
n := i.node
|
||||
i.node = nil
|
||||
search := key
|
||||
|
||||
found := func(n *Node) {
|
||||
i.stack = append(i.stack, edges{edge{node: n}})
|
||||
}
|
||||
|
||||
findMin := func(n *Node) {
|
||||
n = i.recurseMin(n)
|
||||
if n != nil {
|
||||
found(n)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
// Compare current prefix with the search key's same-length prefix.
|
||||
var prefixCmp int
|
||||
if len(n.prefix) < len(search) {
|
||||
prefixCmp = bytes.Compare(n.prefix, search[0:len(n.prefix)])
|
||||
} else {
|
||||
prefixCmp = bytes.Compare(n.prefix, search)
|
||||
}
|
||||
|
||||
if prefixCmp > 0 {
|
||||
// Prefix is larger, that means the lower bound is greater than the search
|
||||
// and from now on we need to follow the minimum path to the smallest
|
||||
// leaf under this subtree.
|
||||
findMin(n)
|
||||
return
|
||||
}
|
||||
|
||||
if prefixCmp < 0 {
|
||||
// Prefix is smaller than search prefix, that means there is no lower
|
||||
// bound
|
||||
i.node = nil
|
||||
return
|
||||
}
|
||||
|
||||
// Prefix is equal, we are still heading for an exact match. If this is a
|
||||
// leaf and an exact match we're done.
|
||||
if n.leaf != nil && bytes.Equal(n.leaf.key, key) {
|
||||
found(n)
|
||||
return
|
||||
}
|
||||
|
||||
// Consume the search prefix if the current node has one. Note that this is
|
||||
// safe because if n.prefix is longer than the search slice prefixCmp would
|
||||
// have been > 0 above and the method would have already returned.
|
||||
search = search[len(n.prefix):]
|
||||
|
||||
if len(search) == 0 {
|
||||
// We've exhausted the search key, but the current node is not an exact
|
||||
// match or not a leaf. That means that the leaf value if it exists, and
|
||||
// all child nodes must be strictly greater, the smallest key in this
|
||||
// subtree must be the lower bound.
|
||||
findMin(n)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, take the lower bound next edge.
|
||||
idx, lbNode := n.getLowerBoundEdge(search[0])
|
||||
if lbNode == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Create stack edges for the all strictly higher edges in this node.
|
||||
if idx+1 < len(n.edges) {
|
||||
i.stack = append(i.stack, n.edges[idx+1:])
|
||||
}
|
||||
|
||||
// Recurse
|
||||
n = lbNode
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns the next node in order
|
||||
func (i *Iterator) Next() ([]byte, interface{}, bool) {
|
||||
// Initialize our stack if needed
|
||||
if i.stack == nil && i.node != nil {
|
||||
i.stack = []edges{
|
||||
edges{
|
||||
{
|
||||
edge{node: i.node},
|
||||
},
|
||||
}
|
||||
|
||||
+42
@@ -79,6 +79,18 @@ func (n *Node) getEdge(label byte) (int, *Node) {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (n *Node) getLowerBoundEdge(label byte) (int, *Node) {
|
||||
num := len(n.edges)
|
||||
idx := sort.Search(num, func(i int) bool {
|
||||
return n.edges[i].label >= label
|
||||
})
|
||||
// we want lower bound behavior so return even if it's not an exact match
|
||||
if idx < num {
|
||||
return idx, n.edges[idx].node
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (n *Node) delEdge(label byte) {
|
||||
num := len(n.edges)
|
||||
idx := sort.Search(num, func(i int) bool {
|
||||
@@ -199,6 +211,12 @@ func (n *Node) Iterator() *Iterator {
|
||||
return &Iterator{node: n}
|
||||
}
|
||||
|
||||
// ReverseIterator is used to return an iterator at
|
||||
// the given node to walk the tree backwards
|
||||
func (n *Node) ReverseIterator() *ReverseIterator {
|
||||
return NewReverseIterator(n)
|
||||
}
|
||||
|
||||
// rawIterator is used to return a raw iterator at the given node to walk the
|
||||
// tree.
|
||||
func (n *Node) rawIterator() *rawIterator {
|
||||
@@ -212,6 +230,11 @@ func (n *Node) Walk(fn WalkFn) {
|
||||
recursiveWalk(n, fn)
|
||||
}
|
||||
|
||||
// WalkBackwards is used to walk the tree in reverse order
|
||||
func (n *Node) WalkBackwards(fn WalkFn) {
|
||||
reverseRecursiveWalk(n, fn)
|
||||
}
|
||||
|
||||
// WalkPrefix is used to walk the tree under a prefix
|
||||
func (n *Node) WalkPrefix(prefix []byte, fn WalkFn) {
|
||||
search := prefix
|
||||
@@ -290,3 +313,22 @@ func recursiveWalk(n *Node, fn WalkFn) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// reverseRecursiveWalk is used to do a reverse pre-order
|
||||
// walk of a node recursively. Returns true if the walk
|
||||
// should be aborted
|
||||
func reverseRecursiveWalk(n *Node, fn WalkFn) bool {
|
||||
// Visit the leaf values if any
|
||||
if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Recurse on the children in reverse order
|
||||
for i := len(n.edges) - 1; i >= 0; i-- {
|
||||
e := n.edges[i]
|
||||
if reverseRecursiveWalk(e.node, fn) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ func (i *rawIterator) Next() {
|
||||
// Initialize our stack if needed.
|
||||
if i.stack == nil && i.node != nil {
|
||||
i.stack = []rawStackEntry{
|
||||
rawStackEntry{
|
||||
{
|
||||
edges: edges{
|
||||
edge{node: i.node},
|
||||
},
|
||||
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
package iradix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// ReverseIterator is used to iterate over a set of nodes
|
||||
// in reverse in-order
|
||||
type ReverseIterator struct {
|
||||
i *Iterator
|
||||
|
||||
// expandedParents stores the set of parent nodes whose relevant children have
|
||||
// already been pushed into the stack. This can happen during seek or during
|
||||
// iteration.
|
||||
//
|
||||
// Unlike forward iteration we need to recurse into children before we can
|
||||
// output the value stored in an internal leaf since all children are greater.
|
||||
// We use this to track whether we have already ensured all the children are
|
||||
// in the stack.
|
||||
expandedParents map[*Node]struct{}
|
||||
}
|
||||
|
||||
// NewReverseIterator returns a new ReverseIterator at a node
|
||||
func NewReverseIterator(n *Node) *ReverseIterator {
|
||||
return &ReverseIterator{
|
||||
i: &Iterator{node: n},
|
||||
}
|
||||
}
|
||||
|
||||
// SeekPrefixWatch is used to seek the iterator to a given prefix
|
||||
// and returns the watch channel of the finest granularity
|
||||
func (ri *ReverseIterator) SeekPrefixWatch(prefix []byte) (watch <-chan struct{}) {
|
||||
return ri.i.SeekPrefixWatch(prefix)
|
||||
}
|
||||
|
||||
// SeekPrefix is used to seek the iterator to a given prefix
|
||||
func (ri *ReverseIterator) SeekPrefix(prefix []byte) {
|
||||
ri.i.SeekPrefixWatch(prefix)
|
||||
}
|
||||
|
||||
// SeekReverseLowerBound is used to seek the iterator to the largest key that is
|
||||
// lower or equal to the given key. There is no watch variant as it's hard to
|
||||
// predict based on the radix structure which node(s) changes might affect the
|
||||
// result.
|
||||
func (ri *ReverseIterator) SeekReverseLowerBound(key []byte) {
|
||||
// Wipe the stack. Unlike Prefix iteration, we need to build the stack as we
|
||||
// go because we need only a subset of edges of many nodes in the path to the
|
||||
// leaf with the lower bound. Note that the iterator will still recurse into
|
||||
// children that we don't traverse on the way to the reverse lower bound as it
|
||||
// walks the stack.
|
||||
ri.i.stack = []edges{}
|
||||
// ri.i.node starts off in the common case as pointing to the root node of the
|
||||
// tree. By the time we return we have either found a lower bound and setup
|
||||
// the stack to traverse all larger keys, or we have not and the stack and
|
||||
// node should both be nil to prevent the iterator from assuming it is just
|
||||
// iterating the whole tree from the root node. Either way this needs to end
|
||||
// up as nil so just set it here.
|
||||
n := ri.i.node
|
||||
ri.i.node = nil
|
||||
search := key
|
||||
|
||||
if ri.expandedParents == nil {
|
||||
ri.expandedParents = make(map[*Node]struct{})
|
||||
}
|
||||
|
||||
found := func(n *Node) {
|
||||
ri.i.stack = append(ri.i.stack, edges{edge{node: n}})
|
||||
// We need to mark this node as expanded in advance too otherwise the
|
||||
// iterator will attempt to walk all of its children even though they are
|
||||
// greater than the lower bound we have found. We've expanded it in the
|
||||
// sense that all of its children that we want to walk are already in the
|
||||
// stack (i.e. none of them).
|
||||
ri.expandedParents[n] = struct{}{}
|
||||
}
|
||||
|
||||
for {
|
||||
// Compare current prefix with the search key's same-length prefix.
|
||||
var prefixCmp int
|
||||
if len(n.prefix) < len(search) {
|
||||
prefixCmp = bytes.Compare(n.prefix, search[0:len(n.prefix)])
|
||||
} else {
|
||||
prefixCmp = bytes.Compare(n.prefix, search)
|
||||
}
|
||||
|
||||
if prefixCmp < 0 {
|
||||
// Prefix is smaller than search prefix, that means there is no exact
|
||||
// match for the search key. But we are looking in reverse, so the reverse
|
||||
// lower bound will be the largest leaf under this subtree, since it is
|
||||
// the value that would come right before the current search key if it
|
||||
// were in the tree. So we need to follow the maximum path in this subtree
|
||||
// to find it. Note that this is exactly what the iterator will already do
|
||||
// if it finds a node in the stack that has _not_ been marked as expanded
|
||||
// so in this one case we don't call `found` and instead let the iterator
|
||||
// do the expansion and recursion through all the children.
|
||||
ri.i.stack = append(ri.i.stack, edges{edge{node: n}})
|
||||
return
|
||||
}
|
||||
|
||||
if prefixCmp > 0 {
|
||||
// Prefix is larger than search prefix, or there is no prefix but we've
|
||||
// also exhausted the search key. Either way, that means there is no
|
||||
// reverse lower bound since nothing comes before our current search
|
||||
// prefix.
|
||||
return
|
||||
}
|
||||
|
||||
// If this is a leaf, something needs to happen! Note that if it's a leaf
|
||||
// and prefixCmp was zero (which it must be to get here) then the leaf value
|
||||
// is either an exact match for the search, or it's lower. It can't be
|
||||
// greater.
|
||||
if n.isLeaf() {
|
||||
|
||||
// Firstly, if it's an exact match, we're done!
|
||||
if bytes.Equal(n.leaf.key, key) {
|
||||
found(n)
|
||||
return
|
||||
}
|
||||
|
||||
// It's not so this node's leaf value must be lower and could still be a
|
||||
// valid contender for reverse lower bound.
|
||||
|
||||
// If it has no children then we are also done.
|
||||
if len(n.edges) == 0 {
|
||||
// This leaf is the lower bound.
|
||||
found(n)
|
||||
return
|
||||
}
|
||||
|
||||
// Finally, this leaf is internal (has children) so we'll keep searching,
|
||||
// but we need to add it to the iterator's stack since it has a leaf value
|
||||
// that needs to be iterated over. It needs to be added to the stack
|
||||
// before its children below as it comes first.
|
||||
ri.i.stack = append(ri.i.stack, edges{edge{node: n}})
|
||||
// We also need to mark it as expanded since we'll be adding any of its
|
||||
// relevant children below and so don't want the iterator to re-add them
|
||||
// on its way back up the stack.
|
||||
ri.expandedParents[n] = struct{}{}
|
||||
}
|
||||
|
||||
// Consume the search prefix. Note that this is safe because if n.prefix is
|
||||
// longer than the search slice prefixCmp would have been > 0 above and the
|
||||
// method would have already returned.
|
||||
search = search[len(n.prefix):]
|
||||
|
||||
if len(search) == 0 {
|
||||
// We've exhausted the search key but we are not at a leaf. That means all
|
||||
// children are greater than the search key so a reverse lower bound
|
||||
// doesn't exist in this subtree. Note that there might still be one in
|
||||
// the whole radix tree by following a different path somewhere further
|
||||
// up. If that's the case then the iterator's stack will contain all the
|
||||
// smaller nodes already and Previous will walk through them correctly.
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, take the lower bound next edge.
|
||||
idx, lbNode := n.getLowerBoundEdge(search[0])
|
||||
|
||||
// From here, we need to update the stack with all values lower than
|
||||
// the lower bound edge. Since getLowerBoundEdge() returns -1 when the
|
||||
// search prefix is larger than all edges, we need to place idx at the
|
||||
// last edge index so they can all be place in the stack, since they
|
||||
// come before our search prefix.
|
||||
if idx == -1 {
|
||||
idx = len(n.edges)
|
||||
}
|
||||
|
||||
// Create stack edges for the all strictly lower edges in this node.
|
||||
if len(n.edges[:idx]) > 0 {
|
||||
ri.i.stack = append(ri.i.stack, n.edges[:idx])
|
||||
}
|
||||
|
||||
// Exit if there's no lower bound edge. The stack will have the previous
|
||||
// nodes already.
|
||||
if lbNode == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Recurse
|
||||
n = lbNode
|
||||
}
|
||||
}
|
||||
|
||||
// Previous returns the previous node in reverse order
|
||||
func (ri *ReverseIterator) Previous() ([]byte, interface{}, bool) {
|
||||
// Initialize our stack if needed
|
||||
if ri.i.stack == nil && ri.i.node != nil {
|
||||
ri.i.stack = []edges{
|
||||
{
|
||||
edge{node: ri.i.node},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ri.expandedParents == nil {
|
||||
ri.expandedParents = make(map[*Node]struct{})
|
||||
}
|
||||
|
||||
for len(ri.i.stack) > 0 {
|
||||
// Inspect the last element of the stack
|
||||
n := len(ri.i.stack)
|
||||
last := ri.i.stack[n-1]
|
||||
m := len(last)
|
||||
elem := last[m-1].node
|
||||
|
||||
_, alreadyExpanded := ri.expandedParents[elem]
|
||||
|
||||
// If this is an internal node and we've not seen it already, we need to
|
||||
// leave it in the stack so we can return its possible leaf value _after_
|
||||
// we've recursed through all its children.
|
||||
if len(elem.edges) > 0 && !alreadyExpanded {
|
||||
// record that we've seen this node!
|
||||
ri.expandedParents[elem] = struct{}{}
|
||||
// push child edges onto stack and skip the rest of the loop to recurse
|
||||
// into the largest one.
|
||||
ri.i.stack = append(ri.i.stack, elem.edges)
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove the node from the stack
|
||||
if m > 1 {
|
||||
ri.i.stack[n-1] = last[:m-1]
|
||||
} else {
|
||||
ri.i.stack = ri.i.stack[:n-1]
|
||||
}
|
||||
// We don't need this state any more as it's no longer in the stack so we
|
||||
// won't visit it again
|
||||
if alreadyExpanded {
|
||||
delete(ri.expandedParents, elem)
|
||||
}
|
||||
|
||||
// If this is a leaf, return it
|
||||
if elem.leaf != nil {
|
||||
return elem.leaf.key, elem.leaf.val, true
|
||||
}
|
||||
|
||||
// it's not a leaf so keep walking the stack to find the previous leaf
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
Reference in New Issue
Block a user