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

732 lines
20 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.
// Code generated from file "commonmethods_tmpl.go"; DO NOT EDIT.
// Copyright (c) 2026 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
import (
"bytes"
"encoding/json"
"fmt"
"io"
"iter"
"net/netip"
"slices"
"strings"
"github.com/gaissmai/bart/internal/nodes"
"github.com/gaissmai/bart/internal/value"
)
func (t *liteTable[V]) sizeUpdate(is4 bool, delta int) {
if is4 {
t.size4 += delta
return
}
t.size6 += delta
}
// insert adds or updates a prefix-value pair in the routing table.
// If the prefix already exists, its value is updated; otherwise a new entry is created.
// Invalid prefixes are silently ignored.
//
// The prefix is automatically canonicalized using pfx.Masked() to ensure
// consistent behavior regardless of host bits in the input.
func (t *liteTable[V]) insert(pfx netip.Prefix, val V) {
if !pfx.IsValid() {
return
}
// canonicalize prefix
pfx = pfx.Masked()
is4 := pfx.Addr().Is4()
n := t.rootNodeByVersion(is4)
if exists := n.Insert(pfx, val, 0); exists {
return
}
// true insert, update size
t.sizeUpdate(is4, 1)
}
// insertPersist is similar to insert but the receiver isn't modified.
//
// All nodes touched during insert are cloned and a new liteTable is returned.
// This is not a full [liteTable.Clone], all untouched nodes are still referenced
// from both Tables.
//
// If the payload type V contains pointers or needs deep copying,
// implement:
//
// func (v V) Clone() V
//
// The bart package detects this via structural typing and deep‑copies
// values during persistent ops.
//
// Due to cloning overhead this is significantly slower than insert,
// typically taking μsec instead of nsec.
func (t *liteTable[V]) insertPersist(pfx netip.Prefix, val V) *liteTable[V] {
if !pfx.IsValid() {
return t
}
// canonicalize prefix
pfx = pfx.Masked()
is4 := pfx.Addr().Is4()
// share size counters; root nodes cloned selectively.
pt := &liteTable[V]{
size4: t.size4,
size6: t.size6,
}
// Create a cloning function for deep copying values;
// returns nil if V does not provide a Clone() V method.
cloneFn := value.CloneFnFactory[V]()
// Clone root node corresponding to the IP version, for copy-on-write.
n := &pt.root4
if is4 {
pt.root4 = *t.root4.CloneFlat(cloneFn)
pt.root6 = t.root6
} else {
pt.root4 = t.root4
pt.root6 = *t.root6.CloneFlat(cloneFn)
n = &pt.root6
}
if !n.InsertPersist(cloneFn, pfx, val, 0) {
pt.sizeUpdate(is4, 1)
}
return pt
}
// Delete removes the exact prefix pfx from the table in-place.
//
// This is an exact-match operation (no LPM). If pfx exists, the entry is
// removed. If pfx does not exist or pfx is invalid, the table is left unchanged.
//
// The prefix is canonicalized (Masked) before lookup.
func (t *liteTable[V]) Delete(pfx netip.Prefix) {
if !pfx.IsValid() {
return
}
// canonicalize prefix
pfx = pfx.Masked()
is4 := pfx.Addr().Is4()
n := t.rootNodeByVersion(is4)
if exists := n.Delete(pfx); exists {
t.sizeUpdate(is4, -1)
}
}
// Get performs an exact-prefix lookup and returns whether the exact
// prefix exists. The prefix is canonicalized (Masked) before lookup.
//
// This is an exact-match operation (no LPM). The prefix must match exactly
// in both address and prefix length to be found. If pfx exists, the
// associated value (zero value for Lite) and found=true is returned.
// If pfx does not exist or pfx is invalid, the zero value for V and
// exists=false is returned.
//
// For longest-prefix-match (LPM) lookups, use Contains(ip), Lookup(ip),
// LookupPrefix(pfx) or LookupPrefixLPM(pfx) instead.
func (t *liteTable[V]) Get(pfx netip.Prefix) (val V, exists bool) {
if !pfx.IsValid() {
return val, exists
}
// canonicalize prefix
pfx = pfx.Masked()
is4 := pfx.Addr().Is4()
n := t.rootNodeByVersion(is4)
return n.Get(pfx)
}
// DeletePersist is similar to Delete but does not modify the receiver.
//
// It performs a copy-on-write delete operation, cloning all nodes touched during
// deletion and returning a new liteTable reflecting the change.
//
// If the prefix is invalid or doesn't exist, the original table is
// returned unchanged.
//
// If the payload type V contains pointers or requires deep copying,
// it must implement the Clone method for correct cloning.
//
// Due to cloning overhead this is significantly slower than Delete,
// typically taking μsec instead of nsec.
func (t *liteTable[V]) DeletePersist(pfx netip.Prefix) *liteTable[V] {
if !pfx.IsValid() {
return t
}
// canonicalize prefix
pfx = pfx.Masked()
is4 := pfx.Addr().Is4()
// Preflight check: avoid cloning if prefix doesn't exist
n := t.rootNodeByVersion(is4)
if _, found := n.Get(pfx); !found {
return t
}
// share size counters; root nodes cloned selectively.
pt := &liteTable[V]{
size4: t.size4,
size6: t.size6,
}
// Create a cloning function for deep copying values;
// returns nil if V does not provide a Clone() V method.
cloneFn := value.CloneFnFactory[V]()
// Clone root node corresponding to the IP version, for copy-on-write.
if is4 {
pt.root4 = *t.root4.CloneFlat(cloneFn)
pt.root6 = t.root6
n = &pt.root4
} else {
pt.root4 = t.root4
pt.root6 = *t.root6.CloneFlat(cloneFn)
n = &pt.root6
}
if exists := n.DeletePersist(cloneFn, pfx); exists {
pt.sizeUpdate(is4, -1)
}
return pt
}
// ModifyPersist is similar to Modify but the receiver isn't modified and
// a new *liteTable is returned.
func (t *liteTable[V]) ModifyPersist(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)) *liteTable[V] {
if !pfx.IsValid() {
return t
}
// make a cheap test in front of expensive operation
oldVal, ok := t.Get(pfx)
val := oldVal
// to clone or not to clone ...
cloneFn := value.CloneFnFactory[V]()
if cloneFn != nil && ok {
val = cloneFn(oldVal)
}
newVal, del := cb(val, ok)
switch {
case !ok && del: // no-op
return t
case !ok && !del: // insert
return t.InsertPersist(pfx, newVal)
case ok && !del: // update
return t.InsertPersist(pfx, newVal)
case ok && del: // delete
return t.DeletePersist(pfx)
}
panic("unreachable")
}
// Supernets returns an iterator over all supernet routes that cover the given prefix pfx.
//
// The traversal searches both exact-length and shorter (less specific) prefixes that
// include pfx. Starting from the most specific position in the trie,
// it walks upward through parent nodes and yields any matching entries found at each level.
//
// The iteration order is reverse-CIDR: from longest prefix match (LPM) towards
// least-specific routes.
//
// This can be used to enumerate all covering supernet routes in routing-based
// policy engines, diagnostics tools, or fallback resolution logic.
//
// Example:
//
// for supernet, val := range table.Supernets(netip.MustParsePrefix("192.0.2.128/25")) {
// fmt.Println("Covered by:", supernet, "->", val)
// }
//
// The iteration can be stopped early by breaking from the range loop.
// Returns an empty iterator if the prefix is invalid.
func (t *liteTable[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
if !pfx.IsValid() {
return
}
// canonicalize the prefix
pfx = pfx.Masked()
is4 := pfx.Addr().Is4()
n := t.rootNodeByVersion(is4)
n.Supernets(pfx, yield)
}
}
// Subnets returns an iterator over all subnets of the given prefix
// in natural CIDR sort order. This includes prefixes of the same length
// (exact match) and longer (more specific) prefixes that are contained
// within the given prefix.
//
// Example:
//
// for sub, val := range table.Subnets(netip.MustParsePrefix("10.0.0.0/8")) {
// fmt.Println("Covered:", sub, "->", val)
// }
//
// The iteration can be stopped early by breaking from the range loop.
// Returns an empty iterator if the prefix is invalid.
func (t *liteTable[V]) Subnets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
if !pfx.IsValid() {
return
}
pfx = pfx.Masked()
is4 := pfx.Addr().Is4()
n := t.rootNodeByVersion(is4)
n.Subnets(pfx, yield)
}
}
// OverlapsPrefix reports whether any prefix in the routing table overlaps with
// the given prefix. Two prefixes overlap if they share any IP addresses.
//
// The check is bidirectional: it returns true if the input prefix is covered by an existing
// route, or if any stored route is itself contained within the input prefix.
//
// Internally, the function normalizes the prefix and descends the relevant trie branch,
// using stride-based logic to identify overlap without performing a full lookup.
//
// This is useful for containment tests, route validation, or policy checks using prefix
// semantics without retrieving exact matches.
func (t *liteTable[V]) OverlapsPrefix(pfx netip.Prefix) bool {
if !pfx.IsValid() {
return false
}
// canonicalize the prefix
pfx = pfx.Masked()
is4 := pfx.Addr().Is4()
n := t.rootNodeByVersion(is4)
return n.OverlapsPrefixAtDepth(pfx, 0)
}
// Overlaps reports whether any route in the receiver table overlaps
// with a route in the other table, in either direction.
//
// The overlap check is bidirectional: it returns true if any IP prefix
// in the receiver is covered by the other table, or vice versa.
// This includes partial overlaps, exact matches, and supernet/subnet relationships.
//
// Both IPv4 and IPv6 route trees are compared independently. If either
// tree has overlapping routes, the function returns true.
//
// This is useful for conflict detection, policy enforcement,
// or validating mutually exclusive routing domains.
func (t *liteTable[V]) Overlaps(o *liteTable[V]) bool {
return t.Overlaps4(o) || t.Overlaps6(o)
}
// Overlaps4 is like [liteTable.Overlaps] but for the v4 routing table only.
func (t *liteTable[V]) Overlaps4(o *liteTable[V]) bool {
if t.size4 == 0 || o.size4 == 0 {
return false
}
return t.root4.Overlaps(&o.root4, 0)
}
// Overlaps6 is like [liteTable.Overlaps] but for the v6 routing table only.
func (t *liteTable[V]) Overlaps6(o *liteTable[V]) bool {
if t.size6 == 0 || o.size6 == 0 {
return false
}
return t.root6.Overlaps(&o.root6, 0)
}
// Union merges another routing table into the receiver table, modifying it in-place.
//
// All prefixes and values from the other table (o) are inserted into the receiver.
// If a duplicate prefix exists in both tables, the value from o replaces the existing entry.
// This duplicate is shallow-copied by default, but if the value type V implements the
// Clone method, the value is deeply cloned before insertion. See also liteTable.Clone.
func (t *liteTable[V]) Union(o *liteTable[V]) {
// panics on nil receiver
_ = t.size4
// panics on nil argument
if o.size4 == 0 && o.size6 == 0 {
return
}
// t is unchanged
if o == t {
return
}
// Create a cloning function for deep copying values;
// returns nil if V does not provide a Clone() V method.
cloneFn := value.CloneFnFactory[V]()
dup4 := t.root4.UnionRec(cloneFn, &o.root4, 0)
dup6 := t.root6.UnionRec(cloneFn, &o.root6, 0)
t.size4 += o.size4 - dup4
t.size6 += o.size6 - dup6
}
// UnionPersist is similar to [Union] but the receiver isn't modified.
//
// All nodes touched during union are cloned and a new *liteTable is returned.
// If o is empty, no nodes are touched and the receiver may be
// returned unchanged.
func (t *liteTable[V]) UnionPersist(o *liteTable[V]) *liteTable[V] {
// panics on nil receiver
_ = t.size4
// panics on nil argument
if o.size4 == 0 && o.size6 == 0 {
return t
}
if o == t {
return t
}
// Create a cloning function for deep copying values;
// returns nil if V does not provide a Clone() V method.
cloneFn := value.CloneFnFactory[V]()
// new liteTable with root nodes just copied.
pt := &liteTable[V]{
root4: t.root4,
root6: t.root6,
//
size4: t.size4,
size6: t.size6,
}
// only clone the root node if there is something to union
if o.size4 != 0 {
pt.root4 = *t.root4.CloneFlat(cloneFn)
}
if o.size6 != 0 {
pt.root6 = *t.root6.CloneFlat(cloneFn)
}
dup4 := pt.root4.UnionRecPersist(cloneFn, &o.root4, 0)
dup6 := pt.root6.UnionRecPersist(cloneFn, &o.root6, 0)
pt.size4 += o.size4 - dup4
pt.size6 += o.size6 - dup6
return pt
}
// Equal checks whether two tables are structurally and semantically equal.
// It ensures both trees (IPv4-based and IPv6-based) have the same sizes and
// recursively compares their root nodes.
//
// If V implements an `Equal(V) bool` method, its custom equality logic is used.
// Otherwise, values are compared directly using the == operator.
//
// Note: If V implements `Equal(V) bool` with a pointer receiver, the Equal
// method should handle nil receivers gracefully.
//
// ATTENTION: If V is not comparable at runtime (such as a slice or map without an `Equal`
// method), a runtime panic will occur.
func (t *liteTable[V]) Equal(o *liteTable[V]) bool {
if t.size4 != o.size4 || t.size6 != o.size6 {
return false
}
if o == t {
return true
}
return t.root4.EqualRec(&o.root4) && t.root6.EqualRec(&o.root6)
}
// Clone returns a copy of the routing table.
// The payload of type V is shallow copied by default. To enable deep copying,
// implement the following method on your value type:
//
// Clone() V
//
// Example:
//
// type MyValue struct { Data []byte }
// func (v MyValue) Clone() MyValue {
// return MyValue{Data: slices.Clone(v.Data)}
// }
//
// The bart package will automatically detect and use this method via Go's
// structural typing.
//
// Note: If V implements Clone() V with a pointer receiver, the Clone
// method should handle nil receivers gracefully.
func (t *liteTable[V]) Clone() *liteTable[V] {
c := new(liteTable[V])
cloneFn := value.CloneFnFactory[V]()
c.root4 = *t.root4.CloneRec(cloneFn)
c.root6 = *t.root6.CloneRec(cloneFn)
c.size4 = t.size4
c.size6 = t.size6
return c
}
// Size returns the prefix count.
func (t *liteTable[V]) Size() int {
return t.size4 + t.size6
}
// Size4 returns the IPv4 prefix count.
func (t *liteTable[V]) Size4() int {
return t.size4
}
// Size6 returns the IPv6 prefix count.
func (t *liteTable[V]) Size6() int {
return t.size6
}
// All returns an iterator over all prefix–value pairs in the table.
//
// The iteration order is unspecified and may vary between calls; for a stable order,
// use [liteTable.AllSorted].
//
// IMPORTANT: Modifying the table during iteration is not allowed,
// as this would interfere with the internal traversal and may corrupt or
// prematurely terminate the iteration.
func (t *liteTable[V]) All() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
_ = t.root4.AllRec(stridePath{}, 0, true, yield) && t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
// All4 is like [liteTable.All] but only for the v4 routing table.
func (t *liteTable[V]) All4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
_ = t.root4.AllRec(stridePath{}, 0, true, yield)
}
}
// All6 is like [liteTable.All] but only for the v6 routing table.
func (t *liteTable[V]) All6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
_ = t.root6.AllRec(stridePath{}, 0, false, yield)
}
}
// AllSorted is like [liteTable.All] but the iteration is ordered in canonical
// CIDR prefix sort order.
func (t *liteTable[V]) AllSorted() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield) &&
t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
}
// AllSorted4 is like [liteTable.AllSorted] but only for the v4 routing table.
func (t *liteTable[V]) AllSorted4() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
_ = t.root4.AllRecSorted(stridePath{}, 0, true, yield)
}
}
// AllSorted6 is like [liteTable.AllSorted] but only for the v6 routing table.
func (t *liteTable[V]) AllSorted6() iter.Seq2[netip.Prefix, V] {
return func(yield func(netip.Prefix, V) bool) {
_ = t.root6.AllRecSorted(stridePath{}, 0, false, yield)
}
}
// Fprint writes a hierarchical tree diagram of the ordered CIDRs
// with default formatted payload V to w.
//
// The order from top to bottom is in ascending order of the prefix address
// and the subtree structure is determined by the CIDRs coverage.
//
// ▼
// ├─ 10.0.0.0/8 (V)
// │ ├─ 10.0.0.0/24 (V)
// │ └─ 10.0.1.0/24 (V)
// ├─ 127.0.0.0/8 (V)
// │ └─ 127.0.0.1/32 (V)
// ├─ 169.254.0.0/16 (V)
// ├─ 172.16.0.0/12 (V)
// └─ 192.168.0.0/16 (V)
// └─ 192.168.1.0/24 (V)
// ▼
// └─ ::/0 (V)
// ├─ ::1/128 (V)
// ├─ 2000::/3 (V)
// │ └─ 2001:db8::/32 (V)
// └─ fe80::/10 (V)
func (t *liteTable[V]) Fprint(w io.Writer) error {
if w == nil && t != nil {
return fmt.Errorf("nil writer")
}
// v4
if err := t.fprint(w, true); err != nil {
return err
}
// v6
if err := t.fprint(w, false); err != nil {
return err
}
return nil
}
// fprint is the version dependent adapter to fprintRec.
func (t *liteTable[V]) fprint(w io.Writer, is4 bool) error {
n := t.rootNodeByVersion(is4)
if n.IsEmpty() {
return nil
}
if _, err := fmt.Fprint(w, "▼\n"); err != nil {
return err
}
startParent := nodes.TrieItem[V]{
Node: nil,
Idx: 0,
Path: stridePath{},
Is4: is4,
}
return n.FprintRec(w, startParent, "")
}
// MarshalText implements the [encoding.TextMarshaler] interface,
// just a wrapper for [liteTable.Fprint].
func (t *liteTable[V]) MarshalText() ([]byte, error) {
w := new(bytes.Buffer)
if err := t.Fprint(w); err != nil {
return nil, err
}
return w.Bytes(), nil
}
// MarshalJSON dumps the table into two sorted lists: for ipv4 and ipv6.
// Every root and subnet is an array, not a map, because the order matters.
func (t *liteTable[V]) MarshalJSON() ([]byte, error) {
result := struct {
Ipv4 []DumpListNode[V] `json:"ipv4,omitempty"`
Ipv6 []DumpListNode[V] `json:"ipv6,omitempty"`
}{
Ipv4: t.DumpList4(),
Ipv6: t.DumpList6(),
}
buf, err := json.Marshal(result)
if err != nil {
return nil, err
}
return buf, nil
}
// DumpList4 dumps the ipv4 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build the text or JSON serialization.
func (t *liteTable[V]) DumpList4() []DumpListNode[V] {
return t.dumpListRec(&t.root4, 0, stridePath{}, 0, true)
}
// DumpList6 dumps the ipv6 tree into a list of roots and their subnets.
// It can be used to analyze the tree or build custom JSON representation.
func (t *liteTable[V]) DumpList6() []DumpListNode[V] {
return t.dumpListRec(&t.root6, 0, stridePath{}, 0, false)
}
// dumpListRec, build the data structure rec-descent with the help of directItemsRec.
// anyNode is nodes.BartNode, nodes.FastNode or nodes.LiteNode
func (t *liteTable[V]) dumpListRec(anyNode any, parentIdx uint8, path stridePath, depth int, is4 bool) []DumpListNode[V] {
// recursion stop condition
if anyNode == nil {
return nil
}
// the same method is generated for all table types, therefore
// type assert to the smallest needed interface.
// The panic on wrong type assertion is by intention, MUST NOT happen
n := anyNode.(interface {
DirectItemsRec(uint8, stridePath, int, bool) []nodes.TrieItem[V]
})
directItems := n.DirectItemsRec(parentIdx, path, depth, is4)
// sort the items by prefix
slices.SortFunc(directItems, func(a, b nodes.TrieItem[V]) int {
return nodes.CmpPrefix(a.Cidr, b.Cidr)
})
dumpNodes := make([]DumpListNode[V], 0, len(directItems))
for _, item := range directItems {
dumpNodes = append(dumpNodes, DumpListNode[V]{
CIDR: item.Cidr,
Value: item.Val,
// build it rec-descent, item.Node is also from type any
Subnets: t.dumpListRec(item.Node, item.Idx, item.Path, item.Depth, is4),
})
}
return dumpNodes
}
// dumpString is just a wrapper for dump.
func (t *liteTable[V]) dumpString() string {
w := new(strings.Builder)
t.dump(w)
return w.String()
}
// dump the table structure and all the nodes to w.
func (t *liteTable[V]) dump(w io.Writer) {
if t.size4 > 0 {
stats := t.root4.StatsRec()
fmt.Fprintln(w)
fmt.Fprintf(w, "### IPv4: size(%d), subnodes(%d), prefixes(%d), leaves(%d), fringes(%d)",
t.size4, stats.SubNodes, stats.Prefixes, stats.Leaves, stats.Fringes)
t.root4.DumpRec(w, stridePath{}, 0, true)
}
if t.size6 > 0 {
stats := t.root6.StatsRec()
fmt.Fprintln(w)
fmt.Fprintf(w, "### IPv6: size(%d), subnodes(%d), prefixes(%d), leaves(%d), fringes(%d)",
t.size6, stats.SubNodes, stats.Prefixes, stats.Leaves, stats.Fringes)
t.root6.DumpRec(w, stridePath{}, 0, false)
}
}