Files
tsnet-proxy/vendor/github.com/gaissmai/bart/internal/nodes/lite.go
T
2026-10-22 12:27:19 +01:00

222 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
}