chore: revert custom index in favor of key_Split

Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
This commit is contained in:
Keith Zantow
2026-09-20 11:22:10 -04:00
parent 99615b7dfa
commit 53cd4254e4
81 changed files with 7461 additions and 6624 deletions
+40 -20
View File
@@ -1,12 +1,10 @@
// Package index provides a concurrent key-split (radix) index and a forward/reverse pair over it.
//
// Ported from a prototype dir scan package whose search strategy this
// exists to serve: a path index that answers "which entries end in .jar" and "which entries live
// under this directory" without walking a tree, so a cataloger's `**/*.ext` glob is a lookup keyed
// on the extension rather than a scan of every path.
// It answers path queries like "which entries end in .jar" or "live under this directory" as keyed
// lookups rather than tree walks, so a cataloger's `**/*.ext` glob need not scan every path.
//
// Kept close to the original so the two can be diffed. The lock type is local (see lock.go) rather
// than pulled in from the prototype's sync/atomic package, which carries unrelated helpers.
// Ported from a prototype dir-scan package and kept close to the original for diffing. The lock type
// is local (see lock.go) rather than the prototype's, which carries unrelated helpers.
package index
import (
@@ -81,11 +79,6 @@ func (n *Node[T]) Value() (v T) {
return
}
func (n *Node[T]) Contains(s string) bool {
_, equal := n._find(s)
return equal
}
func (n *Node[T]) Get(s string) (out T) {
v, equal := n._find(s)
if !equal || v == nil {
@@ -108,6 +101,41 @@ func (n *Node[T]) ByPrefix(s string) []T {
return v.Collect()
}
// ByPrefixUpTo returns the values under s, or false once there are more than limit of them. It lets a
// caller choosing between two lookups take whichever is selective without paying for the other: a
// complete result costs at most limit. The values returned alongside false are partial; discard them.
func (n *Node[T]) ByPrefixUpTo(s string, limit int) (values []T, complete bool) {
v, _ := n._find(s)
if v == nil {
return nil, true
}
return v.CollectUpTo(limit)
}
// CollectUpTo gathers this node's values, giving up as soon as there are more than limit.
func (n *Node[T]) CollectUpTo(limit int) (values []T, complete bool) {
complete = n._collectUpTo(&values, limit)
return values, complete
}
func (n *Node[T]) _collectUpTo(values *[]T, limit int) bool {
defer n.RLock()()
if n.set {
if len(*values) >= limit {
return false
}
*values = append(*values, n.value)
}
for _, v := range n.keyMap {
if !v._collectUpTo(values, limit) {
return false
}
}
return true
}
func (n *Node[T]) Update(name string, f NodeUpdateFunc[T]) {
unlock := n.Lock()
node := n._makeNodeP(&unlock, name, nil)
@@ -123,14 +151,6 @@ func (n *Node[T]) SetValue(value T) {
unlock()
}
func (n *Node[T]) UnsetValue() {
unlock := n.Lock()
var zero T
n.value = zero
n.set = false
unlock()
}
func (n *Node[T]) Collect() (values []T) {
n._collect(&values)
return values
@@ -149,7 +169,7 @@ func (n *Node[T]) _collect(values *[]T) {
unlock()
}
// _startsWith returns the node starting with the given string
// _find returns the node for s and whether it matched exactly.
func (n *Node[T]) _find(s string) (node *Node[T], equal bool) {
defer n.RLock()()
+3 -5
View File
@@ -5,12 +5,10 @@ import (
"sync"
)
// lockable is a read-write lock that hands back its own unlock function.
// lockable is a read-write lock that returns its own unlock function.
//
// Returning the unlocker rather than exposing Unlock is what makes the index's lock upgrades safe to
// write: a caller holds an opaque `unlock`, and the node it is walking can tell whether that unlock
// belongs to the write lock or the read lock and upgrade only when it has to. Carried over from the
// prototype this index came from, where the pattern is used throughout.
// Handing back an opaque unlocker (rather than exposing Unlock) lets a node tell whether the caller
// holds the read or write lock and upgrade only when needed. Carried over from the prototype index.
type lockable struct {
lock sync.RWMutex
}
+10
View File
@@ -28,6 +28,16 @@ func (f *PrefixSuffix[T]) BySuffix(suffix string) []T {
return f.reverseIndex.ByPrefix(reverse(suffix))
}
// ByPrefixUpTo and BySuffixUpTo are the bounded forms, reporting false rather than returning more than
// limit values, so a caller can take whichever side is selective without paying for the other.
func (f *PrefixSuffix[T]) ByPrefixUpTo(prefix string, limit int) ([]T, bool) {
return f.forwardIndex.ByPrefixUpTo(prefix, limit)
}
func (f *PrefixSuffix[T]) BySuffixUpTo(suffix string, limit int) ([]T, bool) {
return f.reverseIndex.ByPrefixUpTo(reverse(suffix), limit)
}
func reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {