Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+221
@@ -0,0 +1,221 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"iter"
|
||||
|
||||
"github.com/gaissmai/bart/internal/bitset"
|
||||
"github.com/gaissmai/bart/internal/lpm"
|
||||
"github.com/gaissmai/bart/internal/sparse"
|
||||
)
|
||||
|
||||
// LiteNode is a space-optimized version of [BartNode] that tracks prefix existence
|
||||
// without storing associated values.
|
||||
type LiteNode[V any] struct {
|
||||
Children sparse.Array256[any]
|
||||
Prefixes struct {
|
||||
// BitSet256 tracks the presence of prefixes at this level.
|
||||
bitset.BitSet256
|
||||
// Count maintains the current number of set bits, updated on modification
|
||||
// to avoid expensive population counting.
|
||||
Count uint16
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the node contains no routing entries (prefixes)
|
||||
// and no child nodes. Empty nodes are candidates for compression or removal
|
||||
// during trie optimization.
|
||||
func (n *LiteNode[V]) IsEmpty() bool {
|
||||
if n == nil {
|
||||
return true
|
||||
}
|
||||
return n.Prefixes.Count == 0 && n.Children.Len() == 0
|
||||
}
|
||||
|
||||
// PrefixCount returns the number of prefixes stored in this node.
|
||||
func (n *LiteNode[V]) PrefixCount() int {
|
||||
return int(n.Prefixes.Count)
|
||||
}
|
||||
|
||||
// ChildCount returns the number of slots used in this node.
|
||||
func (n *LiteNode[V]) ChildCount() int {
|
||||
return n.Children.Len()
|
||||
}
|
||||
|
||||
// InsertPrefix adds a routing entry at the specified index.
|
||||
// It returns true if a prefix already existed at that index,
|
||||
// false if this is a new insertion.
|
||||
func (n *LiteNode[V]) InsertPrefix(idx uint8, _ V) (exists bool) {
|
||||
if exists = n.Prefixes.Test(idx); exists {
|
||||
return exists
|
||||
}
|
||||
n.Prefixes.Set(idx)
|
||||
n.Prefixes.Count++
|
||||
return exists
|
||||
}
|
||||
|
||||
func (n *LiteNode[V]) GetPrefix(idx uint8) (_ V, exists bool) {
|
||||
// no docstring by intention
|
||||
exists = n.Prefixes.Test(idx)
|
||||
return
|
||||
}
|
||||
|
||||
func (n *LiteNode[V]) MustGetPrefix(idx uint8) (_ V) {
|
||||
// no docstring by intention
|
||||
return
|
||||
}
|
||||
|
||||
// AllIndices returns an iterator over all prefix entries.
|
||||
// Each iteration yields the prefix index (uint8) and its associated value (V).
|
||||
func (n *LiteNode[V]) AllIndices() iter.Seq2[uint8, V] {
|
||||
var zero V
|
||||
return func(yield func(uint8, V) bool) {
|
||||
var buf [256]uint8
|
||||
for _, idx := range n.Prefixes.AsSlice(&buf) {
|
||||
if !yield(idx, zero) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeletePrefix removes the prefix at the specified index.
|
||||
// Returns true if the prefix existed, and false otherwise.
|
||||
func (n *LiteNode[V]) DeletePrefix(idx uint8) (exists bool) {
|
||||
if exists = n.Prefixes.Test(idx); !exists {
|
||||
return false
|
||||
}
|
||||
n.Prefixes.Clear(idx)
|
||||
n.Prefixes.Count--
|
||||
return true
|
||||
}
|
||||
|
||||
// InsertChild adds a child node at the specified address (0-255).
|
||||
// The child can be a *LiteNode[V], *LeafNode, or *FringeNode.
|
||||
// Returns true if a child already existed at that address.
|
||||
func (n *LiteNode[V]) InsertChild(addr uint8, child any) (exists bool) {
|
||||
_, exists = n.Children.InsertAt(addr, child)
|
||||
return
|
||||
}
|
||||
|
||||
// GetChild retrieves the child node at the specified address.
|
||||
// Returns the child and true if found, or nil and false if not present.
|
||||
func (n *LiteNode[V]) GetChild(addr uint8) (any, bool) {
|
||||
return n.Children.Get(addr)
|
||||
}
|
||||
|
||||
// MustGetChild retrieves the child at the specified address, panicking if not found.
|
||||
// This method should only be used when the caller is certain the child exists.
|
||||
func (n *LiteNode[V]) MustGetChild(addr uint8) any {
|
||||
return n.Children.MustGet(addr)
|
||||
}
|
||||
|
||||
// AllChildren returns an iterator over all child nodes.
|
||||
// Each iteration yields the child's address (uint8) and the child node (any).
|
||||
func (n *LiteNode[V]) AllChildren() iter.Seq2[uint8, any] {
|
||||
return func(yield func(addr uint8, child any) bool) {
|
||||
var buf [256]uint8
|
||||
addrs := n.Children.AsSlice(&buf)
|
||||
for i, addr := range addrs {
|
||||
if !yield(addr, n.Children.Items[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteChild removes the child node at the specified address.
|
||||
// This operation is idempotent - removing a non-existent child is safe.
|
||||
func (n *LiteNode[V]) DeleteChild(addr uint8) (exists bool) {
|
||||
_, exists = n.Children.DeleteAt(addr)
|
||||
return exists
|
||||
}
|
||||
|
||||
// Contains returns true if an index (idx) has any matching longest-prefix
|
||||
// in the current node’s prefix table.
|
||||
//
|
||||
// This function performs a presence check.
|
||||
//
|
||||
// The prefix table is structured as a complete binary tree (CBT), and LPM testing
|
||||
// is done via a bitset operation that maps the traversal path from the given index
|
||||
// toward its possible ancestors.
|
||||
func (n *LiteNode[V]) Contains(idx uint8) bool {
|
||||
return n.Prefixes.Intersects(&lpm.LookupTbl[idx])
|
||||
}
|
||||
|
||||
// LookupIdx performs a longest-prefix match (LPM) lookup for the given index (idx)
|
||||
// within the 8-bit stride-based prefix table at this trie depth.
|
||||
//
|
||||
// The function returns the matched index and whether a matching prefix
|
||||
// exists at this level. The value type parameter exists only to satisfy interfaces.
|
||||
//
|
||||
// Internally, the prefix table is organized as a complete binary tree (CBT) indexed
|
||||
// via the baseIndex function. Unlike the original ART algorithm, this implementation
|
||||
// does not use an allotment-based approach. Instead, it performs CBT backtracking
|
||||
// using a bitset-based operation with a precomputed backtracking pattern specific to idx.
|
||||
func (n *LiteNode[V]) LookupIdx(idx uint8) (top uint8, _ V, ok bool) {
|
||||
top, ok = n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx])
|
||||
return
|
||||
}
|
||||
|
||||
// Lookup is just a simple wrapper for LookupIdx.
|
||||
func (n *LiteNode[V]) Lookup(idx uint8) (_ V, ok bool) {
|
||||
_, _, ok = n.LookupIdx(idx)
|
||||
return
|
||||
}
|
||||
|
||||
// CloneFlat returns a shallow copy of the current node.
|
||||
//
|
||||
// CloneFn is only used for interface satisfaction.
|
||||
func (n *LiteNode[V]) CloneFlat(_ func(V) V) *LiteNode[V] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := new(LiteNode[V])
|
||||
if n.IsEmpty() {
|
||||
return c
|
||||
}
|
||||
|
||||
// copy simple values
|
||||
c.Prefixes = n.Prefixes
|
||||
|
||||
// sparse array
|
||||
c.Children = *(n.Children.Copy())
|
||||
|
||||
// no values to copy
|
||||
return c
|
||||
}
|
||||
|
||||
// CloneRec performs a recursive deep copy of the node and all its descendants.
|
||||
//
|
||||
// cloneFn is only used for interface satisfaction.
|
||||
//
|
||||
// It first creates a shallow clone of the current node using CloneFlat.
|
||||
// Then it recursively clones all child nodes of type *LiteNode[V],
|
||||
// performing a full deep clone down the subtree.
|
||||
//
|
||||
// Child nodes of type *LeafNode and *FringeNode are already copied
|
||||
// by CloneFlat.
|
||||
//
|
||||
// Returns a new instance of LiteNode[V] which is a complete deep clone of the
|
||||
// receiver node with all descendants.
|
||||
func (n *LiteNode[V]) CloneRec(_ func(V) V) *LiteNode[V] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Perform a flat clone of the current node.
|
||||
c := n.CloneFlat(nil)
|
||||
|
||||
// Recursively clone all child nodes of type *LiteNode[V]
|
||||
for i, kidAny := range c.Children.Items {
|
||||
if kid, ok := kidAny.(*LiteNode[V]); ok {
|
||||
c.Children.Items[i] = kid.CloneRec(nil)
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
Reference in new issue
Block a user