Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+112
@@ -0,0 +1,112 @@
|
||||
language: en-US
|
||||
tone_instructions: ''
|
||||
early_access: true
|
||||
enable_free_tier: true
|
||||
reviews:
|
||||
profile: chill
|
||||
request_changes_workflow: false
|
||||
high_level_summary: true
|
||||
high_level_summary_placeholder: '@coderabbitai summary'
|
||||
high_level_summary_in_walkthrough: false
|
||||
auto_title_placeholder: '@coderabbitai'
|
||||
auto_title_instructions: ''
|
||||
review_status: true
|
||||
commit_status: true
|
||||
fail_commit_status: false
|
||||
collapse_walkthrough: true
|
||||
changed_files_summary: true
|
||||
sequence_diagrams: true
|
||||
estimate_code_review_effort: true
|
||||
assess_linked_issues: true
|
||||
related_issues: true
|
||||
related_prs: true
|
||||
suggested_labels: true
|
||||
auto_apply_labels: false
|
||||
suggested_reviewers: false
|
||||
auto_assign_reviewers: false
|
||||
poem: false
|
||||
labeling_instructions: []
|
||||
path_filters:
|
||||
- '!**/testdata/**'
|
||||
path_instructions: []
|
||||
abort_on_close: true
|
||||
disable_cache: false
|
||||
auto_review:
|
||||
enabled: true
|
||||
auto_incremental_review: true
|
||||
ignore_title_keywords: ['skip ci']
|
||||
labels: []
|
||||
drafts: false
|
||||
base_branches: []
|
||||
ignore_usernames: []
|
||||
finishing_touches:
|
||||
docstrings:
|
||||
enabled: true
|
||||
unit_tests:
|
||||
enabled: true
|
||||
pre_merge_checks:
|
||||
docstrings:
|
||||
mode: warning
|
||||
threshold: 80
|
||||
title:
|
||||
mode: warning
|
||||
requirements: ''
|
||||
description:
|
||||
mode: warning
|
||||
issue_assessment:
|
||||
mode: warning
|
||||
tools:
|
||||
markdownlint:
|
||||
enabled: true
|
||||
github-checks:
|
||||
enabled: true
|
||||
timeout_ms: 300000
|
||||
languagetool:
|
||||
enabled: true
|
||||
enabled_rules: []
|
||||
disabled_rules: []
|
||||
enabled_categories: []
|
||||
disabled_categories: []
|
||||
enabled_only: false
|
||||
level: default
|
||||
golangci-lint:
|
||||
enabled: true
|
||||
config_file: .golangci.yml
|
||||
yamllint:
|
||||
enabled: true
|
||||
gitleaks:
|
||||
enabled: true
|
||||
actionlint:
|
||||
enabled: true
|
||||
chat:
|
||||
art: false
|
||||
auto_reply: true
|
||||
integrations: {}
|
||||
knowledge_base:
|
||||
opt_out: false
|
||||
web_search:
|
||||
enabled: true
|
||||
code_guidelines:
|
||||
enabled: true
|
||||
filePatterns: []
|
||||
learnings:
|
||||
scope: auto
|
||||
issues:
|
||||
scope: auto
|
||||
jira:
|
||||
usage: disabled
|
||||
project_keys: []
|
||||
linear:
|
||||
usage: disabled
|
||||
team_keys: []
|
||||
pull_requests:
|
||||
scope: auto
|
||||
mcp:
|
||||
usage: auto
|
||||
disabled_servers: []
|
||||
code_generation:
|
||||
docstrings:
|
||||
language: en-US
|
||||
path_instructions: []
|
||||
unit_tests:
|
||||
path_instructions: []
|
||||
+2
-2
@@ -23,5 +23,5 @@
|
||||
# ...even if they are in subdirectories
|
||||
!*/
|
||||
|
||||
!doc/*
|
||||
!testdata/*
|
||||
!scripts/**
|
||||
!**/testdata/**
|
||||
+54
-25
@@ -1,34 +1,63 @@
|
||||
version: '2'
|
||||
run:
|
||||
timeout: 5m
|
||||
linters:
|
||||
enable:
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
- govet
|
||||
- gosimple
|
||||
- gocyclo
|
||||
- gosmopolitan
|
||||
- errcheck
|
||||
- staticcheck
|
||||
- intrange
|
||||
- ineffassign
|
||||
- unused
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- misspell
|
||||
- decorder
|
||||
- errorlint
|
||||
- gocyclo
|
||||
- govet
|
||||
- gosec
|
||||
- gosmopolitan
|
||||
- intrange
|
||||
- misspell
|
||||
- predeclared
|
||||
- reassign
|
||||
- stylecheck
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- usestdlibvars
|
||||
- wastedassign
|
||||
- nakedret
|
||||
#
|
||||
# - gosec
|
||||
# - dupl
|
||||
# - wrapcheck
|
||||
- copyloopvar
|
||||
- tparallel
|
||||
- gochecknoglobals
|
||||
- nolintlint
|
||||
- modernize
|
||||
- gocritic
|
||||
settings:
|
||||
govet:
|
||||
enable:
|
||||
- shadow
|
||||
gocyclo:
|
||||
min-complexity: 30
|
||||
gosec:
|
||||
excludes:
|
||||
- G404 # deterministic RNG in tests/benchmarks only
|
||||
exclusions:
|
||||
generated: lax
|
||||
rules:
|
||||
- linters:
|
||||
- gochecknoglobals
|
||||
- gosec
|
||||
- gocyclo
|
||||
path: _test\.go
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
linters-settings:
|
||||
gocyclo:
|
||||
min-complexity: 30
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
+14
-1
@@ -1 +1,14 @@
|
||||
Contributions are welcome, but please open an issue for discussion before sending a pull request.
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please open an issue first to discuss substantial changes.
|
||||
|
||||
### Pull Request Checklist
|
||||
- Follow Go formatting and lint rules (golangci-lint passes locally).
|
||||
- Run `go test ./...` before sending PRs.
|
||||
- Add/adjust tests with good coverage.
|
||||
- Update README when behavior or APIs change.
|
||||
- Keep commits focused; prefer small, reviewable PRs.
|
||||
|
||||
### Governance
|
||||
- Be respectful.
|
||||
- License your contributions under MIT.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Karl Gaissmaier
|
||||
Copyright (c) 2026 Karl Gaissmaier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
# package bart
|
||||
|
||||
# Node Types Comparison
|
||||
|
||||
BART implements three different node types, each optimized for specific use cases:
|
||||
|
||||
## Memory Footprint (64-bit systems)
|
||||
|
||||
**Base Components:**
|
||||
- `BitSet256`: `[4]uint64` = **32 bytes**
|
||||
- `sparse.Array256[T]`: `BitSet256 + []T` = **56 bytes + n×sizeof(T)**
|
||||
|
||||
**Child Reference Sizing:**
|
||||
- `childRef`: 8 bytes (pointer) or 16 bytes (interface value storage)
|
||||
- The actual size depends on implementation: 8B for `*node` pointers, 16B for `interface{}` values
|
||||
|
||||
### BartNode[V] - Dynamic Sparse Node
|
||||
```go
|
||||
type BartNode[V any] struct {
|
||||
prefixes sparse.Array256[V] // 56 + n×sizeof(V)
|
||||
children sparse.Array256[childRef] // 56 + m×sizeof(childRef)
|
||||
}
|
||||
```
|
||||
**Memory Usage:** **112 bytes + n×sizeof(V) + m×sizeof(childRef)**
|
||||
|
||||
### LiteNode - Dynamic Sparse, prefixes Bitset-Only Node
|
||||
```go
|
||||
type LiteNode struct {
|
||||
prefixes bitset.BitSet256 // 32 bytes (presence only)
|
||||
children sparse.Array256[childRef] // 56 + m×sizeof(childRef)
|
||||
pfxCount uint16 // 2 bytes + padding
|
||||
}
|
||||
```
|
||||
**Memory Usage:** **96 bytes + m×sizeof(childRef)** (no value storage)
|
||||
|
||||
### FastNode[V] - Fixed Array Node
|
||||
```go
|
||||
type FastNode[V any] struct {
|
||||
prefixes sparse.Array256[V] // 56 + n×sizeof(V)
|
||||
children sparse.Array256[childRef] // 56 + m×sizeof(childRef)
|
||||
childRankCache [256]uint8 // 256
|
||||
}
|
||||
```
|
||||
**Memory Usage:** **256 + 112 bytes + n×sizeof(V) + m×sizeof(childRef)**
|
||||
|
||||
## Real-World Example
|
||||
**Scenario:** Node with 10 prefixes, 5 children
|
||||
|
||||
| Node Type | Base | *Payload | +Children | Total | **Bytes/Prefix** ¹ |
|
||||
|-----------|------|----------|----------|-----------|------------------|
|
||||
| LiteNode | 96 | 0 | 5×16=80 | 176 bytes | **17** |
|
||||
| BartNode[int] | 112 | 10×8=80 | 5×16=80 | 272 bytes | **27** |
|
||||
| FastNode[int] | 368 | 10×8=80 | 5×16=80 | 528 bytes | **53** |
|
||||
|
||||
¹ Values assume childRef = 16 bytes and pointer to payload = 8 bytes
|
||||
|
||||
## Extended Memory Example with Path Compression
|
||||
|
||||
### Path-Compressed Child Types
|
||||
|
||||
**Note:** In realistic scenarios, the value type `V` is typically a **pointer to a
|
||||
payload struct** (e.g., `*RouteInfo`, `*Metadata`).
|
||||
The calculations below assume `V = 8 bytes` (pointer size on 64-bit systems).
|
||||
The actual payload struct referenced by the pointer is **not included** in these per-node calculations.
|
||||
|
||||
**LiteNode:**
|
||||
- **LeafNode**: `netip.Prefix` only = **32 bytes** (Value field unused)
|
||||
- **FringeNode**: **0 bytes** payload (prefix implicit from position, Value field unused)
|
||||
|
||||
**BartNode[V] / FastNode[V]:** (assuming V is a pointer = 8 bytes)
|
||||
- **LeafNode[V]**: `Value (8B) + Prefix (32B)` = **40 bytes**
|
||||
- **FringeNode[V]**: `Value (8B)` = **8 bytes** (prefix implicit from position)
|
||||
|
||||
### Realistic Scenario: Mixed Child Types
|
||||
|
||||
**Setup:** Node with 10 prefixes + 5 child nodes + 5 LeafNodes + 5 FringeNodes
|
||||
|
||||
**Total prefix items:** 10 (in-node) + 5 (leaves) + 5 (fringes) = **20 prefix items**
|
||||
|
||||
| Node Type | Base | Prefixes¹ | Children² | Subtotal | + Leaves³ | + Fringes⁴ | **Bytes/Prefix**⁵ |
|
||||
|-----------|------|-----------|-----------|----------|-----------|------------|-------------------|
|
||||
| **LiteNode** | 96 B | 0 | 15×16=240 B | 336 B | +160 B | +0 B | **16.8** |
|
||||
| **BartNode[V]** | 112 B | 10×8=80 B | 15×16=240 B | 432 B | +200 B | +40 B | **21.6** |
|
||||
| **FastNode[V]** | 368 B | 10×8=80 B | 15×16=240 B | 688 B | +200 B | +40 B | **34.4** |
|
||||
|
||||
**Notes:**
|
||||
1. Prefixes: LiteNode stores no values (0B); BartNode/FastNode store 10×8B pointers to payload structs
|
||||
2. Children: 15 total (5 nodes + 5 leaves + 5 fringes), each as `interface{}` = 16 bytes
|
||||
3. Leaves: LiteNode stores 5×32B LeafNode (unused Value field), BartNode/FastNode store
|
||||
5×40B LeafNode[V] (with 8B pointer each)
|
||||
4. Fringes: LiteNode stores 0B (prefix implicit, unused Value field), BartNode/FastNode store
|
||||
5×8B FringeNode[V] (8B pointer)
|
||||
5. Calculated as: `Subtotal / 20` (node itself only, excluding referenced child nodes and external
|
||||
payload structs)
|
||||
|
||||
### Memory Efficiency Insights
|
||||
|
||||
**LiteNode advantages:**
|
||||
- **No value storage** in LeafNode: saves 8 bytes per leaf (32B vs 40B)
|
||||
- **No value storage** in FringeNode: saves 8 bytes per fringe (0B vs 8B)
|
||||
- **No in-node prefix values**: saves 80 bytes for 10 prefixes vs BartNode
|
||||
- **Total savings:** ~22% smaller than BartNode for typical routing tables
|
||||
|
||||
**Path compression benefits:**
|
||||
- **LeafNode**: Eliminates intermediate nodes for isolated prefixes
|
||||
- **FringeNode**: Compresses /8, /16, /24 boundaries without node overhead
|
||||
- **Reduced trie depth**: Fewer levels = fewer lookups per route
|
||||
|
||||
**Important:** The actual payload structs (e.g., routing information, metadata) are stored externally and referenced by the 8-byte pointers. Their sizes are not included in the per-node calculations above, as they are shared or application-specific.
|
||||
|
||||
## Lookup Performance Deep Dive
|
||||
|
||||
All three node types achieve **O(1) per-level lookup performance**, but must traverse trie levels:
|
||||
|
||||
### Trie Structure & Performance
|
||||
- **8-bit strides per level**: Each trie level handles 8 bits of the IP address
|
||||
- **IPv4 traversal**: Worst case 4 levels (32÷8), real-world typically 3 levels for /24 routes
|
||||
- **IPv6 traversal**: Worst case 16 levels (128÷8), real-world typically 6 levels for /48 routes
|
||||
- **Performance characteristic**: O(trie_depth) not O(number_of_routes)
|
||||
- **IPv6 vs IPv4**: IPv6 inherently ~2× slower due to deeper tree structure
|
||||
|
||||
### BartNode[V] & LiteNode - Optimized Level Operations
|
||||
- **Precomputed lookup tables** (`lpm.LookupTbl[idx]`) eliminate search within each level
|
||||
- **BitSet256 intersections** via `IntersectionTop()` for instant prefix matching
|
||||
- **Rank-based indirection**: Bitset-to-slice mapping uses precomputed Rank masks
|
||||
- **Pipeline-friendly**: Only 4 bitset operations (4×uint64) per level, optimized for CPU pipelining
|
||||
- **No backtracking**: Traditional longest-prefix-match backtracking replaced with direct table lookups
|
||||
|
||||
### FastNode[V] - Cached Child Access per Level
|
||||
- **No rank recomputation per level**: Uses `childRankCache[octet]` to index sparse child storage quickly.
|
||||
- **Performance advantage**: ~25% faster level traversal
|
||||
+230
-134
@@ -1,183 +1,279 @@
|
||||
# package bart
|
||||
|
||||

|
||||
[](https://pkg.go.dev/github.com/gaissmai/bart#section-documentation)
|
||||
[](https://github.com/avelino/awesome-go)
|
||||
[](https://github.com/gaissmai/bart/actions/workflows/go.yml)
|
||||
[](https://coveralls.io/github/gaissmai/bart)
|
||||
[](https://goreportcard.com/report/github.com/gaissmai/bart)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://stand-with-ukraine.pp.ua)
|
||||
|
||||
# package bart
|
||||
|
||||
The bart package provides some **Balanced Routing Tables (BART)** for
|
||||
fastest IP-to-CIDR lookups and related tasks such as:
|
||||
|
||||
- **ACL** determine extremely fast whether an IP address matches any of millions of CIDR rules.
|
||||
- **RIB** handle very large routing tables with low memory overhead, while keeping lookups fast.
|
||||
- **FIB** high-speed lookups, achieve LPM in constant-time for packet forwarding in the datapath.
|
||||
|
||||
BART is designed for workloads where both speed and/or memory efficiency matter,
|
||||
making it a best fit for firewalls, routers, or any system that needs large-scale
|
||||
IP prefix matching.
|
||||
|
||||
## Overview
|
||||
|
||||
`package bart` provides a Balanced-Routing-Table (BART).
|
||||
BART is implemented as a multibit trie with a fixed stride of 8 bits,
|
||||
using a fast mapping function derived from Donald E. Knuth’s
|
||||
**Allotment Routing Table** (ART) algorithm, to map the possible prefixes
|
||||
at each level into a complete binary tree.
|
||||
|
||||
BART is balanced in terms of memory usage and lookup time
|
||||
for the longest-prefix match.
|
||||
BART implements three different routing tables, each optimized for specific
|
||||
use cases:
|
||||
- **bart.Lite**
|
||||
- **bart.Table**
|
||||
- **bart.Fast**
|
||||
|
||||
BART is a multibit-trie with fixed stride length of 8 bits,
|
||||
using the _baseIndex_ function from the ART algorithm to
|
||||
build the complete-binary-tree (CBT) of prefixes for each stride.
|
||||
For **bart.Table** this binary tree is represented with popcount‑compressed
|
||||
sparse arrays for **level compression**.
|
||||
Combined with a **novel path and fringe compression**, this design reduces
|
||||
memory consumption by nearly two orders of magnitude compared to classical ART.
|
||||
|
||||
The CBT is implemented as a bit-vector, backtracking is just
|
||||
a matter of fast cache friendly bitmask operations.
|
||||
For **bart.Fast**, an additional 256 bytes per node are used to
|
||||
achieve faster level traversal in the multibit trie.
|
||||
|
||||
The Table is implemented with popcount compressed sparse arrays
|
||||
together with path compression. This reduces storage consumption
|
||||
by almost two orders of magnitude in comparison to ART with
|
||||
comparable or even better lookup times for longest prefix match.
|
||||
**bart.Lite** is a special form of **bart.Table**, but without a payload, and therefore
|
||||
has the lowest memory overhead while maintaining the same lookup times.
|
||||
|
||||
The algorithm is also excellent for determining whether two tables
|
||||
contain overlapping IP addresses.
|
||||
## Getting Started
|
||||
|
||||
## Example
|
||||
### Example: simple ACL with bart.Lite
|
||||
|
||||
```golang
|
||||
func ExampleTable_Contains() {
|
||||
// Create a new routing table
|
||||
table := new(bart.Table[struct{}])
|
||||
```go
|
||||
package main
|
||||
|
||||
// Insert some prefixes
|
||||
prefixes := []string{
|
||||
"192.168.0.0/16", // corporate
|
||||
"192.168.1.0/24", // department
|
||||
"2001:7c0:3100::/40", // corporate
|
||||
"2001:7c0:3100:1::/64", // department
|
||||
"fc00::/7", // unique local
|
||||
}
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
for _, s := range prefixes {
|
||||
pfx := netip.MustParsePrefix(s)
|
||||
table.Insert(pfx, struct{}{})
|
||||
}
|
||||
"github.com/gaissmai/bart"
|
||||
)
|
||||
|
||||
// Test some IP addresses for black/whitelist containment
|
||||
ips := []string{
|
||||
"192.168.1.100", // must match, department
|
||||
"192.168.2.1", // must match, corporate
|
||||
"2001:7c0:3100:1::1", // must match, department
|
||||
"2001:7c0:3100:2::1", // must match, corporate
|
||||
"fc00::1", // must match, unique local
|
||||
//
|
||||
"172.16.0.1", // must NOT match
|
||||
"2003:dead:beef::1", // must NOT match
|
||||
}
|
||||
var a = netip.MustParseAddr
|
||||
var p = netip.MustParsePrefix
|
||||
|
||||
for _, s := range ips {
|
||||
ip := netip.MustParseAddr(s)
|
||||
fmt.Printf("%-20s is contained: %t\n", ip, table.Contains(ip))
|
||||
}
|
||||
func main() {
|
||||
allowList := new(bart.Lite)
|
||||
|
||||
// Output:
|
||||
// 192.168.1.100 is contained: true
|
||||
// 192.168.2.1 is contained: true
|
||||
// 2001:7c0:3100:1::1 is contained: true
|
||||
// 2001:7c0:3100:2::1 is contained: true
|
||||
// fc00::1 is contained: true
|
||||
// 172.16.0.1 is contained: false
|
||||
// 2003:dead:beef::1 is contained: false
|
||||
// Add allowed networks
|
||||
allowList.Insert(p("192.168.0.0/16"))
|
||||
allowList.Insert(p("2001:db8::/32"))
|
||||
allowList.Insert(p("10.0.0.0/24"))
|
||||
|
||||
// Test some IPs with Contains (longest-prefix match)
|
||||
testIPs := []netip.Addr{
|
||||
a("192.168.1.100"), // allowed (matches 192.168.0.0/16)
|
||||
a("2001:db8::1"), // allowed (matches 2001:db8::/32)
|
||||
a("172.16.0.1"), // denied (no match)
|
||||
}
|
||||
|
||||
for _, ip := range testIPs {
|
||||
if allowList.Contains(ip) {
|
||||
fmt.Printf("✅ %s is allowed\n", ip)
|
||||
} else {
|
||||
fmt.Printf("❌ %s is denied\n", ip)
|
||||
}
|
||||
}
|
||||
|
||||
// Exact-prefix checks with Get (not longest-prefix match)
|
||||
exactPrefixes := []netip.Prefix{
|
||||
p("192.168.0.0/16"), // exists exactly
|
||||
p("192.168.1.0/24"), // doesn't exist (would be covered by /16, but not stored exactly)
|
||||
p("10.0.0.0/24"), // exists exactly
|
||||
p("10.0.0.0/16"), // doesn't exist (only /24 is stored)
|
||||
}
|
||||
|
||||
fmt.Println("\nExact-prefix checks:")
|
||||
for _, pfx := range exactPrefixes {
|
||||
if allowList.Get(pfx) {
|
||||
fmt.Printf("✅ Exact prefix %s exists in table\n", pfx)
|
||||
} else {
|
||||
fmt.Printf("❌ Exact prefix %s not found in table\n", pfx)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Comparison
|
||||
|
||||
| Aspect | Table | Lite | Fast |
|
||||
|--------|-------------|-------------|-------------|
|
||||
| **Per-level Speed** | ⚡ **O(1)** | ⚡ **O(1)** | 🚀 **O(1), ~25% faster** |
|
||||
| **Overall Lookup** | O(trie_depth) | O(trie_depth) | O(trie_depth) |
|
||||
| **IPv4 Performance** | ~3 level traversals | ~3 level traversals | ~3 level traversals |
|
||||
| **IPv6 Performance** | ~6 level traversals | ~6 level traversals | ~6 level traversals |
|
||||
| **IPv6 vs IPv4** | ~2× slower | ~2× slower | ~2× slower |
|
||||
| **Memory** | efficient | very efficient | less efficient |
|
||||
| **Update** | very fast | best | fast |
|
||||
|
||||
A more detailed description can be found in [NODETYPES.md](NODETYPES.md).
|
||||
|
||||
## When to Use Each Type
|
||||
|
||||
### 🎯 **bart.Table[V]** - The Balanced Choice
|
||||
- **Recommended** for most routing table use cases
|
||||
- Near-optimal per-level performance with excellent memory efficiency
|
||||
- Very fast updates (insert/delete)
|
||||
- Perfect balance for both IPv4 and IPv6 routing tables (use it for RIB)
|
||||
|
||||
### 🪶 **bart.Lite** - The Minimalist
|
||||
- **Specialized** for prefix-only operations, no payload
|
||||
- Same per-level performance as *bart.Table[V]* but ~35% less memory
|
||||
- Optimal updates (insert/delete), ~25% faster than *bart.Table[V]*
|
||||
- Ideal for IPv4/IPv6 allowlists and set-based operations (use it for ACL)
|
||||
|
||||
### 🚀 **bart.Fast[V]** - The Lookup Performance Champion
|
||||
- **~25% faster** when memory constraints allow
|
||||
- Fast updates (insert/delete), ~50% slower than *bart.Table[V]*
|
||||
- Best choice for lookup-intensive applications (use it for FIB)
|
||||
|
||||
## Bitset Efficiency
|
||||
|
||||
The BART algorithm is based on fixed-size bit vectors and precomputed lookup tables.
|
||||
Lookups are executed entirely with fast, cache-resident bitmask operations, which
|
||||
modern CPUs accelerate using specialized instructions such as POPCNT, LZCNT, and TZCNT.
|
||||
|
||||
For maximum performance, specify the CPU feature set when compiling.
|
||||
See the [Go minimum requirements](https://go.dev/wiki/MinimumRequirements#architectures) for details.
|
||||
|
||||
```bash
|
||||
# On ARM64, Go auto-selects CPU instructions.
|
||||
# Example for AMD64, choose v2/v3/v4 to match your CPU features.
|
||||
GOAMD64=v3 go build
|
||||
```
|
||||
Critical loops over these fixed-size bitsets can be unrolled for additional speed,
|
||||
ensuring predictable memory access and efficient use of CPU pipelines.
|
||||
|
||||
```go
|
||||
func (b *BitSet256) popcnt() (cnt int) {
|
||||
cnt += bits.OnesCount64(b[0])
|
||||
cnt += bits.OnesCount64(b[1])
|
||||
cnt += bits.OnesCount64(b[2])
|
||||
cnt += bits.OnesCount64(b[3])
|
||||
return
|
||||
}
|
||||
```
|
||||
Future Go versions with SIMD intrinsics for `uint64` vectors may unlock
|
||||
additional speedups on compatible hardware.
|
||||
|
||||
## Concurrency model
|
||||
|
||||
There are examples demonstrating how to use bart concurrently with multiple readers and writers.
|
||||
Readers can always access the table lock‑free. Writers synchronize with a mutex so that only one writer
|
||||
modifies the persistent table at a time, without relying on CAS, which can be problematic with multiple
|
||||
long‑running writers.
|
||||
|
||||
The combination of lock-free concurrency, fast lookup and update times and low memory consumption
|
||||
provides clear advantages for any routing daemon.
|
||||
|
||||
But as always, it depends on the specific use case.
|
||||
|
||||
See the concurrent tests for concrete examples of this pattern:
|
||||
- [ExampleFast](example_fast_concurrent_test.go)
|
||||
- [ExampleLite](example_lite_concurrent_test.go)
|
||||
- [ExampleTable](example_table_concurrent_test.go)
|
||||
|
||||
|
||||
## Additional Use Cases
|
||||
|
||||
Beyond high-performance prefix matching, BART also excels at detecting overlaps
|
||||
between two routing tables.
|
||||
In internal benchmarks the check runs in a few nanoseconds per query with zero
|
||||
heap allocations on a modern CPU.
|
||||
|
||||
## API
|
||||
|
||||
Release v0.18 requires at least go1.23 and we use the `iter.Seq2[netip.Prefix, V]` types for iterators.
|
||||
The lock-free versions of insert, update and delete are added, but still experimentell.
|
||||
BART has a rich API for CRUD, lookup, comparison, iteration,
|
||||
serialization and persistence.
|
||||
|
||||
```golang
|
||||
import "github.com/gaissmai/bart"
|
||||
|
||||
type Table[V any] struct {
|
||||
// Has unexported fields.
|
||||
}
|
||||
Table is an IPv4 and IPv6 routing table with payload V. The zero value is
|
||||
ready to use.
|
||||
**Table** and **Fast** expose the identical API, while **Lite** deviates in
|
||||
its methods from the common API when it comes to the payload, since *Lite*
|
||||
has no payload.
|
||||
|
||||
The Table is safe for concurrent readers but not for concurrent readers
|
||||
and/or writers. Either the update operations must be protected by an
|
||||
external lock mechanism or the various ...Persist functions must be used
|
||||
which return a modified routing table by leaving the original unchanged
|
||||
```go
|
||||
import "github.com/gaissmai/bart"
|
||||
|
||||
A Table must not be copied by value, see Table.Clone.
|
||||
type Table[V any] struct {
|
||||
// Has unexported fields.
|
||||
}
|
||||
|
||||
func (t *Table[V]) Insert(pfx netip.Prefix, val V)
|
||||
func (t *Table[V]) Delete(pfx netip.Prefix)
|
||||
func (t *Table[V]) Update(pfx netip.Prefix, cb func(val V, ok bool) V) (newVal V)
|
||||
func (t *Table[V]) Contains(netip.Addr) bool
|
||||
func (t *Table[V]) Lookup(netip.Addr) (V, bool)
|
||||
|
||||
func (t *Table[V]) InsertPersist(pfx netip.Prefix, val V) *Table[V]
|
||||
func (t *Table[V]) DeletePersist(pfx netip.Prefix) *Table[V]
|
||||
func (t *Table[V]) UpdatePersist(pfx netip.Prefix, cb func(val V, ok bool) V) (pt *Table[V], newVal V)
|
||||
func (t *Table[V]) LookupPrefix(netip.Prefix) (V, bool)
|
||||
func (t *Table[V]) LookupPrefixLPM(netip.Prefix) (netip.Prefix, V, bool)
|
||||
|
||||
func (t *Table[V]) Get(pfx netip.Prefix) (val V, ok bool)
|
||||
func (t *Table[V]) GetAndDelete(pfx netip.Prefix) (val V, ok bool)
|
||||
func (t *Table[V]) GetAndDeletePersist(pfx netip.Prefix) (pt *Table[V], val V, ok bool)
|
||||
func (t *Table[V]) Get(netip.Prefix) (V, bool)
|
||||
func (t *Table[V]) Insert(netip.Prefix, V)
|
||||
func (t *Table[V]) Delete(netip.Prefix)
|
||||
func (t *Table[V]) Modify(netip.Prefix, cb func(V, bool) (V, bool))
|
||||
|
||||
func (t *Table[V]) Union(o *Table[V])
|
||||
func (t *Table[V]) Clone() *Table[V]
|
||||
func (t *Table[V]) InsertPersist(netip.Prefix, V) *Table[V]
|
||||
func (t *Table[V]) DeletePersist(netip.Prefix) *Table[V]
|
||||
func (t *Table[V]) ModifyPersist(netip.Prefix, cb func(V, bool) (V, bool)) *Table[V]
|
||||
|
||||
func (t *Table[V]) Contains(ip netip.Addr) bool
|
||||
func (t *Table[V]) Lookup(ip netip.Addr) (val V, ok bool)
|
||||
func (t *Table[V]) LookupPrefix(pfx netip.Prefix) (val V, ok bool)
|
||||
func (t *Table[V]) LookupPrefixLPM(pfx netip.Prefix) (lpm netip.Prefix, val V, ok bool)
|
||||
func (t *Table[V]) Clone() *Table[V]
|
||||
func (t *Table[V]) Union(o *Table[V])
|
||||
func (t *Table[V]) UnionPersist(o *Table[V]) *Table[V]
|
||||
|
||||
func (t *Table[V]) OverlapsPrefix(pfx netip.Prefix) bool
|
||||
func (t *Table[V]) OverlapsPrefix(netip.Prefix) bool
|
||||
|
||||
func (t *Table[V]) Overlaps(o *Table[V]) bool
|
||||
func (t *Table[V]) Overlaps4(o *Table[V]) bool
|
||||
func (t *Table[V]) Overlaps6(o *Table[V]) bool
|
||||
func (t *Table[V]) Overlaps(o *Table[V]) bool
|
||||
func (t *Table[V]) Overlaps4(o *Table[V]) bool
|
||||
func (t *Table[V]) Overlaps6(o *Table[V]) bool
|
||||
|
||||
func (t *Table[V]) Subnets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) Supernets(pfx netip.Prefix) iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) Equal(o *Table[V]) bool
|
||||
|
||||
func (t *Table[V]) All() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) All4() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) All6() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) Subnets(netip.Prefix) iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) Supernets(netip.Prefix) iter.Seq2[netip.Prefix, V]
|
||||
|
||||
func (t *Table[V]) AllSorted() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) AllSorted4() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) AllSorted6() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) All() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) All4() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) All6() iter.Seq2[netip.Prefix, V]
|
||||
|
||||
func (t *Table[V]) Size() int
|
||||
func (t *Table[V]) Size4() int
|
||||
func (t *Table[V]) Size6() int
|
||||
func (t *Table[V]) AllSorted() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) AllSorted4() iter.Seq2[netip.Prefix, V]
|
||||
func (t *Table[V]) AllSorted6() iter.Seq2[netip.Prefix, V]
|
||||
|
||||
func (t *Table[V]) String() string
|
||||
func (t *Table[V]) Fprint(w io.Writer) error
|
||||
func (t *Table[V]) MarshalText() ([]byte, error)
|
||||
func (t *Table[V]) MarshalJSON() ([]byte, error)
|
||||
func (t *Table[V]) Size() int
|
||||
func (t *Table[V]) Size4() int
|
||||
func (t *Table[V]) Size6() int
|
||||
|
||||
func (t *Table[V]) DumpList4() []DumpListNode[V]
|
||||
func (t *Table[V]) DumpList6() []DumpListNode[V]
|
||||
func (t *Table[V]) Fprint(w io.Writer) error
|
||||
func (t *Table[V]) MarshalText() ([]byte, error)
|
||||
func (t *Table[V]) MarshalJSON() ([]byte, error)
|
||||
|
||||
func (t *Table[V]) DumpList4() []DumpListNode[V]
|
||||
func (t *Table[V]) DumpList6() []DumpListNode[V]
|
||||
```
|
||||
|
||||
## benchmarks
|
||||
## Benchmarks
|
||||
|
||||
Please see the extensive [benchmarks](https://github.com/gaissmai/iprbench) comparing `bart` with other IP routing table implementations.
|
||||
Please see the extensive [benchmarks](https://github.com/gaissmai/iprbench)
|
||||
comparing `bart` with other IP routing table implementations.
|
||||
|
||||
Just a teaser, Contains and Lookups against the full Internet routing table with random IP address probes:
|
||||
Just a teaser, `Fast.Lookup` against the Tier1 full
|
||||
Internet routing table with random IP address probes:
|
||||
|
||||
```
|
||||
```text
|
||||
$ GOAMD64=v3 go test -run=xxx -bench=FullFastM/Lookup$ -cpu=1
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/gaissmai/bart
|
||||
cpu: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
|
||||
BenchmarkFullMatchV4/Contains 55906228 21.39 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFullMatchV6/Contains 100000000 11.38 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFullMissV4/Contains 52927538 22.69 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFullMissV6/Contains 250386540 4.883 ns/op 0 B/op 0 allocs/op
|
||||
cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
||||
BenchmarkFullFastMatch4/Lookup 129484557 9.274 ns/op
|
||||
BenchmarkFullFastMatch6/Lookup 65185531 17.99 ns/op
|
||||
BenchmarkFullFastMiss4/Lookup 92448910 12.81 ns/op
|
||||
BenchmarkFullFastMiss6/Lookup 64323817 18.66 ns/op
|
||||
PASS
|
||||
ok github.com/gaissmai/bart 11.291s
|
||||
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/gaissmai/bart
|
||||
cpu: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
|
||||
BenchmarkFullMatchV4/Lookup 53578766 21.77 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFullMatchV6/Lookup 57054618 21.07 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFullMissV4/Lookup 48289306 25.60 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFullMissV6/Lookup 142917571 8.387 ns/op 0 B/op 0 allocs/op
|
||||
PASS
|
||||
ok github.com/gaissmai/bart 10.455s
|
||||
ok github.com/gaissmai/bart 6.647s
|
||||
```
|
||||
|
||||
## Compatibility Guarantees
|
||||
@@ -188,20 +284,20 @@ to be revisited to better support the use cases that the library solves for.
|
||||
|
||||
These occurrences are expected to be rare in frequency and the API is already quite stable.
|
||||
|
||||
## CONTRIBUTION
|
||||
## Contribution
|
||||
|
||||
Please open an issue for discussion before sending a pull request.
|
||||
|
||||
## CREDIT
|
||||
## Credit
|
||||
|
||||
Standing on the shoulders of giants.
|
||||
|
||||
Credits for many inspirations go to
|
||||
|
||||
- the clever guys at tailscale,
|
||||
- the clever folks at Tailscale,
|
||||
- to Daniel Lemire for his inspiring blog,
|
||||
- to Donald E. Knuth for the **ART** routing algorithm and
|
||||
- to Yoichi Hariguchi who deciphered it for us mere mortals
|
||||
- to Donald E. Knuth for the Allotment Routing Table [ART](https://github.com/hariguchi/art/tree/master/orig) algorithm and
|
||||
- to Yoichi Hariguchi who [deciphered it](https://github.com/hariguchi/art/blob/master/docs/art.pdf) for us mere mortals
|
||||
|
||||
And last but not least to the Go team who do a wonderful job!
|
||||
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
You can report privately a vulnerability by email at karl.gaissmaier@uni-ulm.de (author and maintainer).
|
||||
You can report vulnerabilities privately via email: <mailto:karl.gaissmaier@uni-ulm.de>.
|
||||
-542
@@ -1,542 +0,0 @@
|
||||
// Copyright (c) 2024 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package bart
|
||||
|
||||
import "github.com/gaissmai/bart/internal/bitset"
|
||||
|
||||
// allotLookupTbl, as precalculated bitsets,
|
||||
// map the baseIndex to bitset with precomputed complete binary tree.
|
||||
//
|
||||
// // 1 <= idx <= 511
|
||||
// func allotRec(aTbl *bitset.BitSet, idx uint) {
|
||||
// aTbl = aTbl.Set(idx)
|
||||
// if idx >= 256 {
|
||||
// return
|
||||
// }
|
||||
// allotRec(aTbl, idx<<1)
|
||||
// allotRec(aTbl, idx<<1+1)
|
||||
// }
|
||||
//
|
||||
// Only used for fast bitset intersections instead of
|
||||
// range loops in table overlaps methods.
|
||||
//
|
||||
// Please read the ART paper ./doc/artlookup.pdf to understand the allotment algorithm.
|
||||
var allotLookupTbl = [512]bitset.BitSet{
|
||||
/* idx: 0 */ {}, // idx is invalid
|
||||
/* idx: 1 */ {0xfffffffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff},
|
||||
/* idx: 2 */ {0xffff00ff0f34, 0xffffffff, 0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff},
|
||||
/* idx: 3 */ {0xffff0000ff00f0c8, 0xffffffff00000000, 0x0, 0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff},
|
||||
/* idx: 4 */ {0xff000f0310, 0xffff, 0xffffffff, 0x0, 0xffffffffffffffff},
|
||||
/* idx: 5 */ {0xff0000f00c20, 0xffff0000, 0xffffffff00000000, 0x0, 0x0, 0xffffffffffffffff},
|
||||
/* idx: 6 */ {0xff00000f003040, 0xffff00000000, 0x0, 0xffffffff, 0x0, 0x0, 0xffffffffffffffff},
|
||||
/* idx: 7 */ {0xff000000f000c080, 0xffff000000000000, 0x0, 0xffffffff00000000, 0x0, 0x0, 0x0, 0xffffffffffffffff},
|
||||
/* idx: 8 */ {0xf00030100, 0xff, 0xffff, 0x0, 0xffffffff},
|
||||
/* idx: 9 */ {0xf0000c0200, 0xff00, 0xffff0000, 0x0, 0xffffffff00000000},
|
||||
/* idx: 10 */ {0xf0000300400, 0xff0000, 0xffff00000000, 0x0, 0x0, 0xffffffff},
|
||||
/* idx: 11 */ {0xf00000c00800, 0xff000000, 0xffff000000000000, 0x0, 0x0, 0xffffffff00000000},
|
||||
/* idx: 12 */ {0xf000003001000, 0xff00000000, 0x0, 0xffff, 0x0, 0x0, 0xffffffff},
|
||||
/* idx: 13 */ {0xf000000c002000, 0xff0000000000, 0x0, 0xffff0000, 0x0, 0x0, 0xffffffff00000000},
|
||||
/* idx: 14 */ {0xf00000030004000, 0xff000000000000, 0x0, 0xffff00000000, 0x0, 0x0, 0x0, 0xffffffff},
|
||||
/* idx: 15 */ {0xf0000000c0008000, 0xff00000000000000, 0x0, 0xffff000000000000, 0x0, 0x0, 0x0, 0xffffffff00000000},
|
||||
/* idx: 16 */ {0x300010000, 0xf, 0xff, 0x0, 0xffff},
|
||||
/* idx: 17 */ {0xc00020000, 0xf0, 0xff00, 0x0, 0xffff0000},
|
||||
/* idx: 18 */ {0x3000040000, 0xf00, 0xff0000, 0x0, 0xffff00000000},
|
||||
/* idx: 19 */ {0xc000080000, 0xf000, 0xff000000, 0x0, 0xffff000000000000},
|
||||
/* idx: 20 */ {0x30000100000, 0xf0000, 0xff00000000, 0x0, 0x0, 0xffff},
|
||||
/* idx: 21 */ {0xc0000200000, 0xf00000, 0xff0000000000, 0x0, 0x0, 0xffff0000},
|
||||
/* idx: 22 */ {0x300000400000, 0xf000000, 0xff000000000000, 0x0, 0x0, 0xffff00000000},
|
||||
/* idx: 23 */ {0xc00000800000, 0xf0000000, 0xff00000000000000, 0x0, 0x0, 0xffff000000000000},
|
||||
/* idx: 24 */ {0x3000001000000, 0xf00000000, 0x0, 0xff, 0x0, 0x0, 0xffff},
|
||||
/* idx: 25 */ {0xc000002000000, 0xf000000000, 0x0, 0xff00, 0x0, 0x0, 0xffff0000},
|
||||
/* idx: 26 */ {0x30000004000000, 0xf0000000000, 0x0, 0xff0000, 0x0, 0x0, 0xffff00000000},
|
||||
/* idx: 27 */ {0xc0000008000000, 0xf00000000000, 0x0, 0xff000000, 0x0, 0x0, 0xffff000000000000},
|
||||
/* idx: 28 */ {0x300000010000000, 0xf000000000000, 0x0, 0xff00000000, 0x0, 0x0, 0x0, 0xffff},
|
||||
/* idx: 29 */ {0xc00000020000000, 0xf0000000000000, 0x0, 0xff0000000000, 0x0, 0x0, 0x0, 0xffff0000},
|
||||
/* idx: 30 */ {0x3000000040000000, 0xf00000000000000, 0x0, 0xff000000000000, 0x0, 0x0, 0x0, 0xffff00000000},
|
||||
/* idx: 31 */ {0xc000000080000000, 0xf000000000000000, 0x0, 0xff00000000000000, 0x0, 0x0, 0x0, 0xffff000000000000},
|
||||
/* idx: 32 */ {0x100000000, 0x3, 0xf, 0x0, 0xff},
|
||||
/* idx: 33 */ {0x200000000, 0xc, 0xf0, 0x0, 0xff00},
|
||||
/* idx: 34 */ {0x400000000, 0x30, 0xf00, 0x0, 0xff0000},
|
||||
/* idx: 35 */ {0x800000000, 0xc0, 0xf000, 0x0, 0xff000000},
|
||||
/* idx: 36 */ {0x1000000000, 0x300, 0xf0000, 0x0, 0xff00000000},
|
||||
/* idx: 37 */ {0x2000000000, 0xc00, 0xf00000, 0x0, 0xff0000000000},
|
||||
/* idx: 38 */ {0x4000000000, 0x3000, 0xf000000, 0x0, 0xff000000000000},
|
||||
/* idx: 39 */ {0x8000000000, 0xc000, 0xf0000000, 0x0, 0xff00000000000000},
|
||||
/* idx: 40 */ {0x10000000000, 0x30000, 0xf00000000, 0x0, 0x0, 0xff},
|
||||
/* idx: 41 */ {0x20000000000, 0xc0000, 0xf000000000, 0x0, 0x0, 0xff00},
|
||||
/* idx: 42 */ {0x40000000000, 0x300000, 0xf0000000000, 0x0, 0x0, 0xff0000},
|
||||
/* idx: 43 */ {0x80000000000, 0xc00000, 0xf00000000000, 0x0, 0x0, 0xff000000},
|
||||
/* idx: 44 */ {0x100000000000, 0x3000000, 0xf000000000000, 0x0, 0x0, 0xff00000000},
|
||||
/* idx: 45 */ {0x200000000000, 0xc000000, 0xf0000000000000, 0x0, 0x0, 0xff0000000000},
|
||||
/* idx: 46 */ {0x400000000000, 0x30000000, 0xf00000000000000, 0x0, 0x0, 0xff000000000000},
|
||||
/* idx: 47 */ {0x800000000000, 0xc0000000, 0xf000000000000000, 0x0, 0x0, 0xff00000000000000},
|
||||
/* idx: 48 */ {0x1000000000000, 0x300000000, 0x0, 0xf, 0x0, 0x0, 0xff},
|
||||
/* idx: 49 */ {0x2000000000000, 0xc00000000, 0x0, 0xf0, 0x0, 0x0, 0xff00},
|
||||
/* idx: 50 */ {0x4000000000000, 0x3000000000, 0x0, 0xf00, 0x0, 0x0, 0xff0000},
|
||||
/* idx: 51 */ {0x8000000000000, 0xc000000000, 0x0, 0xf000, 0x0, 0x0, 0xff000000},
|
||||
/* idx: 52 */ {0x10000000000000, 0x30000000000, 0x0, 0xf0000, 0x0, 0x0, 0xff00000000},
|
||||
/* idx: 53 */ {0x20000000000000, 0xc0000000000, 0x0, 0xf00000, 0x0, 0x0, 0xff0000000000},
|
||||
/* idx: 54 */ {0x40000000000000, 0x300000000000, 0x0, 0xf000000, 0x0, 0x0, 0xff000000000000},
|
||||
/* idx: 55 */ {0x80000000000000, 0xc00000000000, 0x0, 0xf0000000, 0x0, 0x0, 0xff00000000000000},
|
||||
/* idx: 56 */ {0x100000000000000, 0x3000000000000, 0x0, 0xf00000000, 0x0, 0x0, 0x0, 0xff},
|
||||
/* idx: 57 */ {0x200000000000000, 0xc000000000000, 0x0, 0xf000000000, 0x0, 0x0, 0x0, 0xff00},
|
||||
/* idx: 58 */ {0x400000000000000, 0x30000000000000, 0x0, 0xf0000000000, 0x0, 0x0, 0x0, 0xff0000},
|
||||
/* idx: 59 */ {0x800000000000000, 0xc0000000000000, 0x0, 0xf00000000000, 0x0, 0x0, 0x0, 0xff000000},
|
||||
/* idx: 60 */ {0x1000000000000000, 0x300000000000000, 0x0, 0xf000000000000, 0x0, 0x0, 0x0, 0xff00000000},
|
||||
/* idx: 61 */ {0x2000000000000000, 0xc00000000000000, 0x0, 0xf0000000000000, 0x0, 0x0, 0x0, 0xff0000000000},
|
||||
/* idx: 62 */ {0x4000000000000000, 0x3000000000000000, 0x0, 0xf00000000000000, 0x0, 0x0, 0x0, 0xff000000000000},
|
||||
/* idx: 63 */ {0x8000000000000000, 0xc000000000000000, 0x0, 0xf000000000000000, 0x0, 0x0, 0x0, 0xff00000000000000},
|
||||
/* idx: 64 */ {0x0, 0x1, 0x3, 0x0, 0xf},
|
||||
/* idx: 65 */ {0x0, 0x2, 0xc, 0x0, 0xf0},
|
||||
/* idx: 66 */ {0x0, 0x4, 0x30, 0x0, 0xf00},
|
||||
/* idx: 67 */ {0x0, 0x8, 0xc0, 0x0, 0xf000},
|
||||
/* idx: 68 */ {0x0, 0x10, 0x300, 0x0, 0xf0000},
|
||||
/* idx: 69 */ {0x0, 0x20, 0xc00, 0x0, 0xf00000},
|
||||
/* idx: 70 */ {0x0, 0x40, 0x3000, 0x0, 0xf000000},
|
||||
/* idx: 71 */ {0x0, 0x80, 0xc000, 0x0, 0xf0000000},
|
||||
/* idx: 72 */ {0x0, 0x100, 0x30000, 0x0, 0xf00000000},
|
||||
/* idx: 73 */ {0x0, 0x200, 0xc0000, 0x0, 0xf000000000},
|
||||
/* idx: 74 */ {0x0, 0x400, 0x300000, 0x0, 0xf0000000000},
|
||||
/* idx: 75 */ {0x0, 0x800, 0xc00000, 0x0, 0xf00000000000},
|
||||
/* idx: 76 */ {0x0, 0x1000, 0x3000000, 0x0, 0xf000000000000},
|
||||
/* idx: 77 */ {0x0, 0x2000, 0xc000000, 0x0, 0xf0000000000000},
|
||||
/* idx: 78 */ {0x0, 0x4000, 0x30000000, 0x0, 0xf00000000000000},
|
||||
/* idx: 79 */ {0x0, 0x8000, 0xc0000000, 0x0, 0xf000000000000000},
|
||||
/* idx: 80 */ {0x0, 0x10000, 0x300000000, 0x0, 0x0, 0xf},
|
||||
/* idx: 81 */ {0x0, 0x20000, 0xc00000000, 0x0, 0x0, 0xf0},
|
||||
/* idx: 82 */ {0x0, 0x40000, 0x3000000000, 0x0, 0x0, 0xf00},
|
||||
/* idx: 83 */ {0x0, 0x80000, 0xc000000000, 0x0, 0x0, 0xf000},
|
||||
/* idx: 84 */ {0x0, 0x100000, 0x30000000000, 0x0, 0x0, 0xf0000},
|
||||
/* idx: 85 */ {0x0, 0x200000, 0xc0000000000, 0x0, 0x0, 0xf00000},
|
||||
/* idx: 86 */ {0x0, 0x400000, 0x300000000000, 0x0, 0x0, 0xf000000},
|
||||
/* idx: 87 */ {0x0, 0x800000, 0xc00000000000, 0x0, 0x0, 0xf0000000},
|
||||
/* idx: 88 */ {0x0, 0x1000000, 0x3000000000000, 0x0, 0x0, 0xf00000000},
|
||||
/* idx: 89 */ {0x0, 0x2000000, 0xc000000000000, 0x0, 0x0, 0xf000000000},
|
||||
/* idx: 90 */ {0x0, 0x4000000, 0x30000000000000, 0x0, 0x0, 0xf0000000000},
|
||||
/* idx: 91 */ {0x0, 0x8000000, 0xc0000000000000, 0x0, 0x0, 0xf00000000000},
|
||||
/* idx: 92 */ {0x0, 0x10000000, 0x300000000000000, 0x0, 0x0, 0xf000000000000},
|
||||
/* idx: 93 */ {0x0, 0x20000000, 0xc00000000000000, 0x0, 0x0, 0xf0000000000000},
|
||||
/* idx: 94 */ {0x0, 0x40000000, 0x3000000000000000, 0x0, 0x0, 0xf00000000000000},
|
||||
/* idx: 95 */ {0x0, 0x80000000, 0xc000000000000000, 0x0, 0x0, 0xf000000000000000},
|
||||
/* idx: 96 */ {0x0, 0x100000000, 0x0, 0x3, 0x0, 0x0, 0xf},
|
||||
/* idx: 97 */ {0x0, 0x200000000, 0x0, 0xc, 0x0, 0x0, 0xf0},
|
||||
/* idx: 98 */ {0x0, 0x400000000, 0x0, 0x30, 0x0, 0x0, 0xf00},
|
||||
/* idx: 99 */ {0x0, 0x800000000, 0x0, 0xc0, 0x0, 0x0, 0xf000},
|
||||
/* idx: 100 */ {0x0, 0x1000000000, 0x0, 0x300, 0x0, 0x0, 0xf0000},
|
||||
/* idx: 101 */ {0x0, 0x2000000000, 0x0, 0xc00, 0x0, 0x0, 0xf00000},
|
||||
/* idx: 102 */ {0x0, 0x4000000000, 0x0, 0x3000, 0x0, 0x0, 0xf000000},
|
||||
/* idx: 103 */ {0x0, 0x8000000000, 0x0, 0xc000, 0x0, 0x0, 0xf0000000},
|
||||
/* idx: 104 */ {0x0, 0x10000000000, 0x0, 0x30000, 0x0, 0x0, 0xf00000000},
|
||||
/* idx: 105 */ {0x0, 0x20000000000, 0x0, 0xc0000, 0x0, 0x0, 0xf000000000},
|
||||
/* idx: 106 */ {0x0, 0x40000000000, 0x0, 0x300000, 0x0, 0x0, 0xf0000000000},
|
||||
/* idx: 107 */ {0x0, 0x80000000000, 0x0, 0xc00000, 0x0, 0x0, 0xf00000000000},
|
||||
/* idx: 108 */ {0x0, 0x100000000000, 0x0, 0x3000000, 0x0, 0x0, 0xf000000000000},
|
||||
/* idx: 109 */ {0x0, 0x200000000000, 0x0, 0xc000000, 0x0, 0x0, 0xf0000000000000},
|
||||
/* idx: 110 */ {0x0, 0x400000000000, 0x0, 0x30000000, 0x0, 0x0, 0xf00000000000000},
|
||||
/* idx: 111 */ {0x0, 0x800000000000, 0x0, 0xc0000000, 0x0, 0x0, 0xf000000000000000},
|
||||
/* idx: 112 */ {0x0, 0x1000000000000, 0x0, 0x300000000, 0x0, 0x0, 0x0, 0xf},
|
||||
/* idx: 113 */ {0x0, 0x2000000000000, 0x0, 0xc00000000, 0x0, 0x0, 0x0, 0xf0},
|
||||
/* idx: 114 */ {0x0, 0x4000000000000, 0x0, 0x3000000000, 0x0, 0x0, 0x0, 0xf00},
|
||||
/* idx: 115 */ {0x0, 0x8000000000000, 0x0, 0xc000000000, 0x0, 0x0, 0x0, 0xf000},
|
||||
/* idx: 116 */ {0x0, 0x10000000000000, 0x0, 0x30000000000, 0x0, 0x0, 0x0, 0xf0000},
|
||||
/* idx: 117 */ {0x0, 0x20000000000000, 0x0, 0xc0000000000, 0x0, 0x0, 0x0, 0xf00000},
|
||||
/* idx: 118 */ {0x0, 0x40000000000000, 0x0, 0x300000000000, 0x0, 0x0, 0x0, 0xf000000},
|
||||
/* idx: 119 */ {0x0, 0x80000000000000, 0x0, 0xc00000000000, 0x0, 0x0, 0x0, 0xf0000000},
|
||||
/* idx: 120 */ {0x0, 0x100000000000000, 0x0, 0x3000000000000, 0x0, 0x0, 0x0, 0xf00000000},
|
||||
/* idx: 121 */ {0x0, 0x200000000000000, 0x0, 0xc000000000000, 0x0, 0x0, 0x0, 0xf000000000},
|
||||
/* idx: 122 */ {0x0, 0x400000000000000, 0x0, 0x30000000000000, 0x0, 0x0, 0x0, 0xf0000000000},
|
||||
/* idx: 123 */ {0x0, 0x800000000000000, 0x0, 0xc0000000000000, 0x0, 0x0, 0x0, 0xf00000000000},
|
||||
/* idx: 124 */ {0x0, 0x1000000000000000, 0x0, 0x300000000000000, 0x0, 0x0, 0x0, 0xf000000000000},
|
||||
/* idx: 125 */ {0x0, 0x2000000000000000, 0x0, 0xc00000000000000, 0x0, 0x0, 0x0, 0xf0000000000000},
|
||||
/* idx: 126 */ {0x0, 0x4000000000000000, 0x0, 0x3000000000000000, 0x0, 0x0, 0x0, 0xf00000000000000},
|
||||
/* idx: 127 */ {0x0, 0x8000000000000000, 0x0, 0xc000000000000000, 0x0, 0x0, 0x0, 0xf000000000000000},
|
||||
/* idx: 128 */ {0x0, 0x0, 0x1, 0x0, 0x3},
|
||||
/* idx: 129 */ {0x0, 0x0, 0x2, 0x0, 0xc},
|
||||
/* idx: 130 */ {0x0, 0x0, 0x4, 0x0, 0x30},
|
||||
/* idx: 131 */ {0x0, 0x0, 0x8, 0x0, 0xc0},
|
||||
/* idx: 132 */ {0x0, 0x0, 0x10, 0x0, 0x300},
|
||||
/* idx: 133 */ {0x0, 0x0, 0x20, 0x0, 0xc00},
|
||||
/* idx: 134 */ {0x0, 0x0, 0x40, 0x0, 0x3000},
|
||||
/* idx: 135 */ {0x0, 0x0, 0x80, 0x0, 0xc000},
|
||||
/* idx: 136 */ {0x0, 0x0, 0x100, 0x0, 0x30000},
|
||||
/* idx: 137 */ {0x0, 0x0, 0x200, 0x0, 0xc0000},
|
||||
/* idx: 138 */ {0x0, 0x0, 0x400, 0x0, 0x300000},
|
||||
/* idx: 139 */ {0x0, 0x0, 0x800, 0x0, 0xc00000},
|
||||
/* idx: 140 */ {0x0, 0x0, 0x1000, 0x0, 0x3000000},
|
||||
/* idx: 141 */ {0x0, 0x0, 0x2000, 0x0, 0xc000000},
|
||||
/* idx: 142 */ {0x0, 0x0, 0x4000, 0x0, 0x30000000},
|
||||
/* idx: 143 */ {0x0, 0x0, 0x8000, 0x0, 0xc0000000},
|
||||
/* idx: 144 */ {0x0, 0x0, 0x10000, 0x0, 0x300000000},
|
||||
/* idx: 145 */ {0x0, 0x0, 0x20000, 0x0, 0xc00000000},
|
||||
/* idx: 146 */ {0x0, 0x0, 0x40000, 0x0, 0x3000000000},
|
||||
/* idx: 147 */ {0x0, 0x0, 0x80000, 0x0, 0xc000000000},
|
||||
/* idx: 148 */ {0x0, 0x0, 0x100000, 0x0, 0x30000000000},
|
||||
/* idx: 149 */ {0x0, 0x0, 0x200000, 0x0, 0xc0000000000},
|
||||
/* idx: 150 */ {0x0, 0x0, 0x400000, 0x0, 0x300000000000},
|
||||
/* idx: 151 */ {0x0, 0x0, 0x800000, 0x0, 0xc00000000000},
|
||||
/* idx: 152 */ {0x0, 0x0, 0x1000000, 0x0, 0x3000000000000},
|
||||
/* idx: 153 */ {0x0, 0x0, 0x2000000, 0x0, 0xc000000000000},
|
||||
/* idx: 154 */ {0x0, 0x0, 0x4000000, 0x0, 0x30000000000000},
|
||||
/* idx: 155 */ {0x0, 0x0, 0x8000000, 0x0, 0xc0000000000000},
|
||||
/* idx: 156 */ {0x0, 0x0, 0x10000000, 0x0, 0x300000000000000},
|
||||
/* idx: 157 */ {0x0, 0x0, 0x20000000, 0x0, 0xc00000000000000},
|
||||
/* idx: 158 */ {0x0, 0x0, 0x40000000, 0x0, 0x3000000000000000},
|
||||
/* idx: 159 */ {0x0, 0x0, 0x80000000, 0x0, 0xc000000000000000},
|
||||
/* idx: 160 */ {0x0, 0x0, 0x100000000, 0x0, 0x0, 0x3},
|
||||
/* idx: 161 */ {0x0, 0x0, 0x200000000, 0x0, 0x0, 0xc},
|
||||
/* idx: 162 */ {0x0, 0x0, 0x400000000, 0x0, 0x0, 0x30},
|
||||
/* idx: 163 */ {0x0, 0x0, 0x800000000, 0x0, 0x0, 0xc0},
|
||||
/* idx: 164 */ {0x0, 0x0, 0x1000000000, 0x0, 0x0, 0x300},
|
||||
/* idx: 165 */ {0x0, 0x0, 0x2000000000, 0x0, 0x0, 0xc00},
|
||||
/* idx: 166 */ {0x0, 0x0, 0x4000000000, 0x0, 0x0, 0x3000},
|
||||
/* idx: 167 */ {0x0, 0x0, 0x8000000000, 0x0, 0x0, 0xc000},
|
||||
/* idx: 168 */ {0x0, 0x0, 0x10000000000, 0x0, 0x0, 0x30000},
|
||||
/* idx: 169 */ {0x0, 0x0, 0x20000000000, 0x0, 0x0, 0xc0000},
|
||||
/* idx: 170 */ {0x0, 0x0, 0x40000000000, 0x0, 0x0, 0x300000},
|
||||
/* idx: 171 */ {0x0, 0x0, 0x80000000000, 0x0, 0x0, 0xc00000},
|
||||
/* idx: 172 */ {0x0, 0x0, 0x100000000000, 0x0, 0x0, 0x3000000},
|
||||
/* idx: 173 */ {0x0, 0x0, 0x200000000000, 0x0, 0x0, 0xc000000},
|
||||
/* idx: 174 */ {0x0, 0x0, 0x400000000000, 0x0, 0x0, 0x30000000},
|
||||
/* idx: 175 */ {0x0, 0x0, 0x800000000000, 0x0, 0x0, 0xc0000000},
|
||||
/* idx: 176 */ {0x0, 0x0, 0x1000000000000, 0x0, 0x0, 0x300000000},
|
||||
/* idx: 177 */ {0x0, 0x0, 0x2000000000000, 0x0, 0x0, 0xc00000000},
|
||||
/* idx: 178 */ {0x0, 0x0, 0x4000000000000, 0x0, 0x0, 0x3000000000},
|
||||
/* idx: 179 */ {0x0, 0x0, 0x8000000000000, 0x0, 0x0, 0xc000000000},
|
||||
/* idx: 180 */ {0x0, 0x0, 0x10000000000000, 0x0, 0x0, 0x30000000000},
|
||||
/* idx: 181 */ {0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0xc0000000000},
|
||||
/* idx: 182 */ {0x0, 0x0, 0x40000000000000, 0x0, 0x0, 0x300000000000},
|
||||
/* idx: 183 */ {0x0, 0x0, 0x80000000000000, 0x0, 0x0, 0xc00000000000},
|
||||
/* idx: 184 */ {0x0, 0x0, 0x100000000000000, 0x0, 0x0, 0x3000000000000},
|
||||
/* idx: 185 */ {0x0, 0x0, 0x200000000000000, 0x0, 0x0, 0xc000000000000},
|
||||
/* idx: 186 */ {0x0, 0x0, 0x400000000000000, 0x0, 0x0, 0x30000000000000},
|
||||
/* idx: 187 */ {0x0, 0x0, 0x800000000000000, 0x0, 0x0, 0xc0000000000000},
|
||||
/* idx: 188 */ {0x0, 0x0, 0x1000000000000000, 0x0, 0x0, 0x300000000000000},
|
||||
/* idx: 189 */ {0x0, 0x0, 0x2000000000000000, 0x0, 0x0, 0xc00000000000000},
|
||||
/* idx: 190 */ {0x0, 0x0, 0x4000000000000000, 0x0, 0x0, 0x3000000000000000},
|
||||
/* idx: 191 */ {0x0, 0x0, 0x8000000000000000, 0x0, 0x0, 0xc000000000000000},
|
||||
/* idx: 192 */ {0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x3},
|
||||
/* idx: 193 */ {0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc},
|
||||
/* idx: 194 */ {0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x30},
|
||||
/* idx: 195 */ {0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0xc0},
|
||||
/* idx: 196 */ {0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x300},
|
||||
/* idx: 197 */ {0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0xc00},
|
||||
/* idx: 198 */ {0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x3000},
|
||||
/* idx: 199 */ {0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xc000},
|
||||
/* idx: 200 */ {0x0, 0x0, 0x0, 0x100, 0x0, 0x0, 0x30000},
|
||||
/* idx: 201 */ {0x0, 0x0, 0x0, 0x200, 0x0, 0x0, 0xc0000},
|
||||
/* idx: 202 */ {0x0, 0x0, 0x0, 0x400, 0x0, 0x0, 0x300000},
|
||||
/* idx: 203 */ {0x0, 0x0, 0x0, 0x800, 0x0, 0x0, 0xc00000},
|
||||
/* idx: 204 */ {0x0, 0x0, 0x0, 0x1000, 0x0, 0x0, 0x3000000},
|
||||
/* idx: 205 */ {0x0, 0x0, 0x0, 0x2000, 0x0, 0x0, 0xc000000},
|
||||
/* idx: 206 */ {0x0, 0x0, 0x0, 0x4000, 0x0, 0x0, 0x30000000},
|
||||
/* idx: 207 */ {0x0, 0x0, 0x0, 0x8000, 0x0, 0x0, 0xc0000000},
|
||||
/* idx: 208 */ {0x0, 0x0, 0x0, 0x10000, 0x0, 0x0, 0x300000000},
|
||||
/* idx: 209 */ {0x0, 0x0, 0x0, 0x20000, 0x0, 0x0, 0xc00000000},
|
||||
/* idx: 210 */ {0x0, 0x0, 0x0, 0x40000, 0x0, 0x0, 0x3000000000},
|
||||
/* idx: 211 */ {0x0, 0x0, 0x0, 0x80000, 0x0, 0x0, 0xc000000000},
|
||||
/* idx: 212 */ {0x0, 0x0, 0x0, 0x100000, 0x0, 0x0, 0x30000000000},
|
||||
/* idx: 213 */ {0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0xc0000000000},
|
||||
/* idx: 214 */ {0x0, 0x0, 0x0, 0x400000, 0x0, 0x0, 0x300000000000},
|
||||
/* idx: 215 */ {0x0, 0x0, 0x0, 0x800000, 0x0, 0x0, 0xc00000000000},
|
||||
/* idx: 216 */ {0x0, 0x0, 0x0, 0x1000000, 0x0, 0x0, 0x3000000000000},
|
||||
/* idx: 217 */ {0x0, 0x0, 0x0, 0x2000000, 0x0, 0x0, 0xc000000000000},
|
||||
/* idx: 218 */ {0x0, 0x0, 0x0, 0x4000000, 0x0, 0x0, 0x30000000000000},
|
||||
/* idx: 219 */ {0x0, 0x0, 0x0, 0x8000000, 0x0, 0x0, 0xc0000000000000},
|
||||
/* idx: 220 */ {0x0, 0x0, 0x0, 0x10000000, 0x0, 0x0, 0x300000000000000},
|
||||
/* idx: 221 */ {0x0, 0x0, 0x0, 0x20000000, 0x0, 0x0, 0xc00000000000000},
|
||||
/* idx: 222 */ {0x0, 0x0, 0x0, 0x40000000, 0x0, 0x0, 0x3000000000000000},
|
||||
/* idx: 223 */ {0x0, 0x0, 0x0, 0x80000000, 0x0, 0x0, 0xc000000000000000},
|
||||
/* idx: 224 */ {0x0, 0x0, 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x3},
|
||||
/* idx: 225 */ {0x0, 0x0, 0x0, 0x200000000, 0x0, 0x0, 0x0, 0xc},
|
||||
/* idx: 226 */ {0x0, 0x0, 0x0, 0x400000000, 0x0, 0x0, 0x0, 0x30},
|
||||
/* idx: 227 */ {0x0, 0x0, 0x0, 0x800000000, 0x0, 0x0, 0x0, 0xc0},
|
||||
/* idx: 228 */ {0x0, 0x0, 0x0, 0x1000000000, 0x0, 0x0, 0x0, 0x300},
|
||||
/* idx: 229 */ {0x0, 0x0, 0x0, 0x2000000000, 0x0, 0x0, 0x0, 0xc00},
|
||||
/* idx: 230 */ {0x0, 0x0, 0x0, 0x4000000000, 0x0, 0x0, 0x0, 0x3000},
|
||||
/* idx: 231 */ {0x0, 0x0, 0x0, 0x8000000000, 0x0, 0x0, 0x0, 0xc000},
|
||||
/* idx: 232 */ {0x0, 0x0, 0x0, 0x10000000000, 0x0, 0x0, 0x0, 0x30000},
|
||||
/* idx: 233 */ {0x0, 0x0, 0x0, 0x20000000000, 0x0, 0x0, 0x0, 0xc0000},
|
||||
/* idx: 234 */ {0x0, 0x0, 0x0, 0x40000000000, 0x0, 0x0, 0x0, 0x300000},
|
||||
/* idx: 235 */ {0x0, 0x0, 0x0, 0x80000000000, 0x0, 0x0, 0x0, 0xc00000},
|
||||
/* idx: 236 */ {0x0, 0x0, 0x0, 0x100000000000, 0x0, 0x0, 0x0, 0x3000000},
|
||||
/* idx: 237 */ {0x0, 0x0, 0x0, 0x200000000000, 0x0, 0x0, 0x0, 0xc000000},
|
||||
/* idx: 238 */ {0x0, 0x0, 0x0, 0x400000000000, 0x0, 0x0, 0x0, 0x30000000},
|
||||
/* idx: 239 */ {0x0, 0x0, 0x0, 0x800000000000, 0x0, 0x0, 0x0, 0xc0000000},
|
||||
/* idx: 240 */ {0x0, 0x0, 0x0, 0x1000000000000, 0x0, 0x0, 0x0, 0x300000000},
|
||||
/* idx: 241 */ {0x0, 0x0, 0x0, 0x2000000000000, 0x0, 0x0, 0x0, 0xc00000000},
|
||||
/* idx: 242 */ {0x0, 0x0, 0x0, 0x4000000000000, 0x0, 0x0, 0x0, 0x3000000000},
|
||||
/* idx: 243 */ {0x0, 0x0, 0x0, 0x8000000000000, 0x0, 0x0, 0x0, 0xc000000000},
|
||||
/* idx: 244 */ {0x0, 0x0, 0x0, 0x10000000000000, 0x0, 0x0, 0x0, 0x30000000000},
|
||||
/* idx: 245 */ {0x0, 0x0, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0xc0000000000},
|
||||
/* idx: 246 */ {0x0, 0x0, 0x0, 0x40000000000000, 0x0, 0x0, 0x0, 0x300000000000},
|
||||
/* idx: 247 */ {0x0, 0x0, 0x0, 0x80000000000000, 0x0, 0x0, 0x0, 0xc00000000000},
|
||||
/* idx: 248 */ {0x0, 0x0, 0x0, 0x100000000000000, 0x0, 0x0, 0x0, 0x3000000000000},
|
||||
/* idx: 249 */ {0x0, 0x0, 0x0, 0x200000000000000, 0x0, 0x0, 0x0, 0xc000000000000},
|
||||
/* idx: 250 */ {0x0, 0x0, 0x0, 0x400000000000000, 0x0, 0x0, 0x0, 0x30000000000000},
|
||||
/* idx: 251 */ {0x0, 0x0, 0x0, 0x800000000000000, 0x0, 0x0, 0x0, 0xc0000000000000},
|
||||
/* idx: 252 */ {0x0, 0x0, 0x0, 0x1000000000000000, 0x0, 0x0, 0x0, 0x300000000000000},
|
||||
/* idx: 253 */ {0x0, 0x0, 0x0, 0x2000000000000000, 0x0, 0x0, 0x0, 0xc00000000000000},
|
||||
/* idx: 254 */ {0x0, 0x0, 0x0, 0x4000000000000000, 0x0, 0x0, 0x0, 0x3000000000000000},
|
||||
/* idx: 255 */ {0x0, 0x0, 0x0, 0x8000000000000000, 0x0, 0x0, 0x0, 0xc000000000000000},
|
||||
//
|
||||
// START of HOST ROUTES, pfxLen == 8
|
||||
// [:4] are all 0 and only one bit is set in [4:]
|
||||
//
|
||||
/* idx: 256 */ {0x0, 0x0, 0x0, 0x0, 0x1},
|
||||
/* idx: 257 */ {0x0, 0x0, 0x0, 0x0, 0x2},
|
||||
/* idx: 258 */ {0x0, 0x0, 0x0, 0x0, 0x4},
|
||||
/* idx: 259 */ {0x0, 0x0, 0x0, 0x0, 0x8},
|
||||
/* idx: 260 */ {0x0, 0x0, 0x0, 0x0, 0x10},
|
||||
/* idx: 261 */ {0x0, 0x0, 0x0, 0x0, 0x20},
|
||||
/* idx: 262 */ {0x0, 0x0, 0x0, 0x0, 0x40},
|
||||
/* idx: 263 */ {0x0, 0x0, 0x0, 0x0, 0x80},
|
||||
/* idx: 264 */ {0x0, 0x0, 0x0, 0x0, 0x100},
|
||||
/* idx: 265 */ {0x0, 0x0, 0x0, 0x0, 0x200},
|
||||
/* idx: 266 */ {0x0, 0x0, 0x0, 0x0, 0x400},
|
||||
/* idx: 267 */ {0x0, 0x0, 0x0, 0x0, 0x800},
|
||||
/* idx: 268 */ {0x0, 0x0, 0x0, 0x0, 0x1000},
|
||||
/* idx: 269 */ {0x0, 0x0, 0x0, 0x0, 0x2000},
|
||||
/* idx: 270 */ {0x0, 0x0, 0x0, 0x0, 0x4000},
|
||||
/* idx: 271 */ {0x0, 0x0, 0x0, 0x0, 0x8000},
|
||||
/* idx: 272 */ {0x0, 0x0, 0x0, 0x0, 0x10000},
|
||||
/* idx: 273 */ {0x0, 0x0, 0x0, 0x0, 0x20000},
|
||||
/* idx: 274 */ {0x0, 0x0, 0x0, 0x0, 0x40000},
|
||||
/* idx: 275 */ {0x0, 0x0, 0x0, 0x0, 0x80000},
|
||||
/* idx: 276 */ {0x0, 0x0, 0x0, 0x0, 0x100000},
|
||||
/* idx: 277 */ {0x0, 0x0, 0x0, 0x0, 0x200000},
|
||||
/* idx: 278 */ {0x0, 0x0, 0x0, 0x0, 0x400000},
|
||||
/* idx: 279 */ {0x0, 0x0, 0x0, 0x0, 0x800000},
|
||||
/* idx: 280 */ {0x0, 0x0, 0x0, 0x0, 0x1000000},
|
||||
/* idx: 281 */ {0x0, 0x0, 0x0, 0x0, 0x2000000},
|
||||
/* idx: 282 */ {0x0, 0x0, 0x0, 0x0, 0x4000000},
|
||||
/* idx: 283 */ {0x0, 0x0, 0x0, 0x0, 0x8000000},
|
||||
/* idx: 284 */ {0x0, 0x0, 0x0, 0x0, 0x10000000},
|
||||
/* idx: 285 */ {0x0, 0x0, 0x0, 0x0, 0x20000000},
|
||||
/* idx: 286 */ {0x0, 0x0, 0x0, 0x0, 0x40000000},
|
||||
/* idx: 287 */ {0x0, 0x0, 0x0, 0x0, 0x80000000},
|
||||
/* idx: 288 */ {0x0, 0x0, 0x0, 0x0, 0x100000000},
|
||||
/* idx: 289 */ {0x0, 0x0, 0x0, 0x0, 0x200000000},
|
||||
/* idx: 290 */ {0x0, 0x0, 0x0, 0x0, 0x400000000},
|
||||
/* idx: 291 */ {0x0, 0x0, 0x0, 0x0, 0x800000000},
|
||||
/* idx: 292 */ {0x0, 0x0, 0x0, 0x0, 0x1000000000},
|
||||
/* idx: 293 */ {0x0, 0x0, 0x0, 0x0, 0x2000000000},
|
||||
/* idx: 294 */ {0x0, 0x0, 0x0, 0x0, 0x4000000000},
|
||||
/* idx: 295 */ {0x0, 0x0, 0x0, 0x0, 0x8000000000},
|
||||
/* idx: 296 */ {0x0, 0x0, 0x0, 0x0, 0x10000000000},
|
||||
/* idx: 297 */ {0x0, 0x0, 0x0, 0x0, 0x20000000000},
|
||||
/* idx: 298 */ {0x0, 0x0, 0x0, 0x0, 0x40000000000},
|
||||
/* idx: 299 */ {0x0, 0x0, 0x0, 0x0, 0x80000000000},
|
||||
/* idx: 300 */ {0x0, 0x0, 0x0, 0x0, 0x100000000000},
|
||||
/* idx: 301 */ {0x0, 0x0, 0x0, 0x0, 0x200000000000},
|
||||
/* idx: 302 */ {0x0, 0x0, 0x0, 0x0, 0x400000000000},
|
||||
/* idx: 303 */ {0x0, 0x0, 0x0, 0x0, 0x800000000000},
|
||||
/* idx: 304 */ {0x0, 0x0, 0x0, 0x0, 0x1000000000000},
|
||||
/* idx: 305 */ {0x0, 0x0, 0x0, 0x0, 0x2000000000000},
|
||||
/* idx: 306 */ {0x0, 0x0, 0x0, 0x0, 0x4000000000000},
|
||||
/* idx: 307 */ {0x0, 0x0, 0x0, 0x0, 0x8000000000000},
|
||||
/* idx: 308 */ {0x0, 0x0, 0x0, 0x0, 0x10000000000000},
|
||||
/* idx: 309 */ {0x0, 0x0, 0x0, 0x0, 0x20000000000000},
|
||||
/* idx: 310 */ {0x0, 0x0, 0x0, 0x0, 0x40000000000000},
|
||||
/* idx: 311 */ {0x0, 0x0, 0x0, 0x0, 0x80000000000000},
|
||||
/* idx: 312 */ {0x0, 0x0, 0x0, 0x0, 0x100000000000000},
|
||||
/* idx: 313 */ {0x0, 0x0, 0x0, 0x0, 0x200000000000000},
|
||||
/* idx: 314 */ {0x0, 0x0, 0x0, 0x0, 0x400000000000000},
|
||||
/* idx: 315 */ {0x0, 0x0, 0x0, 0x0, 0x800000000000000},
|
||||
/* idx: 316 */ {0x0, 0x0, 0x0, 0x0, 0x1000000000000000},
|
||||
/* idx: 317 */ {0x0, 0x0, 0x0, 0x0, 0x2000000000000000},
|
||||
/* idx: 318 */ {0x0, 0x0, 0x0, 0x0, 0x4000000000000000},
|
||||
/* idx: 319 */ {0x0, 0x0, 0x0, 0x0, 0x8000000000000000},
|
||||
/* idx: 320 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
|
||||
/* idx: 321 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
|
||||
/* idx: 322 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x4},
|
||||
/* idx: 323 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
|
||||
/* idx: 324 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x10},
|
||||
/* idx: 325 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x20},
|
||||
/* idx: 326 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x40},
|
||||
/* idx: 327 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
|
||||
/* idx: 328 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x100},
|
||||
/* idx: 329 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x200},
|
||||
/* idx: 330 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x400},
|
||||
/* idx: 331 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x800},
|
||||
/* idx: 332 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x1000},
|
||||
/* idx: 333 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x2000},
|
||||
/* idx: 334 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x4000},
|
||||
/* idx: 335 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x8000},
|
||||
/* idx: 336 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x10000},
|
||||
/* idx: 337 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x20000},
|
||||
/* idx: 338 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x40000},
|
||||
/* idx: 339 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x80000},
|
||||
/* idx: 340 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x100000},
|
||||
/* idx: 341 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x200000},
|
||||
/* idx: 342 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x400000},
|
||||
/* idx: 343 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x800000},
|
||||
/* idx: 344 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000},
|
||||
/* idx: 345 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000},
|
||||
/* idx: 346 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000},
|
||||
/* idx: 347 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000},
|
||||
/* idx: 348 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000},
|
||||
/* idx: 349 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000},
|
||||
/* idx: 350 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000},
|
||||
/* idx: 351 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000},
|
||||
/* idx: 352 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000},
|
||||
/* idx: 353 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000},
|
||||
/* idx: 354 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000},
|
||||
/* idx: 355 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000},
|
||||
/* idx: 356 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000},
|
||||
/* idx: 357 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000},
|
||||
/* idx: 358 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000},
|
||||
/* idx: 359 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000},
|
||||
/* idx: 360 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000},
|
||||
/* idx: 361 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000},
|
||||
/* idx: 362 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000000},
|
||||
/* idx: 363 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000000},
|
||||
/* idx: 364 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000000},
|
||||
/* idx: 365 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000000},
|
||||
/* idx: 366 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000000},
|
||||
/* idx: 367 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000000},
|
||||
/* idx: 368 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000},
|
||||
/* idx: 369 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000},
|
||||
/* idx: 370 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000000},
|
||||
/* idx: 371 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000000},
|
||||
/* idx: 372 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000},
|
||||
/* idx: 373 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000000},
|
||||
/* idx: 374 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000000000},
|
||||
/* idx: 375 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000000000},
|
||||
/* idx: 376 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000000000},
|
||||
/* idx: 377 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000000000},
|
||||
/* idx: 378 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000000000},
|
||||
/* idx: 379 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000000000},
|
||||
/* idx: 380 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000000},
|
||||
/* idx: 381 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000000},
|
||||
/* idx: 382 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000000000},
|
||||
/* idx: 383 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000000000},
|
||||
/* idx: 384 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
|
||||
/* idx: 385 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
|
||||
/* idx: 386 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4},
|
||||
/* idx: 387 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
|
||||
/* idx: 388 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10},
|
||||
/* idx: 389 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20},
|
||||
/* idx: 390 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40},
|
||||
/* idx: 391 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
|
||||
/* idx: 392 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100},
|
||||
/* idx: 393 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200},
|
||||
/* idx: 394 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400},
|
||||
/* idx: 395 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800},
|
||||
/* idx: 396 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000},
|
||||
/* idx: 397 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000},
|
||||
/* idx: 398 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000},
|
||||
/* idx: 399 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000},
|
||||
/* idx: 400 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000},
|
||||
/* idx: 401 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000},
|
||||
/* idx: 402 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000},
|
||||
/* idx: 403 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000},
|
||||
/* idx: 404 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000},
|
||||
/* idx: 405 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000},
|
||||
/* idx: 406 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000},
|
||||
/* idx: 407 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000},
|
||||
/* idx: 408 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000},
|
||||
/* idx: 409 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000},
|
||||
/* idx: 410 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000},
|
||||
/* idx: 411 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000},
|
||||
/* idx: 412 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000},
|
||||
/* idx: 413 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000},
|
||||
/* idx: 414 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000},
|
||||
/* idx: 415 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000},
|
||||
/* idx: 416 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000},
|
||||
/* idx: 417 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000},
|
||||
/* idx: 418 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000},
|
||||
/* idx: 419 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000},
|
||||
/* idx: 420 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000},
|
||||
/* idx: 421 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000},
|
||||
/* idx: 422 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000},
|
||||
/* idx: 423 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000},
|
||||
/* idx: 424 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000},
|
||||
/* idx: 425 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000},
|
||||
/* idx: 426 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000000},
|
||||
/* idx: 427 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000000},
|
||||
/* idx: 428 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000000},
|
||||
/* idx: 429 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000000},
|
||||
/* idx: 430 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000000},
|
||||
/* idx: 431 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000000},
|
||||
/* idx: 432 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000},
|
||||
/* idx: 433 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000},
|
||||
/* idx: 434 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000000},
|
||||
/* idx: 435 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000000},
|
||||
/* idx: 436 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000},
|
||||
/* idx: 437 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000000},
|
||||
/* idx: 438 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000000000},
|
||||
/* idx: 439 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000000000},
|
||||
/* idx: 440 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000000000},
|
||||
/* idx: 441 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000000000},
|
||||
/* idx: 442 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000000000},
|
||||
/* idx: 443 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000000000},
|
||||
/* idx: 444 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000000},
|
||||
/* idx: 445 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000000},
|
||||
/* idx: 446 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000000000},
|
||||
/* idx: 447 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000000000},
|
||||
/* idx: 448 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1},
|
||||
/* idx: 449 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
|
||||
/* idx: 450 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4},
|
||||
/* idx: 451 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8},
|
||||
/* idx: 452 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10},
|
||||
/* idx: 453 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20},
|
||||
/* idx: 454 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40},
|
||||
/* idx: 455 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
|
||||
/* idx: 456 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100},
|
||||
/* idx: 457 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200},
|
||||
/* idx: 458 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400},
|
||||
/* idx: 459 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800},
|
||||
/* idx: 460 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000},
|
||||
/* idx: 461 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000},
|
||||
/* idx: 462 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000},
|
||||
/* idx: 463 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000},
|
||||
/* idx: 464 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000},
|
||||
/* idx: 465 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000},
|
||||
/* idx: 466 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000},
|
||||
/* idx: 467 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000},
|
||||
/* idx: 468 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000},
|
||||
/* idx: 469 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000},
|
||||
/* idx: 470 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000},
|
||||
/* idx: 471 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000},
|
||||
/* idx: 472 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000},
|
||||
/* idx: 473 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000},
|
||||
/* idx: 474 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000},
|
||||
/* idx: 475 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000},
|
||||
/* idx: 476 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000},
|
||||
/* idx: 477 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000},
|
||||
/* idx: 478 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000},
|
||||
/* idx: 479 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000},
|
||||
/* idx: 480 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000},
|
||||
/* idx: 481 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000},
|
||||
/* idx: 482 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000},
|
||||
/* idx: 483 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000},
|
||||
/* idx: 484 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000},
|
||||
/* idx: 485 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000},
|
||||
/* idx: 486 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000},
|
||||
/* idx: 487 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000},
|
||||
/* idx: 488 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000},
|
||||
/* idx: 489 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000},
|
||||
/* idx: 490 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000000},
|
||||
/* idx: 491 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000000},
|
||||
/* idx: 492 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000000},
|
||||
/* idx: 493 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000000},
|
||||
/* idx: 494 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000000},
|
||||
/* idx: 495 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000000},
|
||||
/* idx: 496 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000},
|
||||
/* idx: 497 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000},
|
||||
/* idx: 498 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000000},
|
||||
/* idx: 499 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000000},
|
||||
/* idx: 500 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000},
|
||||
/* idx: 501 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20000000000000},
|
||||
/* idx: 502 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40000000000000},
|
||||
/* idx: 503 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000000000000},
|
||||
/* idx: 504 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000000000},
|
||||
/* idx: 505 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x200000000000000},
|
||||
/* idx: 506 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000000000000},
|
||||
/* idx: 507 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x800000000000000},
|
||||
/* idx: 508 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000000000000000},
|
||||
/* idx: 509 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2000000000000000},
|
||||
/* idx: 510 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4000000000000000},
|
||||
/* idx: 511 */ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8000000000000000},
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package bart
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/gaissmai/bart/internal/art"
|
||||
"github.com/gaissmai/bart/internal/lpm"
|
||||
"github.com/gaissmai/bart/internal/nodes"
|
||||
)
|
||||
|
||||
// Table represents an IPv4 and IPv6 routing table with payload V.
|
||||
//
|
||||
// The zero value is ready to use.
|
||||
//
|
||||
// A Table must not be copied by value; always pass by pointer.
|
||||
// Nil pointers as receivers or arguments are forbidden and will panic.
|
||||
//
|
||||
// The Table is safe for concurrent reads, but concurrent reads and writes
|
||||
// must be externally synchronized. Mutation via Insert/Delete requires locks,
|
||||
// or alternatively, use ...Persist methods which return a modified copy
|
||||
// without altering the original table (copy-on-write).
|
||||
//
|
||||
// Performance note: Do not pass IPv4-in-IPv6 addresses (e.g., ::ffff:192.0.2.1)
|
||||
// as input. The methods do not perform automatic unmapping to avoid unnecessary
|
||||
// overhead for the common case where native addresses are used.
|
||||
// Users should unmap IPv4-in-IPv6 addresses to their native IPv4 form
|
||||
// (e.g., 192.0.2.1) before calling these methods.
|
||||
type Table[V any] struct {
|
||||
// used by -copylocks checker from `go vet`.
|
||||
_ [0]sync.Mutex
|
||||
|
||||
// the root nodes, implemented as popcount compressed multibit tries
|
||||
root4 nodes.BartNode[V]
|
||||
root6 nodes.BartNode[V]
|
||||
|
||||
// the number of prefixes in the routing table
|
||||
size4 int
|
||||
size6 int
|
||||
}
|
||||
|
||||
// rootNodeByVersion, root node getter for ip version.
|
||||
func (t *Table[V]) rootNodeByVersion(is4 bool) *nodes.BartNode[V] {
|
||||
if is4 {
|
||||
return &t.root4
|
||||
}
|
||||
return &t.root6
|
||||
}
|
||||
|
||||
// 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 *Table[V]) Insert(pfx netip.Prefix, val V) {
|
||||
t.insert(pfx, val)
|
||||
}
|
||||
|
||||
// InsertPersist is similar to Insert but the receiver isn't modified.
|
||||
//
|
||||
// All nodes touched during insert are cloned and a new Table is returned.
|
||||
// This is not a full [Table.Clone], all untouched nodes are still referenced
|
||||
// from both Tables.
|
||||
//
|
||||
// If the payload type V contains pointers or needs deep copying,
|
||||
// it must implement the Clone method to support correct cloning.
|
||||
//
|
||||
// Due to cloning overhead this is significantly slower than Insert,
|
||||
// typically taking μsec instead of nsec.
|
||||
func (t *Table[V]) InsertPersist(pfx netip.Prefix, val V) *Table[V] {
|
||||
return t.insertPersist(pfx, val)
|
||||
}
|
||||
|
||||
// Modify applies an insert, update, or delete operation for the value
|
||||
// associated with the given prefix. The supplied callback decides the
|
||||
// operation: it is called with the current value (or zero if not found)
|
||||
// and a boolean indicating whether the prefix exists. The callback must
|
||||
// return a new value and a delete flag: del == false inserts or updates,
|
||||
// del == true deletes the entry if it exists (otherwise no-op).
|
||||
//
|
||||
// The callback is invoked at most once per call.
|
||||
//
|
||||
// The operation is determined by the callback function, which is called with:
|
||||
//
|
||||
// val: the current value (or zero value if not found)
|
||||
// found: true if the prefix currently exists, false otherwise
|
||||
//
|
||||
// The callback returns:
|
||||
//
|
||||
// val: the new value to insert or update (ignored if del == true)
|
||||
// del: true to delete the entry, false to insert or update
|
||||
//
|
||||
// Summary of callback semantics:
|
||||
//
|
||||
// | cb-input | cb-return | Ops |
|
||||
// ------------------------------------- --------
|
||||
// | (zero, false) | (_, true) | no-op |
|
||||
// | (zero, false) | (newVal, false) | insert |
|
||||
// | (oldVal, true) | (newVal, false) | update |
|
||||
// | (oldVal, true) | (_, true) | delete |
|
||||
// ------------------------------------- --------
|
||||
func (t *Table[V]) Modify(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)) {
|
||||
if !pfx.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
// canonicalize prefix
|
||||
pfx = pfx.Masked()
|
||||
|
||||
is4 := pfx.Addr().Is4()
|
||||
|
||||
n := t.rootNodeByVersion(is4)
|
||||
|
||||
delta := n.Modify(pfx, cb)
|
||||
t.sizeUpdate(is4, delta)
|
||||
}
|
||||
|
||||
// Contains reports whether any stored prefix covers the given IP address.
|
||||
// Returns false for invalid IP addresses.
|
||||
//
|
||||
// This performs longest-prefix matching and returns true if any prefix
|
||||
// in the routing table contains the IP address, regardless of the associated value.
|
||||
//
|
||||
// It does not return the value nor the prefix of the matching item,
|
||||
// but as a test against an allow-/deny-list it's often sufficient
|
||||
// and even few nanoseconds faster than [Table.Lookup].
|
||||
func (t *Table[V]) Contains(ip netip.Addr) bool {
|
||||
// speed is top priority: no explicit test for ip.IsValid
|
||||
// if ip is invalid, AsSlice() returns nil, Contains returns false.
|
||||
is4 := ip.Is4()
|
||||
n := t.rootNodeByVersion(is4)
|
||||
|
||||
for _, octet := range ip.AsSlice() {
|
||||
// for contains, any lpm match is good enough, no backtracking needed
|
||||
if n.PrefixCount() != 0 && n.Contains(art.OctetToIdx(octet)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// stop traversing?
|
||||
if !n.Children.Test(octet) {
|
||||
return false
|
||||
}
|
||||
kid := n.MustGetChild(octet)
|
||||
|
||||
// kid is node or leaf or fringe at octet
|
||||
switch kid := kid.(type) {
|
||||
case *nodes.BartNode[V]:
|
||||
n = kid // descend down to next trie level
|
||||
|
||||
case *nodes.FringeNode[V]:
|
||||
// fringe is the default-route for all possible octets below
|
||||
return true
|
||||
|
||||
case *nodes.LeafNode[V]:
|
||||
return kid.Prefix.Contains(ip)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Lookup performs a longest prefix match (LPM) lookup for the given address.
|
||||
// It finds the most specific (longest) prefix in the routing table that
|
||||
// contains the given address and returns its associated value.
|
||||
//
|
||||
// This is the fundamental operation for IP routing decisions, finding the
|
||||
// best matching route for a destination address.
|
||||
//
|
||||
// Returns the associated value and true if a matching prefix is found.
|
||||
// Returns zero value and false if no prefix contains the address.
|
||||
func (t *Table[V]) Lookup(ip netip.Addr) (val V, ok bool) {
|
||||
if !ip.IsValid() {
|
||||
return val, ok
|
||||
}
|
||||
|
||||
is4 := ip.Is4()
|
||||
octets := ip.AsSlice()
|
||||
|
||||
n := t.rootNodeByVersion(is4)
|
||||
|
||||
// stack of the traversed nodes for fast backtracking, if needed
|
||||
stack := [nodes.MaxTreeDepth]*nodes.BartNode[V]{}
|
||||
|
||||
// run variable, used after for loop
|
||||
var depth int
|
||||
var octet byte
|
||||
|
||||
LOOP:
|
||||
// find leaf node
|
||||
for depth, octet = range octets {
|
||||
depth &= nodes.DepthMask // BCE, Lookup must be fast
|
||||
|
||||
// push current node on stack for fast backtracking
|
||||
stack[depth] = n
|
||||
|
||||
// go down in tight loop to last octet
|
||||
if !n.Children.Test(octet) {
|
||||
// no more nodes below octet
|
||||
break LOOP
|
||||
}
|
||||
kid := n.MustGetChild(octet)
|
||||
|
||||
// kid is node or leaf or fringe at octet
|
||||
switch kid := kid.(type) {
|
||||
case *nodes.BartNode[V]:
|
||||
n = kid
|
||||
continue LOOP // descend down to next trie level
|
||||
|
||||
case *nodes.FringeNode[V]:
|
||||
// fringe is the default-route for all possible nodes below
|
||||
return kid.Value, true
|
||||
|
||||
case *nodes.LeafNode[V]:
|
||||
if kid.Prefix.Contains(ip) {
|
||||
return kid.Value, true
|
||||
}
|
||||
// reached a path compressed prefix, stop traversing
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
|
||||
// start backtracking, unwind the stack, bounds check eliminated
|
||||
for ; depth >= 0; depth-- {
|
||||
depth &= nodes.DepthMask // BCE
|
||||
|
||||
n = stack[depth]
|
||||
|
||||
// longest prefix match, skip if node has no prefixes
|
||||
if n.PrefixCount() != 0 {
|
||||
idx := art.OctetToIdx(octets[depth])
|
||||
// lookupIdx() manually inlined
|
||||
if lpmIdx, ok2 := n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok2 {
|
||||
return n.MustGetPrefix(lpmIdx), ok2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// LookupPrefix performs a longest prefix match lookup for any address within
|
||||
// the given prefix. It finds the most specific routing table entry that would
|
||||
// match any address in the provided prefix range.
|
||||
//
|
||||
// This is functionally identical to LookupPrefixLPM but returns only the
|
||||
// associated value, not the matching prefix itself.
|
||||
//
|
||||
// Returns the value and true if a matching prefix is found.
|
||||
// Returns zero value and false if no match exists.
|
||||
func (t *Table[V]) LookupPrefix(pfx netip.Prefix) (val V, ok bool) {
|
||||
_, val, ok = t.lookupPrefixLPM(pfx, false)
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// LookupPrefixLPM performs a longest prefix match lookup for any address within
|
||||
// the given prefix. It finds the most specific routing table entry that would
|
||||
// match any address in the provided prefix range.
|
||||
//
|
||||
// This is functionally identical to LookupPrefix but additionally returns the
|
||||
// matching LPM prefix itself along with the value.
|
||||
//
|
||||
// This method is slower than LookupPrefix and should only be used if the
|
||||
// matching lpm entry is also required for other reasons.
|
||||
//
|
||||
// Returns the matching prefix, its associated value, and true if found.
|
||||
// Returns zero values and false if no match exists.
|
||||
func (t *Table[V]) LookupPrefixLPM(pfx netip.Prefix) (lpmPfx netip.Prefix, val V, ok bool) {
|
||||
return t.lookupPrefixLPM(pfx, true)
|
||||
}
|
||||
|
||||
func (t *Table[V]) lookupPrefixLPM(pfx netip.Prefix, withLPM bool) (lpmPfx netip.Prefix, val V, ok bool) {
|
||||
if !pfx.IsValid() {
|
||||
return lpmPfx, val, ok
|
||||
}
|
||||
|
||||
// canonicalize the prefix
|
||||
pfx = pfx.Masked()
|
||||
|
||||
ip := pfx.Addr()
|
||||
pfxLen := pfx.Bits()
|
||||
is4 := ip.Is4()
|
||||
octets := ip.AsSlice()
|
||||
strideCount, modBits := nodes.DivMod8(pfxLen)
|
||||
|
||||
n := t.rootNodeByVersion(is4)
|
||||
|
||||
// record path to leaf node
|
||||
stack := [nodes.MaxTreeDepth]*nodes.BartNode[V]{}
|
||||
|
||||
var depth int
|
||||
var octet byte
|
||||
|
||||
LOOP:
|
||||
// find the last node on the octets path in the trie,
|
||||
for depth, octet = range octets {
|
||||
depth &= nodes.DepthMask // BCE
|
||||
|
||||
// stepped one past the last stride of interest; back up to last and break
|
||||
if depth > strideCount {
|
||||
depth--
|
||||
break
|
||||
}
|
||||
// push current node on stack
|
||||
stack[depth] = n
|
||||
|
||||
// go down in tight loop to leaf node
|
||||
if !n.Children.Test(octet) {
|
||||
break LOOP
|
||||
}
|
||||
kid := n.MustGetChild(octet)
|
||||
|
||||
// kid is node or leaf or fringe at octet
|
||||
switch kid := kid.(type) {
|
||||
case *nodes.BartNode[V]:
|
||||
n = kid
|
||||
continue LOOP // descend down to next trie level
|
||||
|
||||
case *nodes.LeafNode[V]:
|
||||
// reached a path compressed prefix, stop traversing
|
||||
if kid.Prefix.Bits() > pfxLen || !kid.Prefix.Contains(ip) {
|
||||
break LOOP
|
||||
}
|
||||
return kid.Prefix, kid.Value, true
|
||||
|
||||
case *nodes.FringeNode[V]:
|
||||
// the bits of the fringe are defined by the depth
|
||||
// maybe the LPM isn't needed, saves some cycles
|
||||
fringeBits := (depth + 1) << 3
|
||||
if fringeBits > pfxLen {
|
||||
break LOOP
|
||||
}
|
||||
|
||||
// the LPM isn't needed, saves some cycles
|
||||
if !withLPM {
|
||||
return netip.Prefix{}, kid.Value, true
|
||||
}
|
||||
|
||||
// get the LPM prefix back from ip and depth
|
||||
// it's a fringe, bits are always /8, /16, /24, ...
|
||||
fringePfx, _ := ip.Prefix((depth + 1) << 3)
|
||||
return fringePfx, kid.Value, true
|
||||
}
|
||||
}
|
||||
|
||||
// start backtracking, unwind the stack
|
||||
for ; depth >= 0; depth-- {
|
||||
depth &= nodes.DepthMask // BCE
|
||||
|
||||
n = stack[depth]
|
||||
|
||||
// longest prefix match, skip if node has no prefixes
|
||||
if n.Prefixes.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var idx uint8
|
||||
octet = octets[depth]
|
||||
|
||||
// only the final stride may have a different prefix len
|
||||
// all others are just host routes
|
||||
if depth == strideCount {
|
||||
idx = art.PfxToIdx(octet, modBits)
|
||||
} else {
|
||||
idx = art.OctetToIdx(octet)
|
||||
}
|
||||
|
||||
// manually inlined: lookupIdx(idx)
|
||||
var topIdx uint8
|
||||
if topIdx, ok = n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok {
|
||||
val = n.MustGetPrefix(topIdx)
|
||||
|
||||
// called from LookupPrefix
|
||||
if !withLPM {
|
||||
return netip.Prefix{}, val, ok
|
||||
}
|
||||
|
||||
// called from LookupPrefixLPM
|
||||
|
||||
// get the bits from depth and top idx
|
||||
pfxBits := int(art.PfxBits(depth, topIdx))
|
||||
|
||||
// calculate the lpmPfx from incoming ip and new mask
|
||||
// netip.Addr.Prefix canonicalizes. Invariant: art.PfxBits(depth, topIdx)
|
||||
// yields a valid mask (v4: 0..32, v6: 0..128), so error is impossible.
|
||||
lpmPfx, _ = ip.Prefix(pfxBits)
|
||||
return lpmPfx, val, ok
|
||||
}
|
||||
}
|
||||
|
||||
return lpmPfx, val, ok
|
||||
}
|
||||
+731
@@ -0,0 +1,731 @@
|
||||
// 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 *Table[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 *Table[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 Table is returned.
|
||||
// This is not a full [Table.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 *Table[V]) insertPersist(pfx netip.Prefix, val V) *Table[V] {
|
||||
if !pfx.IsValid() {
|
||||
return t
|
||||
}
|
||||
|
||||
// canonicalize prefix
|
||||
pfx = pfx.Masked()
|
||||
is4 := pfx.Addr().Is4()
|
||||
|
||||
// share size counters; root nodes cloned selectively.
|
||||
pt := &Table[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 *Table[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 *Table[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 Table 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 *Table[V]) DeletePersist(pfx netip.Prefix) *Table[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 := &Table[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 *Table is returned.
|
||||
func (t *Table[V]) ModifyPersist(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)) *Table[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 *Table[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 *Table[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 *Table[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 *Table[V]) Overlaps(o *Table[V]) bool {
|
||||
return t.Overlaps4(o) || t.Overlaps6(o)
|
||||
}
|
||||
|
||||
// Overlaps4 is like [Table.Overlaps] but for the v4 routing table only.
|
||||
func (t *Table[V]) Overlaps4(o *Table[V]) bool {
|
||||
if t.size4 == 0 || o.size4 == 0 {
|
||||
return false
|
||||
}
|
||||
return t.root4.Overlaps(&o.root4, 0)
|
||||
}
|
||||
|
||||
// Overlaps6 is like [Table.Overlaps] but for the v6 routing table only.
|
||||
func (t *Table[V]) Overlaps6(o *Table[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 Table.Clone.
|
||||
func (t *Table[V]) Union(o *Table[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 *Table is returned.
|
||||
// If o is empty, no nodes are touched and the receiver may be
|
||||
// returned unchanged.
|
||||
func (t *Table[V]) UnionPersist(o *Table[V]) *Table[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 Table with root nodes just copied.
|
||||
pt := &Table[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 *Table[V]) Equal(o *Table[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 *Table[V]) Clone() *Table[V] {
|
||||
c := new(Table[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 *Table[V]) Size() int {
|
||||
return t.size4 + t.size6
|
||||
}
|
||||
|
||||
// Size4 returns the IPv4 prefix count.
|
||||
func (t *Table[V]) Size4() int {
|
||||
return t.size4
|
||||
}
|
||||
|
||||
// Size6 returns the IPv6 prefix count.
|
||||
func (t *Table[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 [Table.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 *Table[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 [Table.All] but only for the v4 routing table.
|
||||
func (t *Table[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 [Table.All] but only for the v6 routing table.
|
||||
func (t *Table[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 [Table.All] but the iteration is ordered in canonical
|
||||
// CIDR prefix sort order.
|
||||
func (t *Table[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 [Table.AllSorted] but only for the v4 routing table.
|
||||
func (t *Table[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 [Table.AllSorted] but only for the v6 routing table.
|
||||
func (t *Table[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 *Table[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 *Table[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 [Table.Fprint].
|
||||
func (t *Table[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 *Table[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 *Table[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 *Table[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 *Table[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 *Table[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 *Table[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)
|
||||
}
|
||||
}
|
||||
-595
@@ -1,595 +0,0 @@
|
||||
// Copyright (c) 2024 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Please read the ART paper ./doc/artlookup.pdf
|
||||
// to understand the baseIndex algorithm.
|
||||
|
||||
package bart
|
||||
|
||||
// netmask for bits
|
||||
//
|
||||
// 0b0000_0000, // bits == 0
|
||||
// 0b1000_0000, // bits == 1
|
||||
// 0b1100_0000, // bits == 2
|
||||
// 0b1110_0000, // bits == 3
|
||||
// 0b1111_0000, // bits == 4
|
||||
// 0b1111_1000, // bits == 5
|
||||
// 0b1111_1100, // bits == 6
|
||||
// 0b1111_1110, // bits == 7
|
||||
// 0b1111_1111, // bits == 8
|
||||
func netMask(bits int) uint8 {
|
||||
return 0b1111_1111 << (8 - bits)
|
||||
}
|
||||
|
||||
// baseIndex of the first host route 0/8: pfxToIdx(0,8)
|
||||
const firstHostIdx = 256
|
||||
|
||||
// pfxToIdx, maps a prefix table as a 'complete binary tree'.
|
||||
func pfxToIdx(octet byte, prefixLen int) uint {
|
||||
return uint(octet>>(strideLen-prefixLen)) + (1 << prefixLen)
|
||||
}
|
||||
|
||||
// hostIndex, just pfxToIdx(octet, 8) but faster.
|
||||
func hostIndex(octet uint) uint {
|
||||
return octet + firstHostIdx
|
||||
}
|
||||
|
||||
// cmpIndexRank, sort indexes in prefix sort order.
|
||||
func cmpIndexRank(aIdx, bIdx uint) int {
|
||||
// convert idx to prefix
|
||||
aOctet, aBits := idxToPfx(aIdx)
|
||||
bOctet, bBits := idxToPfx(bIdx)
|
||||
|
||||
// cmp the prefixes, first by address and then by bits
|
||||
if aOctet == bOctet {
|
||||
if aBits <= bBits {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
if aOctet < bOctet {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
// idxToPfx returns the octet and prefix len of baseIdx.
|
||||
// It's the inverse to pfxToIdx.
|
||||
func idxToPfx(idx uint) (octet byte, pfxLen int) {
|
||||
return baseIdxLookupTbl[idx].octet, int(baseIdxLookupTbl[idx].pfxLen)
|
||||
}
|
||||
|
||||
// baseIdxLookupTbl, maps back from idx => octet/bits
|
||||
//
|
||||
// Use the pre computed lookup table, the func baseIndexToPrefix is too slow
|
||||
// in comparison to an array access.
|
||||
//
|
||||
// func baseIndexToPrefix(idx uint) (octet byte, pfxLen int) {
|
||||
// nlz := bits.LeadingZeros(idx)
|
||||
// pfxLen = strconv.IntSize - nlz - 1
|
||||
// octet = byte((idx & (0xFF >> (8 - pfxLen))) << (8 - pfxLen))
|
||||
// return octet, pfxLen
|
||||
// }
|
||||
var baseIdxLookupTbl = [512]struct {
|
||||
octet byte
|
||||
pfxLen int8
|
||||
}{
|
||||
{0, -1}, // idx == 0, invalid
|
||||
{0, 0}, // idx == 1
|
||||
{0, 1}, // idx == 2
|
||||
{128, 1}, // idx == 3, e.g. 128.0.0.0/1, 8000::/1
|
||||
{0, 2}, // idx == 4
|
||||
{64, 2}, // idx == 5
|
||||
{128, 2}, // idx == 6
|
||||
{192, 2}, // idx == 7
|
||||
{0, 3}, // idx == 8
|
||||
{32, 3}, // idx == 9
|
||||
{64, 3}, // idx == 10
|
||||
{96, 3}, // idx == 11
|
||||
{128, 3}, // idx == 12
|
||||
{160, 3}, // idx == 13
|
||||
{192, 3}, // idx == 14
|
||||
{224, 3}, // idx == 15
|
||||
{0, 4}, // idx == 16
|
||||
{16, 4}, // idx == 17
|
||||
{32, 4}, // idx == 18
|
||||
{48, 4}, // idx == 19
|
||||
{64, 4}, // idx == 20
|
||||
{80, 4}, // idx == 21
|
||||
{96, 4}, // idx == 22
|
||||
{112, 4}, // idx == 23
|
||||
{128, 4}, // idx == 24
|
||||
{144, 4}, // idx == 25
|
||||
{160, 4}, // idx == 26
|
||||
{176, 4}, // idx == 27
|
||||
{192, 4}, // idx == 28
|
||||
{208, 4}, // idx == 29
|
||||
{224, 4}, // idx == 30
|
||||
{240, 4}, // idx == 31
|
||||
{0, 5}, // idx == 32
|
||||
{8, 5}, // idx == 33
|
||||
{16, 5}, // idx == 34
|
||||
{24, 5}, // idx == 35
|
||||
{32, 5}, // idx == 36
|
||||
{40, 5}, // idx == 37
|
||||
{48, 5}, // idx == 38
|
||||
{56, 5}, // idx == 39
|
||||
{64, 5}, // idx == 40
|
||||
{72, 5}, // idx == 41
|
||||
{80, 5}, // idx == 42
|
||||
{88, 5}, // idx == 43
|
||||
{96, 5}, // idx == 44
|
||||
{104, 5}, // idx == 45
|
||||
{112, 5}, // idx == 46
|
||||
{120, 5}, // idx == 47
|
||||
{128, 5}, // idx == 48
|
||||
{136, 5}, // idx == 49
|
||||
{144, 5}, // idx == 50
|
||||
{152, 5}, // idx == 51
|
||||
{160, 5}, // idx == 52
|
||||
{168, 5}, // idx == 53
|
||||
{176, 5}, // idx == 54
|
||||
{184, 5}, // idx == 55
|
||||
{192, 5}, // idx == 56
|
||||
{200, 5}, // idx == 57
|
||||
{208, 5}, // idx == 58
|
||||
{216, 5}, // idx == 59
|
||||
{224, 5}, // idx == 60
|
||||
{232, 5}, // idx == 61
|
||||
{240, 5}, // idx == 62
|
||||
{248, 5}, // idx == 63
|
||||
{0, 6}, // idx == 64
|
||||
{4, 6}, // idx == 65
|
||||
{8, 6}, // idx == 66
|
||||
{12, 6}, // idx == 67
|
||||
{16, 6}, // idx == 68
|
||||
{20, 6}, // idx == 69
|
||||
{24, 6}, // idx == 70
|
||||
{28, 6}, // idx == 71
|
||||
{32, 6}, // idx == 72
|
||||
{36, 6}, // idx == 73
|
||||
{40, 6}, // idx == 74
|
||||
{44, 6}, // idx == 75
|
||||
{48, 6}, // idx == 76
|
||||
{52, 6}, // idx == 77
|
||||
{56, 6}, // idx == 78
|
||||
{60, 6}, // idx == 79
|
||||
{64, 6}, // idx == 80
|
||||
{68, 6}, // idx == 81
|
||||
{72, 6}, // idx == 82
|
||||
{76, 6}, // idx == 83
|
||||
{80, 6}, // idx == 84
|
||||
{84, 6}, // idx == 85
|
||||
{88, 6}, // idx == 86
|
||||
{92, 6}, // idx == 87
|
||||
{96, 6}, // idx == 88
|
||||
{100, 6}, // idx == 89
|
||||
{104, 6}, // idx == 90
|
||||
{108, 6}, // idx == 91
|
||||
{112, 6}, // idx == 92
|
||||
{116, 6}, // idx == 93
|
||||
{120, 6}, // idx == 94
|
||||
{124, 6}, // idx == 95
|
||||
{128, 6}, // idx == 96
|
||||
{132, 6}, // idx == 97
|
||||
{136, 6}, // idx == 98
|
||||
{140, 6}, // idx == 99
|
||||
{144, 6}, // idx == 100
|
||||
{148, 6}, // idx == 101
|
||||
{152, 6}, // idx == 102
|
||||
{156, 6}, // idx == 103
|
||||
{160, 6}, // idx == 104
|
||||
{164, 6}, // idx == 105
|
||||
{168, 6}, // idx == 106
|
||||
{172, 6}, // idx == 107
|
||||
{176, 6}, // idx == 108
|
||||
{180, 6}, // idx == 109
|
||||
{184, 6}, // idx == 110
|
||||
{188, 6}, // idx == 111
|
||||
{192, 6}, // idx == 112
|
||||
{196, 6}, // idx == 113
|
||||
{200, 6}, // idx == 114
|
||||
{204, 6}, // idx == 115
|
||||
{208, 6}, // idx == 116
|
||||
{212, 6}, // idx == 117
|
||||
{216, 6}, // idx == 118
|
||||
{220, 6}, // idx == 119
|
||||
{224, 6}, // idx == 120
|
||||
{228, 6}, // idx == 121
|
||||
{232, 6}, // idx == 122
|
||||
{236, 6}, // idx == 123
|
||||
{240, 6}, // idx == 124
|
||||
{244, 6}, // idx == 125
|
||||
{248, 6}, // idx == 126
|
||||
{252, 6}, // idx == 127
|
||||
{0, 7}, // idx == 128
|
||||
{2, 7}, // idx == 129
|
||||
{4, 7}, // idx == 130
|
||||
{6, 7}, // idx == 131
|
||||
{8, 7}, // idx == 132
|
||||
{10, 7}, // idx == 133
|
||||
{12, 7}, // idx == 134
|
||||
{14, 7}, // idx == 135
|
||||
{16, 7}, // idx == 136
|
||||
{18, 7}, // idx == 137
|
||||
{20, 7}, // idx == 138
|
||||
{22, 7}, // idx == 139
|
||||
{24, 7}, // idx == 140
|
||||
{26, 7}, // idx == 141
|
||||
{28, 7}, // idx == 142
|
||||
{30, 7}, // idx == 143
|
||||
{32, 7}, // idx == 144
|
||||
{34, 7}, // idx == 145
|
||||
{36, 7}, // idx == 146
|
||||
{38, 7}, // idx == 147
|
||||
{40, 7}, // idx == 148
|
||||
{42, 7}, // idx == 149
|
||||
{44, 7}, // idx == 150
|
||||
{46, 7}, // idx == 151
|
||||
{48, 7}, // idx == 152
|
||||
{50, 7}, // idx == 153
|
||||
{52, 7}, // idx == 154
|
||||
{54, 7}, // idx == 155
|
||||
{56, 7}, // idx == 156
|
||||
{58, 7}, // idx == 157
|
||||
{60, 7}, // idx == 158
|
||||
{62, 7}, // idx == 159
|
||||
{64, 7}, // idx == 160
|
||||
{66, 7}, // idx == 161
|
||||
{68, 7}, // idx == 162
|
||||
{70, 7}, // idx == 163
|
||||
{72, 7}, // idx == 164
|
||||
{74, 7}, // idx == 165
|
||||
{76, 7}, // idx == 166
|
||||
{78, 7}, // idx == 167
|
||||
{80, 7}, // idx == 168
|
||||
{82, 7}, // idx == 169
|
||||
{84, 7}, // idx == 170
|
||||
{86, 7}, // idx == 171
|
||||
{88, 7}, // idx == 172
|
||||
{90, 7}, // idx == 173
|
||||
{92, 7}, // idx == 174
|
||||
{94, 7}, // idx == 175
|
||||
{96, 7}, // idx == 176
|
||||
{98, 7}, // idx == 177
|
||||
{100, 7}, // idx == 178
|
||||
{102, 7}, // idx == 179
|
||||
{104, 7}, // idx == 180
|
||||
{106, 7}, // idx == 181
|
||||
{108, 7}, // idx == 182
|
||||
{110, 7}, // idx == 183
|
||||
{112, 7}, // idx == 184
|
||||
{114, 7}, // idx == 185
|
||||
{116, 7}, // idx == 186
|
||||
{118, 7}, // idx == 187
|
||||
{120, 7}, // idx == 188
|
||||
{122, 7}, // idx == 189
|
||||
{124, 7}, // idx == 190
|
||||
{126, 7}, // idx == 191
|
||||
{128, 7}, // idx == 192
|
||||
{130, 7}, // idx == 193
|
||||
{132, 7}, // idx == 194
|
||||
{134, 7}, // idx == 195
|
||||
{136, 7}, // idx == 196
|
||||
{138, 7}, // idx == 197
|
||||
{140, 7}, // idx == 198
|
||||
{142, 7}, // idx == 199
|
||||
{144, 7}, // idx == 200
|
||||
{146, 7}, // idx == 201
|
||||
{148, 7}, // idx == 202
|
||||
{150, 7}, // idx == 203
|
||||
{152, 7}, // idx == 204
|
||||
{154, 7}, // idx == 205
|
||||
{156, 7}, // idx == 206
|
||||
{158, 7}, // idx == 207
|
||||
{160, 7}, // idx == 208
|
||||
{162, 7}, // idx == 209
|
||||
{164, 7}, // idx == 210
|
||||
{166, 7}, // idx == 211
|
||||
{168, 7}, // idx == 212
|
||||
{170, 7}, // idx == 213
|
||||
{172, 7}, // idx == 214
|
||||
{174, 7}, // idx == 215
|
||||
{176, 7}, // idx == 216
|
||||
{178, 7}, // idx == 217
|
||||
{180, 7}, // idx == 218
|
||||
{182, 7}, // idx == 219
|
||||
{184, 7}, // idx == 220
|
||||
{186, 7}, // idx == 221
|
||||
{188, 7}, // idx == 222
|
||||
{190, 7}, // idx == 223
|
||||
{192, 7}, // idx == 224
|
||||
{194, 7}, // idx == 225
|
||||
{196, 7}, // idx == 226
|
||||
{198, 7}, // idx == 227
|
||||
{200, 7}, // idx == 228
|
||||
{202, 7}, // idx == 229
|
||||
{204, 7}, // idx == 230
|
||||
{206, 7}, // idx == 231
|
||||
{208, 7}, // idx == 232
|
||||
{210, 7}, // idx == 233
|
||||
{212, 7}, // idx == 234
|
||||
{214, 7}, // idx == 235
|
||||
{216, 7}, // idx == 236
|
||||
{218, 7}, // idx == 237
|
||||
{220, 7}, // idx == 238
|
||||
{222, 7}, // idx == 239
|
||||
{224, 7}, // idx == 240
|
||||
{226, 7}, // idx == 241
|
||||
{228, 7}, // idx == 242
|
||||
{230, 7}, // idx == 243
|
||||
{232, 7}, // idx == 244
|
||||
{234, 7}, // idx == 245
|
||||
{236, 7}, // idx == 246
|
||||
{238, 7}, // idx == 247
|
||||
{240, 7}, // idx == 248
|
||||
{242, 7}, // idx == 249
|
||||
{244, 7}, // idx == 250
|
||||
{246, 7}, // idx == 251
|
||||
{248, 7}, // idx == 252
|
||||
{250, 7}, // idx == 253
|
||||
{252, 7}, // idx == 254
|
||||
{254, 7}, // idx == 255
|
||||
//
|
||||
// host routes, pfxLen == 8
|
||||
//
|
||||
{0, 8}, // idx == 256, e.g. 0.0.0.0/8, ::/8
|
||||
{1, 8}, // idx == 257
|
||||
{2, 8}, // idx == 258
|
||||
{3, 8}, // idx == 259
|
||||
{4, 8}, // idx == 260
|
||||
{5, 8}, // idx == 261
|
||||
{6, 8}, // idx == 262
|
||||
{7, 8}, // idx == 263
|
||||
{8, 8}, // idx == 264
|
||||
{9, 8}, // idx == 265
|
||||
{10, 8}, // idx == 266, 10.0.0.0/8, 0a00::/8
|
||||
{11, 8}, // idx == 267
|
||||
{12, 8}, // idx == 268
|
||||
{13, 8}, // idx == 269
|
||||
{14, 8}, // idx == 270
|
||||
{15, 8}, // idx == 271
|
||||
{16, 8}, // idx == 272
|
||||
{17, 8}, // idx == 273
|
||||
{18, 8}, // idx == 274
|
||||
{19, 8}, // idx == 275
|
||||
{20, 8}, // idx == 276
|
||||
{21, 8}, // idx == 277
|
||||
{22, 8}, // idx == 278
|
||||
{23, 8}, // idx == 279
|
||||
{24, 8}, // idx == 280
|
||||
{25, 8}, // idx == 281
|
||||
{26, 8}, // idx == 282
|
||||
{27, 8}, // idx == 283
|
||||
{28, 8}, // idx == 284
|
||||
{29, 8}, // idx == 285
|
||||
{30, 8}, // idx == 286
|
||||
{31, 8}, // idx == 287
|
||||
{32, 8}, // idx == 288
|
||||
{33, 8}, // idx == 289
|
||||
{34, 8}, // idx == 290
|
||||
{35, 8}, // idx == 291
|
||||
{36, 8}, // idx == 292
|
||||
{37, 8}, // idx == 293
|
||||
{38, 8}, // idx == 294
|
||||
{39, 8}, // idx == 295
|
||||
{40, 8}, // idx == 296
|
||||
{41, 8}, // idx == 297
|
||||
{42, 8}, // idx == 298
|
||||
{43, 8}, // idx == 299
|
||||
{44, 8}, // idx == 300
|
||||
{45, 8}, // idx == 301
|
||||
{46, 8}, // idx == 302
|
||||
{47, 8}, // idx == 303
|
||||
{48, 8}, // idx == 304
|
||||
{49, 8}, // idx == 305
|
||||
{50, 8}, // idx == 306
|
||||
{51, 8}, // idx == 307
|
||||
{52, 8}, // idx == 308
|
||||
{53, 8}, // idx == 309
|
||||
{54, 8}, // idx == 310
|
||||
{55, 8}, // idx == 311
|
||||
{56, 8}, // idx == 312
|
||||
{57, 8}, // idx == 313
|
||||
{58, 8}, // idx == 314
|
||||
{59, 8}, // idx == 315
|
||||
{60, 8}, // idx == 316
|
||||
{61, 8}, // idx == 317
|
||||
{62, 8}, // idx == 318
|
||||
{63, 8}, // idx == 319
|
||||
{64, 8}, // idx == 320
|
||||
{65, 8}, // idx == 321
|
||||
{66, 8}, // idx == 322
|
||||
{67, 8}, // idx == 323
|
||||
{68, 8}, // idx == 324
|
||||
{69, 8}, // idx == 325
|
||||
{70, 8}, // idx == 326
|
||||
{71, 8}, // idx == 327
|
||||
{72, 8}, // idx == 328
|
||||
{73, 8}, // idx == 329
|
||||
{74, 8}, // idx == 330
|
||||
{75, 8}, // idx == 331
|
||||
{76, 8}, // idx == 332
|
||||
{77, 8}, // idx == 333
|
||||
{78, 8}, // idx == 334
|
||||
{79, 8}, // idx == 335
|
||||
{80, 8}, // idx == 336
|
||||
{81, 8}, // idx == 337
|
||||
{82, 8}, // idx == 338
|
||||
{83, 8}, // idx == 339
|
||||
{84, 8}, // idx == 340
|
||||
{85, 8}, // idx == 341
|
||||
{86, 8}, // idx == 342
|
||||
{87, 8}, // idx == 343
|
||||
{88, 8}, // idx == 344
|
||||
{89, 8}, // idx == 345
|
||||
{90, 8}, // idx == 346
|
||||
{91, 8}, // idx == 347
|
||||
{92, 8}, // idx == 348
|
||||
{93, 8}, // idx == 349
|
||||
{94, 8}, // idx == 350
|
||||
{95, 8}, // idx == 351
|
||||
{96, 8}, // idx == 352
|
||||
{97, 8}, // idx == 353
|
||||
{98, 8}, // idx == 354
|
||||
{99, 8}, // idx == 355
|
||||
{100, 8}, // idx == 356
|
||||
{101, 8}, // idx == 357
|
||||
{102, 8}, // idx == 358
|
||||
{103, 8}, // idx == 359
|
||||
{104, 8}, // idx == 360
|
||||
{105, 8}, // idx == 361
|
||||
{106, 8}, // idx == 362
|
||||
{107, 8}, // idx == 363
|
||||
{108, 8}, // idx == 364
|
||||
{109, 8}, // idx == 365
|
||||
{110, 8}, // idx == 366
|
||||
{111, 8}, // idx == 367
|
||||
{112, 8}, // idx == 368
|
||||
{113, 8}, // idx == 369
|
||||
{114, 8}, // idx == 370
|
||||
{115, 8}, // idx == 371
|
||||
{116, 8}, // idx == 372
|
||||
{117, 8}, // idx == 373
|
||||
{118, 8}, // idx == 374
|
||||
{119, 8}, // idx == 375
|
||||
{120, 8}, // idx == 376
|
||||
{121, 8}, // idx == 377
|
||||
{122, 8}, // idx == 378
|
||||
{123, 8}, // idx == 379
|
||||
{124, 8}, // idx == 380
|
||||
{125, 8}, // idx == 381
|
||||
{126, 8}, // idx == 382
|
||||
{127, 8}, // idx == 383
|
||||
{128, 8}, // idx == 384
|
||||
{129, 8}, // idx == 385
|
||||
{130, 8}, // idx == 386
|
||||
{131, 8}, // idx == 387
|
||||
{132, 8}, // idx == 388
|
||||
{133, 8}, // idx == 389
|
||||
{134, 8}, // idx == 390
|
||||
{135, 8}, // idx == 391
|
||||
{136, 8}, // idx == 392
|
||||
{137, 8}, // idx == 393
|
||||
{138, 8}, // idx == 394
|
||||
{139, 8}, // idx == 395
|
||||
{140, 8}, // idx == 396
|
||||
{141, 8}, // idx == 397
|
||||
{142, 8}, // idx == 398
|
||||
{143, 8}, // idx == 399
|
||||
{144, 8}, // idx == 400
|
||||
{145, 8}, // idx == 401
|
||||
{146, 8}, // idx == 402
|
||||
{147, 8}, // idx == 403
|
||||
{148, 8}, // idx == 404
|
||||
{149, 8}, // idx == 405
|
||||
{150, 8}, // idx == 406
|
||||
{151, 8}, // idx == 407
|
||||
{152, 8}, // idx == 408
|
||||
{153, 8}, // idx == 409
|
||||
{154, 8}, // idx == 410
|
||||
{155, 8}, // idx == 411
|
||||
{156, 8}, // idx == 412
|
||||
{157, 8}, // idx == 413
|
||||
{158, 8}, // idx == 414
|
||||
{159, 8}, // idx == 415
|
||||
{160, 8}, // idx == 416
|
||||
{161, 8}, // idx == 417
|
||||
{162, 8}, // idx == 418
|
||||
{163, 8}, // idx == 419
|
||||
{164, 8}, // idx == 420
|
||||
{165, 8}, // idx == 421
|
||||
{166, 8}, // idx == 422
|
||||
{167, 8}, // idx == 423
|
||||
{168, 8}, // idx == 424
|
||||
{169, 8}, // idx == 425
|
||||
{170, 8}, // idx == 426
|
||||
{171, 8}, // idx == 427
|
||||
{172, 8}, // idx == 428
|
||||
{173, 8}, // idx == 429
|
||||
{174, 8}, // idx == 430
|
||||
{175, 8}, // idx == 431
|
||||
{176, 8}, // idx == 432
|
||||
{177, 8}, // idx == 433
|
||||
{178, 8}, // idx == 434
|
||||
{179, 8}, // idx == 435
|
||||
{180, 8}, // idx == 436
|
||||
{181, 8}, // idx == 437
|
||||
{182, 8}, // idx == 438
|
||||
{183, 8}, // idx == 439
|
||||
{184, 8}, // idx == 440
|
||||
{185, 8}, // idx == 441
|
||||
{186, 8}, // idx == 442
|
||||
{187, 8}, // idx == 443
|
||||
{188, 8}, // idx == 444
|
||||
{189, 8}, // idx == 445
|
||||
{190, 8}, // idx == 446
|
||||
{191, 8}, // idx == 447
|
||||
{192, 8}, // idx == 448
|
||||
{193, 8}, // idx == 449
|
||||
{194, 8}, // idx == 450
|
||||
{195, 8}, // idx == 451
|
||||
{196, 8}, // idx == 452
|
||||
{197, 8}, // idx == 453
|
||||
{198, 8}, // idx == 454
|
||||
{199, 8}, // idx == 455
|
||||
{200, 8}, // idx == 456
|
||||
{201, 8}, // idx == 457
|
||||
{202, 8}, // idx == 458
|
||||
{203, 8}, // idx == 459
|
||||
{204, 8}, // idx == 460
|
||||
{205, 8}, // idx == 461
|
||||
{206, 8}, // idx == 462
|
||||
{207, 8}, // idx == 463
|
||||
{208, 8}, // idx == 464
|
||||
{209, 8}, // idx == 465
|
||||
{210, 8}, // idx == 466
|
||||
{211, 8}, // idx == 467
|
||||
{212, 8}, // idx == 468
|
||||
{213, 8}, // idx == 469
|
||||
{214, 8}, // idx == 470
|
||||
{215, 8}, // idx == 471
|
||||
{216, 8}, // idx == 472
|
||||
{217, 8}, // idx == 473
|
||||
{218, 8}, // idx == 474
|
||||
{219, 8}, // idx == 475
|
||||
{220, 8}, // idx == 476
|
||||
{221, 8}, // idx == 477
|
||||
{222, 8}, // idx == 478
|
||||
{223, 8}, // idx == 479
|
||||
{224, 8}, // idx == 480
|
||||
{225, 8}, // idx == 481
|
||||
{226, 8}, // idx == 482
|
||||
{227, 8}, // idx == 483
|
||||
{228, 8}, // idx == 484
|
||||
{229, 8}, // idx == 485
|
||||
{230, 8}, // idx == 486
|
||||
{231, 8}, // idx == 487
|
||||
{232, 8}, // idx == 488
|
||||
{233, 8}, // idx == 489
|
||||
{234, 8}, // idx == 490
|
||||
{235, 8}, // idx == 491
|
||||
{236, 8}, // idx == 492
|
||||
{237, 8}, // idx == 493
|
||||
{238, 8}, // idx == 494
|
||||
{239, 8}, // idx == 495
|
||||
{240, 8}, // idx == 496
|
||||
{241, 8}, // idx == 497
|
||||
{242, 8}, // idx == 498
|
||||
{243, 8}, // idx == 499
|
||||
{244, 8}, // idx == 500
|
||||
{245, 8}, // idx == 501
|
||||
{246, 8}, // idx == 502
|
||||
{247, 8}, // idx == 503
|
||||
{248, 8}, // idx == 504
|
||||
{249, 8}, // idx == 505
|
||||
{250, 8}, // idx == 506
|
||||
{251, 8}, // idx == 507
|
||||
{252, 8}, // idx == 508
|
||||
{253, 8}, // idx == 509
|
||||
{254, 8}, // idx == 510
|
||||
{255, 8}, // idx == 511
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package bart provides high-performance Balanced Routing Tables (BART)
|
||||
// for fast IP-to-CIDR lookups on IPv4 and IPv6 addresses.
|
||||
//
|
||||
// BART offers three table variants optimized for different use cases:
|
||||
//
|
||||
// - Lite: Memory-optimized with popcount-compressed sparse arrays
|
||||
// - Table: Full-featured with popcount-compressed sparse arrays
|
||||
// - Fast: Speed-optimized with additional rank caching
|
||||
//
|
||||
// The implementation is based on Knuth's ART algorithm with novel
|
||||
// optimizations for memory efficiency and lookup speed.
|
||||
//
|
||||
// `Table` and `Lite` use popcount compression for memory efficiency, while
|
||||
// `Fast` trades additional memory and slower updates for maximum lookup
|
||||
// speed: a child-rank cache is maintained on every insert/delete, so
|
||||
// lookups avoid popcount but updates pay the caching cost.
|
||||
//
|
||||
// BART excels at efficient set operations on routing tables including Union,
|
||||
// Overlaps, Equal, Subnets, and Supernets with optimal complexity, making it
|
||||
// ideal for large-scale IP prefix matching in ACLs, RIBs, FIBs, firewalls,
|
||||
// and routers.
|
||||
//
|
||||
// All variants also support copy-on-write persistence.
|
||||
//
|
||||
// For complex or pointer value types, you must provide custom deep cloning
|
||||
// using Go's structural typing, implementing the following method on your
|
||||
// value type V:
|
||||
//
|
||||
// func (v V) Clone() V
|
||||
//
|
||||
// For custom Equality implement the following method on your value type V:
|
||||
//
|
||||
// func (v V) Equal(other V) bool
|
||||
//
|
||||
// The bart package detects the interface satisfaction automatically at
|
||||
// runtime using type assertions.
|
||||
package bart
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/gaissmai/bart/internal/nodes"
|
||||
|
||||
// inlining hint, see also the TestInlineBitSet256Functions.
|
||||
// without this silent import the BitSet256 functions are not inlined
|
||||
_ "github.com/gaissmai/bart/internal/bitset"
|
||||
)
|
||||
|
||||
// stridePath is required in many places in the bart package and in
|
||||
// internal/nodes. Aliased to keep the code readable.
|
||||
type stridePath = nodes.StridePath
|
||||
|
||||
// DumpListNode contains CIDR, Value and Subnets, representing the trie
|
||||
// in a sorted, recursive representation, especially useful for serialization.
|
||||
type DumpListNode[V any] struct {
|
||||
CIDR netip.Prefix `json:"cidr"`
|
||||
Value V `json:"value"`
|
||||
Subnets []DumpListNode[V] `json:"subnets,omitempty"`
|
||||
}
|
||||
+792
@@ -0,0 +1,792 @@
|
||||
//go:build generate
|
||||
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:generate ./scripts/generate-table-methods.sh
|
||||
|
||||
package bart
|
||||
|
||||
// ### GENERATE DELETE START ###
|
||||
|
||||
// stub code for generator types and methods
|
||||
// useful for gopls during development, deleted during go generate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/gaissmai/bart/internal/nodes"
|
||||
"github.com/gaissmai/bart/internal/value"
|
||||
)
|
||||
|
||||
type _NODE_TYPE[V any] struct{}
|
||||
|
||||
type _TABLE_TYPE[V any] struct {
|
||||
root4 _NODE_TYPE[V]
|
||||
root6 _NODE_TYPE[V]
|
||||
size4 int
|
||||
size6 int
|
||||
}
|
||||
|
||||
func (n *_NODE_TYPE[V]) IsEmpty() (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) StatsRec() (_ nodes.StatsT) { return }
|
||||
func (n *_NODE_TYPE[V]) PrefixCount() (_ int) { return }
|
||||
func (n *_NODE_TYPE[V]) ChildCount() (_ int) { return }
|
||||
func (n *_NODE_TYPE[V]) GetPrefix(uint8) (_ V, _ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) GetChild(uint8) (_ any, _ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) MustGetPrefix(uint8) (_ V) { return }
|
||||
func (n *_NODE_TYPE[V]) MustGetChild(uint8) (_ any) { return }
|
||||
func (n *_NODE_TYPE[V]) Insert(netip.Prefix, V, int) (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) Modify(netip.Prefix, func(V, bool) (V, bool)) (_ int) { return }
|
||||
func (n *_NODE_TYPE[V]) Delete(netip.Prefix) (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) InsertPersist(nodes.CloneFunc[V], netip.Prefix, V, int) (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) DeletePersist(nodes.CloneFunc[V], netip.Prefix) (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) Get(netip.Prefix) (_ V, _ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) OverlapsPrefixAtDepth(netip.Prefix, int) (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) Overlaps(*_NODE_TYPE[V], int) (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) UnionRec(nodes.CloneFunc[V], *_NODE_TYPE[V], int) (_ int) { return }
|
||||
func (n *_NODE_TYPE[V]) UnionRecPersist(nodes.CloneFunc[V], *_NODE_TYPE[V], int) (_ int) { return }
|
||||
func (n *_NODE_TYPE[V]) EqualRec(*_NODE_TYPE[V]) (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) CloneRec(nodes.CloneFunc[V]) (_ *_NODE_TYPE[V]) { return }
|
||||
func (n *_NODE_TYPE[V]) CloneFlat(nodes.CloneFunc[V]) (_ *_NODE_TYPE[V]) { return }
|
||||
func (n *_NODE_TYPE[V]) GetChildAddrs(*[256]uint8) (_ []uint8) { return }
|
||||
func (n *_NODE_TYPE[V]) GetIndices(*[256]uint8) (_ []uint8) { return }
|
||||
func (n *_NODE_TYPE[V]) AllChildren() (_ iter.Seq2[uint8, any]) { return }
|
||||
func (n *_NODE_TYPE[V]) AllIndices() (_ iter.Seq2[uint8, V]) { return }
|
||||
func (n *_NODE_TYPE[V]) Contains(uint8) (_ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) Lookup(uint8) (_ V, _ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) LookupIdx(uint8) (_ uint8, _ V, _ bool) { return }
|
||||
func (n *_NODE_TYPE[V]) Supernets(netip.Prefix, func(netip.Prefix, V) bool) { return }
|
||||
func (n *_NODE_TYPE[V]) Subnets(netip.Prefix, func(netip.Prefix, V) bool) { return }
|
||||
func (n *_NODE_TYPE[V]) FprintRec(io.Writer, nodes.TrieItem[V], string) (_ error) { return }
|
||||
func (n *_NODE_TYPE[V]) DumpRec(io.Writer, stridePath, int, bool) { return }
|
||||
func (n *_NODE_TYPE[V]) AllRec(stridePath, int, bool, func(netip.Prefix, V) bool) (_ bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (n *_NODE_TYPE[V]) AllRecSorted(stridePath, int, bool, func(netip.Prefix, V) bool) (_ bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (t *_TABLE_TYPE[V]) rootNodeByVersion(is4 bool) (_ *_NODE_TYPE[V]) { return }
|
||||
func (t *_TABLE_TYPE[V]) InsertPersist(netip.Prefix, V) (_ *_TABLE_TYPE[V]) { return }
|
||||
|
||||
// ### GENERATE DELETE END ###
|
||||
|
||||
func (t *_TABLE_TYPE[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 *_TABLE_TYPE[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 _TABLE_TYPE is returned.
|
||||
// This is not a full [_TABLE_TYPE.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 *_TABLE_TYPE[V]) insertPersist(pfx netip.Prefix, val V) *_TABLE_TYPE[V] {
|
||||
if !pfx.IsValid() {
|
||||
return t
|
||||
}
|
||||
|
||||
// canonicalize prefix
|
||||
pfx = pfx.Masked()
|
||||
is4 := pfx.Addr().Is4()
|
||||
|
||||
// share size counters; root nodes cloned selectively.
|
||||
pt := &_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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 _TABLE_TYPE 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 *_TABLE_TYPE[V]) DeletePersist(pfx netip.Prefix) *_TABLE_TYPE[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 := &_TABLE_TYPE[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 *_TABLE_TYPE is returned.
|
||||
func (t *_TABLE_TYPE[V]) ModifyPersist(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)) *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[V]) Overlaps(o *_TABLE_TYPE[V]) bool {
|
||||
return t.Overlaps4(o) || t.Overlaps6(o)
|
||||
}
|
||||
|
||||
// Overlaps4 is like [_TABLE_TYPE.Overlaps] but for the v4 routing table only.
|
||||
func (t *_TABLE_TYPE[V]) Overlaps4(o *_TABLE_TYPE[V]) bool {
|
||||
if t.size4 == 0 || o.size4 == 0 {
|
||||
return false
|
||||
}
|
||||
return t.root4.Overlaps(&o.root4, 0)
|
||||
}
|
||||
|
||||
// Overlaps6 is like [_TABLE_TYPE.Overlaps] but for the v6 routing table only.
|
||||
func (t *_TABLE_TYPE[V]) Overlaps6(o *_TABLE_TYPE[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 _TABLE_TYPE.Clone.
|
||||
func (t *_TABLE_TYPE[V]) Union(o *_TABLE_TYPE[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 *_TABLE_TYPE is returned.
|
||||
// If o is empty, no nodes are touched and the receiver may be
|
||||
// returned unchanged.
|
||||
func (t *_TABLE_TYPE[V]) UnionPersist(o *_TABLE_TYPE[V]) *_TABLE_TYPE[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 _TABLE_TYPE with root nodes just copied.
|
||||
pt := &_TABLE_TYPE[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 *_TABLE_TYPE[V]) Equal(o *_TABLE_TYPE[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 *_TABLE_TYPE[V]) Clone() *_TABLE_TYPE[V] {
|
||||
c := new(_TABLE_TYPE[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 *_TABLE_TYPE[V]) Size() int {
|
||||
return t.size4 + t.size6
|
||||
}
|
||||
|
||||
// Size4 returns the IPv4 prefix count.
|
||||
func (t *_TABLE_TYPE[V]) Size4() int {
|
||||
return t.size4
|
||||
}
|
||||
|
||||
// Size6 returns the IPv6 prefix count.
|
||||
func (t *_TABLE_TYPE[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 [_TABLE_TYPE.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 *_TABLE_TYPE[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 [_TABLE_TYPE.All] but only for the v4 routing table.
|
||||
func (t *_TABLE_TYPE[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 [_TABLE_TYPE.All] but only for the v6 routing table.
|
||||
func (t *_TABLE_TYPE[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 [_TABLE_TYPE.All] but the iteration is ordered in canonical
|
||||
// CIDR prefix sort order.
|
||||
func (t *_TABLE_TYPE[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 [_TABLE_TYPE.AllSorted] but only for the v4 routing table.
|
||||
func (t *_TABLE_TYPE[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 [_TABLE_TYPE.AllSorted] but only for the v6 routing table.
|
||||
func (t *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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 [_TABLE_TYPE.Fprint].
|
||||
func (t *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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 *_TABLE_TYPE[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)
|
||||
}
|
||||
}
|
||||
+2299
File diff suppressed because it is too large.
Load diff
-290
@@ -1,290 +0,0 @@
|
||||
// Copyright (c) 2024 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package bart
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type nodeType byte
|
||||
|
||||
const (
|
||||
nullNode nodeType = iota // empty node
|
||||
fullNode // prefixes and children or path-compressed prefixes
|
||||
leafNode // no children, only prefixes or path-compressed prefixes
|
||||
intermediateNode // only children, no prefix nor path-compressed prefixes
|
||||
)
|
||||
|
||||
// ##################################################
|
||||
// useful during development, debugging and testing
|
||||
// ##################################################
|
||||
|
||||
// dumpString is just a wrapper for dump.
|
||||
func (t *Table[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 *Table[V]) dump(w io.Writer) {
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if t.size4 > 0 {
|
||||
fmt.Fprintln(w)
|
||||
fmt.Fprintf(w, "### IPv4: size(%d), nodes(%d)", t.size4, t.root4.nodeStatsRec().nodes)
|
||||
t.root4.dumpRec(w, stridePath{}, 0, true)
|
||||
}
|
||||
|
||||
if t.size6 > 0 {
|
||||
fmt.Fprintln(w)
|
||||
fmt.Fprintf(w, "### IPv6: size(%d), nodes(%d)", t.size6, t.root6.nodeStatsRec().nodes)
|
||||
t.root6.dumpRec(w, stridePath{}, 0, false)
|
||||
}
|
||||
}
|
||||
|
||||
// dumpRec, rec-descent the trie.
|
||||
func (n *node[V]) dumpRec(w io.Writer, path stridePath, depth int, is4 bool) {
|
||||
// dump this node
|
||||
n.dump(w, path, depth, is4)
|
||||
|
||||
// the node may have childs, rec-descent down
|
||||
for i, addr := range n.children.All() {
|
||||
octet := byte(addr)
|
||||
path[depth] = octet
|
||||
|
||||
if child, ok := n.children.Items[i].(*node[V]); ok {
|
||||
child.dumpRec(w, path, depth+1, is4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dump the node to w.
|
||||
func (n *node[V]) dump(w io.Writer, path stridePath, depth int, is4 bool) {
|
||||
bits := depth * strideLen
|
||||
indent := strings.Repeat(".", depth)
|
||||
|
||||
// node type with depth and octet path and bits.
|
||||
fmt.Fprintf(w, "\n%s[%s] depth: %d path: [%s] / %d\n",
|
||||
indent, n.hasType(), depth, ipStridePath(path, depth, is4), bits)
|
||||
|
||||
if nPfxCount := n.prefixes.Len(); nPfxCount != 0 {
|
||||
// no heap allocs
|
||||
allIndices := n.prefixes.All()
|
||||
|
||||
// print the baseIndices for this node.
|
||||
fmt.Fprintf(w, "%sindexs(#%d): %v\n", indent, nPfxCount, allIndices)
|
||||
|
||||
// print the prefixes for this node
|
||||
fmt.Fprintf(w, "%sprefxs(#%d):", indent, nPfxCount)
|
||||
|
||||
for _, idx := range allIndices {
|
||||
octet, pfxLen := idxToPfx(idx)
|
||||
fmt.Fprintf(w, " %s/%d", octetFmt(octet, is4), pfxLen)
|
||||
}
|
||||
|
||||
fmt.Fprintln(w)
|
||||
|
||||
// print the values for this node
|
||||
fmt.Fprintf(w, "%svalues(#%d):", indent, nPfxCount)
|
||||
|
||||
for _, val := range n.prefixes.Items {
|
||||
fmt.Fprintf(w, " %v", val)
|
||||
}
|
||||
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
|
||||
if n.children.Len() != 0 {
|
||||
|
||||
nodeAddrs := make([]uint, 0, maxNodeChildren)
|
||||
leafAddrs := make([]uint, 0, maxNodeChildren)
|
||||
|
||||
// the node has recursive child nodes or path-compressed leaves
|
||||
for i, addr := range n.children.All() {
|
||||
switch n.children.Items[i].(type) {
|
||||
case *node[V]:
|
||||
nodeAddrs = append(nodeAddrs, addr)
|
||||
continue
|
||||
|
||||
case *leaf[V]:
|
||||
leafAddrs = append(leafAddrs, addr)
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
if nodeCount := len(nodeAddrs); nodeCount > 0 {
|
||||
// print the childs for this node
|
||||
fmt.Fprintf(w, "%schilds(#%d):", indent, nodeCount)
|
||||
|
||||
for _, addr := range nodeAddrs {
|
||||
octet := byte(addr)
|
||||
fmt.Fprintf(w, " %s", octetFmt(octet, is4))
|
||||
}
|
||||
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
|
||||
if leafCount := len(leafAddrs); leafCount > 0 {
|
||||
// print the pathcomp prefixes for this node
|
||||
fmt.Fprintf(w, "%sleaves(#%d):", indent, leafCount)
|
||||
|
||||
for _, addr := range leafAddrs {
|
||||
octet := byte(addr)
|
||||
k := n.children.MustGet(addr)
|
||||
pc := k.(*leaf[V])
|
||||
|
||||
fmt.Fprintf(w, " %s:{%s, %v}", octetFmt(octet, is4), pc.prefix, pc.value)
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hasType returns the nodeType.
|
||||
func (n *node[V]) hasType() nodeType {
|
||||
s := n.nodeStats()
|
||||
|
||||
switch {
|
||||
case s.pfxs == 0 && s.childs == 0:
|
||||
return nullNode
|
||||
case s.nodes == 0:
|
||||
return leafNode
|
||||
case (s.pfxs > 0 || s.leaves > 0) && s.nodes > 0:
|
||||
return fullNode
|
||||
case (s.pfxs == 0 && s.leaves == 0) && s.nodes > 0:
|
||||
return intermediateNode
|
||||
default:
|
||||
panic(fmt.Sprintf("UNREACHABLE: pfx: %d, chld: %d, node: %d, leaf: %d",
|
||||
s.pfxs, s.childs, s.nodes, s.leaves))
|
||||
}
|
||||
}
|
||||
|
||||
// octetFmt, different format strings for IPv4 and IPv6, decimal versus hex.
|
||||
func octetFmt(octet byte, is4 bool) string {
|
||||
if is4 {
|
||||
return fmt.Sprintf("%d", octet)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("0x%02x", octet)
|
||||
}
|
||||
|
||||
// ip stride path, different formats for IPv4 and IPv6, dotted decimal or hex.
|
||||
//
|
||||
// 127.0.0
|
||||
// 2001:0d
|
||||
func ipStridePath(path stridePath, depth int, is4 bool) string {
|
||||
buf := new(strings.Builder)
|
||||
|
||||
if is4 {
|
||||
for i, b := range path[:depth] {
|
||||
if i != 0 {
|
||||
buf.WriteString(".")
|
||||
}
|
||||
|
||||
buf.WriteString(strconv.Itoa(int(b)))
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
for i, b := range path[:depth] {
|
||||
if i != 0 && i%2 == 0 {
|
||||
buf.WriteString(":")
|
||||
}
|
||||
|
||||
buf.WriteString(fmt.Sprintf("%02x", b))
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// String implements Stringer for nodeType.
|
||||
func (nt nodeType) String() string {
|
||||
switch nt {
|
||||
case nullNode:
|
||||
return "NULL"
|
||||
case fullNode:
|
||||
return "FULL"
|
||||
case leafNode:
|
||||
return "LEAF"
|
||||
case intermediateNode:
|
||||
return "IMED"
|
||||
default:
|
||||
return "unreachable"
|
||||
}
|
||||
}
|
||||
|
||||
// stats, only used for dump, tests and benchmarks
|
||||
type stats struct {
|
||||
pfxs int
|
||||
childs int
|
||||
nodes int
|
||||
leaves int
|
||||
}
|
||||
|
||||
// node statistics for this single node
|
||||
func (n *node[V]) nodeStats() stats {
|
||||
var s stats
|
||||
|
||||
s.pfxs = n.prefixes.Len()
|
||||
s.childs = n.children.Len()
|
||||
|
||||
for i := range n.children.All() {
|
||||
switch n.children.Items[i].(type) {
|
||||
case *node[V]:
|
||||
s.nodes++
|
||||
|
||||
case *leaf[V]:
|
||||
s.leaves++
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// nodeStatsRec, calculate the number of pfxs, nodes and leaves under n, rec-descent.
|
||||
func (n *node[V]) nodeStatsRec() stats {
|
||||
var s stats
|
||||
if n == nil || n.isEmpty() {
|
||||
return s
|
||||
}
|
||||
|
||||
s.pfxs = n.prefixes.Len()
|
||||
s.childs = n.children.Len()
|
||||
s.nodes = 1 // this node
|
||||
s.leaves = 0
|
||||
|
||||
for _, kidAny := range n.children.Items {
|
||||
switch kid := kidAny.(type) {
|
||||
case *node[V]:
|
||||
// rec-descent
|
||||
rs := kid.nodeStatsRec()
|
||||
|
||||
s.pfxs += rs.pfxs
|
||||
s.childs += rs.childs
|
||||
s.nodes += rs.nodes
|
||||
s.leaves += rs.leaves
|
||||
|
||||
case *leaf[V]:
|
||||
s.leaves++
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
+399
@@ -0,0 +1,399 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package bart
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/gaissmai/bart/internal/art"
|
||||
"github.com/gaissmai/bart/internal/lpm"
|
||||
"github.com/gaissmai/bart/internal/nodes"
|
||||
)
|
||||
|
||||
// Fast is similar to Table but uses additional 256 bytes in each node
|
||||
// for faster level traversing in the multibit trie.
|
||||
//
|
||||
// As a result Fast sacrifices memory efficiency to achieve 25% better
|
||||
// speed in lookup and contains.
|
||||
//
|
||||
// The zero value is ready to use.
|
||||
//
|
||||
// A Fast must not be copied by value; always pass by pointer.
|
||||
// Nil pointers as receivers or arguments are forbidden and will panic.
|
||||
//
|
||||
// The Fast is safe for concurrent reads, but concurrent reads and writes
|
||||
// must be externally synchronized. Mutation via Insert/Delete requires locks,
|
||||
// or alternatively, use ...Persist methods which return a modified copy
|
||||
// without altering the original table (copy-on-write).
|
||||
//
|
||||
// Performance note: Do not pass IPv4-in-IPv6 addresses (e.g., ::ffff:192.0.2.1)
|
||||
// as input. The methods do not perform automatic unmapping to avoid unnecessary
|
||||
// overhead for the common case where native addresses are used.
|
||||
// Users should unmap IPv4-in-IPv6 addresses to their native IPv4 form
|
||||
// (e.g., 192.0.2.1) before calling these methods.
|
||||
type Fast[V any] struct {
|
||||
// used by -copylocks checker from `go vet`.
|
||||
_ [0]sync.Mutex
|
||||
|
||||
// the root nodes, implemented as popcount compressed multibit tries
|
||||
root4 nodes.FastNode[V]
|
||||
root6 nodes.FastNode[V]
|
||||
|
||||
// the number of prefixes in the routing table
|
||||
size4 int
|
||||
size6 int
|
||||
}
|
||||
|
||||
// rootNodeByVersion, root node getter for ip version.
|
||||
func (f *Fast[V]) rootNodeByVersion(is4 bool) *nodes.FastNode[V] {
|
||||
if is4 {
|
||||
return &f.root4
|
||||
}
|
||||
return &f.root6
|
||||
}
|
||||
|
||||
// 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 (f *Fast[V]) Insert(pfx netip.Prefix, val V) {
|
||||
f.insert(pfx, val)
|
||||
}
|
||||
|
||||
// InsertPersist is similar to Insert but the receiver isn't modified.
|
||||
//
|
||||
// All nodes touched during insert are cloned and a new Fast is returned.
|
||||
// This is not a full [Fast.Clone], all untouched nodes are still referenced
|
||||
// from both Fasts.
|
||||
//
|
||||
// If the payload type V contains pointers or needs deep copying,
|
||||
// it must implement the Clone method to support correct cloning.
|
||||
//
|
||||
// Due to cloning overhead this is significantly slower than Insert,
|
||||
// typically taking μsec instead of nsec.
|
||||
func (f *Fast[V]) InsertPersist(pfx netip.Prefix, val V) *Fast[V] {
|
||||
return f.insertPersist(pfx, val)
|
||||
}
|
||||
|
||||
// Modify applies an insert, update, or delete operation for the value
|
||||
// associated with the given prefix. The supplied callback decides the
|
||||
// operation: it is called with the current value (or zero if not found)
|
||||
// and a boolean indicating whether the prefix exists. The callback must
|
||||
// return a new value and a delete flag: del == false inserts or updates,
|
||||
// del == true deletes the entry if it exists (otherwise no-op).
|
||||
//
|
||||
// The callback is invoked at most once per call.
|
||||
//
|
||||
// The operation is determined by the callback function, which is called with:
|
||||
//
|
||||
// val: the current value (or zero value if not found)
|
||||
// found: true if the prefix currently exists, false otherwise
|
||||
//
|
||||
// The callback returns:
|
||||
//
|
||||
// val: the new value to insert or update (ignored if del == true)
|
||||
// del: true to delete the entry, false to insert or update
|
||||
//
|
||||
// Summary of callback semantics:
|
||||
//
|
||||
// | cb-input | cb-return | Ops |
|
||||
// |-----------------|-----------------|--------|
|
||||
// | (zero, false) | (_, true) | no-op |
|
||||
// | (zero, false) | (newVal, false) | insert |
|
||||
// | (oldVal, true) | (newVal, false) | update |
|
||||
// | (oldVal, true) | (_, true) | delete |
|
||||
// ------------------------------------- --------
|
||||
func (f *Fast[V]) Modify(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)) {
|
||||
if !pfx.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
// canonicalize prefix
|
||||
pfx = pfx.Masked()
|
||||
|
||||
is4 := pfx.Addr().Is4()
|
||||
|
||||
n := f.rootNodeByVersion(is4)
|
||||
|
||||
delta := n.Modify(pfx, cb)
|
||||
f.sizeUpdate(is4, delta)
|
||||
}
|
||||
|
||||
// Contains reports whether any stored prefix covers the given IP address.
|
||||
// Returns false for invalid IP addresses.
|
||||
//
|
||||
// This performs longest-prefix matching and returns true if any prefix
|
||||
// in the routing table contains the IP address, regardless of the associated value.
|
||||
//
|
||||
// It does not return the value or the prefix of the matching item,
|
||||
// but as a test against an allow-/deny-list it's often sufficient
|
||||
// and even few nanoseconds faster than [Fast.Lookup].
|
||||
func (f *Fast[V]) Contains(ip netip.Addr) bool {
|
||||
// speed is top priority: no explicit test for ip.IsValid
|
||||
// if ip is invalid, AsSlice() returns nil, Contains returns false.
|
||||
is4 := ip.Is4()
|
||||
n := f.rootNodeByVersion(is4)
|
||||
|
||||
for _, octet := range ip.AsSlice() {
|
||||
// for contains, any lpm match is good enough, no backtracking needed
|
||||
if n.PrefixCount() != 0 && n.Contains(art.OctetToIdx(octet)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// stop traversing?
|
||||
if !n.Children.Test(octet) {
|
||||
return false
|
||||
}
|
||||
kid := n.MustGetChild(octet)
|
||||
|
||||
// kid is node or leaf or fringe at octet
|
||||
switch kid := kid.(type) {
|
||||
case *nodes.FastNode[V]:
|
||||
n = kid // descend down to next trie level
|
||||
|
||||
case *nodes.FringeNode[V]:
|
||||
// fringe is the default-route for all possible octets below
|
||||
return true
|
||||
|
||||
case *nodes.LeafNode[V]:
|
||||
return kid.Prefix.Contains(ip)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Lookup performs a longest prefix match (LPM) lookup for the given address.
|
||||
// It finds the most specific (longest) prefix in the routing table that
|
||||
// contains the given address and returns its associated value.
|
||||
//
|
||||
// This is the fundamental operation for IP routing decisions, finding the
|
||||
// best matching route for a destination address.
|
||||
//
|
||||
// Returns the associated value and true if a matching prefix is found.
|
||||
// Returns zero value and false if no prefix contains the address.
|
||||
func (f *Fast[V]) Lookup(ip netip.Addr) (val V, ok bool) {
|
||||
if !ip.IsValid() {
|
||||
return val, ok
|
||||
}
|
||||
|
||||
is4 := ip.Is4()
|
||||
octets := ip.AsSlice()
|
||||
|
||||
n := f.rootNodeByVersion(is4)
|
||||
|
||||
// stack of the traversed nodes for fast backtracking, if needed
|
||||
stack := [nodes.MaxTreeDepth]*nodes.FastNode[V]{}
|
||||
|
||||
// run variable, used after for loop
|
||||
var depth int
|
||||
var octet byte
|
||||
|
||||
LOOP:
|
||||
// find leaf node
|
||||
for depth, octet = range octets {
|
||||
depth &= nodes.DepthMask // BCE, Lookup must be fast
|
||||
|
||||
// push current node on stack for fast backtracking
|
||||
stack[depth] = n
|
||||
|
||||
// go down in tight loop to last octet
|
||||
if !n.Children.Test(octet) {
|
||||
// no more nodes below octet
|
||||
break LOOP
|
||||
}
|
||||
kid := n.MustGetChild(octet)
|
||||
|
||||
// kid is node or leaf or fringe at octet
|
||||
switch kid := kid.(type) {
|
||||
case *nodes.FastNode[V]:
|
||||
n = kid
|
||||
continue LOOP // descend down to next trie level
|
||||
|
||||
case *nodes.FringeNode[V]:
|
||||
// fringe is the default-route for all possible octets below
|
||||
return kid.Value, true
|
||||
|
||||
case *nodes.LeafNode[V]:
|
||||
if kid.Prefix.Contains(ip) {
|
||||
return kid.Value, true
|
||||
}
|
||||
// reached a path compressed prefix, stop traversing
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
|
||||
// start backtracking, unwind the stack, bounds check eliminated
|
||||
for ; depth >= 0; depth-- {
|
||||
depth &= nodes.DepthMask // BCE
|
||||
|
||||
n = stack[depth]
|
||||
|
||||
// longest prefix match, skip if node has no prefixes
|
||||
if n.PrefixCount() != 0 {
|
||||
idx := art.OctetToIdx(octets[depth])
|
||||
// lookupIdx() manually inlined
|
||||
if lpmIdx, ok2 := n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok2 {
|
||||
return n.MustGetPrefix(lpmIdx), ok2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// LookupPrefix performs a longest prefix match lookup for any address within
|
||||
// the given prefix. It finds the most specific routing table entry that would
|
||||
// match any address in the provided prefix range.
|
||||
//
|
||||
// This is functionally identical to LookupPrefixLPM but returns only the
|
||||
// associated value, not the matching prefix itself.
|
||||
//
|
||||
// Returns the value and true if a matching prefix is found.
|
||||
// Returns zero value and false if no match exists.
|
||||
func (f *Fast[V]) LookupPrefix(pfx netip.Prefix) (val V, ok bool) {
|
||||
_, val, ok = f.lookupPrefixLPM(pfx, false)
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// LookupPrefixLPM performs a longest prefix match lookup for any address within
|
||||
// the given prefix. It finds the most specific routing table entry that would
|
||||
// match any address in the provided prefix range.
|
||||
//
|
||||
// This is functionally identical to LookupPrefix but additionally returns the
|
||||
// matching LPM prefix itself along with the value.
|
||||
//
|
||||
// This method is slower than LookupPrefix and should only be used if the
|
||||
// matching lpm entry is also required for other reasons.
|
||||
//
|
||||
// Returns the matching prefix, its associated value, and true if found.
|
||||
// Returns zero values and false if no match exists.
|
||||
func (f *Fast[V]) LookupPrefixLPM(pfx netip.Prefix) (lpmPfx netip.Prefix, val V, ok bool) {
|
||||
return f.lookupPrefixLPM(pfx, true)
|
||||
}
|
||||
|
||||
func (f *Fast[V]) lookupPrefixLPM(pfx netip.Prefix, withLPM bool) (lpmPfx netip.Prefix, val V, ok bool) {
|
||||
if !pfx.IsValid() {
|
||||
return lpmPfx, val, ok
|
||||
}
|
||||
|
||||
// canonicalize the prefix
|
||||
pfx = pfx.Masked()
|
||||
|
||||
ip := pfx.Addr()
|
||||
pfxLen := pfx.Bits()
|
||||
is4 := ip.Is4()
|
||||
octets := ip.AsSlice()
|
||||
strideCount, modBits := nodes.DivMod8(pfxLen)
|
||||
|
||||
n := f.rootNodeByVersion(is4)
|
||||
|
||||
// record path to leaf node
|
||||
stack := [nodes.MaxTreeDepth]*nodes.FastNode[V]{}
|
||||
|
||||
var depth int
|
||||
var octet byte
|
||||
|
||||
LOOP:
|
||||
// find the last node on the octets path in the trie,
|
||||
for depth, octet = range octets {
|
||||
depth &= nodes.DepthMask // BCE
|
||||
|
||||
// stepped one past the last stride of interest; back up to last and break
|
||||
if depth > strideCount {
|
||||
depth--
|
||||
break
|
||||
}
|
||||
// push current node on stack
|
||||
stack[depth] = n
|
||||
|
||||
// go down in tight loop to leaf node
|
||||
if !n.Children.Test(octet) {
|
||||
break LOOP
|
||||
}
|
||||
kid := n.MustGetChild(octet)
|
||||
|
||||
// kid is node or leaf or fringe at octet
|
||||
switch kid := kid.(type) {
|
||||
case *nodes.FastNode[V]:
|
||||
n = kid
|
||||
continue LOOP // descend down to next trie level
|
||||
|
||||
case *nodes.LeafNode[V]:
|
||||
// reached a path compressed prefix, stop traversing
|
||||
if kid.Prefix.Bits() > pfxLen || !kid.Prefix.Contains(ip) {
|
||||
break LOOP
|
||||
}
|
||||
return kid.Prefix, kid.Value, true
|
||||
|
||||
case *nodes.FringeNode[V]:
|
||||
// the bits of the fringe are defined by the depth
|
||||
// maybe the LPM isn't needed, saves some cycles
|
||||
fringeBits := (depth + 1) << 3
|
||||
if fringeBits > pfxLen {
|
||||
break LOOP
|
||||
}
|
||||
|
||||
// the LPM isn't needed, saves some cycles
|
||||
if !withLPM {
|
||||
return netip.Prefix{}, kid.Value, true
|
||||
}
|
||||
|
||||
// get the LPM prefix back from ip and depth
|
||||
// it's a fringe, bits are always /8, /16, /24, ...
|
||||
fringePfx, _ := ip.Prefix((depth + 1) << 3)
|
||||
return fringePfx, kid.Value, true
|
||||
}
|
||||
}
|
||||
|
||||
// start backtracking, unwind the stack
|
||||
for ; depth >= 0; depth-- {
|
||||
depth &= nodes.DepthMask // BCE
|
||||
|
||||
n = stack[depth]
|
||||
|
||||
// longest prefix match, skip if node has no prefixes
|
||||
if n.Prefixes.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var idx uint8
|
||||
octet = octets[depth]
|
||||
|
||||
// only the final stride may have a different prefix len
|
||||
// all others are just host routes
|
||||
if depth == strideCount {
|
||||
idx = art.PfxToIdx(octet, modBits)
|
||||
} else {
|
||||
idx = art.OctetToIdx(octet)
|
||||
}
|
||||
|
||||
// manually inlined: lookupIdx(idx)
|
||||
var topIdx uint8
|
||||
if topIdx, ok = n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok {
|
||||
val = n.MustGetPrefix(topIdx)
|
||||
|
||||
// called from LookupPrefix
|
||||
if !withLPM {
|
||||
return netip.Prefix{}, val, ok
|
||||
}
|
||||
|
||||
// called from LookupPrefixLPM
|
||||
|
||||
// get the bits from depth and top idx
|
||||
pfxBits := int(art.PfxBits(depth, topIdx))
|
||||
|
||||
// calculate the lpmPfx from incoming ip and new mask
|
||||
// netip.Addr.Prefix canonicalizes. Invariant: art.PfxBits(depth, topIdx)
|
||||
// yields a valid mask (v4: 0..32, v6: 0..128), so error is impossible.
|
||||
lpmPfx, _ = ip.Prefix(pfxBits)
|
||||
return lpmPfx, val, ok
|
||||
}
|
||||
}
|
||||
|
||||
return lpmPfx, val, ok
|
||||
}
|
||||
+731
@@ -0,0 +1,731 @@
|
||||
// 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 *Fast[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 *Fast[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 Fast is returned.
|
||||
// This is not a full [Fast.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 *Fast[V]) insertPersist(pfx netip.Prefix, val V) *Fast[V] {
|
||||
if !pfx.IsValid() {
|
||||
return t
|
||||
}
|
||||
|
||||
// canonicalize prefix
|
||||
pfx = pfx.Masked()
|
||||
is4 := pfx.Addr().Is4()
|
||||
|
||||
// share size counters; root nodes cloned selectively.
|
||||
pt := &Fast[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 *Fast[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 *Fast[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 Fast 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 *Fast[V]) DeletePersist(pfx netip.Prefix) *Fast[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 := &Fast[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 *Fast is returned.
|
||||
func (t *Fast[V]) ModifyPersist(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)) *Fast[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 *Fast[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 *Fast[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 *Fast[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 *Fast[V]) Overlaps(o *Fast[V]) bool {
|
||||
return t.Overlaps4(o) || t.Overlaps6(o)
|
||||
}
|
||||
|
||||
// Overlaps4 is like [Fast.Overlaps] but for the v4 routing table only.
|
||||
func (t *Fast[V]) Overlaps4(o *Fast[V]) bool {
|
||||
if t.size4 == 0 || o.size4 == 0 {
|
||||
return false
|
||||
}
|
||||
return t.root4.Overlaps(&o.root4, 0)
|
||||
}
|
||||
|
||||
// Overlaps6 is like [Fast.Overlaps] but for the v6 routing table only.
|
||||
func (t *Fast[V]) Overlaps6(o *Fast[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 Fast.Clone.
|
||||
func (t *Fast[V]) Union(o *Fast[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 *Fast is returned.
|
||||
// If o is empty, no nodes are touched and the receiver may be
|
||||
// returned unchanged.
|
||||
func (t *Fast[V]) UnionPersist(o *Fast[V]) *Fast[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 Fast with root nodes just copied.
|
||||
pt := &Fast[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 *Fast[V]) Equal(o *Fast[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 *Fast[V]) Clone() *Fast[V] {
|
||||
c := new(Fast[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 *Fast[V]) Size() int {
|
||||
return t.size4 + t.size6
|
||||
}
|
||||
|
||||
// Size4 returns the IPv4 prefix count.
|
||||
func (t *Fast[V]) Size4() int {
|
||||
return t.size4
|
||||
}
|
||||
|
||||
// Size6 returns the IPv6 prefix count.
|
||||
func (t *Fast[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 [Fast.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 *Fast[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 [Fast.All] but only for the v4 routing table.
|
||||
func (t *Fast[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 [Fast.All] but only for the v6 routing table.
|
||||
func (t *Fast[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 [Fast.All] but the iteration is ordered in canonical
|
||||
// CIDR prefix sort order.
|
||||
func (t *Fast[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 [Fast.AllSorted] but only for the v4 routing table.
|
||||
func (t *Fast[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 [Fast.AllSorted] but only for the v6 routing table.
|
||||
func (t *Fast[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 *Fast[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 *Fast[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 [Fast.Fprint].
|
||||
func (t *Fast[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 *Fast[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 *Fast[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 *Fast[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 *Fast[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 *Fast[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 *Fast[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)
|
||||
}
|
||||
}
|
||||
+547
@@ -0,0 +1,547 @@
|
||||
// Code generated by generate_allottbl.go; DO NOT EDIT.
|
||||
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package allot
|
||||
|
||||
import "github.com/gaissmai/bart/internal/bitset"
|
||||
|
||||
// PfxRoutesLookupTbl is a lookup table of BitSet256 entries.
|
||||
//
|
||||
// It maps a baseIndex (1..255) to a bitset that contains all more
|
||||
// specific baseIndices that are covered by the prefix at that index.
|
||||
//
|
||||
// The idea:
|
||||
//
|
||||
// If prefix P has baseIndex idx, then P covers all prefixes in the subtree
|
||||
// rooted at idx. This table enumerates and encodes those indices.
|
||||
//
|
||||
// The table is split:
|
||||
// - PfxRoutesLookupTbl holds prefixes up to /7
|
||||
// - FringeRoutesLookupTbl holds /8 prefixes
|
||||
//
|
||||
// This structure allows for very fast set inclusion checks using simple bitwise AND.
|
||||
//
|
||||
//nolint:gochecknoglobals // Precomputed read‑only table used in hot paths.
|
||||
var PfxRoutesLookupTbl = [256]bitset.BitSet256{
|
||||
/* idx: 0 */ {0x0, 0x0, 0x0, 0x0}, // invalid
|
||||
/* idx: 1 */ {0xfffffffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}, // [1, 2, 3, 4, 5, ... ]
|
||||
/* idx: 2 */ {0xffff00ff0f34, 0xffffffff, 0xffffffffffffffff, 0x0}, // [2, 4, 5, 8, 9, ... ]
|
||||
/* idx: 3 */ {0xffff0000ff00f0c8, 0xffffffff00000000, 0x0, 0xffffffffffffffff}, // [3, 6, 7, 12, 13, ... ]
|
||||
/* idx: 4 */ {0xff000f0310, 0xffff, 0xffffffff, 0x0}, // [4, 8, 9, 16, 17, ... ]
|
||||
/* idx: 5 */ {0xff0000f00c20, 0xffff0000, 0xffffffff00000000, 0x0}, // [5, 10, 11, 20, 21, ... ]
|
||||
/* idx: 6 */ {0xff00000f003040, 0xffff00000000, 0x0, 0xffffffff}, // [6, 12, 13, 24, 25, ... ]
|
||||
/* idx: 7 */ {0xff000000f000c080, 0xffff000000000000, 0x0, 0xffffffff00000000}, // [7, 14, 15, 28, 29, ... ]
|
||||
/* idx: 8 */ {0xf00030100, 0xff, 0xffff, 0x0}, // [8, 16, 17, 32, 33, ... ]
|
||||
/* idx: 9 */ {0xf0000c0200, 0xff00, 0xffff0000, 0x0}, // [9, 18, 19, 36, 37, ... ]
|
||||
/* idx: 10 */ {0xf0000300400, 0xff0000, 0xffff00000000, 0x0}, // [10, 20, 21, 40, 41, ... ]
|
||||
/* idx: 11 */ {0xf00000c00800, 0xff000000, 0xffff000000000000, 0x0}, // [11, 22, 23, 44, 45, ... ]
|
||||
/* idx: 12 */ {0xf000003001000, 0xff00000000, 0x0, 0xffff}, // [12, 24, 25, 48, 49, ... ]
|
||||
/* idx: 13 */ {0xf000000c002000, 0xff0000000000, 0x0, 0xffff0000}, // [13, 26, 27, 52, 53, ... ]
|
||||
/* idx: 14 */ {0xf00000030004000, 0xff000000000000, 0x0, 0xffff00000000}, // [14, 28, 29, 56, 57, ... ]
|
||||
/* idx: 15 */ {0xf0000000c0008000, 0xff00000000000000, 0x0, 0xffff000000000000}, // [15, 30, 31, 60, 61, ... ]
|
||||
/* idx: 16 */ {0x300010000, 0xf, 0xff, 0x0}, // [16, 32, 33, 64, 65, ... ]
|
||||
/* idx: 17 */ {0xc00020000, 0xf0, 0xff00, 0x0}, // [17, 34, 35, 68, 69, ... ]
|
||||
/* idx: 18 */ {0x3000040000, 0xf00, 0xff0000, 0x0}, // [18, 36, 37, 72, 73, ... ]
|
||||
/* idx: 19 */ {0xc000080000, 0xf000, 0xff000000, 0x0}, // [19, 38, 39, 76, 77, ... ]
|
||||
/* idx: 20 */ {0x30000100000, 0xf0000, 0xff00000000, 0x0}, // [20, 40, 41, 80, 81, ... ]
|
||||
/* idx: 21 */ {0xc0000200000, 0xf00000, 0xff0000000000, 0x0}, // [21, 42, 43, 84, 85, ... ]
|
||||
/* idx: 22 */ {0x300000400000, 0xf000000, 0xff000000000000, 0x0}, // [22, 44, 45, 88, 89, ... ]
|
||||
/* idx: 23 */ {0xc00000800000, 0xf0000000, 0xff00000000000000, 0x0}, // [23, 46, 47, 92, 93, ... ]
|
||||
/* idx: 24 */ {0x3000001000000, 0xf00000000, 0x0, 0xff}, // [24, 48, 49, 96, 97, ... ]
|
||||
/* idx: 25 */ {0xc000002000000, 0xf000000000, 0x0, 0xff00}, // [25, 50, 51, 100, 101, ... ]
|
||||
/* idx: 26 */ {0x30000004000000, 0xf0000000000, 0x0, 0xff0000}, // [26, 52, 53, 104, 105, ... ]
|
||||
/* idx: 27 */ {0xc0000008000000, 0xf00000000000, 0x0, 0xff000000}, // [27, 54, 55, 108, 109, ... ]
|
||||
/* idx: 28 */ {0x300000010000000, 0xf000000000000, 0x0, 0xff00000000}, // [28, 56, 57, 112, 113, ... ]
|
||||
/* idx: 29 */ {0xc00000020000000, 0xf0000000000000, 0x0, 0xff0000000000}, // [29, 58, 59, 116, 117, ... ]
|
||||
/* idx: 30 */ {0x3000000040000000, 0xf00000000000000, 0x0, 0xff000000000000}, // [30, 60, 61, 120, 121, ... ]
|
||||
/* idx: 31 */ {0xc000000080000000, 0xf000000000000000, 0x0, 0xff00000000000000}, // [31, 62, 63, 124, 125, ... ]
|
||||
/* idx: 32 */ {0x100000000, 0x3, 0xf, 0x0}, // [32 64 65 128 129 130 131]
|
||||
/* idx: 33 */ {0x200000000, 0xc, 0xf0, 0x0}, // [33 66 67 132 133 134 135]
|
||||
/* idx: 34 */ {0x400000000, 0x30, 0xf00, 0x0}, // [34 68 69 136 137 138 139]
|
||||
/* idx: 35 */ {0x800000000, 0xc0, 0xf000, 0x0}, // [35 70 71 140 141 142 143]
|
||||
/* idx: 36 */ {0x1000000000, 0x300, 0xf0000, 0x0}, // [36 72 73 144 145 146 147]
|
||||
/* idx: 37 */ {0x2000000000, 0xc00, 0xf00000, 0x0}, // [37 74 75 148 149 150 151]
|
||||
/* idx: 38 */ {0x4000000000, 0x3000, 0xf000000, 0x0}, // [38 76 77 152 153 154 155]
|
||||
/* idx: 39 */ {0x8000000000, 0xc000, 0xf0000000, 0x0}, // [39 78 79 156 157 158 159]
|
||||
/* idx: 40 */ {0x10000000000, 0x30000, 0xf00000000, 0x0}, // [40 80 81 160 161 162 163]
|
||||
/* idx: 41 */ {0x20000000000, 0xc0000, 0xf000000000, 0x0}, // [41 82 83 164 165 166 167]
|
||||
/* idx: 42 */ {0x40000000000, 0x300000, 0xf0000000000, 0x0}, // [42 84 85 168 169 170 171]
|
||||
/* idx: 43 */ {0x80000000000, 0xc00000, 0xf00000000000, 0x0}, // [43 86 87 172 173 174 175]
|
||||
/* idx: 44 */ {0x100000000000, 0x3000000, 0xf000000000000, 0x0}, // [44 88 89 176 177 178 179]
|
||||
/* idx: 45 */ {0x200000000000, 0xc000000, 0xf0000000000000, 0x0}, // [45 90 91 180 181 182 183]
|
||||
/* idx: 46 */ {0x400000000000, 0x30000000, 0xf00000000000000, 0x0}, // [46 92 93 184 185 186 187]
|
||||
/* idx: 47 */ {0x800000000000, 0xc0000000, 0xf000000000000000, 0x0}, // [47 94 95 188 189 190 191]
|
||||
/* idx: 48 */ {0x1000000000000, 0x300000000, 0x0, 0xf}, // [48 96 97 192 193 194 195]
|
||||
/* idx: 49 */ {0x2000000000000, 0xc00000000, 0x0, 0xf0}, // [49 98 99 196 197 198 199]
|
||||
/* idx: 50 */ {0x4000000000000, 0x3000000000, 0x0, 0xf00}, // [50 100 101 200 201 202 203]
|
||||
/* idx: 51 */ {0x8000000000000, 0xc000000000, 0x0, 0xf000}, // [51 102 103 204 205 206 207]
|
||||
/* idx: 52 */ {0x10000000000000, 0x30000000000, 0x0, 0xf0000}, // [52 104 105 208 209 210 211]
|
||||
/* idx: 53 */ {0x20000000000000, 0xc0000000000, 0x0, 0xf00000}, // [53 106 107 212 213 214 215]
|
||||
/* idx: 54 */ {0x40000000000000, 0x300000000000, 0x0, 0xf000000}, // [54 108 109 216 217 218 219]
|
||||
/* idx: 55 */ {0x80000000000000, 0xc00000000000, 0x0, 0xf0000000}, // [55 110 111 220 221 222 223]
|
||||
/* idx: 56 */ {0x100000000000000, 0x3000000000000, 0x0, 0xf00000000}, // [56 112 113 224 225 226 227]
|
||||
/* idx: 57 */ {0x200000000000000, 0xc000000000000, 0x0, 0xf000000000}, // [57 114 115 228 229 230 231]
|
||||
/* idx: 58 */ {0x400000000000000, 0x30000000000000, 0x0, 0xf0000000000}, // [58 116 117 232 233 234 235]
|
||||
/* idx: 59 */ {0x800000000000000, 0xc0000000000000, 0x0, 0xf00000000000}, // [59 118 119 236 237 238 239]
|
||||
/* idx: 60 */ {0x1000000000000000, 0x300000000000000, 0x0, 0xf000000000000}, // [60 120 121 240 241 242 243]
|
||||
/* idx: 61 */ {0x2000000000000000, 0xc00000000000000, 0x0, 0xf0000000000000}, // [61 122 123 244 245 246 247]
|
||||
/* idx: 62 */ {0x4000000000000000, 0x3000000000000000, 0x0, 0xf00000000000000}, // [62 124 125 248 249 250 251]
|
||||
/* idx: 63 */ {0x8000000000000000, 0xc000000000000000, 0x0, 0xf000000000000000}, // [63 126 127 252 253 254 255]
|
||||
/* idx: 64 */ {0x0, 0x1, 0x3, 0x0}, // [64 128 129]
|
||||
/* idx: 65 */ {0x0, 0x2, 0xc, 0x0}, // [65 130 131]
|
||||
/* idx: 66 */ {0x0, 0x4, 0x30, 0x0}, // [66 132 133]
|
||||
/* idx: 67 */ {0x0, 0x8, 0xc0, 0x0}, // [67 134 135]
|
||||
/* idx: 68 */ {0x0, 0x10, 0x300, 0x0}, // [68 136 137]
|
||||
/* idx: 69 */ {0x0, 0x20, 0xc00, 0x0}, // [69 138 139]
|
||||
/* idx: 70 */ {0x0, 0x40, 0x3000, 0x0}, // [70 140 141]
|
||||
/* idx: 71 */ {0x0, 0x80, 0xc000, 0x0}, // [71 142 143]
|
||||
/* idx: 72 */ {0x0, 0x100, 0x30000, 0x0}, // [72 144 145]
|
||||
/* idx: 73 */ {0x0, 0x200, 0xc0000, 0x0}, // [73 146 147]
|
||||
/* idx: 74 */ {0x0, 0x400, 0x300000, 0x0}, // [74 148 149]
|
||||
/* idx: 75 */ {0x0, 0x800, 0xc00000, 0x0}, // [75 150 151]
|
||||
/* idx: 76 */ {0x0, 0x1000, 0x3000000, 0x0}, // [76 152 153]
|
||||
/* idx: 77 */ {0x0, 0x2000, 0xc000000, 0x0}, // [77 154 155]
|
||||
/* idx: 78 */ {0x0, 0x4000, 0x30000000, 0x0}, // [78 156 157]
|
||||
/* idx: 79 */ {0x0, 0x8000, 0xc0000000, 0x0}, // [79 158 159]
|
||||
/* idx: 80 */ {0x0, 0x10000, 0x300000000, 0x0}, // [80 160 161]
|
||||
/* idx: 81 */ {0x0, 0x20000, 0xc00000000, 0x0}, // [81 162 163]
|
||||
/* idx: 82 */ {0x0, 0x40000, 0x3000000000, 0x0}, // [82 164 165]
|
||||
/* idx: 83 */ {0x0, 0x80000, 0xc000000000, 0x0}, // [83 166 167]
|
||||
/* idx: 84 */ {0x0, 0x100000, 0x30000000000, 0x0}, // [84 168 169]
|
||||
/* idx: 85 */ {0x0, 0x200000, 0xc0000000000, 0x0}, // [85 170 171]
|
||||
/* idx: 86 */ {0x0, 0x400000, 0x300000000000, 0x0}, // [86 172 173]
|
||||
/* idx: 87 */ {0x0, 0x800000, 0xc00000000000, 0x0}, // [87 174 175]
|
||||
/* idx: 88 */ {0x0, 0x1000000, 0x3000000000000, 0x0}, // [88 176 177]
|
||||
/* idx: 89 */ {0x0, 0x2000000, 0xc000000000000, 0x0}, // [89 178 179]
|
||||
/* idx: 90 */ {0x0, 0x4000000, 0x30000000000000, 0x0}, // [90 180 181]
|
||||
/* idx: 91 */ {0x0, 0x8000000, 0xc0000000000000, 0x0}, // [91 182 183]
|
||||
/* idx: 92 */ {0x0, 0x10000000, 0x300000000000000, 0x0}, // [92 184 185]
|
||||
/* idx: 93 */ {0x0, 0x20000000, 0xc00000000000000, 0x0}, // [93 186 187]
|
||||
/* idx: 94 */ {0x0, 0x40000000, 0x3000000000000000, 0x0}, // [94 188 189]
|
||||
/* idx: 95 */ {0x0, 0x80000000, 0xc000000000000000, 0x0}, // [95 190 191]
|
||||
/* idx: 96 */ {0x0, 0x100000000, 0x0, 0x3}, // [96 192 193]
|
||||
/* idx: 97 */ {0x0, 0x200000000, 0x0, 0xc}, // [97 194 195]
|
||||
/* idx: 98 */ {0x0, 0x400000000, 0x0, 0x30}, // [98 196 197]
|
||||
/* idx: 99 */ {0x0, 0x800000000, 0x0, 0xc0}, // [99 198 199]
|
||||
/* idx: 100 */ {0x0, 0x1000000000, 0x0, 0x300}, // [100 200 201]
|
||||
/* idx: 101 */ {0x0, 0x2000000000, 0x0, 0xc00}, // [101 202 203]
|
||||
/* idx: 102 */ {0x0, 0x4000000000, 0x0, 0x3000}, // [102 204 205]
|
||||
/* idx: 103 */ {0x0, 0x8000000000, 0x0, 0xc000}, // [103 206 207]
|
||||
/* idx: 104 */ {0x0, 0x10000000000, 0x0, 0x30000}, // [104 208 209]
|
||||
/* idx: 105 */ {0x0, 0x20000000000, 0x0, 0xc0000}, // [105 210 211]
|
||||
/* idx: 106 */ {0x0, 0x40000000000, 0x0, 0x300000}, // [106 212 213]
|
||||
/* idx: 107 */ {0x0, 0x80000000000, 0x0, 0xc00000}, // [107 214 215]
|
||||
/* idx: 108 */ {0x0, 0x100000000000, 0x0, 0x3000000}, // [108 216 217]
|
||||
/* idx: 109 */ {0x0, 0x200000000000, 0x0, 0xc000000}, // [109 218 219]
|
||||
/* idx: 110 */ {0x0, 0x400000000000, 0x0, 0x30000000}, // [110 220 221]
|
||||
/* idx: 111 */ {0x0, 0x800000000000, 0x0, 0xc0000000}, // [111 222 223]
|
||||
/* idx: 112 */ {0x0, 0x1000000000000, 0x0, 0x300000000}, // [112 224 225]
|
||||
/* idx: 113 */ {0x0, 0x2000000000000, 0x0, 0xc00000000}, // [113 226 227]
|
||||
/* idx: 114 */ {0x0, 0x4000000000000, 0x0, 0x3000000000}, // [114 228 229]
|
||||
/* idx: 115 */ {0x0, 0x8000000000000, 0x0, 0xc000000000}, // [115 230 231]
|
||||
/* idx: 116 */ {0x0, 0x10000000000000, 0x0, 0x30000000000}, // [116 232 233]
|
||||
/* idx: 117 */ {0x0, 0x20000000000000, 0x0, 0xc0000000000}, // [117 234 235]
|
||||
/* idx: 118 */ {0x0, 0x40000000000000, 0x0, 0x300000000000}, // [118 236 237]
|
||||
/* idx: 119 */ {0x0, 0x80000000000000, 0x0, 0xc00000000000}, // [119 238 239]
|
||||
/* idx: 120 */ {0x0, 0x100000000000000, 0x0, 0x3000000000000}, // [120 240 241]
|
||||
/* idx: 121 */ {0x0, 0x200000000000000, 0x0, 0xc000000000000}, // [121 242 243]
|
||||
/* idx: 122 */ {0x0, 0x400000000000000, 0x0, 0x30000000000000}, // [122 244 245]
|
||||
/* idx: 123 */ {0x0, 0x800000000000000, 0x0, 0xc0000000000000}, // [123 246 247]
|
||||
/* idx: 124 */ {0x0, 0x1000000000000000, 0x0, 0x300000000000000}, // [124 248 249]
|
||||
/* idx: 125 */ {0x0, 0x2000000000000000, 0x0, 0xc00000000000000}, // [125 250 251]
|
||||
/* idx: 126 */ {0x0, 0x4000000000000000, 0x0, 0x3000000000000000}, // [126 252 253]
|
||||
/* idx: 127 */ {0x0, 0x8000000000000000, 0x0, 0xc000000000000000}, // [127 254 255]
|
||||
/* idx: 128 */ {0x0, 0x0, 0x1, 0x0}, // [128]
|
||||
/* idx: 129 */ {0x0, 0x0, 0x2, 0x0}, // [129]
|
||||
/* idx: 130 */ {0x0, 0x0, 0x4, 0x0}, // [130]
|
||||
/* idx: 131 */ {0x0, 0x0, 0x8, 0x0}, // [131]
|
||||
/* idx: 132 */ {0x0, 0x0, 0x10, 0x0}, // [132]
|
||||
/* idx: 133 */ {0x0, 0x0, 0x20, 0x0}, // [133]
|
||||
/* idx: 134 */ {0x0, 0x0, 0x40, 0x0}, // [134]
|
||||
/* idx: 135 */ {0x0, 0x0, 0x80, 0x0}, // [135]
|
||||
/* idx: 136 */ {0x0, 0x0, 0x100, 0x0}, // [136]
|
||||
/* idx: 137 */ {0x0, 0x0, 0x200, 0x0}, // [137]
|
||||
/* idx: 138 */ {0x0, 0x0, 0x400, 0x0}, // [138]
|
||||
/* idx: 139 */ {0x0, 0x0, 0x800, 0x0}, // [139]
|
||||
/* idx: 140 */ {0x0, 0x0, 0x1000, 0x0}, // [140]
|
||||
/* idx: 141 */ {0x0, 0x0, 0x2000, 0x0}, // [141]
|
||||
/* idx: 142 */ {0x0, 0x0, 0x4000, 0x0}, // [142]
|
||||
/* idx: 143 */ {0x0, 0x0, 0x8000, 0x0}, // [143]
|
||||
/* idx: 144 */ {0x0, 0x0, 0x10000, 0x0}, // [144]
|
||||
/* idx: 145 */ {0x0, 0x0, 0x20000, 0x0}, // [145]
|
||||
/* idx: 146 */ {0x0, 0x0, 0x40000, 0x0}, // [146]
|
||||
/* idx: 147 */ {0x0, 0x0, 0x80000, 0x0}, // [147]
|
||||
/* idx: 148 */ {0x0, 0x0, 0x100000, 0x0}, // [148]
|
||||
/* idx: 149 */ {0x0, 0x0, 0x200000, 0x0}, // [149]
|
||||
/* idx: 150 */ {0x0, 0x0, 0x400000, 0x0}, // [150]
|
||||
/* idx: 151 */ {0x0, 0x0, 0x800000, 0x0}, // [151]
|
||||
/* idx: 152 */ {0x0, 0x0, 0x1000000, 0x0}, // [152]
|
||||
/* idx: 153 */ {0x0, 0x0, 0x2000000, 0x0}, // [153]
|
||||
/* idx: 154 */ {0x0, 0x0, 0x4000000, 0x0}, // [154]
|
||||
/* idx: 155 */ {0x0, 0x0, 0x8000000, 0x0}, // [155]
|
||||
/* idx: 156 */ {0x0, 0x0, 0x10000000, 0x0}, // [156]
|
||||
/* idx: 157 */ {0x0, 0x0, 0x20000000, 0x0}, // [157]
|
||||
/* idx: 158 */ {0x0, 0x0, 0x40000000, 0x0}, // [158]
|
||||
/* idx: 159 */ {0x0, 0x0, 0x80000000, 0x0}, // [159]
|
||||
/* idx: 160 */ {0x0, 0x0, 0x100000000, 0x0}, // [160]
|
||||
/* idx: 161 */ {0x0, 0x0, 0x200000000, 0x0}, // [161]
|
||||
/* idx: 162 */ {0x0, 0x0, 0x400000000, 0x0}, // [162]
|
||||
/* idx: 163 */ {0x0, 0x0, 0x800000000, 0x0}, // [163]
|
||||
/* idx: 164 */ {0x0, 0x0, 0x1000000000, 0x0}, // [164]
|
||||
/* idx: 165 */ {0x0, 0x0, 0x2000000000, 0x0}, // [165]
|
||||
/* idx: 166 */ {0x0, 0x0, 0x4000000000, 0x0}, // [166]
|
||||
/* idx: 167 */ {0x0, 0x0, 0x8000000000, 0x0}, // [167]
|
||||
/* idx: 168 */ {0x0, 0x0, 0x10000000000, 0x0}, // [168]
|
||||
/* idx: 169 */ {0x0, 0x0, 0x20000000000, 0x0}, // [169]
|
||||
/* idx: 170 */ {0x0, 0x0, 0x40000000000, 0x0}, // [170]
|
||||
/* idx: 171 */ {0x0, 0x0, 0x80000000000, 0x0}, // [171]
|
||||
/* idx: 172 */ {0x0, 0x0, 0x100000000000, 0x0}, // [172]
|
||||
/* idx: 173 */ {0x0, 0x0, 0x200000000000, 0x0}, // [173]
|
||||
/* idx: 174 */ {0x0, 0x0, 0x400000000000, 0x0}, // [174]
|
||||
/* idx: 175 */ {0x0, 0x0, 0x800000000000, 0x0}, // [175]
|
||||
/* idx: 176 */ {0x0, 0x0, 0x1000000000000, 0x0}, // [176]
|
||||
/* idx: 177 */ {0x0, 0x0, 0x2000000000000, 0x0}, // [177]
|
||||
/* idx: 178 */ {0x0, 0x0, 0x4000000000000, 0x0}, // [178]
|
||||
/* idx: 179 */ {0x0, 0x0, 0x8000000000000, 0x0}, // [179]
|
||||
/* idx: 180 */ {0x0, 0x0, 0x10000000000000, 0x0}, // [180]
|
||||
/* idx: 181 */ {0x0, 0x0, 0x20000000000000, 0x0}, // [181]
|
||||
/* idx: 182 */ {0x0, 0x0, 0x40000000000000, 0x0}, // [182]
|
||||
/* idx: 183 */ {0x0, 0x0, 0x80000000000000, 0x0}, // [183]
|
||||
/* idx: 184 */ {0x0, 0x0, 0x100000000000000, 0x0}, // [184]
|
||||
/* idx: 185 */ {0x0, 0x0, 0x200000000000000, 0x0}, // [185]
|
||||
/* idx: 186 */ {0x0, 0x0, 0x400000000000000, 0x0}, // [186]
|
||||
/* idx: 187 */ {0x0, 0x0, 0x800000000000000, 0x0}, // [187]
|
||||
/* idx: 188 */ {0x0, 0x0, 0x1000000000000000, 0x0}, // [188]
|
||||
/* idx: 189 */ {0x0, 0x0, 0x2000000000000000, 0x0}, // [189]
|
||||
/* idx: 190 */ {0x0, 0x0, 0x4000000000000000, 0x0}, // [190]
|
||||
/* idx: 191 */ {0x0, 0x0, 0x8000000000000000, 0x0}, // [191]
|
||||
/* idx: 192 */ {0x0, 0x0, 0x0, 0x1}, // [192]
|
||||
/* idx: 193 */ {0x0, 0x0, 0x0, 0x2}, // [193]
|
||||
/* idx: 194 */ {0x0, 0x0, 0x0, 0x4}, // [194]
|
||||
/* idx: 195 */ {0x0, 0x0, 0x0, 0x8}, // [195]
|
||||
/* idx: 196 */ {0x0, 0x0, 0x0, 0x10}, // [196]
|
||||
/* idx: 197 */ {0x0, 0x0, 0x0, 0x20}, // [197]
|
||||
/* idx: 198 */ {0x0, 0x0, 0x0, 0x40}, // [198]
|
||||
/* idx: 199 */ {0x0, 0x0, 0x0, 0x80}, // [199]
|
||||
/* idx: 200 */ {0x0, 0x0, 0x0, 0x100}, // [200]
|
||||
/* idx: 201 */ {0x0, 0x0, 0x0, 0x200}, // [201]
|
||||
/* idx: 202 */ {0x0, 0x0, 0x0, 0x400}, // [202]
|
||||
/* idx: 203 */ {0x0, 0x0, 0x0, 0x800}, // [203]
|
||||
/* idx: 204 */ {0x0, 0x0, 0x0, 0x1000}, // [204]
|
||||
/* idx: 205 */ {0x0, 0x0, 0x0, 0x2000}, // [205]
|
||||
/* idx: 206 */ {0x0, 0x0, 0x0, 0x4000}, // [206]
|
||||
/* idx: 207 */ {0x0, 0x0, 0x0, 0x8000}, // [207]
|
||||
/* idx: 208 */ {0x0, 0x0, 0x0, 0x10000}, // [208]
|
||||
/* idx: 209 */ {0x0, 0x0, 0x0, 0x20000}, // [209]
|
||||
/* idx: 210 */ {0x0, 0x0, 0x0, 0x40000}, // [210]
|
||||
/* idx: 211 */ {0x0, 0x0, 0x0, 0x80000}, // [211]
|
||||
/* idx: 212 */ {0x0, 0x0, 0x0, 0x100000}, // [212]
|
||||
/* idx: 213 */ {0x0, 0x0, 0x0, 0x200000}, // [213]
|
||||
/* idx: 214 */ {0x0, 0x0, 0x0, 0x400000}, // [214]
|
||||
/* idx: 215 */ {0x0, 0x0, 0x0, 0x800000}, // [215]
|
||||
/* idx: 216 */ {0x0, 0x0, 0x0, 0x1000000}, // [216]
|
||||
/* idx: 217 */ {0x0, 0x0, 0x0, 0x2000000}, // [217]
|
||||
/* idx: 218 */ {0x0, 0x0, 0x0, 0x4000000}, // [218]
|
||||
/* idx: 219 */ {0x0, 0x0, 0x0, 0x8000000}, // [219]
|
||||
/* idx: 220 */ {0x0, 0x0, 0x0, 0x10000000}, // [220]
|
||||
/* idx: 221 */ {0x0, 0x0, 0x0, 0x20000000}, // [221]
|
||||
/* idx: 222 */ {0x0, 0x0, 0x0, 0x40000000}, // [222]
|
||||
/* idx: 223 */ {0x0, 0x0, 0x0, 0x80000000}, // [223]
|
||||
/* idx: 224 */ {0x0, 0x0, 0x0, 0x100000000}, // [224]
|
||||
/* idx: 225 */ {0x0, 0x0, 0x0, 0x200000000}, // [225]
|
||||
/* idx: 226 */ {0x0, 0x0, 0x0, 0x400000000}, // [226]
|
||||
/* idx: 227 */ {0x0, 0x0, 0x0, 0x800000000}, // [227]
|
||||
/* idx: 228 */ {0x0, 0x0, 0x0, 0x1000000000}, // [228]
|
||||
/* idx: 229 */ {0x0, 0x0, 0x0, 0x2000000000}, // [229]
|
||||
/* idx: 230 */ {0x0, 0x0, 0x0, 0x4000000000}, // [230]
|
||||
/* idx: 231 */ {0x0, 0x0, 0x0, 0x8000000000}, // [231]
|
||||
/* idx: 232 */ {0x0, 0x0, 0x0, 0x10000000000}, // [232]
|
||||
/* idx: 233 */ {0x0, 0x0, 0x0, 0x20000000000}, // [233]
|
||||
/* idx: 234 */ {0x0, 0x0, 0x0, 0x40000000000}, // [234]
|
||||
/* idx: 235 */ {0x0, 0x0, 0x0, 0x80000000000}, // [235]
|
||||
/* idx: 236 */ {0x0, 0x0, 0x0, 0x100000000000}, // [236]
|
||||
/* idx: 237 */ {0x0, 0x0, 0x0, 0x200000000000}, // [237]
|
||||
/* idx: 238 */ {0x0, 0x0, 0x0, 0x400000000000}, // [238]
|
||||
/* idx: 239 */ {0x0, 0x0, 0x0, 0x800000000000}, // [239]
|
||||
/* idx: 240 */ {0x0, 0x0, 0x0, 0x1000000000000}, // [240]
|
||||
/* idx: 241 */ {0x0, 0x0, 0x0, 0x2000000000000}, // [241]
|
||||
/* idx: 242 */ {0x0, 0x0, 0x0, 0x4000000000000}, // [242]
|
||||
/* idx: 243 */ {0x0, 0x0, 0x0, 0x8000000000000}, // [243]
|
||||
/* idx: 244 */ {0x0, 0x0, 0x0, 0x10000000000000}, // [244]
|
||||
/* idx: 245 */ {0x0, 0x0, 0x0, 0x20000000000000}, // [245]
|
||||
/* idx: 246 */ {0x0, 0x0, 0x0, 0x40000000000000}, // [246]
|
||||
/* idx: 247 */ {0x0, 0x0, 0x0, 0x80000000000000}, // [247]
|
||||
/* idx: 248 */ {0x0, 0x0, 0x0, 0x100000000000000}, // [248]
|
||||
/* idx: 249 */ {0x0, 0x0, 0x0, 0x200000000000000}, // [249]
|
||||
/* idx: 250 */ {0x0, 0x0, 0x0, 0x400000000000000}, // [250]
|
||||
/* idx: 251 */ {0x0, 0x0, 0x0, 0x800000000000000}, // [251]
|
||||
/* idx: 252 */ {0x0, 0x0, 0x0, 0x1000000000000000}, // [252]
|
||||
/* idx: 253 */ {0x0, 0x0, 0x0, 0x2000000000000000}, // [253]
|
||||
/* idx: 254 */ {0x0, 0x0, 0x0, 0x4000000000000000}, // [254]
|
||||
/* idx: 255 */ {0x0, 0x0, 0x0, 0x8000000000000000}, // [255]
|
||||
}
|
||||
|
||||
// FringeRoutesLookupTbl, the second 256 Bits, see also the PfxRoutesLookupTbl for the first 256 Bits
|
||||
// we split 512 bits into 2×256 to leverage BitSet256 optimizations.
|
||||
//
|
||||
//nolint:gochecknoglobals // Precomputed read‑only table used in hot paths.
|
||||
var FringeRoutesLookupTbl = [256]bitset.BitSet256{
|
||||
/* idx: 0 */ {0x0, 0x0, 0x0, 0x0}, // invalid
|
||||
/* idx: 1 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}, // [0, 1, 2, 3, 4, ... ]
|
||||
/* idx: 2 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0}, // [0, 1, 2, 3, 4, ... ]
|
||||
/* idx: 3 */ {0x0, 0x0, 0xffffffffffffffff, 0xffffffffffffffff}, // [128, 129, 130, 131, 132, ... ]
|
||||
/* idx: 4 */ {0xffffffffffffffff, 0x0, 0x0, 0x0}, // [0, 1, 2, 3, 4, ... ]
|
||||
/* idx: 5 */ {0x0, 0xffffffffffffffff, 0x0, 0x0}, // [64, 65, 66, 67, 68, ... ]
|
||||
/* idx: 6 */ {0x0, 0x0, 0xffffffffffffffff, 0x0}, // [128, 129, 130, 131, 132, ... ]
|
||||
/* idx: 7 */ {0x0, 0x0, 0x0, 0xffffffffffffffff}, // [192, 193, 194, 195, 196, ... ]
|
||||
/* idx: 8 */ {0xffffffff, 0x0, 0x0, 0x0}, // [0, 1, 2, 3, 4, ... ]
|
||||
/* idx: 9 */ {0xffffffff00000000, 0x0, 0x0, 0x0}, // [32, 33, 34, 35, 36, ... ]
|
||||
/* idx: 10 */ {0x0, 0xffffffff, 0x0, 0x0}, // [64, 65, 66, 67, 68, ... ]
|
||||
/* idx: 11 */ {0x0, 0xffffffff00000000, 0x0, 0x0}, // [96, 97, 98, 99, 100, ... ]
|
||||
/* idx: 12 */ {0x0, 0x0, 0xffffffff, 0x0}, // [128, 129, 130, 131, 132, ... ]
|
||||
/* idx: 13 */ {0x0, 0x0, 0xffffffff00000000, 0x0}, // [160, 161, 162, 163, 164, ... ]
|
||||
/* idx: 14 */ {0x0, 0x0, 0x0, 0xffffffff}, // [192, 193, 194, 195, 196, ... ]
|
||||
/* idx: 15 */ {0x0, 0x0, 0x0, 0xffffffff00000000}, // [224, 225, 226, 227, 228, ... ]
|
||||
/* idx: 16 */ {0xffff, 0x0, 0x0, 0x0}, // [0, 1, 2, 3, 4, ... ]
|
||||
/* idx: 17 */ {0xffff0000, 0x0, 0x0, 0x0}, // [16, 17, 18, 19, 20, ... ]
|
||||
/* idx: 18 */ {0xffff00000000, 0x0, 0x0, 0x0}, // [32, 33, 34, 35, 36, ... ]
|
||||
/* idx: 19 */ {0xffff000000000000, 0x0, 0x0, 0x0}, // [48, 49, 50, 51, 52, ... ]
|
||||
/* idx: 20 */ {0x0, 0xffff, 0x0, 0x0}, // [64, 65, 66, 67, 68, ... ]
|
||||
/* idx: 21 */ {0x0, 0xffff0000, 0x0, 0x0}, // [80, 81, 82, 83, 84, ... ]
|
||||
/* idx: 22 */ {0x0, 0xffff00000000, 0x0, 0x0}, // [96, 97, 98, 99, 100, ... ]
|
||||
/* idx: 23 */ {0x0, 0xffff000000000000, 0x0, 0x0}, // [112, 113, 114, 115, 116, ... ]
|
||||
/* idx: 24 */ {0x0, 0x0, 0xffff, 0x0}, // [128, 129, 130, 131, 132, ... ]
|
||||
/* idx: 25 */ {0x0, 0x0, 0xffff0000, 0x0}, // [144, 145, 146, 147, 148, ... ]
|
||||
/* idx: 26 */ {0x0, 0x0, 0xffff00000000, 0x0}, // [160, 161, 162, 163, 164, ... ]
|
||||
/* idx: 27 */ {0x0, 0x0, 0xffff000000000000, 0x0}, // [176, 177, 178, 179, 180, ... ]
|
||||
/* idx: 28 */ {0x0, 0x0, 0x0, 0xffff}, // [192, 193, 194, 195, 196, ... ]
|
||||
/* idx: 29 */ {0x0, 0x0, 0x0, 0xffff0000}, // [208, 209, 210, 211, 212, ... ]
|
||||
/* idx: 30 */ {0x0, 0x0, 0x0, 0xffff00000000}, // [224, 225, 226, 227, 228, ... ]
|
||||
/* idx: 31 */ {0x0, 0x0, 0x0, 0xffff000000000000}, // [240, 241, 242, 243, 244, ... ]
|
||||
/* idx: 32 */ {0xff, 0x0, 0x0, 0x0}, // [0 1 2 3 4 5 6 7]
|
||||
/* idx: 33 */ {0xff00, 0x0, 0x0, 0x0}, // [8 9 10 11 12 13 14 15]
|
||||
/* idx: 34 */ {0xff0000, 0x0, 0x0, 0x0}, // [16 17 18 19 20 21 22 23]
|
||||
/* idx: 35 */ {0xff000000, 0x0, 0x0, 0x0}, // [24 25 26 27 28 29 30 31]
|
||||
/* idx: 36 */ {0xff00000000, 0x0, 0x0, 0x0}, // [32 33 34 35 36 37 38 39]
|
||||
/* idx: 37 */ {0xff0000000000, 0x0, 0x0, 0x0}, // [40 41 42 43 44 45 46 47]
|
||||
/* idx: 38 */ {0xff000000000000, 0x0, 0x0, 0x0}, // [48 49 50 51 52 53 54 55]
|
||||
/* idx: 39 */ {0xff00000000000000, 0x0, 0x0, 0x0}, // [56 57 58 59 60 61 62 63]
|
||||
/* idx: 40 */ {0x0, 0xff, 0x0, 0x0}, // [64 65 66 67 68 69 70 71]
|
||||
/* idx: 41 */ {0x0, 0xff00, 0x0, 0x0}, // [72 73 74 75 76 77 78 79]
|
||||
/* idx: 42 */ {0x0, 0xff0000, 0x0, 0x0}, // [80 81 82 83 84 85 86 87]
|
||||
/* idx: 43 */ {0x0, 0xff000000, 0x0, 0x0}, // [88 89 90 91 92 93 94 95]
|
||||
/* idx: 44 */ {0x0, 0xff00000000, 0x0, 0x0}, // [96 97 98 99 100 101 102 103]
|
||||
/* idx: 45 */ {0x0, 0xff0000000000, 0x0, 0x0}, // [104 105 106 107 108 109 110 111]
|
||||
/* idx: 46 */ {0x0, 0xff000000000000, 0x0, 0x0}, // [112 113 114 115 116 117 118 119]
|
||||
/* idx: 47 */ {0x0, 0xff00000000000000, 0x0, 0x0}, // [120 121 122 123 124 125 126 127]
|
||||
/* idx: 48 */ {0x0, 0x0, 0xff, 0x0}, // [128 129 130 131 132 133 134 135]
|
||||
/* idx: 49 */ {0x0, 0x0, 0xff00, 0x0}, // [136 137 138 139 140 141 142 143]
|
||||
/* idx: 50 */ {0x0, 0x0, 0xff0000, 0x0}, // [144 145 146 147 148 149 150 151]
|
||||
/* idx: 51 */ {0x0, 0x0, 0xff000000, 0x0}, // [152 153 154 155 156 157 158 159]
|
||||
/* idx: 52 */ {0x0, 0x0, 0xff00000000, 0x0}, // [160 161 162 163 164 165 166 167]
|
||||
/* idx: 53 */ {0x0, 0x0, 0xff0000000000, 0x0}, // [168 169 170 171 172 173 174 175]
|
||||
/* idx: 54 */ {0x0, 0x0, 0xff000000000000, 0x0}, // [176 177 178 179 180 181 182 183]
|
||||
/* idx: 55 */ {0x0, 0x0, 0xff00000000000000, 0x0}, // [184 185 186 187 188 189 190 191]
|
||||
/* idx: 56 */ {0x0, 0x0, 0x0, 0xff}, // [192 193 194 195 196 197 198 199]
|
||||
/* idx: 57 */ {0x0, 0x0, 0x0, 0xff00}, // [200 201 202 203 204 205 206 207]
|
||||
/* idx: 58 */ {0x0, 0x0, 0x0, 0xff0000}, // [208 209 210 211 212 213 214 215]
|
||||
/* idx: 59 */ {0x0, 0x0, 0x0, 0xff000000}, // [216 217 218 219 220 221 222 223]
|
||||
/* idx: 60 */ {0x0, 0x0, 0x0, 0xff00000000}, // [224 225 226 227 228 229 230 231]
|
||||
/* idx: 61 */ {0x0, 0x0, 0x0, 0xff0000000000}, // [232 233 234 235 236 237 238 239]
|
||||
/* idx: 62 */ {0x0, 0x0, 0x0, 0xff000000000000}, // [240 241 242 243 244 245 246 247]
|
||||
/* idx: 63 */ {0x0, 0x0, 0x0, 0xff00000000000000}, // [248 249 250 251 252 253 254 255]
|
||||
/* idx: 64 */ {0xf, 0x0, 0x0, 0x0}, // [0 1 2 3]
|
||||
/* idx: 65 */ {0xf0, 0x0, 0x0, 0x0}, // [4 5 6 7]
|
||||
/* idx: 66 */ {0xf00, 0x0, 0x0, 0x0}, // [8 9 10 11]
|
||||
/* idx: 67 */ {0xf000, 0x0, 0x0, 0x0}, // [12 13 14 15]
|
||||
/* idx: 68 */ {0xf0000, 0x0, 0x0, 0x0}, // [16 17 18 19]
|
||||
/* idx: 69 */ {0xf00000, 0x0, 0x0, 0x0}, // [20 21 22 23]
|
||||
/* idx: 70 */ {0xf000000, 0x0, 0x0, 0x0}, // [24 25 26 27]
|
||||
/* idx: 71 */ {0xf0000000, 0x0, 0x0, 0x0}, // [28 29 30 31]
|
||||
/* idx: 72 */ {0xf00000000, 0x0, 0x0, 0x0}, // [32 33 34 35]
|
||||
/* idx: 73 */ {0xf000000000, 0x0, 0x0, 0x0}, // [36 37 38 39]
|
||||
/* idx: 74 */ {0xf0000000000, 0x0, 0x0, 0x0}, // [40 41 42 43]
|
||||
/* idx: 75 */ {0xf00000000000, 0x0, 0x0, 0x0}, // [44 45 46 47]
|
||||
/* idx: 76 */ {0xf000000000000, 0x0, 0x0, 0x0}, // [48 49 50 51]
|
||||
/* idx: 77 */ {0xf0000000000000, 0x0, 0x0, 0x0}, // [52 53 54 55]
|
||||
/* idx: 78 */ {0xf00000000000000, 0x0, 0x0, 0x0}, // [56 57 58 59]
|
||||
/* idx: 79 */ {0xf000000000000000, 0x0, 0x0, 0x0}, // [60 61 62 63]
|
||||
/* idx: 80 */ {0x0, 0xf, 0x0, 0x0}, // [64 65 66 67]
|
||||
/* idx: 81 */ {0x0, 0xf0, 0x0, 0x0}, // [68 69 70 71]
|
||||
/* idx: 82 */ {0x0, 0xf00, 0x0, 0x0}, // [72 73 74 75]
|
||||
/* idx: 83 */ {0x0, 0xf000, 0x0, 0x0}, // [76 77 78 79]
|
||||
/* idx: 84 */ {0x0, 0xf0000, 0x0, 0x0}, // [80 81 82 83]
|
||||
/* idx: 85 */ {0x0, 0xf00000, 0x0, 0x0}, // [84 85 86 87]
|
||||
/* idx: 86 */ {0x0, 0xf000000, 0x0, 0x0}, // [88 89 90 91]
|
||||
/* idx: 87 */ {0x0, 0xf0000000, 0x0, 0x0}, // [92 93 94 95]
|
||||
/* idx: 88 */ {0x0, 0xf00000000, 0x0, 0x0}, // [96 97 98 99]
|
||||
/* idx: 89 */ {0x0, 0xf000000000, 0x0, 0x0}, // [100 101 102 103]
|
||||
/* idx: 90 */ {0x0, 0xf0000000000, 0x0, 0x0}, // [104 105 106 107]
|
||||
/* idx: 91 */ {0x0, 0xf00000000000, 0x0, 0x0}, // [108 109 110 111]
|
||||
/* idx: 92 */ {0x0, 0xf000000000000, 0x0, 0x0}, // [112 113 114 115]
|
||||
/* idx: 93 */ {0x0, 0xf0000000000000, 0x0, 0x0}, // [116 117 118 119]
|
||||
/* idx: 94 */ {0x0, 0xf00000000000000, 0x0, 0x0}, // [120 121 122 123]
|
||||
/* idx: 95 */ {0x0, 0xf000000000000000, 0x0, 0x0}, // [124 125 126 127]
|
||||
/* idx: 96 */ {0x0, 0x0, 0xf, 0x0}, // [128 129 130 131]
|
||||
/* idx: 97 */ {0x0, 0x0, 0xf0, 0x0}, // [132 133 134 135]
|
||||
/* idx: 98 */ {0x0, 0x0, 0xf00, 0x0}, // [136 137 138 139]
|
||||
/* idx: 99 */ {0x0, 0x0, 0xf000, 0x0}, // [140 141 142 143]
|
||||
/* idx: 100 */ {0x0, 0x0, 0xf0000, 0x0}, // [144 145 146 147]
|
||||
/* idx: 101 */ {0x0, 0x0, 0xf00000, 0x0}, // [148 149 150 151]
|
||||
/* idx: 102 */ {0x0, 0x0, 0xf000000, 0x0}, // [152 153 154 155]
|
||||
/* idx: 103 */ {0x0, 0x0, 0xf0000000, 0x0}, // [156 157 158 159]
|
||||
/* idx: 104 */ {0x0, 0x0, 0xf00000000, 0x0}, // [160 161 162 163]
|
||||
/* idx: 105 */ {0x0, 0x0, 0xf000000000, 0x0}, // [164 165 166 167]
|
||||
/* idx: 106 */ {0x0, 0x0, 0xf0000000000, 0x0}, // [168 169 170 171]
|
||||
/* idx: 107 */ {0x0, 0x0, 0xf00000000000, 0x0}, // [172 173 174 175]
|
||||
/* idx: 108 */ {0x0, 0x0, 0xf000000000000, 0x0}, // [176 177 178 179]
|
||||
/* idx: 109 */ {0x0, 0x0, 0xf0000000000000, 0x0}, // [180 181 182 183]
|
||||
/* idx: 110 */ {0x0, 0x0, 0xf00000000000000, 0x0}, // [184 185 186 187]
|
||||
/* idx: 111 */ {0x0, 0x0, 0xf000000000000000, 0x0}, // [188 189 190 191]
|
||||
/* idx: 112 */ {0x0, 0x0, 0x0, 0xf}, // [192 193 194 195]
|
||||
/* idx: 113 */ {0x0, 0x0, 0x0, 0xf0}, // [196 197 198 199]
|
||||
/* idx: 114 */ {0x0, 0x0, 0x0, 0xf00}, // [200 201 202 203]
|
||||
/* idx: 115 */ {0x0, 0x0, 0x0, 0xf000}, // [204 205 206 207]
|
||||
/* idx: 116 */ {0x0, 0x0, 0x0, 0xf0000}, // [208 209 210 211]
|
||||
/* idx: 117 */ {0x0, 0x0, 0x0, 0xf00000}, // [212 213 214 215]
|
||||
/* idx: 118 */ {0x0, 0x0, 0x0, 0xf000000}, // [216 217 218 219]
|
||||
/* idx: 119 */ {0x0, 0x0, 0x0, 0xf0000000}, // [220 221 222 223]
|
||||
/* idx: 120 */ {0x0, 0x0, 0x0, 0xf00000000}, // [224 225 226 227]
|
||||
/* idx: 121 */ {0x0, 0x0, 0x0, 0xf000000000}, // [228 229 230 231]
|
||||
/* idx: 122 */ {0x0, 0x0, 0x0, 0xf0000000000}, // [232 233 234 235]
|
||||
/* idx: 123 */ {0x0, 0x0, 0x0, 0xf00000000000}, // [236 237 238 239]
|
||||
/* idx: 124 */ {0x0, 0x0, 0x0, 0xf000000000000}, // [240 241 242 243]
|
||||
/* idx: 125 */ {0x0, 0x0, 0x0, 0xf0000000000000}, // [244 245 246 247]
|
||||
/* idx: 126 */ {0x0, 0x0, 0x0, 0xf00000000000000}, // [248 249 250 251]
|
||||
/* idx: 127 */ {0x0, 0x0, 0x0, 0xf000000000000000}, // [252 253 254 255]
|
||||
/* idx: 128 */ {0x3, 0x0, 0x0, 0x0}, // [0 1]
|
||||
/* idx: 129 */ {0xc, 0x0, 0x0, 0x0}, // [2 3]
|
||||
/* idx: 130 */ {0x30, 0x0, 0x0, 0x0}, // [4 5]
|
||||
/* idx: 131 */ {0xc0, 0x0, 0x0, 0x0}, // [6 7]
|
||||
/* idx: 132 */ {0x300, 0x0, 0x0, 0x0}, // [8 9]
|
||||
/* idx: 133 */ {0xc00, 0x0, 0x0, 0x0}, // [10 11]
|
||||
/* idx: 134 */ {0x3000, 0x0, 0x0, 0x0}, // [12 13]
|
||||
/* idx: 135 */ {0xc000, 0x0, 0x0, 0x0}, // [14 15]
|
||||
/* idx: 136 */ {0x30000, 0x0, 0x0, 0x0}, // [16 17]
|
||||
/* idx: 137 */ {0xc0000, 0x0, 0x0, 0x0}, // [18 19]
|
||||
/* idx: 138 */ {0x300000, 0x0, 0x0, 0x0}, // [20 21]
|
||||
/* idx: 139 */ {0xc00000, 0x0, 0x0, 0x0}, // [22 23]
|
||||
/* idx: 140 */ {0x3000000, 0x0, 0x0, 0x0}, // [24 25]
|
||||
/* idx: 141 */ {0xc000000, 0x0, 0x0, 0x0}, // [26 27]
|
||||
/* idx: 142 */ {0x30000000, 0x0, 0x0, 0x0}, // [28 29]
|
||||
/* idx: 143 */ {0xc0000000, 0x0, 0x0, 0x0}, // [30 31]
|
||||
/* idx: 144 */ {0x300000000, 0x0, 0x0, 0x0}, // [32 33]
|
||||
/* idx: 145 */ {0xc00000000, 0x0, 0x0, 0x0}, // [34 35]
|
||||
/* idx: 146 */ {0x3000000000, 0x0, 0x0, 0x0}, // [36 37]
|
||||
/* idx: 147 */ {0xc000000000, 0x0, 0x0, 0x0}, // [38 39]
|
||||
/* idx: 148 */ {0x30000000000, 0x0, 0x0, 0x0}, // [40 41]
|
||||
/* idx: 149 */ {0xc0000000000, 0x0, 0x0, 0x0}, // [42 43]
|
||||
/* idx: 150 */ {0x300000000000, 0x0, 0x0, 0x0}, // [44 45]
|
||||
/* idx: 151 */ {0xc00000000000, 0x0, 0x0, 0x0}, // [46 47]
|
||||
/* idx: 152 */ {0x3000000000000, 0x0, 0x0, 0x0}, // [48 49]
|
||||
/* idx: 153 */ {0xc000000000000, 0x0, 0x0, 0x0}, // [50 51]
|
||||
/* idx: 154 */ {0x30000000000000, 0x0, 0x0, 0x0}, // [52 53]
|
||||
/* idx: 155 */ {0xc0000000000000, 0x0, 0x0, 0x0}, // [54 55]
|
||||
/* idx: 156 */ {0x300000000000000, 0x0, 0x0, 0x0}, // [56 57]
|
||||
/* idx: 157 */ {0xc00000000000000, 0x0, 0x0, 0x0}, // [58 59]
|
||||
/* idx: 158 */ {0x3000000000000000, 0x0, 0x0, 0x0}, // [60 61]
|
||||
/* idx: 159 */ {0xc000000000000000, 0x0, 0x0, 0x0}, // [62 63]
|
||||
/* idx: 160 */ {0x0, 0x3, 0x0, 0x0}, // [64 65]
|
||||
/* idx: 161 */ {0x0, 0xc, 0x0, 0x0}, // [66 67]
|
||||
/* idx: 162 */ {0x0, 0x30, 0x0, 0x0}, // [68 69]
|
||||
/* idx: 163 */ {0x0, 0xc0, 0x0, 0x0}, // [70 71]
|
||||
/* idx: 164 */ {0x0, 0x300, 0x0, 0x0}, // [72 73]
|
||||
/* idx: 165 */ {0x0, 0xc00, 0x0, 0x0}, // [74 75]
|
||||
/* idx: 166 */ {0x0, 0x3000, 0x0, 0x0}, // [76 77]
|
||||
/* idx: 167 */ {0x0, 0xc000, 0x0, 0x0}, // [78 79]
|
||||
/* idx: 168 */ {0x0, 0x30000, 0x0, 0x0}, // [80 81]
|
||||
/* idx: 169 */ {0x0, 0xc0000, 0x0, 0x0}, // [82 83]
|
||||
/* idx: 170 */ {0x0, 0x300000, 0x0, 0x0}, // [84 85]
|
||||
/* idx: 171 */ {0x0, 0xc00000, 0x0, 0x0}, // [86 87]
|
||||
/* idx: 172 */ {0x0, 0x3000000, 0x0, 0x0}, // [88 89]
|
||||
/* idx: 173 */ {0x0, 0xc000000, 0x0, 0x0}, // [90 91]
|
||||
/* idx: 174 */ {0x0, 0x30000000, 0x0, 0x0}, // [92 93]
|
||||
/* idx: 175 */ {0x0, 0xc0000000, 0x0, 0x0}, // [94 95]
|
||||
/* idx: 176 */ {0x0, 0x300000000, 0x0, 0x0}, // [96 97]
|
||||
/* idx: 177 */ {0x0, 0xc00000000, 0x0, 0x0}, // [98 99]
|
||||
/* idx: 178 */ {0x0, 0x3000000000, 0x0, 0x0}, // [100 101]
|
||||
/* idx: 179 */ {0x0, 0xc000000000, 0x0, 0x0}, // [102 103]
|
||||
/* idx: 180 */ {0x0, 0x30000000000, 0x0, 0x0}, // [104 105]
|
||||
/* idx: 181 */ {0x0, 0xc0000000000, 0x0, 0x0}, // [106 107]
|
||||
/* idx: 182 */ {0x0, 0x300000000000, 0x0, 0x0}, // [108 109]
|
||||
/* idx: 183 */ {0x0, 0xc00000000000, 0x0, 0x0}, // [110 111]
|
||||
/* idx: 184 */ {0x0, 0x3000000000000, 0x0, 0x0}, // [112 113]
|
||||
/* idx: 185 */ {0x0, 0xc000000000000, 0x0, 0x0}, // [114 115]
|
||||
/* idx: 186 */ {0x0, 0x30000000000000, 0x0, 0x0}, // [116 117]
|
||||
/* idx: 187 */ {0x0, 0xc0000000000000, 0x0, 0x0}, // [118 119]
|
||||
/* idx: 188 */ {0x0, 0x300000000000000, 0x0, 0x0}, // [120 121]
|
||||
/* idx: 189 */ {0x0, 0xc00000000000000, 0x0, 0x0}, // [122 123]
|
||||
/* idx: 190 */ {0x0, 0x3000000000000000, 0x0, 0x0}, // [124 125]
|
||||
/* idx: 191 */ {0x0, 0xc000000000000000, 0x0, 0x0}, // [126 127]
|
||||
/* idx: 192 */ {0x0, 0x0, 0x3, 0x0}, // [128 129]
|
||||
/* idx: 193 */ {0x0, 0x0, 0xc, 0x0}, // [130 131]
|
||||
/* idx: 194 */ {0x0, 0x0, 0x30, 0x0}, // [132 133]
|
||||
/* idx: 195 */ {0x0, 0x0, 0xc0, 0x0}, // [134 135]
|
||||
/* idx: 196 */ {0x0, 0x0, 0x300, 0x0}, // [136 137]
|
||||
/* idx: 197 */ {0x0, 0x0, 0xc00, 0x0}, // [138 139]
|
||||
/* idx: 198 */ {0x0, 0x0, 0x3000, 0x0}, // [140 141]
|
||||
/* idx: 199 */ {0x0, 0x0, 0xc000, 0x0}, // [142 143]
|
||||
/* idx: 200 */ {0x0, 0x0, 0x30000, 0x0}, // [144 145]
|
||||
/* idx: 201 */ {0x0, 0x0, 0xc0000, 0x0}, // [146 147]
|
||||
/* idx: 202 */ {0x0, 0x0, 0x300000, 0x0}, // [148 149]
|
||||
/* idx: 203 */ {0x0, 0x0, 0xc00000, 0x0}, // [150 151]
|
||||
/* idx: 204 */ {0x0, 0x0, 0x3000000, 0x0}, // [152 153]
|
||||
/* idx: 205 */ {0x0, 0x0, 0xc000000, 0x0}, // [154 155]
|
||||
/* idx: 206 */ {0x0, 0x0, 0x30000000, 0x0}, // [156 157]
|
||||
/* idx: 207 */ {0x0, 0x0, 0xc0000000, 0x0}, // [158 159]
|
||||
/* idx: 208 */ {0x0, 0x0, 0x300000000, 0x0}, // [160 161]
|
||||
/* idx: 209 */ {0x0, 0x0, 0xc00000000, 0x0}, // [162 163]
|
||||
/* idx: 210 */ {0x0, 0x0, 0x3000000000, 0x0}, // [164 165]
|
||||
/* idx: 211 */ {0x0, 0x0, 0xc000000000, 0x0}, // [166 167]
|
||||
/* idx: 212 */ {0x0, 0x0, 0x30000000000, 0x0}, // [168 169]
|
||||
/* idx: 213 */ {0x0, 0x0, 0xc0000000000, 0x0}, // [170 171]
|
||||
/* idx: 214 */ {0x0, 0x0, 0x300000000000, 0x0}, // [172 173]
|
||||
/* idx: 215 */ {0x0, 0x0, 0xc00000000000, 0x0}, // [174 175]
|
||||
/* idx: 216 */ {0x0, 0x0, 0x3000000000000, 0x0}, // [176 177]
|
||||
/* idx: 217 */ {0x0, 0x0, 0xc000000000000, 0x0}, // [178 179]
|
||||
/* idx: 218 */ {0x0, 0x0, 0x30000000000000, 0x0}, // [180 181]
|
||||
/* idx: 219 */ {0x0, 0x0, 0xc0000000000000, 0x0}, // [182 183]
|
||||
/* idx: 220 */ {0x0, 0x0, 0x300000000000000, 0x0}, // [184 185]
|
||||
/* idx: 221 */ {0x0, 0x0, 0xc00000000000000, 0x0}, // [186 187]
|
||||
/* idx: 222 */ {0x0, 0x0, 0x3000000000000000, 0x0}, // [188 189]
|
||||
/* idx: 223 */ {0x0, 0x0, 0xc000000000000000, 0x0}, // [190 191]
|
||||
/* idx: 224 */ {0x0, 0x0, 0x0, 0x3}, // [192 193]
|
||||
/* idx: 225 */ {0x0, 0x0, 0x0, 0xc}, // [194 195]
|
||||
/* idx: 226 */ {0x0, 0x0, 0x0, 0x30}, // [196 197]
|
||||
/* idx: 227 */ {0x0, 0x0, 0x0, 0xc0}, // [198 199]
|
||||
/* idx: 228 */ {0x0, 0x0, 0x0, 0x300}, // [200 201]
|
||||
/* idx: 229 */ {0x0, 0x0, 0x0, 0xc00}, // [202 203]
|
||||
/* idx: 230 */ {0x0, 0x0, 0x0, 0x3000}, // [204 205]
|
||||
/* idx: 231 */ {0x0, 0x0, 0x0, 0xc000}, // [206 207]
|
||||
/* idx: 232 */ {0x0, 0x0, 0x0, 0x30000}, // [208 209]
|
||||
/* idx: 233 */ {0x0, 0x0, 0x0, 0xc0000}, // [210 211]
|
||||
/* idx: 234 */ {0x0, 0x0, 0x0, 0x300000}, // [212 213]
|
||||
/* idx: 235 */ {0x0, 0x0, 0x0, 0xc00000}, // [214 215]
|
||||
/* idx: 236 */ {0x0, 0x0, 0x0, 0x3000000}, // [216 217]
|
||||
/* idx: 237 */ {0x0, 0x0, 0x0, 0xc000000}, // [218 219]
|
||||
/* idx: 238 */ {0x0, 0x0, 0x0, 0x30000000}, // [220 221]
|
||||
/* idx: 239 */ {0x0, 0x0, 0x0, 0xc0000000}, // [222 223]
|
||||
/* idx: 240 */ {0x0, 0x0, 0x0, 0x300000000}, // [224 225]
|
||||
/* idx: 241 */ {0x0, 0x0, 0x0, 0xc00000000}, // [226 227]
|
||||
/* idx: 242 */ {0x0, 0x0, 0x0, 0x3000000000}, // [228 229]
|
||||
/* idx: 243 */ {0x0, 0x0, 0x0, 0xc000000000}, // [230 231]
|
||||
/* idx: 244 */ {0x0, 0x0, 0x0, 0x30000000000}, // [232 233]
|
||||
/* idx: 245 */ {0x0, 0x0, 0x0, 0xc0000000000}, // [234 235]
|
||||
/* idx: 246 */ {0x0, 0x0, 0x0, 0x300000000000}, // [236 237]
|
||||
/* idx: 247 */ {0x0, 0x0, 0x0, 0xc00000000000}, // [238 239]
|
||||
/* idx: 248 */ {0x0, 0x0, 0x0, 0x3000000000000}, // [240 241]
|
||||
/* idx: 249 */ {0x0, 0x0, 0x0, 0xc000000000000}, // [242 243]
|
||||
/* idx: 250 */ {0x0, 0x0, 0x0, 0x30000000000000}, // [244 245]
|
||||
/* idx: 251 */ {0x0, 0x0, 0x0, 0xc0000000000000}, // [246 247]
|
||||
/* idx: 252 */ {0x0, 0x0, 0x0, 0x300000000000000}, // [248 249]
|
||||
/* idx: 253 */ {0x0, 0x0, 0x0, 0xc00000000000000}, // [250 251]
|
||||
/* idx: 254 */ {0x0, 0x0, 0x0, 0x3000000000000000}, // [252 253]
|
||||
/* idx: 255 */ {0x0, 0x0, 0x0, 0xc000000000000000}, // [254 255]
|
||||
}
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
//go:build generate
|
||||
|
||||
//go:generate go run $GOFILE
|
||||
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package main generates lookup tables for route allocation in the BART routing table.
|
||||
//
|
||||
// This generator creates two precomputed lookup tables (PfxRoutesLookupTbl and
|
||||
// FringeRoutesLookupTbl) that enable fast prefix coverage queries. Each table maps
|
||||
// a base index (1..255) to a bitset containing all more specific indices covered
|
||||
// by that prefix.
|
||||
//
|
||||
// The tables model a complete binary tree where:
|
||||
// - Indices 1..255 represent prefixes up to /7
|
||||
// - Indices 256..511 represent /8 prefixes (stored separately as "fringe")
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/gaissmai/bart/internal/bitset"
|
||||
)
|
||||
|
||||
var (
|
||||
thisFname = os.Getenv("GOFILE")
|
||||
outFname = "allotroutesgenerated.go"
|
||||
)
|
||||
|
||||
var (
|
||||
// prefixAllotTbl holds the bitsets for indices 1..255 (prefixes up to /7).
|
||||
// For each index idx, the bitset contains all descendant indices in the
|
||||
// range 1..255 that are covered by the prefix at idx.
|
||||
prefixAllotTbl [256]bitset.BitSet256
|
||||
|
||||
// fringeAllotTbl holds the bitsets for indices 1..255, but tracks descendants
|
||||
// in the range 256..511 (/8 prefixes). The bitset at index idx contains all
|
||||
// /8 fringe indices (minus 256 offset) covered by the prefix at idx.
|
||||
fringeAllotTbl [256]bitset.BitSet256
|
||||
)
|
||||
|
||||
// data holds the template variables for code generation
|
||||
var data struct {
|
||||
File string
|
||||
PrefixAllotTable string
|
||||
FringeAllotTable string
|
||||
}
|
||||
|
||||
// colored prefix for informational and error messages
|
||||
const (
|
||||
INFO = "\x1b[34mINFO:\x1b[0m"
|
||||
DIE = "\x1b[31mERROR:\x1b[0m"
|
||||
)
|
||||
|
||||
func main() {
|
||||
outFile, err := os.Create(outFname)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %v\n", DIE, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Generate both lookup tables using the stack-based algorithm
|
||||
prefixAllotTbl, fringeAllotTbl = genAllotTables()
|
||||
|
||||
// Populate template data
|
||||
data.File = thisFname
|
||||
data.PrefixAllotTable = asString(prefixAllotTbl)
|
||||
data.FringeAllotTable = asString(fringeAllotTbl)
|
||||
|
||||
// Parse the template ...
|
||||
t := template.Must(template.New("foo").Parse(codeTemplate))
|
||||
|
||||
// ... and execute it with our data
|
||||
err = t.Execute(outFile, data)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %v\n", DIE, err)
|
||||
_ = outFile.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Ensure the file is properly closed before formatting
|
||||
if err := outFile.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %v\n", DIE, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "%s ✓ Generated %s\n", INFO, outFname)
|
||||
|
||||
// Run goimports to organize imports and format the code
|
||||
goimports := exec.Command("goimports", "-w", outFname)
|
||||
fmt.Fprintf(os.Stdout, "%s Running goimports on %s\n", INFO, outFname)
|
||||
if out, err := goimports.CombinedOutput(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %s\n", DIE, string(out))
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "%s ✓ goimports completed\n", INFO)
|
||||
|
||||
// Run gofumpt for additional formatting consistency
|
||||
gofumpt := exec.Command("gofumpt", "-w", outFname)
|
||||
fmt.Fprintf(os.Stdout, "%s Running gofumpt on %s\n", INFO, outFname)
|
||||
if out, err := gofumpt.CombinedOutput(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %s\n", DIE, string(out))
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "%s ✓ gofumpt completed\n", INFO)
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// genAllotTables generates both prefix and fringe allocation lookup tables.
|
||||
//
|
||||
// For each base index idx (1..255), it performs a breadth-first traversal of the
|
||||
// complete binary tree rooted at idx, collecting all descendant indices. The
|
||||
// traversal uses a stack-based approach instead of recursion.
|
||||
//
|
||||
// The algorithm:
|
||||
// 1. Start with the root index idx in the stack
|
||||
// 2. For each index j in the stack:
|
||||
// - If j < 256: add j to prefixAllotTbl[idx]
|
||||
// - If j >= 256: add (j-256) to fringeAllotTbl[idx]
|
||||
// - If j < 256: push children (2*j and 2*j+1) onto the stack
|
||||
// 3. Continue until the stack is exhausted
|
||||
//
|
||||
// This builds a complete binary tree where each node knows all its descendants,
|
||||
// split into two ranges: [1..255] and [256..511].
|
||||
//
|
||||
// Returns the prefix table (indices 1..255) and fringe table (indices 256..511).
|
||||
func genAllotTables() (prefix, fringe [256]bitset.BitSet256) {
|
||||
prefixAllotTbl := [256]bitset.BitSet256{}
|
||||
fringeAllotTbl := [256]bitset.BitSet256{}
|
||||
|
||||
for idx := 1; idx < 256; idx++ {
|
||||
// allot algorithm, stack based instead of recursion
|
||||
stack := make([]int, 0, 512)
|
||||
stack = append(stack, idx)
|
||||
|
||||
for i := 0; i < len(stack); i++ {
|
||||
j := stack[i]
|
||||
if j < 256 {
|
||||
prefixAllotTbl[idx].Set(uint8(j))
|
||||
} else {
|
||||
fringeAllotTbl[idx].Set(uint8(j - 256))
|
||||
}
|
||||
|
||||
// max j is 511, so stop the duplication at 256 and above
|
||||
if j >= 256 {
|
||||
continue
|
||||
}
|
||||
|
||||
// build a complete binary tree
|
||||
// left: j*2
|
||||
// right: (j*2)+1
|
||||
stack = append(stack, j<<1, (j<<1)+1)
|
||||
}
|
||||
}
|
||||
return prefixAllotTbl, fringeAllotTbl
|
||||
}
|
||||
|
||||
// asString converts a lookup table to a formatted Go source string.
|
||||
//
|
||||
// Each entry in the table is formatted as:
|
||||
//
|
||||
// /* idx: N */ BitSet256{...}, // [list of set bits]
|
||||
//
|
||||
// For bitsets with 10 or more set bits, the list is truncated with "..." to
|
||||
// keep the generated code readable. Index 0 is marked as "invalid" since no
|
||||
// valid prefix maps to base index 0 in the BART structure.
|
||||
//
|
||||
// The returned string is ready to be embedded into the generated Go file.
|
||||
func asString(tbl [256]bitset.BitSet256) string {
|
||||
builder := strings.Builder{}
|
||||
|
||||
// Index 0 is invalid (no valid prefix maps to 0)
|
||||
fmt.Fprintf(&builder, " /* idx: %3d */ %#v, // %s\n", 0, tbl[0], "invalid")
|
||||
|
||||
// Format each valid index with its bitset and the list of set bits, e.g.
|
||||
// /* idx: 13 */ {0x204a, 0x0, 0x0, 0x0}, // [1 3 6 13]
|
||||
for idx := 1; idx < 256; idx++ {
|
||||
fmt.Fprintf(&builder, " /* idx: %3d */ %#v, // ", idx, tbl[idx])
|
||||
|
||||
bits := tbl[idx].Bits()
|
||||
if len(bits) < 10 {
|
||||
fmt.Fprintf(&builder, "%v\n", bits)
|
||||
} else {
|
||||
fmt.Fprint(&builder, "[")
|
||||
for _, bit := range bits[:5] {
|
||||
fmt.Fprintf(&builder, "%d, ", bit)
|
||||
}
|
||||
fmt.Fprintln(&builder, "... ]")
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// codeTemplate is the Go source template for the generated allotRoutes_gen.go file.
|
||||
// It embeds the precomputed lookup tables and includes documentation about
|
||||
// their structure and usage.
|
||||
const codeTemplate = `// Code generated by {{.File}}; DO NOT EDIT.
|
||||
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package allot
|
||||
|
||||
// PfxRoutesLookupTbl is a lookup table of BitSet256 entries.
|
||||
//
|
||||
// It maps a baseIndex (1..255) to a bitset that contains all more
|
||||
// specific baseIndices that are covered by the prefix at that index.
|
||||
//
|
||||
// The idea:
|
||||
//
|
||||
// If prefix P has baseIndex idx, then P covers all prefixes in the subtree
|
||||
// rooted at idx. This table enumerates and encodes those indices.
|
||||
//
|
||||
// The table is split:
|
||||
// - PfxRoutesLookupTbl holds prefixes up to /7
|
||||
// - FringeRoutesLookupTbl holds /8 prefixes
|
||||
//
|
||||
// This structure allows for very fast set inclusion checks using simple bitwise AND.
|
||||
//
|
||||
//nolint:gochecknoglobals // Precomputed read‑only table used in hot paths.
|
||||
var PfxRoutesLookupTbl = [256]bitset.BitSet256{
|
||||
{{.PrefixAllotTable}} }
|
||||
|
||||
// FringeRoutesLookupTbl, the second 256 Bits, see also the PfxRoutesLookupTbl for the first 256 Bits
|
||||
// we split 512 bits into 2×256 to leverage BitSet256 optimizations.
|
||||
//
|
||||
//nolint:gochecknoglobals // Precomputed read‑only table used in hot paths.
|
||||
var FringeRoutesLookupTbl = [256]bitset.BitSet256{
|
||||
{{.FringeAllotTable}} }
|
||||
`
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package art summarizes the functions and inverse functions
|
||||
// for mapping between a prefix and a baseIndex.
|
||||
//
|
||||
// can inline OctetToIdx with cost 6
|
||||
// can inline PfxToIdx with cost 10
|
||||
// can inline NetMask with cost 13
|
||||
// can inline PfxBits with cost 21
|
||||
// can inline IdxToPfx with cost 39
|
||||
// can inline IdxToRange with cost 71
|
||||
package art
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// PfxToIdx maps 8bit prefixes to numbers. The prefixes range from 0/0 to 255/7
|
||||
// The return values range from 1 to 255.
|
||||
//
|
||||
// [0x0000_00001 .. 0x1111_1111] = [1 .. 255]
|
||||
//
|
||||
// example: octet/pfxLen: 160/3 = 0b1010_0000/3 => IdxToPfx(160/3) => 13
|
||||
//
|
||||
// 0b1010_0000 => 0b0000_0101
|
||||
// ^^^ >> (8-3) ^^^
|
||||
//
|
||||
// 0b0000_0001 => 0b0000_1000
|
||||
// ^ << 3 ^
|
||||
// + -----------------------
|
||||
// 0b0000_1101 = 13
|
||||
//
|
||||
// ATTENTION: For pfxLen values > 7 the result is undefined!
|
||||
func PfxToIdx(octet, pfxLen uint8) uint8 {
|
||||
// use '|' instead of '+', maybe a little bit faster
|
||||
return octet>>(8-pfxLen) | 1<<pfxLen
|
||||
}
|
||||
|
||||
// OctetToIdx maps octet/8 prefixes from [256..511] => [128..255] to start
|
||||
// with the parent (>>1) in the complete binary tree.
|
||||
//
|
||||
// Same formula as PfxToIdx, but for octet/8 and a shift (>>1).
|
||||
//
|
||||
// octet>>(8-pfxLen) + 1<<pfxLen
|
||||
// octet>>(8-8) + 1<<8
|
||||
// octet + 256
|
||||
//
|
||||
// got to parent idx:
|
||||
// (octet+256)>>1 == octet>>1 + 128
|
||||
func OctetToIdx(octet uint8) uint8 {
|
||||
// use '|' instead of '+', maybe a little bit faster
|
||||
return octet>>1 | 128
|
||||
}
|
||||
|
||||
// IdxToPfx returns the octet and prefix len of baseIdx.
|
||||
// It's the inverse to PfxToIdx.
|
||||
func IdxToPfx(idx uint8) (octet, pfxLen uint8) {
|
||||
if idx == 0 {
|
||||
panic("IdxToPfx: invalid idx 0")
|
||||
}
|
||||
// The prefix length corresponds to the number of leading bits in idx.
|
||||
// bits.Len8 returns the number of bits needed to represent idx as binary,
|
||||
// so we subtract 1 to recover the prefix length (which is always >= 0).
|
||||
// Invariant: idx==0 is invalid
|
||||
|
||||
//nolint:gosec //G115: integer overflow conversion int -> uint8 (gosec)
|
||||
pfxLen = uint8(bits.Len8(idx)) - 1
|
||||
|
||||
// Compute the number of bits to shift back to obtain the original octet.
|
||||
shiftBits := 8 - pfxLen
|
||||
|
||||
// Extract the original prefix bits from idx by masking out the low bits.
|
||||
mask := uint8(0xff) >> shiftBits
|
||||
|
||||
// Shift the masked prefix bits back to their original position.
|
||||
octet = (idx & mask) << shiftBits
|
||||
|
||||
return octet, pfxLen
|
||||
}
|
||||
|
||||
// PfxBits returns the bit position of a prefix represented by a base index at a given trie depth.
|
||||
//
|
||||
// Each trie level represents an 8-bit stride (one octet).
|
||||
// The base index contains enough information to recover the prefix length.
|
||||
// This function returns the full bit offset of the prefix in the address space.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// depth = 2 (i.e. third trie level)
|
||||
// idx = 13 (which encodes a prefix of length 3 bits within that stride)
|
||||
//
|
||||
// => PfxBits = 2*8 + 3 = 19
|
||||
func PfxBits(depth int, idx uint8) uint8 {
|
||||
// bits.Len8(idx) gives the number of significant bits (i.e. prefix length + 1)
|
||||
// subtract 1 to get the actual prefix length used in this stride
|
||||
pfxLenInStride := bits.Len8(idx) - 1
|
||||
|
||||
// Each trie level represents 8 bits (an octet), max depth is 16
|
||||
baseBits := depth << 3 // same as depth * 8
|
||||
|
||||
// Total prefix length in bits, [0..128]
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint8
|
||||
return uint8(baseBits + pfxLenInStride)
|
||||
}
|
||||
|
||||
// IdxToRange returns the first and last octets covered by a base index.
|
||||
//
|
||||
// The base index encodes a prefix of up to 8 bits inside a single stride (octet).
|
||||
// This function computes the numerical start and end of the value range for that prefix.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// - A 0-bit prefix (idx == 1) covers the full range: 0..255
|
||||
//
|
||||
// - A 3-bit prefix like 0b101xxxx (idx == 13) covers: 160..191
|
||||
//
|
||||
// idx := PfxToIdx(0b10100000, 3) // 13
|
||||
// first, last := IdxToRange(13) // 160, 191
|
||||
//
|
||||
// Internally, this function decodes (octet, prefixLength) via IdxToPfx,
|
||||
// then computes the broadcast address (last octet) by masking all bits below the prefix length.
|
||||
func IdxToRange(idx uint8) (first, last uint8) {
|
||||
// Decode the prefix base (octet) and length (number of fixed bits)
|
||||
first, pfxLen := IdxToPfx(idx)
|
||||
|
||||
// Compute the "broadcast" value by filling trailing (host) bits with 1s.
|
||||
// This gives the maximum octet value that still matches the prefix.
|
||||
last = first | ^NetMask(pfxLen)
|
||||
|
||||
return first, last
|
||||
}
|
||||
|
||||
// NetMask returns an 8-bit left-aligned network mask for the given number of prefix bits.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// bits = 0 -> 0b00000000
|
||||
// bits = 1 -> 0b10000000
|
||||
// bits = 2 -> 0b11000000
|
||||
// bits = 3 -> 0b11100000
|
||||
// ...
|
||||
// bits = 8 -> 0b11111111
|
||||
//
|
||||
// This mask is used to extract or identify the fixed (prefix) portion of an octet.
|
||||
// The rightmost (8 - bits) bits are cleared (set to zero), and the upper 'bits' are set to 1.
|
||||
func NetMask(bits uint8) uint8 {
|
||||
if bits > 8 {
|
||||
panic("NetMask: invalid bits > 8")
|
||||
}
|
||||
|
||||
// Use a full 8-bit mask and shift left to clear trailing (host) bits.
|
||||
// Shifts 0..8 on a uint8 are well-defined; safe for bits in [0..8].
|
||||
return ^uint8(0) << (8 - bits)
|
||||
}
|
||||
-326
@@ -1,326 +0,0 @@
|
||||
// Copyright (c) 2024 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package bitset implements bitsets, a mapping
|
||||
// between non-negative integers and boolean values.
|
||||
//
|
||||
// Studied [github.com/bits-and-blooms/bitset] inside out
|
||||
// and rewrote needed parts from scratch for this project.
|
||||
//
|
||||
// This implementation is smaller and faster as the more
|
||||
// general [github.com/bits-and-blooms/bitset].
|
||||
//
|
||||
// All functions can be inlined!
|
||||
//
|
||||
// can inline BitSet.Set with cost 63
|
||||
// can inline BitSet.Clear with cost 24
|
||||
// can inline BitSet.Test with cost 26
|
||||
// can inline BitSet.Rank0 with cost 66
|
||||
// can inline BitSet.Clone with cost 7
|
||||
// can inline BitSet.Compact with cost 35
|
||||
// can inline BitSet.FirstSet with cost 25
|
||||
// can inline BitSet.NextSet with cost 71
|
||||
// can inline BitSet.AsSlice with cost 50
|
||||
// can inline BitSet.All with cost 62
|
||||
// can inline BitSet.IntersectsAny with cost 42
|
||||
// can inline BitSet.IntersectionTop with cost 56
|
||||
// can inline BitSet.IntersectionCardinality with cost 35
|
||||
// can inline (*BitSet).InPlaceIntersection with cost 71
|
||||
// can inline (*BitSet).InPlaceUnion with cost 77
|
||||
// can inline BitSet.Size with cost 16
|
||||
// can inline popcount with cost 12
|
||||
// can inline popcountAnd with cost 30
|
||||
package bitset
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// A BitSet is a slice of words. This is an internal package
|
||||
// with a wide open public API.
|
||||
type BitSet []uint64
|
||||
|
||||
// xIdx calculates the index of i in a []uint64
|
||||
// func wIdx(i uint) int {
|
||||
// return int(i >> 6) // like (i / 64) but faster
|
||||
// }
|
||||
|
||||
// bIdx calculates the index of i in a `uint64`
|
||||
// func bIdx(i uint) uint {
|
||||
// return i & 63 // like (i % 64) but faster
|
||||
// }
|
||||
//
|
||||
// just as an explanation of the expressions,
|
||||
//
|
||||
// i>>6 or i<<6 and i&63
|
||||
//
|
||||
// not factored out as functions to make most of the methods
|
||||
// inlineable with minimal costs.
|
||||
|
||||
// Set bit i to 1, the capacity of the bitset is increased accordingly.
|
||||
func (b BitSet) Set(i uint) BitSet {
|
||||
// grow?
|
||||
if i >= uint(len(b)<<6) {
|
||||
words := int((i + 64) >> 6)
|
||||
switch {
|
||||
case b == nil:
|
||||
b = make([]uint64, words)
|
||||
case cap(b) >= words:
|
||||
b = b[:words]
|
||||
default:
|
||||
// be exact, don't use append!
|
||||
// max 512 prefixes/node (8*uint64), and a cache line has 64 Bytes
|
||||
newset := make([]uint64, words)
|
||||
copy(newset, b)
|
||||
b = newset
|
||||
}
|
||||
}
|
||||
|
||||
b[i>>6] |= 1 << (i & 63)
|
||||
return b
|
||||
}
|
||||
|
||||
// Clear bit i to 0.
|
||||
func (b BitSet) Clear(i uint) BitSet {
|
||||
if x := int(i >> 6); x < len(b) {
|
||||
b[x] &^= 1 << (i & 63)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Test if bit i is set.
|
||||
func (b BitSet) Test(i uint) (ok bool) {
|
||||
if x := int(i >> 6); x < len(b) {
|
||||
return b[x]&(1<<(i&63)) != 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Clone this BitSet, returning a new BitSet that has the same bits set.
|
||||
func (b BitSet) Clone() BitSet {
|
||||
return append(b[:0:0], b...)
|
||||
}
|
||||
|
||||
// Compact, preserve all set bits, while minimizing memory usage.
|
||||
func (b BitSet) Compact() BitSet {
|
||||
last := len(b) - 1
|
||||
|
||||
// find last word with at least one bit set.
|
||||
for ; last >= 0; last-- {
|
||||
if b[last] != 0 {
|
||||
b = b[: last+1 : last+1]
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
// BitSet was empty, shrink to nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// FirstSet returns the first bit set along with an ok code.
|
||||
func (b BitSet) FirstSet() (uint, bool) {
|
||||
for x, word := range b {
|
||||
if word != 0 {
|
||||
return uint(x<<6 + bits.TrailingZeros64(word)), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// NextSet returns the next bit set from the specified index,
|
||||
// including possibly the current index along with an ok code.
|
||||
func (b BitSet) NextSet(i uint) (uint, bool) {
|
||||
x := int(i >> 6)
|
||||
if x >= len(b) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// process the first (maybe partial) word
|
||||
first := b[x] >> (i & 63) // i % 64
|
||||
if first != 0 {
|
||||
return i + uint(bits.TrailingZeros64(first)), true
|
||||
}
|
||||
|
||||
// process the following words until next bit is set
|
||||
// x < len(b), no out-of-bounds panic in following slice expression
|
||||
x++
|
||||
for j, word := range b[x:] {
|
||||
if word != 0 {
|
||||
return uint((x+j)<<6 + bits.TrailingZeros64(word)), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// AsSlice returns all set bits as slice of uint without
|
||||
// heap allocations.
|
||||
//
|
||||
// This is faster than All, but also more dangerous,
|
||||
// it panics if the capacity of buf is < b.Size()
|
||||
func (b BitSet) AsSlice(buf []uint) []uint {
|
||||
buf = buf[:cap(buf)] // len = cap
|
||||
|
||||
size := 0
|
||||
for idx, word := range b {
|
||||
for ; word != 0; size++ {
|
||||
// panics if capacity of buf is exceeded.
|
||||
buf[size] = uint(idx<<6 + bits.TrailingZeros64(word))
|
||||
|
||||
// clear the rightmost set bit
|
||||
word &= word - 1
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:size]
|
||||
return buf
|
||||
}
|
||||
|
||||
// All returns all set bits. This is simpler but slower than AsSlice.
|
||||
func (b BitSet) All() []uint {
|
||||
buf := make([]uint, b.Size())
|
||||
|
||||
slot := 0
|
||||
for idx, word := range b {
|
||||
for word != 0 {
|
||||
buf[slot] = uint(idx<<6 + bits.TrailingZeros64(word))
|
||||
slot++
|
||||
|
||||
// clear the rightmost set bit
|
||||
word &= word - 1
|
||||
}
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// IntersectsAny returns true if the intersection of base set with the compare set
|
||||
// is not the empty set.
|
||||
func (b BitSet) IntersectsAny(c BitSet) bool {
|
||||
i := min(len(b), len(c)) - 1
|
||||
// bounds check eliminated (BCE)
|
||||
for ; i >= 0 && i < len(b) && i < len(c); i-- {
|
||||
if b[i]&c[i] != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IntersectionTop computes the intersection of base set with the compare set.
|
||||
// If the result set isn't empty, it returns the top most set bit and true.
|
||||
func (b BitSet) IntersectionTop(c BitSet) (top uint, ok bool) {
|
||||
i := min(len(b), len(c)) - 1
|
||||
// bounds check eliminated (BCE)
|
||||
for ; i >= 0 && i < len(b) && i < len(c); i-- {
|
||||
if word := b[i] & c[i]; word != 0 {
|
||||
return uint(i<<6+bits.Len64(word)) - 1, true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IntersectionCardinality computes the popcount of the intersection.
|
||||
func (b BitSet) IntersectionCardinality(c BitSet) int {
|
||||
return popcntAnd(b, c)
|
||||
}
|
||||
|
||||
// InPlaceIntersection overwrites and computes the intersection of
|
||||
// base set with the compare set. This is the BitSet equivalent of & (and).
|
||||
// If len(c) > len(b), new memory is allocated.
|
||||
func (b *BitSet) InPlaceIntersection(c BitSet) {
|
||||
// bounds check eliminated, range until minLen(b,c)
|
||||
for i := 0; i < len(*b) && i < len(c); i++ {
|
||||
(*b)[i] &= c[i]
|
||||
}
|
||||
|
||||
// b >= c
|
||||
if len(*b) >= len(c) {
|
||||
// bounds check eliminated
|
||||
for i := len(c); i < len(*b); i++ {
|
||||
(*b)[i] = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// b < c
|
||||
newset := make([]uint64, len(c))
|
||||
copy(newset, *b)
|
||||
*b = newset
|
||||
}
|
||||
|
||||
// InPlaceUnion creates the destructive union of base set with compare set.
|
||||
// This is the BitSet equivalent of | (or).
|
||||
// If len(c) > len(b), new memory is allocated.
|
||||
func (b *BitSet) InPlaceUnion(c BitSet) {
|
||||
// b >= c
|
||||
if len(*b) >= len(c) {
|
||||
// bounds check eliminated
|
||||
for i := 0; i < len(*b) && i < len(c); i++ {
|
||||
(*b)[i] |= c[i]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// b < c
|
||||
newset := make([]uint64, len(c))
|
||||
copy(newset, *b)
|
||||
*b = newset
|
||||
|
||||
// bounds check eliminated
|
||||
for i := 0; i < len(*b) && i < len(c); i++ {
|
||||
(*b)[i] |= c[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Size (number of set bits).
|
||||
func (b BitSet) Size() int {
|
||||
return popcntSlice(b)
|
||||
}
|
||||
|
||||
// Rank0 is equal to Rank(i) - 1
|
||||
//
|
||||
// With inlined popcount to make Rank0 itself inlineable.
|
||||
func (b BitSet) Rank0(i uint) (rnk int) {
|
||||
// Rank count is inclusive
|
||||
i++
|
||||
|
||||
if wordIdx := int(i >> 6); wordIdx >= len(b) {
|
||||
// inlined popcount, whole slice
|
||||
for _, x := range b {
|
||||
rnk += bits.OnesCount64(x)
|
||||
}
|
||||
} else {
|
||||
// inlined popcount, partial slice ...
|
||||
for _, x := range b[:wordIdx] {
|
||||
rnk += bits.OnesCount64(x)
|
||||
}
|
||||
|
||||
// ... plus partial word?
|
||||
if bitsIdx := i & 63; bitsIdx != 0 {
|
||||
rnk += bits.OnesCount64(b[wordIdx] << (64 - bitsIdx))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// correct for offset by one
|
||||
return rnk - 1
|
||||
}
|
||||
|
||||
// popcntSlice
|
||||
func popcntSlice(s []uint64) (cnt int) {
|
||||
for _, x := range s {
|
||||
// count all the bits set in slice.
|
||||
cnt += bits.OnesCount64(x)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// popcntAnd
|
||||
func popcntAnd(s, m []uint64) (cnt int) {
|
||||
for j := 0; j < len(s) && j < len(m); j++ {
|
||||
// words are bitwise & followed by popcount.
|
||||
cnt += bits.OnesCount64(s[j] & m[j])
|
||||
}
|
||||
return
|
||||
}
|
||||
+595
@@ -0,0 +1,595 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package bitset provides a compact and efficient implementation of a fixed-length
|
||||
// bitset for the range [0..255].
|
||||
//
|
||||
// This implementation is optimized for internal use in a compressed routing trie
|
||||
// and prioritizes minimal allocation, performance, and inlining. It supports
|
||||
// constant-time set/test operations, iteration over set bits, ranking, and masked
|
||||
// intersections.
|
||||
//
|
||||
// Internally, the bitset is represented by four uint64 words, providing
|
||||
// fast bit-level access through direct indexing and hardware-accelerated primitives.
|
||||
//
|
||||
// If Go eventually supports SIMD intrinsics, this can be further optimized.
|
||||
//
|
||||
// For external consumers, the API intentionally avoids dynamic allocation except
|
||||
// when explicitly requested (via Bits()).
|
||||
package bitset
|
||||
|
||||
// can inline (*BitSet256).AsSlice with cost 42
|
||||
// can inline (*BitSet256).Bits with cost 47
|
||||
// can inline (*BitSet256).Clear with cost 12
|
||||
// can inline (*BitSet256).FirstSet with cost 79
|
||||
// can inline (*BitSet256).Intersects with cost 48
|
||||
// can inline (*BitSet256).Intersection with cost 53
|
||||
// can inline (*BitSet256).IntersectionTop with cost 67
|
||||
// can inline (*BitSet256).IsEmpty with cost 22
|
||||
// can inline (*BitSet256).LastSet with cost 75
|
||||
// can inline (*BitSet256).NextSet with cost 65
|
||||
// can inline (*BitSet256).Rank with cost 57
|
||||
// can inline (*BitSet256).Set with cost 12
|
||||
// can inline (*BitSet256).Size with cost 33
|
||||
// can inline (*BitSet256).Test with cost 15
|
||||
// can inline (*BitSet256).Union with cost 36
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// wordIdx calculates the wordIndex of bit i in a []uint64
|
||||
// func wordIdx(i uint) int {
|
||||
// return int(i >> 6) // like (i / 64) but faster
|
||||
// }
|
||||
|
||||
// bitIdx calculates the bitIndex of i in an `uint64`
|
||||
// func bitIdx(i uint) uint {
|
||||
// return i & 63 // like (i % 64) but mostly faster
|
||||
// }
|
||||
//
|
||||
// just as an explanation of the expressions,
|
||||
//
|
||||
// i>>6 or i<<6 and i&63
|
||||
//
|
||||
// not factored out as functions to make most of the methods
|
||||
// inlineable with minimal costs.
|
||||
|
||||
// BitSet256 represents a fixed-size bitset for the range [0..255],
|
||||
// stored as four uint64 words (256 bits total).
|
||||
type BitSet256 [4]uint64
|
||||
|
||||
// Set sets the bit at position bit (0..255).
|
||||
func (b *BitSet256) Set(bit uint8) {
|
||||
b[bit>>6] |= 1 << (bit & 63)
|
||||
}
|
||||
|
||||
// Clear clears the bit at position bit (0..255).
|
||||
func (b *BitSet256) Clear(bit uint8) {
|
||||
b[bit>>6] &^= 1 << (bit & 63)
|
||||
}
|
||||
|
||||
// Test reports whether the bit at position bit (0..255) is set.
|
||||
func (b *BitSet256) Test(bit uint8) (ok bool) {
|
||||
return b[bit>>6]&(1<<(bit&63)) != 0
|
||||
}
|
||||
|
||||
// FirstSet returns the index of the lowest (first) bit that is set in the BitSet256.
|
||||
//
|
||||
// It searches the 256-bit set in ascending order and returns the position of the
|
||||
// first bit with value 1. If at least one bit is set, ok is true.
|
||||
// If no bits are set, ok is false and first is undefined.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var bs BitSet256
|
||||
// bs.Set(17)
|
||||
// bs.Set(42)
|
||||
// bs.Set(130)
|
||||
// bs.Set(255)
|
||||
// index, ok := bs.FirstSet() // index == 17, ok == true
|
||||
//
|
||||
// Note: This implementation avoids a for loop for optimal speed.
|
||||
// On modern CPUs, computing all four trailing-zero counts up front allows
|
||||
// the CPU to parallelize these operations internally (pipelining), avoiding
|
||||
// branch misprediction and maximizing instruction throughput. This technique
|
||||
// is especially effective for bitsets with known, fixed word count.
|
||||
func (b *BitSet256) FirstSet() (first uint8, ok bool) {
|
||||
// optimized for pipelining, can still inline with cost 79
|
||||
x0 := bits.TrailingZeros64(b[0])
|
||||
x1 := bits.TrailingZeros64(b[1])
|
||||
x2 := bits.TrailingZeros64(b[2])
|
||||
x3 := bits.TrailingZeros64(b[3])
|
||||
|
||||
if x0 != 64 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(x0), true
|
||||
}
|
||||
if x1 != 64 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(x1 + 64), true
|
||||
}
|
||||
if x2 != 64 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(x2 + 128), true
|
||||
}
|
||||
if x3 != 64 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(x3 + 192), true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NextSet returns the index of the next set bit that is greater than or equal to bit.
|
||||
//
|
||||
// If such a bit exists, it returns its index as next and ok=true.
|
||||
// Otherwise, ok is false and next is undefined.
|
||||
//
|
||||
// The search starts at the given bit index and proceeds toward higher indices, scanning
|
||||
// across all four 64-bit segments of the internal bitset representation.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// b.Set(5)
|
||||
// b.Set(130)
|
||||
// b.NextSet(0) -> 5, true
|
||||
// b.NextSet(5) -> 5, true
|
||||
// b.NextSet(6) -> 130, true
|
||||
// b.NextSet(200) -> 0, false
|
||||
func (b *BitSet256) NextSet(bit uint8) (next uint8, ok bool) {
|
||||
wIdx := int(bit >> 6)
|
||||
|
||||
// process the first (maybe partial) word
|
||||
first := b[wIdx] >> (bit & 63)
|
||||
if first != 0 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return bit + uint8(bits.TrailingZeros64(first)), true
|
||||
}
|
||||
|
||||
// process the following words until next bit is set
|
||||
for wIdx++; wIdx < 4; wIdx++ {
|
||||
if next := b[wIdx]; next != 0 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(wIdx<<6 + bits.TrailingZeros64(next)), true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LastSet returns the index of the highest (last) bit that is set in the BitSet256.
|
||||
//
|
||||
// It searches the bitset in descending order and returns the position of the
|
||||
// first bit (top bit) with value 1. If at least one bit is set, ok is true.
|
||||
// If no bits are set, ok is false and last is 0.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var bs BitSet256
|
||||
// bs.Set(2)
|
||||
// bs.Set(130)
|
||||
// bs.Set(214)
|
||||
// index, ok := bs.LastSet() // index == 214, ok == true
|
||||
func (b *BitSet256) LastSet() (last uint8, ok bool) {
|
||||
// optimized by unrolling the loop.
|
||||
// This enables compiler inlining and is ~20% faster.
|
||||
if b[3] != 0 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(bits.Len64(b[3]) + 191), true
|
||||
}
|
||||
if b[2] != 0 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(bits.Len64(b[2]) + 127), true
|
||||
}
|
||||
if b[1] != 0 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(bits.Len64(b[1]) + 63), true
|
||||
}
|
||||
if b[0] != 0 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
return uint8(bits.Len64(b[0]) - 1), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// AsSlice extracts the indices of all set bits in the BitSet256, returning them
|
||||
// as uint8 values in strictly ascending order.
|
||||
//
|
||||
// Performance Considerations:
|
||||
// To guarantee zero heap allocations and enable compiler inlining, the caller must
|
||||
// provide a pointer to a 256-byte array (`buf`) as backing storage. The method
|
||||
// populates this array in-place and returns a sliced view (`[]uint8`) tailored to
|
||||
// the actual number of set bits.
|
||||
//
|
||||
// Safety and Lifecycle:
|
||||
// The returned slice directly shares the underlying storage of `buf` and is only
|
||||
// valid until `buf` is modified or reused. This pattern is highly recommended for
|
||||
// hot paths and performance-critical loops where heap churn must be avoided.
|
||||
func (b *BitSet256) AsSlice(buf *[256]uint8) []uint8 {
|
||||
size := 0
|
||||
for wIdx, word := range b {
|
||||
for ; word != 0; size++ {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint
|
||||
buf[size] = uint8(wIdx<<6 + bits.TrailingZeros64(word))
|
||||
word &= word - 1 // clear the rightmost set bit
|
||||
}
|
||||
}
|
||||
|
||||
// tailor to the actual number of set bits
|
||||
return buf[:size]
|
||||
}
|
||||
|
||||
// Bits returns a slice containing the indices of all set bits in strictly
|
||||
// ascending order as uint8 values.
|
||||
//
|
||||
// Performance Considerations:
|
||||
// Unlike [AsSlice], this method dynamically allocates a new slice on the
|
||||
// heap to store the result. It is designed for convenience and APIs where the lifecycle
|
||||
// of the returned slice needs to outlive the immediate caller's stack frame.
|
||||
//
|
||||
// Usage Guidance:
|
||||
// Use Bits when convenience is preferred over raw performance, or when the result
|
||||
// must be returned across boundaries where stack-allocated buffers cannot safely escape.
|
||||
// For high-throughput or allocation-free processing, prefer [AsSlice].
|
||||
func (b *BitSet256) Bits() []uint8 {
|
||||
return b.AsSlice(&[256]uint8{})
|
||||
}
|
||||
|
||||
// IntersectionTop computes the intersection of the receiver with c
|
||||
// and returns the highest (top-most) set bit of the result.
|
||||
// If the intersection is non-empty, it returns the top bit index and true.
|
||||
// If the intersection is empty, ok is false and top is 0.
|
||||
func (b *BitSet256) IntersectionTop(c *BitSet256) (top uint8, ok bool) {
|
||||
// optimized by unrolling the first word check.
|
||||
// This enables compiler inlining and is ~15% faster.
|
||||
if w := b[3] & c[3]; w != 0 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint8
|
||||
return uint8(191 + bits.Len64(w)), true
|
||||
}
|
||||
for wIdx := 2; wIdx >= 0; wIdx-- {
|
||||
if w := b[wIdx] & c[wIdx]; w != 0 {
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint8
|
||||
return uint8(wIdx<<6 + bits.Len64(w) - 1), true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Rank returns the number of bits set (i.e., value 1) in the BitSet256
|
||||
// up to and including the provided index.
|
||||
//
|
||||
// The rank is computed efficiently using precomputed bitmasks (rankMask),
|
||||
// which mask out all bits above the index. For example:
|
||||
//
|
||||
// b.Set(3)
|
||||
// b.Set(5)
|
||||
// b.Set(120)
|
||||
// b.Rank(5) -> 2 // only bits 3 and 5 are ≤ 5
|
||||
// b.Rank(119) -> 2 // only bits 3 and 5 are ≤ 119
|
||||
// b.Rank(120) -> 3 // bit 120 is included here
|
||||
//
|
||||
// Rank is particularly useful in prefix trees, indexing schemes,
|
||||
// and data compression techniques where ordinal positions matter.
|
||||
//
|
||||
// Internally, the function performs four bitwise-and operations
|
||||
// between the bitset words and a precomputed mask covering bits 0..idx,
|
||||
// followed by popcount operations (via bits.OnesCount64).
|
||||
//
|
||||
// This avoids dynamic mask construction and enables branch-free, highly
|
||||
// predictable performance.
|
||||
func (b *BitSet256) Rank(idx uint8) (rnk int) {
|
||||
rnk += bits.OnesCount64(b[0] & rankMask[idx][0])
|
||||
rnk += bits.OnesCount64(b[1] & rankMask[idx][1])
|
||||
rnk += bits.OnesCount64(b[2] & rankMask[idx][2])
|
||||
rnk += bits.OnesCount64(b[3] & rankMask[idx][3])
|
||||
return
|
||||
}
|
||||
|
||||
// IsEmpty reports whether all 256 bits are zero.
|
||||
func (b *BitSet256) IsEmpty() bool {
|
||||
return b[0]|b[1]|b[2]|b[3] == 0
|
||||
}
|
||||
|
||||
// Intersects reports whether the receiver and c have at least one bit in common.
|
||||
func (b *BitSet256) Intersects(c *BitSet256) bool {
|
||||
return b[0]&c[0] != 0 ||
|
||||
b[1]&c[1] != 0 ||
|
||||
b[2]&c[2] != 0 ||
|
||||
b[3]&c[3] != 0
|
||||
}
|
||||
|
||||
// Intersection returns a new BitSet256 containing only the bits
|
||||
// that are set in both the receiver and c (bitwise AND).
|
||||
func (b *BitSet256) Intersection(c *BitSet256) (bs BitSet256) {
|
||||
bs[0] = b[0] & c[0]
|
||||
bs[1] = b[1] & c[1]
|
||||
bs[2] = b[2] & c[2]
|
||||
bs[3] = b[3] & c[3]
|
||||
return
|
||||
}
|
||||
|
||||
// Union sets all bits in the receiver that are set in c (in-place bitwise OR).
|
||||
func (b *BitSet256) Union(c *BitSet256) {
|
||||
b[0] |= c[0]
|
||||
b[1] |= c[1]
|
||||
b[2] |= c[2]
|
||||
b[3] |= c[3]
|
||||
}
|
||||
|
||||
// Size returns the population count, i.e. the number of set bits.
|
||||
func (b *BitSet256) Size() (cnt int) {
|
||||
cnt += bits.OnesCount64(b[0])
|
||||
cnt += bits.OnesCount64(b[1])
|
||||
cnt += bits.OnesCount64(b[2])
|
||||
cnt += bits.OnesCount64(b[3])
|
||||
return
|
||||
}
|
||||
|
||||
// rankMask is a table of bitmasks with all bits set to 1 up to and including a given bit position.
|
||||
// It is used by BitSet256.Rank() to perform efficient popcount operations.
|
||||
//
|
||||
// This approach trades ~8KB of static memory for zero-allocation,
|
||||
// fully branch-free, and cache-friendly Rank() calls with constant runtime.
|
||||
//
|
||||
// Used internally by the trie for position counting, CIDR ordering,
|
||||
// and fast range-limited bit population counts.
|
||||
//
|
||||
//nolint:gochecknoglobals // Precomputed read‑only table used in hot paths.
|
||||
var rankMask = [256]BitSet256{
|
||||
/* 0 */ {0x1, 0x0, 0x0, 0x0},
|
||||
/* 1 */ {0x3, 0x0, 0x0, 0x0},
|
||||
/* 2 */ {0x7, 0x0, 0x0, 0x0},
|
||||
/* 3 */ {0xf, 0x0, 0x0, 0x0},
|
||||
/* 4 */ {0x1f, 0x0, 0x0, 0x0},
|
||||
/* 5 */ {0x3f, 0x0, 0x0, 0x0},
|
||||
/* 6 */ {0x7f, 0x0, 0x0, 0x0},
|
||||
/* 7 */ {0xff, 0x0, 0x0, 0x0},
|
||||
/* 8 */ {0x1ff, 0x0, 0x0, 0x0},
|
||||
/* 9 */ {0x3ff, 0x0, 0x0, 0x0},
|
||||
/* 10 */ {0x7ff, 0x0, 0x0, 0x0},
|
||||
/* 11 */ {0xfff, 0x0, 0x0, 0x0},
|
||||
/* 12 */ {0x1fff, 0x0, 0x0, 0x0},
|
||||
/* 13 */ {0x3fff, 0x0, 0x0, 0x0},
|
||||
/* 14 */ {0x7fff, 0x0, 0x0, 0x0},
|
||||
/* 15 */ {0xffff, 0x0, 0x0, 0x0},
|
||||
/* 16 */ {0x1ffff, 0x0, 0x0, 0x0},
|
||||
/* 17 */ {0x3ffff, 0x0, 0x0, 0x0},
|
||||
/* 18 */ {0x7ffff, 0x0, 0x0, 0x0},
|
||||
/* 19 */ {0xfffff, 0x0, 0x0, 0x0},
|
||||
/* 20 */ {0x1fffff, 0x0, 0x0, 0x0},
|
||||
/* 21 */ {0x3fffff, 0x0, 0x0, 0x0},
|
||||
/* 22 */ {0x7fffff, 0x0, 0x0, 0x0},
|
||||
/* 23 */ {0xffffff, 0x0, 0x0, 0x0},
|
||||
/* 24 */ {0x1ffffff, 0x0, 0x0, 0x0},
|
||||
/* 25 */ {0x3ffffff, 0x0, 0x0, 0x0},
|
||||
/* 26 */ {0x7ffffff, 0x0, 0x0, 0x0},
|
||||
/* 27 */ {0xfffffff, 0x0, 0x0, 0x0},
|
||||
/* 28 */ {0x1fffffff, 0x0, 0x0, 0x0},
|
||||
/* 29 */ {0x3fffffff, 0x0, 0x0, 0x0},
|
||||
/* 30 */ {0x7fffffff, 0x0, 0x0, 0x0},
|
||||
/* 31 */ {0xffffffff, 0x0, 0x0, 0x0},
|
||||
/* 32 */ {0x1ffffffff, 0x0, 0x0, 0x0},
|
||||
/* 33 */ {0x3ffffffff, 0x0, 0x0, 0x0},
|
||||
/* 34 */ {0x7ffffffff, 0x0, 0x0, 0x0},
|
||||
/* 35 */ {0xfffffffff, 0x0, 0x0, 0x0},
|
||||
/* 36 */ {0x1fffffffff, 0x0, 0x0, 0x0},
|
||||
/* 37 */ {0x3fffffffff, 0x0, 0x0, 0x0},
|
||||
/* 38 */ {0x7fffffffff, 0x0, 0x0, 0x0},
|
||||
/* 39 */ {0xffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 40 */ {0x1ffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 41 */ {0x3ffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 42 */ {0x7ffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 43 */ {0xfffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 44 */ {0x1fffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 45 */ {0x3fffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 46 */ {0x7fffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 47 */ {0xffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 48 */ {0x1ffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 49 */ {0x3ffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 50 */ {0x7ffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 51 */ {0xfffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 52 */ {0x1fffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 53 */ {0x3fffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 54 */ {0x7fffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 55 */ {0xffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 56 */ {0x1ffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 57 */ {0x3ffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 58 */ {0x7ffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 59 */ {0xfffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 60 */ {0x1fffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 61 */ {0x3fffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 62 */ {0x7fffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 63 */ {0xffffffffffffffff, 0x0, 0x0, 0x0},
|
||||
/* 64 */ {0xffffffffffffffff, 0x1, 0x0, 0x0},
|
||||
/* 65 */ {0xffffffffffffffff, 0x3, 0x0, 0x0},
|
||||
/* 66 */ {0xffffffffffffffff, 0x7, 0x0, 0x0},
|
||||
/* 67 */ {0xffffffffffffffff, 0xf, 0x0, 0x0},
|
||||
/* 68 */ {0xffffffffffffffff, 0x1f, 0x0, 0x0},
|
||||
/* 69 */ {0xffffffffffffffff, 0x3f, 0x0, 0x0},
|
||||
/* 70 */ {0xffffffffffffffff, 0x7f, 0x0, 0x0},
|
||||
/* 71 */ {0xffffffffffffffff, 0xff, 0x0, 0x0},
|
||||
/* 72 */ {0xffffffffffffffff, 0x1ff, 0x0, 0x0},
|
||||
/* 73 */ {0xffffffffffffffff, 0x3ff, 0x0, 0x0},
|
||||
/* 74 */ {0xffffffffffffffff, 0x7ff, 0x0, 0x0},
|
||||
/* 75 */ {0xffffffffffffffff, 0xfff, 0x0, 0x0},
|
||||
/* 76 */ {0xffffffffffffffff, 0x1fff, 0x0, 0x0},
|
||||
/* 77 */ {0xffffffffffffffff, 0x3fff, 0x0, 0x0},
|
||||
/* 78 */ {0xffffffffffffffff, 0x7fff, 0x0, 0x0},
|
||||
/* 79 */ {0xffffffffffffffff, 0xffff, 0x0, 0x0},
|
||||
/* 80 */ {0xffffffffffffffff, 0x1ffff, 0x0, 0x0},
|
||||
/* 81 */ {0xffffffffffffffff, 0x3ffff, 0x0, 0x0},
|
||||
/* 82 */ {0xffffffffffffffff, 0x7ffff, 0x0, 0x0},
|
||||
/* 83 */ {0xffffffffffffffff, 0xfffff, 0x0, 0x0},
|
||||
/* 84 */ {0xffffffffffffffff, 0x1fffff, 0x0, 0x0},
|
||||
/* 85 */ {0xffffffffffffffff, 0x3fffff, 0x0, 0x0},
|
||||
/* 86 */ {0xffffffffffffffff, 0x7fffff, 0x0, 0x0},
|
||||
/* 87 */ {0xffffffffffffffff, 0xffffff, 0x0, 0x0},
|
||||
/* 88 */ {0xffffffffffffffff, 0x1ffffff, 0x0, 0x0},
|
||||
/* 89 */ {0xffffffffffffffff, 0x3ffffff, 0x0, 0x0},
|
||||
/* 90 */ {0xffffffffffffffff, 0x7ffffff, 0x0, 0x0},
|
||||
/* 91 */ {0xffffffffffffffff, 0xfffffff, 0x0, 0x0},
|
||||
/* 92 */ {0xffffffffffffffff, 0x1fffffff, 0x0, 0x0},
|
||||
/* 93 */ {0xffffffffffffffff, 0x3fffffff, 0x0, 0x0},
|
||||
/* 94 */ {0xffffffffffffffff, 0x7fffffff, 0x0, 0x0},
|
||||
/* 95 */ {0xffffffffffffffff, 0xffffffff, 0x0, 0x0},
|
||||
/* 96 */ {0xffffffffffffffff, 0x1ffffffff, 0x0, 0x0},
|
||||
/* 97 */ {0xffffffffffffffff, 0x3ffffffff, 0x0, 0x0},
|
||||
/* 98 */ {0xffffffffffffffff, 0x7ffffffff, 0x0, 0x0},
|
||||
/* 99 */ {0xffffffffffffffff, 0xfffffffff, 0x0, 0x0},
|
||||
/* 100 */ {0xffffffffffffffff, 0x1fffffffff, 0x0, 0x0},
|
||||
/* 101 */ {0xffffffffffffffff, 0x3fffffffff, 0x0, 0x0},
|
||||
/* 102 */ {0xffffffffffffffff, 0x7fffffffff, 0x0, 0x0},
|
||||
/* 103 */ {0xffffffffffffffff, 0xffffffffff, 0x0, 0x0},
|
||||
/* 104 */ {0xffffffffffffffff, 0x1ffffffffff, 0x0, 0x0},
|
||||
/* 105 */ {0xffffffffffffffff, 0x3ffffffffff, 0x0, 0x0},
|
||||
/* 106 */ {0xffffffffffffffff, 0x7ffffffffff, 0x0, 0x0},
|
||||
/* 107 */ {0xffffffffffffffff, 0xfffffffffff, 0x0, 0x0},
|
||||
/* 108 */ {0xffffffffffffffff, 0x1fffffffffff, 0x0, 0x0},
|
||||
/* 109 */ {0xffffffffffffffff, 0x3fffffffffff, 0x0, 0x0},
|
||||
/* 110 */ {0xffffffffffffffff, 0x7fffffffffff, 0x0, 0x0},
|
||||
/* 111 */ {0xffffffffffffffff, 0xffffffffffff, 0x0, 0x0},
|
||||
/* 112 */ {0xffffffffffffffff, 0x1ffffffffffff, 0x0, 0x0},
|
||||
/* 113 */ {0xffffffffffffffff, 0x3ffffffffffff, 0x0, 0x0},
|
||||
/* 114 */ {0xffffffffffffffff, 0x7ffffffffffff, 0x0, 0x0},
|
||||
/* 115 */ {0xffffffffffffffff, 0xfffffffffffff, 0x0, 0x0},
|
||||
/* 116 */ {0xffffffffffffffff, 0x1fffffffffffff, 0x0, 0x0},
|
||||
/* 117 */ {0xffffffffffffffff, 0x3fffffffffffff, 0x0, 0x0},
|
||||
/* 118 */ {0xffffffffffffffff, 0x7fffffffffffff, 0x0, 0x0},
|
||||
/* 119 */ {0xffffffffffffffff, 0xffffffffffffff, 0x0, 0x0},
|
||||
/* 120 */ {0xffffffffffffffff, 0x1ffffffffffffff, 0x0, 0x0},
|
||||
/* 121 */ {0xffffffffffffffff, 0x3ffffffffffffff, 0x0, 0x0},
|
||||
/* 122 */ {0xffffffffffffffff, 0x7ffffffffffffff, 0x0, 0x0},
|
||||
/* 123 */ {0xffffffffffffffff, 0xfffffffffffffff, 0x0, 0x0},
|
||||
/* 124 */ {0xffffffffffffffff, 0x1fffffffffffffff, 0x0, 0x0},
|
||||
/* 125 */ {0xffffffffffffffff, 0x3fffffffffffffff, 0x0, 0x0},
|
||||
/* 126 */ {0xffffffffffffffff, 0x7fffffffffffffff, 0x0, 0x0},
|
||||
/* 127 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0},
|
||||
/* 128 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1, 0x0},
|
||||
/* 129 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3, 0x0},
|
||||
/* 130 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7, 0x0},
|
||||
/* 131 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xf, 0x0},
|
||||
/* 132 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1f, 0x0},
|
||||
/* 133 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3f, 0x0},
|
||||
/* 134 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7f, 0x0},
|
||||
/* 135 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xff, 0x0},
|
||||
/* 136 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1ff, 0x0},
|
||||
/* 137 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3ff, 0x0},
|
||||
/* 138 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7ff, 0x0},
|
||||
/* 139 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xfff, 0x0},
|
||||
/* 140 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1fff, 0x0},
|
||||
/* 141 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3fff, 0x0},
|
||||
/* 142 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7fff, 0x0},
|
||||
/* 143 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffff, 0x0},
|
||||
/* 144 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1ffff, 0x0},
|
||||
/* 145 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3ffff, 0x0},
|
||||
/* 146 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7ffff, 0x0},
|
||||
/* 147 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xfffff, 0x0},
|
||||
/* 148 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1fffff, 0x0},
|
||||
/* 149 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3fffff, 0x0},
|
||||
/* 150 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7fffff, 0x0},
|
||||
/* 151 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffff, 0x0},
|
||||
/* 152 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffff, 0x0},
|
||||
/* 153 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffff, 0x0},
|
||||
/* 154 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffff, 0x0},
|
||||
/* 155 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xfffffff, 0x0},
|
||||
/* 156 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffff, 0x0},
|
||||
/* 157 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffff, 0x0},
|
||||
/* 158 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffff, 0x0},
|
||||
/* 159 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff, 0x0},
|
||||
/* 160 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffffff, 0x0},
|
||||
/* 161 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffffff, 0x0},
|
||||
/* 162 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffffff, 0x0},
|
||||
/* 163 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffff, 0x0},
|
||||
/* 164 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffffff, 0x0},
|
||||
/* 165 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffff, 0x0},
|
||||
/* 166 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffff, 0x0},
|
||||
/* 167 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffff, 0x0},
|
||||
/* 168 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffffffff, 0x0},
|
||||
/* 169 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffffffff, 0x0},
|
||||
/* 170 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffffffff, 0x0},
|
||||
/* 171 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffff, 0x0},
|
||||
/* 172 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffffffff, 0x0},
|
||||
/* 173 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffff, 0x0},
|
||||
/* 174 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffff, 0x0},
|
||||
/* 175 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff, 0x0},
|
||||
/* 176 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffffffffff, 0x0},
|
||||
/* 177 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffffffffff, 0x0},
|
||||
/* 178 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffffffffff, 0x0},
|
||||
/* 179 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffff, 0x0},
|
||||
/* 180 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffffffffff, 0x0},
|
||||
/* 181 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffff, 0x0},
|
||||
/* 182 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffff, 0x0},
|
||||
/* 183 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffff, 0x0},
|
||||
/* 184 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffffffffffff, 0x0},
|
||||
/* 185 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffffffffffff, 0x0},
|
||||
/* 186 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffffffffffff, 0x0},
|
||||
/* 187 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffffff, 0x0},
|
||||
/* 188 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffffffffffff, 0x0},
|
||||
/* 189 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffffff, 0x0},
|
||||
/* 190 */ {0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff, 0x0},
|
||||
/* 191 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x0},
|
||||
/* 192 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1},
|
||||
/* 193 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3},
|
||||
/* 194 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7},
|
||||
/* 195 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xf},
|
||||
/* 196 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1f},
|
||||
/* 197 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3f},
|
||||
/* 198 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7f},
|
||||
/* 199 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xff},
|
||||
/* 200 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ff},
|
||||
/* 201 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ff},
|
||||
/* 202 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ff},
|
||||
/* 203 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfff},
|
||||
/* 204 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fff},
|
||||
/* 205 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fff},
|
||||
/* 206 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fff},
|
||||
/* 207 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffff},
|
||||
/* 208 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ffff},
|
||||
/* 209 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ffff},
|
||||
/* 210 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ffff},
|
||||
/* 211 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffff},
|
||||
/* 212 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fffff},
|
||||
/* 213 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffff},
|
||||
/* 214 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffff},
|
||||
/* 215 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffff},
|
||||
/* 216 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffff},
|
||||
/* 217 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffff},
|
||||
/* 218 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffff},
|
||||
/* 219 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffff},
|
||||
/* 220 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffff},
|
||||
/* 221 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffff},
|
||||
/* 222 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffff},
|
||||
/* 223 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffff},
|
||||
/* 224 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffffff},
|
||||
/* 225 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffffff},
|
||||
/* 226 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffffff},
|
||||
/* 227 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffff},
|
||||
/* 228 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffffff},
|
||||
/* 229 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffff},
|
||||
/* 230 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffff},
|
||||
/* 231 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffff},
|
||||
/* 232 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffffffff},
|
||||
/* 233 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffffffff},
|
||||
/* 234 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffffffff},
|
||||
/* 235 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffff},
|
||||
/* 236 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffffffff},
|
||||
/* 237 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffff},
|
||||
/* 238 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffff},
|
||||
/* 239 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffff},
|
||||
/* 240 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffffffffff},
|
||||
/* 241 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffffffffff},
|
||||
/* 242 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffffffffff},
|
||||
/* 243 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffff},
|
||||
/* 244 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffffffffff},
|
||||
/* 245 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffff},
|
||||
/* 246 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffff},
|
||||
/* 247 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffff},
|
||||
/* 248 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1ffffffffffffff},
|
||||
/* 249 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3ffffffffffffff},
|
||||
/* 250 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7ffffffffffffff},
|
||||
/* 251 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffffff},
|
||||
/* 252 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x1fffffffffffffff},
|
||||
/* 253 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x3fffffffffffffff},
|
||||
/* 254 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff},
|
||||
/* 255 */ {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff},
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
//go:build generate
|
||||
|
||||
//go:generate go run $GOFILE
|
||||
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package main generates a lookup table for longest-prefix-match (LPM) operations
|
||||
// in the BART routing table.
|
||||
//
|
||||
// This generator creates a precomputed lookup table (LookupTbl) that enables fast
|
||||
// prefix matching by replacing iterative backtracking with a single bitset intersection.
|
||||
//
|
||||
// The core idea:
|
||||
//
|
||||
// In a complete binary tree representation of prefixes, finding the longest matching
|
||||
// prefix requires checking a node and all its ancestors (parent, grandparent, etc.).
|
||||
// Instead of iterating through ancestors at runtime (idx >>= 1 in a loop), this table
|
||||
// precomputes all ancestors for each index.
|
||||
//
|
||||
// Binary tree structure:
|
||||
// - Each index i has a parent at i>>1 (integer division by 2)
|
||||
// - Index 1 is the root (represents the default route)
|
||||
// - Indices 2-255 form a 7-level binary tree
|
||||
//
|
||||
// Example: Index 13 (binary: 0b1101) has ancestors:
|
||||
// - 13 itself (0b1101)
|
||||
// - 6 = 13>>1 (0b0110) - parent
|
||||
// - 3 = 6>>1 (0b0011) - grandparent
|
||||
// - 1 = 3>>1 (0b0001) - root
|
||||
//
|
||||
// With this table, checking if any ancestor exists in a node's prefix set becomes:
|
||||
//
|
||||
// node.prefixes.Intersects(&LookupTbl[idx])
|
||||
//
|
||||
// instead of a loop with multiple Test() calls.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/gaissmai/bart/internal/bitset"
|
||||
)
|
||||
|
||||
var (
|
||||
thisFname = os.Getenv("GOFILE")
|
||||
outFname = "lookuptblgenerated.go"
|
||||
)
|
||||
|
||||
// data holds the template variables for code generation.
|
||||
// The LookupTbl field is populated immediately with the generated table string
|
||||
var data = struct {
|
||||
File string
|
||||
LookupTbl string
|
||||
}{
|
||||
File: thisFname,
|
||||
LookupTbl: genLookupTbl(),
|
||||
}
|
||||
|
||||
// colored prefix for informational and error messages
|
||||
const (
|
||||
INFO = "\x1b[34mINFO:\x1b[0m"
|
||||
DIE = "\x1b[31mERROR:\x1b[0m"
|
||||
)
|
||||
|
||||
func main() {
|
||||
outFile, err := os.Create(outFname)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %v\n", DIE, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Parse the template ...
|
||||
t := template.Must(template.New("foo").Parse(codeTemplate))
|
||||
|
||||
// ... and execute it with our data
|
||||
err = t.Execute(outFile, data)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %v\n", DIE, err)
|
||||
_ = outFile.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Ensure the file is properly closed before formatting
|
||||
if err := outFile.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %v\n", DIE, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "%s ✓ Generated %s\n", INFO, outFname)
|
||||
|
||||
// Run goimports to organize imports and format the code
|
||||
goimports := exec.Command("goimports", "-w", outFname)
|
||||
fmt.Fprintf(os.Stdout, "%s Running goimports on %s\n", INFO, outFname)
|
||||
if out, err := goimports.CombinedOutput(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %s\n", DIE, string(out))
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "%s ✓ goimports completed\n", INFO)
|
||||
|
||||
// Run gofumpt for additional formatting consistency
|
||||
gofumpt := exec.Command("gofumpt", "-w", outFname)
|
||||
fmt.Fprintf(os.Stdout, "%s Running gofumpt on %s\n", INFO, outFname)
|
||||
if out, err := gofumpt.CombinedOutput(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %s\n", DIE, string(out))
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "%s ✓ gofumpt completed\n", INFO)
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// genLookupTbl generates the lookup table as a formatted string.
|
||||
//
|
||||
// For each index i (1-255), the table stores a bitset containing:
|
||||
// - i itself
|
||||
// - all binary ancestors of i (i>>1, i>>2, i>>3, ...)
|
||||
//
|
||||
// Example: For index 13 (binary: 1101):
|
||||
// - Set bit 13 (1101)
|
||||
// - Set bit 6 (0110) = 13>>1
|
||||
// - Set bit 3 (0011) = 6>>1
|
||||
// - Set bit 1 (0001) = 3>>1
|
||||
//
|
||||
// This precomputation allows replacing a loop of bitset tests
|
||||
// with a single bitset intersection operation during longest-prefix-match.
|
||||
|
||||
// genLookupTbl generates the lookup table as a formatted string.
|
||||
//
|
||||
// For each index i (1-255), the table stores a BitSet256 containing:
|
||||
// - i itself (the current node)
|
||||
// - all binary ancestors of i (obtained by repeatedly right-shifting: i>>1, i>>2, ...)
|
||||
//
|
||||
// The algorithm walks up the binary tree from each leaf to the root (index 1).
|
||||
// Each right-shift operation (i >>= 1) moves from a child to its parent node:
|
||||
// - Even indices (LSB=0) are left children: parent = i/2
|
||||
// - Odd indices (LSB=1) are right children: parent = (i-1)/2
|
||||
// - Both cases are handled by integer division via right-shift
|
||||
//
|
||||
// Example walkthrough for index 13 (binary: 0b00001101):
|
||||
//
|
||||
// Iteration 1: i=13 (0b00001101) => Set bit 13
|
||||
// Iteration 2: i=6 (0b00000110) => Set bit 6 (parent of 13)
|
||||
// Iteration 3: i=3 (0b00000011) => Set bit 3 (parent of 6)
|
||||
// Iteration 4: i=1 (0b00000001) => Set bit 1 (root)
|
||||
// Iteration 5: i=0 → Loop terminates
|
||||
//
|
||||
// Result: BitSet256 with bits {1, 3, 6, 13} set
|
||||
//
|
||||
// This allows longest-prefix-match to be computed with a single bitset intersection:
|
||||
//
|
||||
// // Fast O(1) check using precomputed ancestors
|
||||
// return node.prefixes.Intersects(&LookupTbl[idx])
|
||||
//
|
||||
// instead of iterative backtracking:
|
||||
//
|
||||
// // Slow O(log n) loop checking each ancestor individually
|
||||
// for ; idx > 0; idx >>= 1 {
|
||||
// if node.prefixes.Test(idx) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The generated string is formatted for embedding into Go source code.
|
||||
func genLookupTbl() string {
|
||||
// Initialize the lookup table with 256 entries (one per uint8 value)
|
||||
lookupTbl := [256]bitset.BitSet256{}
|
||||
|
||||
for idx := 1; idx <= 255; idx++ {
|
||||
// Walk up the binary tree by repeatedly right-shifting
|
||||
// Each iteration moves to the parent node (idx >> 1)
|
||||
for i := idx; i > 0; i >>= 1 {
|
||||
lookupTbl[idx].Set(uint8(i))
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the lookup table to a formatted string for the generated code
|
||||
builder := strings.Builder{}
|
||||
|
||||
// Index 0 is invalid (no valid prefix maps to 0)
|
||||
fmt.Fprintf(&builder, " /* idx: %3d */ %#v, // %s\n", 0, lookupTbl[0], "invalid")
|
||||
|
||||
// Format each valid index with its bitset and the list of set bits, e.g.
|
||||
// /* idx: 13 */ {0x204a, 0x0, 0x0, 0x0}, // [1 3 6 13]
|
||||
for idx := 1; idx < 256; idx++ {
|
||||
fmt.Fprintf(&builder, " /* idx: %3d */ %#v, // %v\n", idx, lookupTbl[idx], lookupTbl[idx].Bits())
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// codeTemplate is the Go source template for the generated lookuptbl_gen.go file.
|
||||
// It embeds the precomputed lookup table and includes comprehensive documentation
|
||||
// about its structure, usage, and performance benefits.
|
||||
//
|
||||
// The template defines:
|
||||
// - Package-level documentation explaining the LPM optimization
|
||||
// - The LookupTbl variable with all 256 precomputed bitsets
|
||||
// - Usage examples comparing the fast (Intersects) vs slow (loop) approach
|
||||
// - Warnings about not mutating the read-only table
|
||||
const codeTemplate = `// Code generated by {{.File}}; DO NOT EDIT.
|
||||
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package lpm (longest-prefix-match) contains the lookup table with which
|
||||
// the backtracking for the lpm in the complete binary tree of the prefixes
|
||||
// can be replaced by a fast bitset operation.
|
||||
package lpm
|
||||
|
||||
// LookupTbl is a precomputed read‑only table used in hot paths.
|
||||
//
|
||||
// It allows a one-shot bitset intersection algorithm:
|
||||
// Each entry i encodes i and all its binary ancestors (i>>1, i>>2, ...).
|
||||
//
|
||||
// idx must be the uint8 produced by art.OctetToIdx or art.PfxToIdx (0 is invalid).
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// func (n *bartNode[V]) contains(idx uint8) bool {
|
||||
// return n.prefixes.Intersects(&lpm.LookupTbl[idx])
|
||||
// }
|
||||
//
|
||||
// instead of a sequence of single bitset tests:
|
||||
//
|
||||
// func (n *bartNode[V]) contains(idx uint8) bool {
|
||||
// for ; idx > 0; idx >>= 1 {
|
||||
// if n.prefixes.Test(idx) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// DO NOT MUTATE: Precomputed read‑only table used in hot paths.
|
||||
//
|
||||
//nolint:gochecknoglobals
|
||||
var LookupTbl = [256]bitset.BitSet256{
|
||||
{{.LookupTbl}} }`
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
// Code generated by generate_lookuptbl.go; DO NOT EDIT.
|
||||
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package lpm (longest-prefix-match) contains the lookup table with which
|
||||
// the backtracking for the lpm in the complete binary tree of the prefixes
|
||||
// can be replaced by a fast bitset operation.
|
||||
package lpm
|
||||
|
||||
import "github.com/gaissmai/bart/internal/bitset"
|
||||
|
||||
// LookupTbl is a precomputed read‑only table used in hot paths.
|
||||
//
|
||||
// It allows a one-shot bitset intersection algorithm:
|
||||
// Each entry i encodes i and all its binary ancestors (i>>1, i>>2, ...).
|
||||
//
|
||||
// idx must be the uint8 produced by art.OctetToIdx or art.PfxToIdx (0 is invalid).
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// func (n *bartNode[V]) contains(idx uint8) bool {
|
||||
// return n.prefixes.Intersects(&lpm.LookupTbl[idx])
|
||||
// }
|
||||
//
|
||||
// instead of a sequence of single bitset tests:
|
||||
//
|
||||
// func (n *bartNode[V]) contains(idx uint8) bool {
|
||||
// for ; idx > 0; idx >>= 1 {
|
||||
// if n.prefixes.Test(idx) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// DO NOT MUTATE: Precomputed read‑only table used in hot paths.
|
||||
//
|
||||
//nolint:gochecknoglobals
|
||||
var LookupTbl = [256]bitset.BitSet256{
|
||||
/* idx: 0 */ {0x0, 0x0, 0x0, 0x0}, // invalid
|
||||
/* idx: 1 */ {0x2, 0x0, 0x0, 0x0}, // [1]
|
||||
/* idx: 2 */ {0x6, 0x0, 0x0, 0x0}, // [1 2]
|
||||
/* idx: 3 */ {0xa, 0x0, 0x0, 0x0}, // [1 3]
|
||||
/* idx: 4 */ {0x16, 0x0, 0x0, 0x0}, // [1 2 4]
|
||||
/* idx: 5 */ {0x26, 0x0, 0x0, 0x0}, // [1 2 5]
|
||||
/* idx: 6 */ {0x4a, 0x0, 0x0, 0x0}, // [1 3 6]
|
||||
/* idx: 7 */ {0x8a, 0x0, 0x0, 0x0}, // [1 3 7]
|
||||
/* idx: 8 */ {0x116, 0x0, 0x0, 0x0}, // [1 2 4 8]
|
||||
/* idx: 9 */ {0x216, 0x0, 0x0, 0x0}, // [1 2 4 9]
|
||||
/* idx: 10 */ {0x426, 0x0, 0x0, 0x0}, // [1 2 5 10]
|
||||
/* idx: 11 */ {0x826, 0x0, 0x0, 0x0}, // [1 2 5 11]
|
||||
/* idx: 12 */ {0x104a, 0x0, 0x0, 0x0}, // [1 3 6 12]
|
||||
/* idx: 13 */ {0x204a, 0x0, 0x0, 0x0}, // [1 3 6 13]
|
||||
/* idx: 14 */ {0x408a, 0x0, 0x0, 0x0}, // [1 3 7 14]
|
||||
/* idx: 15 */ {0x808a, 0x0, 0x0, 0x0}, // [1 3 7 15]
|
||||
/* idx: 16 */ {0x10116, 0x0, 0x0, 0x0}, // [1 2 4 8 16]
|
||||
/* idx: 17 */ {0x20116, 0x0, 0x0, 0x0}, // [1 2 4 8 17]
|
||||
/* idx: 18 */ {0x40216, 0x0, 0x0, 0x0}, // [1 2 4 9 18]
|
||||
/* idx: 19 */ {0x80216, 0x0, 0x0, 0x0}, // [1 2 4 9 19]
|
||||
/* idx: 20 */ {0x100426, 0x0, 0x0, 0x0}, // [1 2 5 10 20]
|
||||
/* idx: 21 */ {0x200426, 0x0, 0x0, 0x0}, // [1 2 5 10 21]
|
||||
/* idx: 22 */ {0x400826, 0x0, 0x0, 0x0}, // [1 2 5 11 22]
|
||||
/* idx: 23 */ {0x800826, 0x0, 0x0, 0x0}, // [1 2 5 11 23]
|
||||
/* idx: 24 */ {0x100104a, 0x0, 0x0, 0x0}, // [1 3 6 12 24]
|
||||
/* idx: 25 */ {0x200104a, 0x0, 0x0, 0x0}, // [1 3 6 12 25]
|
||||
/* idx: 26 */ {0x400204a, 0x0, 0x0, 0x0}, // [1 3 6 13 26]
|
||||
/* idx: 27 */ {0x800204a, 0x0, 0x0, 0x0}, // [1 3 6 13 27]
|
||||
/* idx: 28 */ {0x1000408a, 0x0, 0x0, 0x0}, // [1 3 7 14 28]
|
||||
/* idx: 29 */ {0x2000408a, 0x0, 0x0, 0x0}, // [1 3 7 14 29]
|
||||
/* idx: 30 */ {0x4000808a, 0x0, 0x0, 0x0}, // [1 3 7 15 30]
|
||||
/* idx: 31 */ {0x8000808a, 0x0, 0x0, 0x0}, // [1 3 7 15 31]
|
||||
/* idx: 32 */ {0x100010116, 0x0, 0x0, 0x0}, // [1 2 4 8 16 32]
|
||||
/* idx: 33 */ {0x200010116, 0x0, 0x0, 0x0}, // [1 2 4 8 16 33]
|
||||
/* idx: 34 */ {0x400020116, 0x0, 0x0, 0x0}, // [1 2 4 8 17 34]
|
||||
/* idx: 35 */ {0x800020116, 0x0, 0x0, 0x0}, // [1 2 4 8 17 35]
|
||||
/* idx: 36 */ {0x1000040216, 0x0, 0x0, 0x0}, // [1 2 4 9 18 36]
|
||||
/* idx: 37 */ {0x2000040216, 0x0, 0x0, 0x0}, // [1 2 4 9 18 37]
|
||||
/* idx: 38 */ {0x4000080216, 0x0, 0x0, 0x0}, // [1 2 4 9 19 38]
|
||||
/* idx: 39 */ {0x8000080216, 0x0, 0x0, 0x0}, // [1 2 4 9 19 39]
|
||||
/* idx: 40 */ {0x10000100426, 0x0, 0x0, 0x0}, // [1 2 5 10 20 40]
|
||||
/* idx: 41 */ {0x20000100426, 0x0, 0x0, 0x0}, // [1 2 5 10 20 41]
|
||||
/* idx: 42 */ {0x40000200426, 0x0, 0x0, 0x0}, // [1 2 5 10 21 42]
|
||||
/* idx: 43 */ {0x80000200426, 0x0, 0x0, 0x0}, // [1 2 5 10 21 43]
|
||||
/* idx: 44 */ {0x100000400826, 0x0, 0x0, 0x0}, // [1 2 5 11 22 44]
|
||||
/* idx: 45 */ {0x200000400826, 0x0, 0x0, 0x0}, // [1 2 5 11 22 45]
|
||||
/* idx: 46 */ {0x400000800826, 0x0, 0x0, 0x0}, // [1 2 5 11 23 46]
|
||||
/* idx: 47 */ {0x800000800826, 0x0, 0x0, 0x0}, // [1 2 5 11 23 47]
|
||||
/* idx: 48 */ {0x100000100104a, 0x0, 0x0, 0x0}, // [1 3 6 12 24 48]
|
||||
/* idx: 49 */ {0x200000100104a, 0x0, 0x0, 0x0}, // [1 3 6 12 24 49]
|
||||
/* idx: 50 */ {0x400000200104a, 0x0, 0x0, 0x0}, // [1 3 6 12 25 50]
|
||||
/* idx: 51 */ {0x800000200104a, 0x0, 0x0, 0x0}, // [1 3 6 12 25 51]
|
||||
/* idx: 52 */ {0x1000000400204a, 0x0, 0x0, 0x0}, // [1 3 6 13 26 52]
|
||||
/* idx: 53 */ {0x2000000400204a, 0x0, 0x0, 0x0}, // [1 3 6 13 26 53]
|
||||
/* idx: 54 */ {0x4000000800204a, 0x0, 0x0, 0x0}, // [1 3 6 13 27 54]
|
||||
/* idx: 55 */ {0x8000000800204a, 0x0, 0x0, 0x0}, // [1 3 6 13 27 55]
|
||||
/* idx: 56 */ {0x10000001000408a, 0x0, 0x0, 0x0}, // [1 3 7 14 28 56]
|
||||
/* idx: 57 */ {0x20000001000408a, 0x0, 0x0, 0x0}, // [1 3 7 14 28 57]
|
||||
/* idx: 58 */ {0x40000002000408a, 0x0, 0x0, 0x0}, // [1 3 7 14 29 58]
|
||||
/* idx: 59 */ {0x80000002000408a, 0x0, 0x0, 0x0}, // [1 3 7 14 29 59]
|
||||
/* idx: 60 */ {0x100000004000808a, 0x0, 0x0, 0x0}, // [1 3 7 15 30 60]
|
||||
/* idx: 61 */ {0x200000004000808a, 0x0, 0x0, 0x0}, // [1 3 7 15 30 61]
|
||||
/* idx: 62 */ {0x400000008000808a, 0x0, 0x0, 0x0}, // [1 3 7 15 31 62]
|
||||
/* idx: 63 */ {0x800000008000808a, 0x0, 0x0, 0x0}, // [1 3 7 15 31 63]
|
||||
/* idx: 64 */ {0x100010116, 0x1, 0x0, 0x0}, // [1 2 4 8 16 32 64]
|
||||
/* idx: 65 */ {0x100010116, 0x2, 0x0, 0x0}, // [1 2 4 8 16 32 65]
|
||||
/* idx: 66 */ {0x200010116, 0x4, 0x0, 0x0}, // [1 2 4 8 16 33 66]
|
||||
/* idx: 67 */ {0x200010116, 0x8, 0x0, 0x0}, // [1 2 4 8 16 33 67]
|
||||
/* idx: 68 */ {0x400020116, 0x10, 0x0, 0x0}, // [1 2 4 8 17 34 68]
|
||||
/* idx: 69 */ {0x400020116, 0x20, 0x0, 0x0}, // [1 2 4 8 17 34 69]
|
||||
/* idx: 70 */ {0x800020116, 0x40, 0x0, 0x0}, // [1 2 4 8 17 35 70]
|
||||
/* idx: 71 */ {0x800020116, 0x80, 0x0, 0x0}, // [1 2 4 8 17 35 71]
|
||||
/* idx: 72 */ {0x1000040216, 0x100, 0x0, 0x0}, // [1 2 4 9 18 36 72]
|
||||
/* idx: 73 */ {0x1000040216, 0x200, 0x0, 0x0}, // [1 2 4 9 18 36 73]
|
||||
/* idx: 74 */ {0x2000040216, 0x400, 0x0, 0x0}, // [1 2 4 9 18 37 74]
|
||||
/* idx: 75 */ {0x2000040216, 0x800, 0x0, 0x0}, // [1 2 4 9 18 37 75]
|
||||
/* idx: 76 */ {0x4000080216, 0x1000, 0x0, 0x0}, // [1 2 4 9 19 38 76]
|
||||
/* idx: 77 */ {0x4000080216, 0x2000, 0x0, 0x0}, // [1 2 4 9 19 38 77]
|
||||
/* idx: 78 */ {0x8000080216, 0x4000, 0x0, 0x0}, // [1 2 4 9 19 39 78]
|
||||
/* idx: 79 */ {0x8000080216, 0x8000, 0x0, 0x0}, // [1 2 4 9 19 39 79]
|
||||
/* idx: 80 */ {0x10000100426, 0x10000, 0x0, 0x0}, // [1 2 5 10 20 40 80]
|
||||
/* idx: 81 */ {0x10000100426, 0x20000, 0x0, 0x0}, // [1 2 5 10 20 40 81]
|
||||
/* idx: 82 */ {0x20000100426, 0x40000, 0x0, 0x0}, // [1 2 5 10 20 41 82]
|
||||
/* idx: 83 */ {0x20000100426, 0x80000, 0x0, 0x0}, // [1 2 5 10 20 41 83]
|
||||
/* idx: 84 */ {0x40000200426, 0x100000, 0x0, 0x0}, // [1 2 5 10 21 42 84]
|
||||
/* idx: 85 */ {0x40000200426, 0x200000, 0x0, 0x0}, // [1 2 5 10 21 42 85]
|
||||
/* idx: 86 */ {0x80000200426, 0x400000, 0x0, 0x0}, // [1 2 5 10 21 43 86]
|
||||
/* idx: 87 */ {0x80000200426, 0x800000, 0x0, 0x0}, // [1 2 5 10 21 43 87]
|
||||
/* idx: 88 */ {0x100000400826, 0x1000000, 0x0, 0x0}, // [1 2 5 11 22 44 88]
|
||||
/* idx: 89 */ {0x100000400826, 0x2000000, 0x0, 0x0}, // [1 2 5 11 22 44 89]
|
||||
/* idx: 90 */ {0x200000400826, 0x4000000, 0x0, 0x0}, // [1 2 5 11 22 45 90]
|
||||
/* idx: 91 */ {0x200000400826, 0x8000000, 0x0, 0x0}, // [1 2 5 11 22 45 91]
|
||||
/* idx: 92 */ {0x400000800826, 0x10000000, 0x0, 0x0}, // [1 2 5 11 23 46 92]
|
||||
/* idx: 93 */ {0x400000800826, 0x20000000, 0x0, 0x0}, // [1 2 5 11 23 46 93]
|
||||
/* idx: 94 */ {0x800000800826, 0x40000000, 0x0, 0x0}, // [1 2 5 11 23 47 94]
|
||||
/* idx: 95 */ {0x800000800826, 0x80000000, 0x0, 0x0}, // [1 2 5 11 23 47 95]
|
||||
/* idx: 96 */ {0x100000100104a, 0x100000000, 0x0, 0x0}, // [1 3 6 12 24 48 96]
|
||||
/* idx: 97 */ {0x100000100104a, 0x200000000, 0x0, 0x0}, // [1 3 6 12 24 48 97]
|
||||
/* idx: 98 */ {0x200000100104a, 0x400000000, 0x0, 0x0}, // [1 3 6 12 24 49 98]
|
||||
/* idx: 99 */ {0x200000100104a, 0x800000000, 0x0, 0x0}, // [1 3 6 12 24 49 99]
|
||||
/* idx: 100 */ {0x400000200104a, 0x1000000000, 0x0, 0x0}, // [1 3 6 12 25 50 100]
|
||||
/* idx: 101 */ {0x400000200104a, 0x2000000000, 0x0, 0x0}, // [1 3 6 12 25 50 101]
|
||||
/* idx: 102 */ {0x800000200104a, 0x4000000000, 0x0, 0x0}, // [1 3 6 12 25 51 102]
|
||||
/* idx: 103 */ {0x800000200104a, 0x8000000000, 0x0, 0x0}, // [1 3 6 12 25 51 103]
|
||||
/* idx: 104 */ {0x1000000400204a, 0x10000000000, 0x0, 0x0}, // [1 3 6 13 26 52 104]
|
||||
/* idx: 105 */ {0x1000000400204a, 0x20000000000, 0x0, 0x0}, // [1 3 6 13 26 52 105]
|
||||
/* idx: 106 */ {0x2000000400204a, 0x40000000000, 0x0, 0x0}, // [1 3 6 13 26 53 106]
|
||||
/* idx: 107 */ {0x2000000400204a, 0x80000000000, 0x0, 0x0}, // [1 3 6 13 26 53 107]
|
||||
/* idx: 108 */ {0x4000000800204a, 0x100000000000, 0x0, 0x0}, // [1 3 6 13 27 54 108]
|
||||
/* idx: 109 */ {0x4000000800204a, 0x200000000000, 0x0, 0x0}, // [1 3 6 13 27 54 109]
|
||||
/* idx: 110 */ {0x8000000800204a, 0x400000000000, 0x0, 0x0}, // [1 3 6 13 27 55 110]
|
||||
/* idx: 111 */ {0x8000000800204a, 0x800000000000, 0x0, 0x0}, // [1 3 6 13 27 55 111]
|
||||
/* idx: 112 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x0}, // [1 3 7 14 28 56 112]
|
||||
/* idx: 113 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x0}, // [1 3 7 14 28 56 113]
|
||||
/* idx: 114 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x0}, // [1 3 7 14 28 57 114]
|
||||
/* idx: 115 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x0}, // [1 3 7 14 28 57 115]
|
||||
/* idx: 116 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x0}, // [1 3 7 14 29 58 116]
|
||||
/* idx: 117 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x0}, // [1 3 7 14 29 58 117]
|
||||
/* idx: 118 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x0}, // [1 3 7 14 29 59 118]
|
||||
/* idx: 119 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x0}, // [1 3 7 14 29 59 119]
|
||||
/* idx: 120 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x0}, // [1 3 7 15 30 60 120]
|
||||
/* idx: 121 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x0}, // [1 3 7 15 30 60 121]
|
||||
/* idx: 122 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x0}, // [1 3 7 15 30 61 122]
|
||||
/* idx: 123 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x0}, // [1 3 7 15 30 61 123]
|
||||
/* idx: 124 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x0}, // [1 3 7 15 31 62 124]
|
||||
/* idx: 125 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x0}, // [1 3 7 15 31 62 125]
|
||||
/* idx: 126 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x0}, // [1 3 7 15 31 63 126]
|
||||
/* idx: 127 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x0}, // [1 3 7 15 31 63 127]
|
||||
/* idx: 128 */ {0x100010116, 0x1, 0x1, 0x0}, // [1 2 4 8 16 32 64 128]
|
||||
/* idx: 129 */ {0x100010116, 0x1, 0x2, 0x0}, // [1 2 4 8 16 32 64 129]
|
||||
/* idx: 130 */ {0x100010116, 0x2, 0x4, 0x0}, // [1 2 4 8 16 32 65 130]
|
||||
/* idx: 131 */ {0x100010116, 0x2, 0x8, 0x0}, // [1 2 4 8 16 32 65 131]
|
||||
/* idx: 132 */ {0x200010116, 0x4, 0x10, 0x0}, // [1 2 4 8 16 33 66 132]
|
||||
/* idx: 133 */ {0x200010116, 0x4, 0x20, 0x0}, // [1 2 4 8 16 33 66 133]
|
||||
/* idx: 134 */ {0x200010116, 0x8, 0x40, 0x0}, // [1 2 4 8 16 33 67 134]
|
||||
/* idx: 135 */ {0x200010116, 0x8, 0x80, 0x0}, // [1 2 4 8 16 33 67 135]
|
||||
/* idx: 136 */ {0x400020116, 0x10, 0x100, 0x0}, // [1 2 4 8 17 34 68 136]
|
||||
/* idx: 137 */ {0x400020116, 0x10, 0x200, 0x0}, // [1 2 4 8 17 34 68 137]
|
||||
/* idx: 138 */ {0x400020116, 0x20, 0x400, 0x0}, // [1 2 4 8 17 34 69 138]
|
||||
/* idx: 139 */ {0x400020116, 0x20, 0x800, 0x0}, // [1 2 4 8 17 34 69 139]
|
||||
/* idx: 140 */ {0x800020116, 0x40, 0x1000, 0x0}, // [1 2 4 8 17 35 70 140]
|
||||
/* idx: 141 */ {0x800020116, 0x40, 0x2000, 0x0}, // [1 2 4 8 17 35 70 141]
|
||||
/* idx: 142 */ {0x800020116, 0x80, 0x4000, 0x0}, // [1 2 4 8 17 35 71 142]
|
||||
/* idx: 143 */ {0x800020116, 0x80, 0x8000, 0x0}, // [1 2 4 8 17 35 71 143]
|
||||
/* idx: 144 */ {0x1000040216, 0x100, 0x10000, 0x0}, // [1 2 4 9 18 36 72 144]
|
||||
/* idx: 145 */ {0x1000040216, 0x100, 0x20000, 0x0}, // [1 2 4 9 18 36 72 145]
|
||||
/* idx: 146 */ {0x1000040216, 0x200, 0x40000, 0x0}, // [1 2 4 9 18 36 73 146]
|
||||
/* idx: 147 */ {0x1000040216, 0x200, 0x80000, 0x0}, // [1 2 4 9 18 36 73 147]
|
||||
/* idx: 148 */ {0x2000040216, 0x400, 0x100000, 0x0}, // [1 2 4 9 18 37 74 148]
|
||||
/* idx: 149 */ {0x2000040216, 0x400, 0x200000, 0x0}, // [1 2 4 9 18 37 74 149]
|
||||
/* idx: 150 */ {0x2000040216, 0x800, 0x400000, 0x0}, // [1 2 4 9 18 37 75 150]
|
||||
/* idx: 151 */ {0x2000040216, 0x800, 0x800000, 0x0}, // [1 2 4 9 18 37 75 151]
|
||||
/* idx: 152 */ {0x4000080216, 0x1000, 0x1000000, 0x0}, // [1 2 4 9 19 38 76 152]
|
||||
/* idx: 153 */ {0x4000080216, 0x1000, 0x2000000, 0x0}, // [1 2 4 9 19 38 76 153]
|
||||
/* idx: 154 */ {0x4000080216, 0x2000, 0x4000000, 0x0}, // [1 2 4 9 19 38 77 154]
|
||||
/* idx: 155 */ {0x4000080216, 0x2000, 0x8000000, 0x0}, // [1 2 4 9 19 38 77 155]
|
||||
/* idx: 156 */ {0x8000080216, 0x4000, 0x10000000, 0x0}, // [1 2 4 9 19 39 78 156]
|
||||
/* idx: 157 */ {0x8000080216, 0x4000, 0x20000000, 0x0}, // [1 2 4 9 19 39 78 157]
|
||||
/* idx: 158 */ {0x8000080216, 0x8000, 0x40000000, 0x0}, // [1 2 4 9 19 39 79 158]
|
||||
/* idx: 159 */ {0x8000080216, 0x8000, 0x80000000, 0x0}, // [1 2 4 9 19 39 79 159]
|
||||
/* idx: 160 */ {0x10000100426, 0x10000, 0x100000000, 0x0}, // [1 2 5 10 20 40 80 160]
|
||||
/* idx: 161 */ {0x10000100426, 0x10000, 0x200000000, 0x0}, // [1 2 5 10 20 40 80 161]
|
||||
/* idx: 162 */ {0x10000100426, 0x20000, 0x400000000, 0x0}, // [1 2 5 10 20 40 81 162]
|
||||
/* idx: 163 */ {0x10000100426, 0x20000, 0x800000000, 0x0}, // [1 2 5 10 20 40 81 163]
|
||||
/* idx: 164 */ {0x20000100426, 0x40000, 0x1000000000, 0x0}, // [1 2 5 10 20 41 82 164]
|
||||
/* idx: 165 */ {0x20000100426, 0x40000, 0x2000000000, 0x0}, // [1 2 5 10 20 41 82 165]
|
||||
/* idx: 166 */ {0x20000100426, 0x80000, 0x4000000000, 0x0}, // [1 2 5 10 20 41 83 166]
|
||||
/* idx: 167 */ {0x20000100426, 0x80000, 0x8000000000, 0x0}, // [1 2 5 10 20 41 83 167]
|
||||
/* idx: 168 */ {0x40000200426, 0x100000, 0x10000000000, 0x0}, // [1 2 5 10 21 42 84 168]
|
||||
/* idx: 169 */ {0x40000200426, 0x100000, 0x20000000000, 0x0}, // [1 2 5 10 21 42 84 169]
|
||||
/* idx: 170 */ {0x40000200426, 0x200000, 0x40000000000, 0x0}, // [1 2 5 10 21 42 85 170]
|
||||
/* idx: 171 */ {0x40000200426, 0x200000, 0x80000000000, 0x0}, // [1 2 5 10 21 42 85 171]
|
||||
/* idx: 172 */ {0x80000200426, 0x400000, 0x100000000000, 0x0}, // [1 2 5 10 21 43 86 172]
|
||||
/* idx: 173 */ {0x80000200426, 0x400000, 0x200000000000, 0x0}, // [1 2 5 10 21 43 86 173]
|
||||
/* idx: 174 */ {0x80000200426, 0x800000, 0x400000000000, 0x0}, // [1 2 5 10 21 43 87 174]
|
||||
/* idx: 175 */ {0x80000200426, 0x800000, 0x800000000000, 0x0}, // [1 2 5 10 21 43 87 175]
|
||||
/* idx: 176 */ {0x100000400826, 0x1000000, 0x1000000000000, 0x0}, // [1 2 5 11 22 44 88 176]
|
||||
/* idx: 177 */ {0x100000400826, 0x1000000, 0x2000000000000, 0x0}, // [1 2 5 11 22 44 88 177]
|
||||
/* idx: 178 */ {0x100000400826, 0x2000000, 0x4000000000000, 0x0}, // [1 2 5 11 22 44 89 178]
|
||||
/* idx: 179 */ {0x100000400826, 0x2000000, 0x8000000000000, 0x0}, // [1 2 5 11 22 44 89 179]
|
||||
/* idx: 180 */ {0x200000400826, 0x4000000, 0x10000000000000, 0x0}, // [1 2 5 11 22 45 90 180]
|
||||
/* idx: 181 */ {0x200000400826, 0x4000000, 0x20000000000000, 0x0}, // [1 2 5 11 22 45 90 181]
|
||||
/* idx: 182 */ {0x200000400826, 0x8000000, 0x40000000000000, 0x0}, // [1 2 5 11 22 45 91 182]
|
||||
/* idx: 183 */ {0x200000400826, 0x8000000, 0x80000000000000, 0x0}, // [1 2 5 11 22 45 91 183]
|
||||
/* idx: 184 */ {0x400000800826, 0x10000000, 0x100000000000000, 0x0}, // [1 2 5 11 23 46 92 184]
|
||||
/* idx: 185 */ {0x400000800826, 0x10000000, 0x200000000000000, 0x0}, // [1 2 5 11 23 46 92 185]
|
||||
/* idx: 186 */ {0x400000800826, 0x20000000, 0x400000000000000, 0x0}, // [1 2 5 11 23 46 93 186]
|
||||
/* idx: 187 */ {0x400000800826, 0x20000000, 0x800000000000000, 0x0}, // [1 2 5 11 23 46 93 187]
|
||||
/* idx: 188 */ {0x800000800826, 0x40000000, 0x1000000000000000, 0x0}, // [1 2 5 11 23 47 94 188]
|
||||
/* idx: 189 */ {0x800000800826, 0x40000000, 0x2000000000000000, 0x0}, // [1 2 5 11 23 47 94 189]
|
||||
/* idx: 190 */ {0x800000800826, 0x80000000, 0x4000000000000000, 0x0}, // [1 2 5 11 23 47 95 190]
|
||||
/* idx: 191 */ {0x800000800826, 0x80000000, 0x8000000000000000, 0x0}, // [1 2 5 11 23 47 95 191]
|
||||
/* idx: 192 */ {0x100000100104a, 0x100000000, 0x0, 0x1}, // [1 3 6 12 24 48 96 192]
|
||||
/* idx: 193 */ {0x100000100104a, 0x100000000, 0x0, 0x2}, // [1 3 6 12 24 48 96 193]
|
||||
/* idx: 194 */ {0x100000100104a, 0x200000000, 0x0, 0x4}, // [1 3 6 12 24 48 97 194]
|
||||
/* idx: 195 */ {0x100000100104a, 0x200000000, 0x0, 0x8}, // [1 3 6 12 24 48 97 195]
|
||||
/* idx: 196 */ {0x200000100104a, 0x400000000, 0x0, 0x10}, // [1 3 6 12 24 49 98 196]
|
||||
/* idx: 197 */ {0x200000100104a, 0x400000000, 0x0, 0x20}, // [1 3 6 12 24 49 98 197]
|
||||
/* idx: 198 */ {0x200000100104a, 0x800000000, 0x0, 0x40}, // [1 3 6 12 24 49 99 198]
|
||||
/* idx: 199 */ {0x200000100104a, 0x800000000, 0x0, 0x80}, // [1 3 6 12 24 49 99 199]
|
||||
/* idx: 200 */ {0x400000200104a, 0x1000000000, 0x0, 0x100}, // [1 3 6 12 25 50 100 200]
|
||||
/* idx: 201 */ {0x400000200104a, 0x1000000000, 0x0, 0x200}, // [1 3 6 12 25 50 100 201]
|
||||
/* idx: 202 */ {0x400000200104a, 0x2000000000, 0x0, 0x400}, // [1 3 6 12 25 50 101 202]
|
||||
/* idx: 203 */ {0x400000200104a, 0x2000000000, 0x0, 0x800}, // [1 3 6 12 25 50 101 203]
|
||||
/* idx: 204 */ {0x800000200104a, 0x4000000000, 0x0, 0x1000}, // [1 3 6 12 25 51 102 204]
|
||||
/* idx: 205 */ {0x800000200104a, 0x4000000000, 0x0, 0x2000}, // [1 3 6 12 25 51 102 205]
|
||||
/* idx: 206 */ {0x800000200104a, 0x8000000000, 0x0, 0x4000}, // [1 3 6 12 25 51 103 206]
|
||||
/* idx: 207 */ {0x800000200104a, 0x8000000000, 0x0, 0x8000}, // [1 3 6 12 25 51 103 207]
|
||||
/* idx: 208 */ {0x1000000400204a, 0x10000000000, 0x0, 0x10000}, // [1 3 6 13 26 52 104 208]
|
||||
/* idx: 209 */ {0x1000000400204a, 0x10000000000, 0x0, 0x20000}, // [1 3 6 13 26 52 104 209]
|
||||
/* idx: 210 */ {0x1000000400204a, 0x20000000000, 0x0, 0x40000}, // [1 3 6 13 26 52 105 210]
|
||||
/* idx: 211 */ {0x1000000400204a, 0x20000000000, 0x0, 0x80000}, // [1 3 6 13 26 52 105 211]
|
||||
/* idx: 212 */ {0x2000000400204a, 0x40000000000, 0x0, 0x100000}, // [1 3 6 13 26 53 106 212]
|
||||
/* idx: 213 */ {0x2000000400204a, 0x40000000000, 0x0, 0x200000}, // [1 3 6 13 26 53 106 213]
|
||||
/* idx: 214 */ {0x2000000400204a, 0x80000000000, 0x0, 0x400000}, // [1 3 6 13 26 53 107 214]
|
||||
/* idx: 215 */ {0x2000000400204a, 0x80000000000, 0x0, 0x800000}, // [1 3 6 13 26 53 107 215]
|
||||
/* idx: 216 */ {0x4000000800204a, 0x100000000000, 0x0, 0x1000000}, // [1 3 6 13 27 54 108 216]
|
||||
/* idx: 217 */ {0x4000000800204a, 0x100000000000, 0x0, 0x2000000}, // [1 3 6 13 27 54 108 217]
|
||||
/* idx: 218 */ {0x4000000800204a, 0x200000000000, 0x0, 0x4000000}, // [1 3 6 13 27 54 109 218]
|
||||
/* idx: 219 */ {0x4000000800204a, 0x200000000000, 0x0, 0x8000000}, // [1 3 6 13 27 54 109 219]
|
||||
/* idx: 220 */ {0x8000000800204a, 0x400000000000, 0x0, 0x10000000}, // [1 3 6 13 27 55 110 220]
|
||||
/* idx: 221 */ {0x8000000800204a, 0x400000000000, 0x0, 0x20000000}, // [1 3 6 13 27 55 110 221]
|
||||
/* idx: 222 */ {0x8000000800204a, 0x800000000000, 0x0, 0x40000000}, // [1 3 6 13 27 55 111 222]
|
||||
/* idx: 223 */ {0x8000000800204a, 0x800000000000, 0x0, 0x80000000}, // [1 3 6 13 27 55 111 223]
|
||||
/* idx: 224 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x100000000}, // [1 3 7 14 28 56 112 224]
|
||||
/* idx: 225 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x200000000}, // [1 3 7 14 28 56 112 225]
|
||||
/* idx: 226 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x400000000}, // [1 3 7 14 28 56 113 226]
|
||||
/* idx: 227 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x800000000}, // [1 3 7 14 28 56 113 227]
|
||||
/* idx: 228 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x1000000000}, // [1 3 7 14 28 57 114 228]
|
||||
/* idx: 229 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x2000000000}, // [1 3 7 14 28 57 114 229]
|
||||
/* idx: 230 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x4000000000}, // [1 3 7 14 28 57 115 230]
|
||||
/* idx: 231 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x8000000000}, // [1 3 7 14 28 57 115 231]
|
||||
/* idx: 232 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x10000000000}, // [1 3 7 14 29 58 116 232]
|
||||
/* idx: 233 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x20000000000}, // [1 3 7 14 29 58 116 233]
|
||||
/* idx: 234 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x40000000000}, // [1 3 7 14 29 58 117 234]
|
||||
/* idx: 235 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x80000000000}, // [1 3 7 14 29 58 117 235]
|
||||
/* idx: 236 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x100000000000}, // [1 3 7 14 29 59 118 236]
|
||||
/* idx: 237 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x200000000000}, // [1 3 7 14 29 59 118 237]
|
||||
/* idx: 238 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x400000000000}, // [1 3 7 14 29 59 119 238]
|
||||
/* idx: 239 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x800000000000}, // [1 3 7 14 29 59 119 239]
|
||||
/* idx: 240 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x1000000000000}, // [1 3 7 15 30 60 120 240]
|
||||
/* idx: 241 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x2000000000000}, // [1 3 7 15 30 60 120 241]
|
||||
/* idx: 242 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x4000000000000}, // [1 3 7 15 30 60 121 242]
|
||||
/* idx: 243 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x8000000000000}, // [1 3 7 15 30 60 121 243]
|
||||
/* idx: 244 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x10000000000000}, // [1 3 7 15 30 61 122 244]
|
||||
/* idx: 245 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x20000000000000}, // [1 3 7 15 30 61 122 245]
|
||||
/* idx: 246 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x40000000000000}, // [1 3 7 15 30 61 123 246]
|
||||
/* idx: 247 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x80000000000000}, // [1 3 7 15 30 61 123 247]
|
||||
/* idx: 248 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x100000000000000}, // [1 3 7 15 31 62 124 248]
|
||||
/* idx: 249 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x200000000000000}, // [1 3 7 15 31 62 124 249]
|
||||
/* idx: 250 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x400000000000000}, // [1 3 7 15 31 62 125 250]
|
||||
/* idx: 251 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x800000000000000}, // [1 3 7 15 31 62 125 251]
|
||||
/* idx: 252 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x1000000000000000}, // [1 3 7 15 31 63 126 252]
|
||||
/* idx: 253 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x2000000000000000}, // [1 3 7 15 31 63 126 253]
|
||||
/* idx: 254 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x4000000000000000}, // [1 3 7 15 31 63 127 254]
|
||||
/* idx: 255 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x8000000000000000}, // [1 3 7 15 31 63 127 255]
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"iter"
|
||||
|
||||
"github.com/gaissmai/bart/internal/lpm"
|
||||
"github.com/gaissmai/bart/internal/sparse"
|
||||
)
|
||||
|
||||
// BartNode represents a single trie level in the multibit routing table.
|
||||
//
|
||||
// Unlike the original ART algorithm, this implementation uses popcount-compressed
|
||||
// sparse arrays instead of fixed-size allocations. Insertions and lookups rely on
|
||||
// fast bitset operations and precomputed lookup tables to maximize CPU pipelining
|
||||
// and cache efficiency.
|
||||
//
|
||||
// Each BartNode maintains two distinct sparse arrays:
|
||||
//
|
||||
// 1. Prefixes: Stores routing entries (prefix -> value) for the current stride.
|
||||
// These are laid out as a complete binary tree using the baseIndex()
|
||||
// function mapping from the ART algorithm. Prefixes that match exactly at
|
||||
// the maximum trie depth are always stored here.
|
||||
//
|
||||
// 2. Children: Holds pointers to the next logical levels with a branching
|
||||
// factor of 256 (8 bits per stride).
|
||||
//
|
||||
// A slot in the Children array may contain one of three types:
|
||||
// - *BartNode[V]: An internal intermediate node for further trie traversal.
|
||||
// - *FringeNode[V]: A path-compressed node if it qualifies as fringe [IsFringe]
|
||||
// - *LeafNode[V]: A path-compressed node otherwise.
|
||||
//
|
||||
// Note: LeafNode and FringeNode are created through path compression and
|
||||
// are automatically split into regular BartNodes when a more specific prefix
|
||||
// is inserted that requires further branching.
|
||||
type BartNode[V any] struct {
|
||||
Prefixes sparse.Array256[V]
|
||||
Children sparse.Array256[any]
|
||||
}
|
||||
|
||||
// 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 *BartNode[V]) IsEmpty() bool {
|
||||
if n == nil {
|
||||
return true
|
||||
}
|
||||
return n.Prefixes.Len() == 0 && n.Children.Len() == 0
|
||||
}
|
||||
|
||||
// PrefixCount returns the number of prefixes stored in this node.
|
||||
func (n *BartNode[V]) PrefixCount() int {
|
||||
return n.Prefixes.Len()
|
||||
}
|
||||
|
||||
// ChildCount returns the number of slots used in this node.
|
||||
func (n *BartNode[V]) ChildCount() int {
|
||||
return n.Children.Len()
|
||||
}
|
||||
|
||||
// InsertPrefix adds or updates a routing entry at the specified index with the given value.
|
||||
// It returns true if a prefix already existed at that index (indicating an update),
|
||||
// false if this is a new insertion.
|
||||
func (n *BartNode[V]) InsertPrefix(idx uint8, val V) (exists bool) {
|
||||
_, exists = n.Prefixes.InsertAt(idx, val)
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrefix retrieves the value associated with the prefix at the given index.
|
||||
// Returns the value and true if found, or zero value and false if not present.
|
||||
func (n *BartNode[V]) GetPrefix(idx uint8) (val V, exists bool) {
|
||||
return n.Prefixes.Get(idx)
|
||||
}
|
||||
|
||||
// MustGetPrefix retrieves the value at the specified index, panicking if not found.
|
||||
// This method should only be used when the caller is certain the index exists.
|
||||
func (n *BartNode[V]) MustGetPrefix(idx uint8) (val V) {
|
||||
return n.Prefixes.MustGet(idx)
|
||||
}
|
||||
|
||||
// AllIndices returns an iterator over all prefix entries.
|
||||
// Each iteration yields the prefix index (uint8) and its associated value (V).
|
||||
func (n *BartNode[V]) AllIndices() iter.Seq2[uint8, V] {
|
||||
return func(yield func(uint8, V) bool) {
|
||||
var buf [256]uint8
|
||||
for i, idx := range n.Prefixes.AsSlice(&buf) {
|
||||
if !yield(idx, n.Prefixes.Items[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeletePrefix removes the prefix at the specified index.
|
||||
// Returns true if the prefix existed, otherwise false.
|
||||
func (n *BartNode[V]) DeletePrefix(idx uint8) (exists bool) {
|
||||
_, exists = n.Prefixes.DeleteAt(idx)
|
||||
return exists
|
||||
}
|
||||
|
||||
// InsertChild adds a child node at the specified address (0-255).
|
||||
// The child can be a *BartNode[V], *LeafNode[V], or *FringeNode[V].
|
||||
// Returns true if a child already existed at that address.
|
||||
func (n *BartNode[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 *BartNode[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 *BartNode[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 *BartNode[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 *BartNode[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 without retrieving the associated value.
|
||||
// It is faster than a full lookup, as it only tests for intersection with the
|
||||
// backtracking bitset for the given index.
|
||||
//
|
||||
// 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 *BartNode[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 base index, associated value, and true if a
|
||||
// matching prefix exists at this level; otherwise, ok is false.
|
||||
//
|
||||
// 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 *BartNode[V]) LookupIdx(idx uint8) (top uint8, val V, ok bool) {
|
||||
// top is the idx of the longest-prefix-match
|
||||
if top, ok = n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok {
|
||||
return top, n.MustGetPrefix(top), true
|
||||
}
|
||||
return top, val, ok
|
||||
}
|
||||
|
||||
// Lookup is just a simple wrapper for LookupIdx.
|
||||
func (n *BartNode[V]) Lookup(idx uint8) (val V, ok bool) {
|
||||
_, val, ok = n.LookupIdx(idx)
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// CloneFlat returns a shallow copy of the current node, optionally performing deep copies of values.
|
||||
//
|
||||
// If cloneFn is nil, the stored values in prefixes are copied directly without modification.
|
||||
// Otherwise, cloneFn is applied to each stored value for deep cloning.
|
||||
// Child nodes are cloned shallowly: LeafNode and FringeNode children are cloned via their clone methods,
|
||||
// but child nodes of type *BartNode[V] (subnodes) are assigned as-is without recursive cloning.
|
||||
// This method does not recursively clone descendants beyond the immediate children.
|
||||
//
|
||||
// Note: The returned node is a new instance with copied slices but only shallow copies of nested nodes,
|
||||
// except for LeafNode and FringeNode children which are cloned according to cloneFn.
|
||||
func (n *BartNode[V]) CloneFlat(cloneFn func(V) V) *BartNode[V] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := new(BartNode[V])
|
||||
if n.IsEmpty() {
|
||||
return c
|
||||
}
|
||||
|
||||
// copy ...
|
||||
c.Prefixes = *(n.Prefixes.Copy())
|
||||
|
||||
// ... and clone the values
|
||||
if cloneFn != nil {
|
||||
for i, v := range c.Prefixes.Items {
|
||||
c.Prefixes.Items[i] = cloneFn(v)
|
||||
}
|
||||
}
|
||||
|
||||
// copy ...
|
||||
c.Children = *(n.Children.Copy())
|
||||
|
||||
// Iterate over children to flat clone leaf/fringe nodes;
|
||||
// for *BartNode[V] children, keep shallow references (no recursive clone)
|
||||
for i, anyKid := range c.Children.Items {
|
||||
switch kid := anyKid.(type) {
|
||||
case *BartNode[V]:
|
||||
// Shallow copy
|
||||
case *LeafNode[V]:
|
||||
// Clone leaf nodes, applying cloneFn as needed
|
||||
c.Children.Items[i] = kid.CloneLeaf(cloneFn)
|
||||
case *FringeNode[V]:
|
||||
// Clone fringe nodes, applying cloneFn as needed
|
||||
c.Children.Items[i] = kid.CloneFringe(cloneFn)
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// CloneRec performs a recursive deep copy of the node and all its descendants.
|
||||
//
|
||||
// If cloneFn is nil, the stored values are copied directly without modification.
|
||||
// Otherwise cloneFn is applied to each stored value for deep cloning.
|
||||
//
|
||||
// This method first creates a shallow clone of the current node using CloneFlat,
|
||||
// applying cloneFn to values as described there. Then it recursively clones all
|
||||
// child nodes of type *BartNode[V], performing a full deep clone down the subtree.
|
||||
//
|
||||
// Child nodes of type *LeafNode[V] and *FringeNode[V] are already cloned
|
||||
// by CloneFlat.
|
||||
//
|
||||
// Returns a new instance of BartNode[V] which is a complete deep clone of the
|
||||
// receiver node with all descendants.
|
||||
func (n *BartNode[V]) CloneRec(cloneFn func(V) V) *BartNode[V] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Perform a flat clone of the current node.
|
||||
c := n.CloneFlat(cloneFn)
|
||||
|
||||
// Recursively clone all child nodes of type *BartNode[V]
|
||||
for i, kidAny := range c.Children.Items {
|
||||
if kid, ok := kidAny.(*BartNode[V]); ok {
|
||||
c.Children.Items[i] = kid.CloneRec(cloneFn)
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
+2427
File diff suppressed because it is too large.
Load diff
+2461
File diff suppressed because it is too large.
Load diff
+1266
File diff suppressed because it is too large.
Load diff
+286
@@ -0,0 +1,286 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"iter"
|
||||
|
||||
"github.com/gaissmai/bart/internal/lpm"
|
||||
"github.com/gaissmai/bart/internal/sparse"
|
||||
)
|
||||
|
||||
// FastNode is based on [BartNode], but it also uses a cache ([256]uint8)
|
||||
// per node to speed up traversal of the multi-bit trie.
|
||||
// Lookups become faster, but this requires more memory per prefix,
|
||||
// and updates (insertions/deletions) also become slower due to the
|
||||
// overhead of managing the cache.
|
||||
type FastNode[V any] struct {
|
||||
Prefixes sparse.Array256[V]
|
||||
Children sparse.Array256[any]
|
||||
// map addr to slice idx (aka rank)
|
||||
childRankCache [256]uint8
|
||||
}
|
||||
|
||||
// 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 *FastNode[V]) IsEmpty() bool {
|
||||
if n == nil {
|
||||
return true
|
||||
}
|
||||
return n.Prefixes.Len() == 0 && n.Children.Len() == 0
|
||||
}
|
||||
|
||||
// PrefixCount returns the number of prefixes stored in this node.
|
||||
func (n *FastNode[V]) PrefixCount() int {
|
||||
return n.Prefixes.Len()
|
||||
}
|
||||
|
||||
// ChildCount returns the number of slots used in this node.
|
||||
func (n *FastNode[V]) ChildCount() int {
|
||||
return n.Children.Len()
|
||||
}
|
||||
|
||||
// InsertPrefix adds or updates a routing entry at the specified index with the given value.
|
||||
// It returns true if a prefix already existed at that index (indicating an update),
|
||||
// false if this is a new insertion.
|
||||
func (n *FastNode[V]) InsertPrefix(idx uint8, val V) (exists bool) {
|
||||
_, exists = n.Prefixes.InsertAt(idx, val)
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrefix retrieves the value associated with the prefix at the given index.
|
||||
// Returns the value and true if found, or zero value and false if not present.
|
||||
func (n *FastNode[V]) GetPrefix(idx uint8) (val V, exists bool) {
|
||||
return n.Prefixes.Get(idx)
|
||||
}
|
||||
|
||||
// MustGetPrefix retrieves the value at the specified index, panicking if not found.
|
||||
// This method should only be used when the caller is certain the index exists.
|
||||
func (n *FastNode[V]) MustGetPrefix(idx uint8) (val V) {
|
||||
return n.Prefixes.MustGet(idx)
|
||||
}
|
||||
|
||||
// AllIndices returns an iterator over all prefix entries.
|
||||
// Each iteration yields the prefix index (uint8) and its associated value (V).
|
||||
func (n *FastNode[V]) AllIndices() iter.Seq2[uint8, V] {
|
||||
return func(yield func(uint8, V) bool) {
|
||||
var buf [256]uint8
|
||||
for i, idx := range n.Prefixes.AsSlice(&buf) {
|
||||
if !yield(idx, n.Prefixes.Items[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeletePrefix removes the prefix at the specified index.
|
||||
// Returns true if the prefix existed, otherwise false.
|
||||
func (n *FastNode[V]) DeletePrefix(idx uint8) (exists bool) {
|
||||
_, exists = n.Prefixes.DeleteAt(idx)
|
||||
return exists
|
||||
}
|
||||
|
||||
// InsertChild adds a child node at the specified address (0-255).
|
||||
// The child can be a *FastNode[V], *LeafNode[V], or *FringeNode[V].
|
||||
// Returns true if a child already existed at that address.
|
||||
func (n *FastNode[V]) InsertChild(addr uint8, child any) (exists bool) {
|
||||
var rank0 int
|
||||
rank0, exists = n.Children.InsertAt(addr, child)
|
||||
if exists {
|
||||
// Update only: the value at addr is overwritten in-place by InsertAt.
|
||||
// childRankCache[addr] is not refreshed because the rank is unchanged
|
||||
// the position of addr in the sparse slice does not shift on an update.
|
||||
return
|
||||
}
|
||||
|
||||
// new child inserted? cache the rank value for this addr
|
||||
//nolint:gosec // G115: integer overflow conversion int -> uint8
|
||||
n.childRankCache[addr] = uint8(rank0)
|
||||
|
||||
// increment all cached ranks after addr
|
||||
for i := 255; i > int(addr); i-- {
|
||||
n.childRankCache[i]++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteChild removes the child node at the specified address.
|
||||
// This operation is idempotent - removing a non-existent child is safe.
|
||||
func (n *FastNode[V]) DeleteChild(addr uint8) (exists bool) {
|
||||
_, exists = n.Children.DeleteAt(addr)
|
||||
|
||||
// nothing deleted
|
||||
if !exists {
|
||||
return exists
|
||||
}
|
||||
|
||||
// decrement all cached ranks after addr
|
||||
for i := 255; i > int(addr); i-- {
|
||||
n.childRankCache[i]--
|
||||
}
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
// 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 *FastNode[V]) GetChild(addr uint8) (any, bool) {
|
||||
if n.Children.Test(addr) {
|
||||
rank0 := n.childRankCache[addr]
|
||||
return n.Children.Items[rank0], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// MustGetChild retrieves the child at addr using the pre-cached rank stored in
|
||||
// childRankCache[addr] for a direct O(1) array access without an existence check.
|
||||
//
|
||||
// The caller must guarantee that addr is present (Children.Test(addr) == true).
|
||||
// If addr is absent, childRankCache[addr] contains the number of occupied
|
||||
// addresses less than addr (maintained by InsertChild/DeleteChild), so the
|
||||
// behaviour is undefined: either a wrong child is returned silently, or the
|
||||
// call panics with an index-out-of-range error.
|
||||
func (n *FastNode[V]) MustGetChild(addr uint8) any {
|
||||
rank0 := n.childRankCache[addr]
|
||||
return n.Children.Items[rank0]
|
||||
}
|
||||
|
||||
// AllChildren returns an iterator over all child nodes.
|
||||
// Each iteration yields the child's address (uint8) and the child node (any).
|
||||
func (n *FastNode[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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 without retrieving the associated value.
|
||||
// It is faster than a full lookup, as it only tests for intersection with the
|
||||
// backtracking bitset for the given index.
|
||||
//
|
||||
// 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 *FastNode[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 base index, associated value, and true if a
|
||||
// matching prefix exists at this level; otherwise, ok is false.
|
||||
//
|
||||
// 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 *FastNode[V]) LookupIdx(idx uint8) (top uint8, val V, ok bool) {
|
||||
// top is the idx of the longest-prefix-match
|
||||
if top, ok = n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok {
|
||||
return top, n.MustGetPrefix(top), true
|
||||
}
|
||||
return top, val, ok
|
||||
}
|
||||
|
||||
// Lookup is just a simple wrapper for LookupIdx.
|
||||
func (n *FastNode[V]) Lookup(idx uint8) (val V, ok bool) {
|
||||
_, val, ok = n.LookupIdx(idx)
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// CloneFlat returns a shallow copy of the current node, optionally performing deep copies of values.
|
||||
//
|
||||
// If cloneFn is nil, the stored values in prefixes are copied directly without modification.
|
||||
// Otherwise, cloneFn is applied to each stored value for deep cloning.
|
||||
// Child nodes are cloned shallowly: LeafNode and FringeNode children are cloned via their clone methods,
|
||||
// but child nodes of type *FastNode[V] (subnodes) are assigned as-is without recursive cloning.
|
||||
// This method does not recursively clone descendants beyond the immediate children.
|
||||
//
|
||||
// Note: The returned node is a new instance with copied slices but only shallow copies of nested nodes,
|
||||
// except for LeafNode and FringeNode children which are cloned according to cloneFn.
|
||||
func (n *FastNode[V]) CloneFlat(cloneFn func(V) V) *FastNode[V] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := new(FastNode[V])
|
||||
if n.IsEmpty() {
|
||||
return c
|
||||
}
|
||||
|
||||
// copy ...
|
||||
c.Prefixes = *(n.Prefixes.Copy())
|
||||
|
||||
// ... and clone the values
|
||||
if cloneFn != nil {
|
||||
for i, v := range c.Prefixes.Items {
|
||||
c.Prefixes.Items[i] = cloneFn(v)
|
||||
}
|
||||
}
|
||||
|
||||
// copy ...
|
||||
c.childRankCache = n.childRankCache
|
||||
c.Children = *(n.Children.Copy())
|
||||
|
||||
// Iterate over children to flat clone leaf/fringe nodes;
|
||||
// for *FastNode[V] children, keep shallow references (no recursive clone)
|
||||
for i, anyKid := range c.Children.Items {
|
||||
switch kid := anyKid.(type) {
|
||||
case *FastNode[V]:
|
||||
// Shallow copy
|
||||
case *LeafNode[V]:
|
||||
// Clone leaf nodes, applying cloneFn as needed
|
||||
c.Children.Items[i] = kid.CloneLeaf(cloneFn)
|
||||
case *FringeNode[V]:
|
||||
// Clone fringe nodes, applying cloneFn as needed
|
||||
c.Children.Items[i] = kid.CloneFringe(cloneFn)
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// CloneRec performs a recursive deep copy of the node and all its descendants.
|
||||
//
|
||||
// If cloneFn is nil, the stored values are copied directly without modification.
|
||||
// Otherwise cloneFn is applied to each stored value for deep cloning.
|
||||
//
|
||||
// This method first creates a shallow clone of the current node using CloneFlat,
|
||||
// applying cloneFn to values as described there. Then it recursively clones all
|
||||
// child nodes of type *FastNode[V], performing a full deep clone down the subtree.
|
||||
//
|
||||
// Child nodes of type *LeafNode[V] and *FringeNode[V] are already cloned
|
||||
// by CloneFlat.
|
||||
//
|
||||
// Returns a new instance of FastNode[V] which is a complete deep clone of the
|
||||
// receiver node with all descendants.
|
||||
func (n *FastNode[V]) CloneRec(cloneFn func(V) V) *FastNode[V] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Perform a flat clone of the current node.
|
||||
c := n.CloneFlat(cloneFn)
|
||||
|
||||
// Recursively clone all child nodes of type *FastNode[V]
|
||||
for i, kidAny := range c.Children.Items {
|
||||
if kid, ok := kidAny.(*FastNode[V]); ok {
|
||||
c.Children.Items[i] = kid.CloneRec(cloneFn)
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
+2427
File diff suppressed because it is too large.
Load diff
+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
|
||||
}
|
||||
+2427
File diff suppressed because it is too large.
Load diff
+337
@@ -0,0 +1,337 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gaissmai/bart/internal/art"
|
||||
)
|
||||
|
||||
// strideLen represents the byte stride length for the multibit trie.
|
||||
// Each stride processes 8 bits (1 byte) at a time.
|
||||
const strideLen = 8
|
||||
|
||||
// MaxItems defines the maximum number of prefixes or children that can be stored in a single node.
|
||||
// This corresponds to 256 possible values for an 8-bit stride.
|
||||
const MaxItems = 256
|
||||
|
||||
// MaxTreeDepth represents the maximum depth of the trie structure.
|
||||
// For IPv6 addresses, this allows up to 16 bytes of depth.
|
||||
const MaxTreeDepth = 16
|
||||
|
||||
// DepthMask is used for bounds check elimination (BCE) when accessing depth-indexed arrays.
|
||||
const DepthMask = MaxTreeDepth - 1
|
||||
|
||||
// StridePath represents a path through the trie, with a maximum depth of 16 octets for IPv6.
|
||||
type StridePath [MaxTreeDepth]uint8
|
||||
|
||||
// TrieItem, a node has no path information about its predecessors,
|
||||
// we collect this during the recursive descent.
|
||||
type TrieItem[V any] struct {
|
||||
// for traversing, Path/Depth/Idx is needed to get the CIDR back from the trie.
|
||||
Node any // BartNode, FastNode, LiteNode
|
||||
Is4 bool
|
||||
Path StridePath
|
||||
Depth int
|
||||
Idx uint8
|
||||
|
||||
// for printing
|
||||
Cidr netip.Prefix
|
||||
Val V
|
||||
}
|
||||
|
||||
// StatsT, only used for dump, tests and benchmarks
|
||||
type StatsT struct {
|
||||
Prefixes int
|
||||
Children int
|
||||
SubNodes int
|
||||
Leaves int
|
||||
Fringes int
|
||||
}
|
||||
|
||||
type nodeType byte
|
||||
|
||||
const (
|
||||
nullNode nodeType = iota // empty node
|
||||
fullNode // prefixes and children or path-compressed prefixes
|
||||
halfNode // no prefixes, only children and path-compressed prefixes
|
||||
pathNode // only children, no prefix nor path-compressed prefixes
|
||||
stopNode // no children, only prefixes or path-compressed prefixes
|
||||
)
|
||||
|
||||
// String implements Stringer for nodeType.
|
||||
func (nt nodeType) String() string {
|
||||
switch nt {
|
||||
case nullNode:
|
||||
return "NULL"
|
||||
case fullNode:
|
||||
return "FULL"
|
||||
case halfNode:
|
||||
return "HALF"
|
||||
case pathNode:
|
||||
return "PATH"
|
||||
case stopNode:
|
||||
return "STOP"
|
||||
default:
|
||||
return "unreachable"
|
||||
}
|
||||
}
|
||||
|
||||
// addrFmt, different format strings for IPv4 and IPv6, decimal versus hex.
|
||||
func addrFmt(addr byte, is4 bool) string {
|
||||
if is4 {
|
||||
return fmt.Sprintf("%d", addr)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("0x%02x", addr)
|
||||
}
|
||||
|
||||
// ip stride path, different formats for IPv4 and IPv6, dotted decimal or hex.
|
||||
//
|
||||
// 127.0.0
|
||||
// 2001:0d
|
||||
func ipStridePath(path StridePath, depth int, is4 bool) string {
|
||||
buf := new(strings.Builder)
|
||||
|
||||
if is4 {
|
||||
for i, b := range path[:depth] {
|
||||
if i != 0 {
|
||||
buf.WriteString(".")
|
||||
}
|
||||
|
||||
buf.WriteString(strconv.Itoa(int(b)))
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
for i, b := range path[:depth] {
|
||||
if i != 0 && i%2 == 0 {
|
||||
buf.WriteString(":")
|
||||
}
|
||||
|
||||
fmt.Fprintf(buf, "%02x", b)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// CmpPrefix, helper function, compare func for prefix sort,
|
||||
// all cidrs are already normalized
|
||||
func CmpPrefix(a, b netip.Prefix) int {
|
||||
if cmpAddr := a.Addr().Compare(b.Addr()); cmpAddr != 0 {
|
||||
return cmpAddr
|
||||
}
|
||||
|
||||
return cmp.Compare(a.Bits(), b.Bits())
|
||||
}
|
||||
|
||||
// LeafNode represents a path-compressed routing entry that stores both prefix and value.
|
||||
// Leaf nodes are used when a prefix doesn't align with trie stride boundaries
|
||||
// and needs to be stored as a compressed path to save memory.
|
||||
type LeafNode[V any] struct {
|
||||
Value V
|
||||
Prefix netip.Prefix
|
||||
}
|
||||
|
||||
// NewLeafNode creates a new leaf node with the specified prefix and value.
|
||||
func NewLeafNode[V any](pfx netip.Prefix, val V) *LeafNode[V] {
|
||||
return &LeafNode[V]{Prefix: pfx, Value: val}
|
||||
}
|
||||
|
||||
// FringeNode represents a path-compressed routing entry that stores only a value.
|
||||
// The prefix is implicitly defined by the node's position in the trie.
|
||||
// Fringe nodes are used for prefixes that align exactly with stride boundaries
|
||||
// (/8, /16, /24, etc.) to save memory by not storing redundant prefix information.
|
||||
type FringeNode[V any] struct {
|
||||
Value V
|
||||
}
|
||||
|
||||
// NewFringeNode creates a new fringe node with the specified value.
|
||||
func NewFringeNode[V any](val V) *FringeNode[V] {
|
||||
return &FringeNode[V]{Value: val}
|
||||
}
|
||||
|
||||
// IsFringe determines whether a prefix qualifies as a "FringeNode".
|
||||
// Only prefixes that are stride-aligned (i.e., /8, /16, ..., /128)
|
||||
// can be fringe-compressed. If these prefixes are inserted at a position
|
||||
// where depth == (strideCount-1), they are treated as FringeNodes;
|
||||
// at positions where depth < (strideCount-1), they are treated as LeafNodes.
|
||||
//
|
||||
// Example for a stride-aligned prefix like 192.168.1.0/24 (strideCount = 3, modBits = 0):
|
||||
//
|
||||
// depth = 3, depth == strideCount : A direct prefix with 0/0 (default route for subtrie).
|
||||
// depth = 2, depth == (strideCount-1) : A path-compressed fringe.
|
||||
// depth < 2, depth < (strideCount-1) : A path-compressed leaf.
|
||||
func IsFringe(depth int, pfxLen int) bool {
|
||||
strideCount, modBits := DivMod8(pfxLen)
|
||||
return depth == strideCount-1 && modBits == 0
|
||||
}
|
||||
|
||||
// CmpIndexRank, sort indexes in prefix sort order.
|
||||
func CmpIndexRank(aIdx, bIdx uint8) int {
|
||||
// convert idx [1..255] to prefix
|
||||
aOctet, aBits := art.IdxToPfx(aIdx)
|
||||
bOctet, bBits := art.IdxToPfx(bIdx)
|
||||
|
||||
// cmp the prefixes, first by address and then by bits
|
||||
if aOctet == bOctet {
|
||||
return cmp.Compare(aBits, bBits)
|
||||
}
|
||||
return cmp.Compare(aOctet, bOctet)
|
||||
}
|
||||
|
||||
// CidrFromPath reconstructs a CIDR prefix from a stride path, depth, and index.
|
||||
// The prefix is determined by the node's position in the trie and the base index
|
||||
// from the ART algorithm's complete binary tree representation.
|
||||
//
|
||||
// Parameters:
|
||||
// - path: The stride path through the trie
|
||||
// - depth: Current depth in the trie
|
||||
// - is4: True for IPv4 processing, false for IPv6
|
||||
// - idx: The base index from the prefix table
|
||||
//
|
||||
// Returns the reconstructed netip.Prefix.
|
||||
func CidrFromPath(path StridePath, depth int, is4 bool, idx uint8) netip.Prefix {
|
||||
depth &= DepthMask // BCE
|
||||
|
||||
// retrieve the last octet and pfxLen
|
||||
octet, pfxLen := art.IdxToPfx(idx)
|
||||
|
||||
// set byte in path at depth with last octet
|
||||
path[depth] = octet
|
||||
|
||||
// canonicalize
|
||||
clear(path[depth+1:])
|
||||
|
||||
// make ip addr from octets
|
||||
var ip netip.Addr
|
||||
if is4 {
|
||||
ip = netip.AddrFrom4([4]byte(path[:4]))
|
||||
} else {
|
||||
ip = netip.AddrFrom16(path)
|
||||
}
|
||||
|
||||
// calc bits with pathLen and pfxLen
|
||||
bits := depth<<3 + int(pfxLen)
|
||||
|
||||
// PrefixFrom does not allocate and does not mask off the host bits of ip.
|
||||
// With the clear(), the non-canonical bytes have already been removed.
|
||||
return netip.PrefixFrom(ip, bits)
|
||||
}
|
||||
|
||||
// CidrForFringe reconstructs a CIDR prefix for a fringe node from the traversal path.
|
||||
// Since fringe nodes don't store their prefix explicitly, it's derived entirely
|
||||
// from the node's position in the trie and its final byte value.
|
||||
//
|
||||
// Parameters:
|
||||
// - octets: The path of previous bytes leading up to the fringe.
|
||||
// - depth: Current depth in the trie (which equals strideCount - 1).
|
||||
// - is4: True for IPv4 processing, false for IPv6.
|
||||
// - fringeByte: The actual 8-bit value (0-255) of the prefix at this final stride.
|
||||
//
|
||||
// Returns the reconstructed netip.Prefix for the fringe.
|
||||
func CidrForFringe(octets []byte, depth int, is4 bool, fringeByte uint8) netip.Prefix {
|
||||
depth &= DepthMask // BCE
|
||||
|
||||
var path StridePath
|
||||
copy(path[:], octets)
|
||||
path[depth] = fringeByte
|
||||
|
||||
// canonicalize, fringe bit boundaries are always a multiple of a byte
|
||||
clear(path[depth+1:])
|
||||
|
||||
// make ip addr from octets
|
||||
var ip netip.Addr
|
||||
if is4 {
|
||||
ip = netip.AddrFrom4([4]byte(path[:4]))
|
||||
} else {
|
||||
ip = netip.AddrFrom16(path)
|
||||
}
|
||||
|
||||
// it's a fringe, bits are always aligned on stride boundaries (/8, /16, /24, ...)
|
||||
bits := (depth + 1) << 3
|
||||
|
||||
// PrefixFrom does not allocate and does not mask off the host bits of ip.
|
||||
// With the clear(), the non-canonical bytes have already been removed.
|
||||
return netip.PrefixFrom(ip, bits)
|
||||
}
|
||||
|
||||
// DivMod8 returns the count of full 8‑bit strides (bits/8)
|
||||
// and the remaining bits in the final stride (bits%8) for pfxLen.
|
||||
//
|
||||
// ATTENTION: Split the IP prefixes at 8-bit borders, count from 0.
|
||||
//
|
||||
// /7, /15, /23, /31, ..., /127
|
||||
//
|
||||
// BitPos: [0-7],[8-15],[16-23],[24-31],[32]
|
||||
// BitPos: [0-7],[8-15],[16-23],[24-31],[32-39],[40-47],[48-55],[56-63],...,[120-127],[128]
|
||||
//
|
||||
// 0.0.0.0/0 => strideCount: 0, modBits: 0 (default route)
|
||||
// 0.0.0.0/7 => strideCount: 0, modBits: 7
|
||||
// 0.0.0.0/8 => strideCount: 1, modBits: 0 (fringe candidate)
|
||||
// 10.0.0.0/8 => strideCount: 1, modBits: 0 (fringe candidate)
|
||||
// 10.0.0.0/22 => strideCount: 2, modBits: 6
|
||||
// 10.0.0.0/29 => strideCount: 3, modBits: 5
|
||||
// 10.0.0.0/32 => strideCount: 4, modBits: 0 (fringe candidate)
|
||||
//
|
||||
// ::/0 => strideCount: 0, modBits: 0 (default route)
|
||||
// ::1/128 => strideCount: 16, modBits: 0 (fringe candidate)
|
||||
// 2001:db8::/42 => strideCount: 5, modBits: 2
|
||||
// 2001:db8::/56 => strideCount: 7, modBits: 0 (fringe candidate)
|
||||
//
|
||||
// /32 and /128 prefixes are special, they never form a new node,
|
||||
// At the end of the trie (IPv4: depth 4, IPv6: depth 16) they are always
|
||||
// inserted as a path‑compressed fringe.
|
||||
//
|
||||
// We are not splitting at /8, /16, ..., because this would mean that the
|
||||
// first node would have 512 prefixes, 9 bits from [0-8]. All remaining nodes
|
||||
// would then only have 8 bits from [9-16], [17-24], [25..32], ...
|
||||
// but the algorithm would then require a variable length bitset
|
||||
// or imply a double-sized bitset.
|
||||
//
|
||||
// If you can commit to a fixed size of [4]uint64, then the algorithm is
|
||||
// much faster due to modern CPUs.
|
||||
//
|
||||
// Perhaps a future Go version that supports SIMD instructions for the [4]uint64 vectors
|
||||
// will make the algorithm even faster on suitable hardware.
|
||||
func DivMod8(pfxLen int) (strideCount int, modBits uint8) {
|
||||
// strideCount: range from 0..4 or 0..16
|
||||
// modBits: range from 0..7
|
||||
return pfxLen >> 3, uint8(pfxLen & 7)
|
||||
}
|
||||
|
||||
// CloneLeaf creates and returns a copy of the leafNode receiver.
|
||||
// If cloneFn is nil, the value is copied directly without modification.
|
||||
// Otherwise, cloneFn is applied to the value for deep cloning.
|
||||
// The prefix field is always copied as is.
|
||||
func (l *LeafNode[V]) CloneLeaf(cloneFn func(V) V) *LeafNode[V] {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cloneFn == nil {
|
||||
return &LeafNode[V]{Prefix: l.Prefix, Value: l.Value}
|
||||
}
|
||||
return &LeafNode[V]{Prefix: l.Prefix, Value: cloneFn(l.Value)}
|
||||
}
|
||||
|
||||
// CloneFringe creates and returns a copy of the FringeNode receiver.
|
||||
// If cloneFn is nil, the value is copied directly without modification.
|
||||
// Otherwise, cloneFn is applied to the value for deep cloning.
|
||||
func (l *FringeNode[V]) CloneFringe(cloneFn func(V) V) *FringeNode[V] {
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cloneFn == nil {
|
||||
return &FringeNode[V]{Value: l.Value}
|
||||
}
|
||||
return &FringeNode[V]{Value: cloneFn(l.Value)}
|
||||
}
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
// Copyright (c) 2024 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// package sparse implements a generic sparse array
|
||||
// with popcount compression.
|
||||
package sparse
|
||||
|
||||
import (
|
||||
"github.com/gaissmai/bart/internal/bitset"
|
||||
)
|
||||
|
||||
// Array, a generic implementation of a sparse array
|
||||
// with popcount compression and payload T.
|
||||
type Array[T any] struct {
|
||||
bitset.BitSet
|
||||
Items []T
|
||||
}
|
||||
|
||||
// Get the value at i from sparse array.
|
||||
//
|
||||
// example: Array.Get(5) -> Array.Items[1]
|
||||
//
|
||||
// ⬇
|
||||
// BitSet: [0|0|1|0|0|1|0|1|...] <- 3 bits set
|
||||
// Items: [*|*|*] <- len(Items) = 3
|
||||
// ⬆
|
||||
//
|
||||
// BitSet.Test(5): true
|
||||
// BitSet.popcount(5): 2, for interval [0,5]
|
||||
// BitSet.Rank0(5): 1, equal popcount(5)-1
|
||||
func (s *Array[T]) Get(i uint) (value T, ok bool) {
|
||||
if s.Test(i) {
|
||||
return s.Items[s.Rank0(i)], true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MustGet, use it only after a successful test
|
||||
// or the behavior is undefined, maybe it panics.
|
||||
func (s *Array[T]) MustGet(i uint) T {
|
||||
return s.Items[s.Rank0(i)]
|
||||
}
|
||||
|
||||
// UpdateAt or set the value at i via callback. The new value is returned
|
||||
// and true if the value was already present.
|
||||
func (s *Array[T]) UpdateAt(i uint, cb func(T, bool) T) (newValue T, wasPresent bool) {
|
||||
var rank0 int
|
||||
|
||||
// if already set, get current value
|
||||
var oldValue T
|
||||
|
||||
if wasPresent = s.Test(i); wasPresent {
|
||||
rank0 = s.Rank0(i)
|
||||
oldValue = s.Items[rank0]
|
||||
}
|
||||
|
||||
// callback function to get updated or new value
|
||||
newValue = cb(oldValue, wasPresent)
|
||||
|
||||
// already set, update and return value
|
||||
if wasPresent {
|
||||
s.Items[rank0] = newValue
|
||||
|
||||
return newValue, wasPresent
|
||||
}
|
||||
|
||||
// new value, insert into bitset ...
|
||||
s.BitSet = s.Set(i)
|
||||
|
||||
// bitset has changed, recalc rank
|
||||
rank0 = s.Rank0(i)
|
||||
|
||||
// ... and insert value into slice
|
||||
s.insertItem(rank0, newValue)
|
||||
|
||||
return newValue, wasPresent
|
||||
}
|
||||
|
||||
// Len returns the number of items in sparse array.
|
||||
func (s *Array[T]) Len() int {
|
||||
return len(s.Items)
|
||||
}
|
||||
|
||||
// Copy returns a shallow copy of the Array.
|
||||
// The elements are copied using assignment, this is no deep clone.
|
||||
func (s *Array[T]) Copy() *Array[T] {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Array[T]{
|
||||
BitSet: s.BitSet.Clone(),
|
||||
Items: append(s.Items[:0:0], s.Items...),
|
||||
}
|
||||
}
|
||||
|
||||
// InsertAt a value at i into the sparse array.
|
||||
// If the value already exists, overwrite it with val and return true.
|
||||
func (s *Array[T]) InsertAt(i uint, value T) (exists bool) {
|
||||
// slot exists, overwrite value
|
||||
if s.Len() != 0 && s.Test(i) {
|
||||
s.Items[s.Rank0(i)] = value
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// new, insert into bitset ...
|
||||
s.BitSet = s.Set(i)
|
||||
|
||||
// ... and slice
|
||||
s.insertItem(s.Rank0(i), value)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DeleteAt a value at i from the sparse array, zeroes the tail.
|
||||
func (s *Array[T]) DeleteAt(i uint) (value T, exists bool) {
|
||||
if s.Len() == 0 || !s.Test(i) {
|
||||
return
|
||||
}
|
||||
|
||||
rank0 := s.Rank0(i)
|
||||
value = s.Items[rank0]
|
||||
|
||||
// delete from slice
|
||||
s.deleteItem(rank0)
|
||||
|
||||
// delete from bitset
|
||||
s.BitSet = s.Clear(i)
|
||||
|
||||
return value, true
|
||||
}
|
||||
|
||||
// insertItem inserts the item at index i, shift the rest one pos right
|
||||
//
|
||||
// It panics if i is out of range.
|
||||
func (s *Array[T]) insertItem(i int, item T) {
|
||||
if len(s.Items) < cap(s.Items) {
|
||||
s.Items = s.Items[:len(s.Items)+1] // fast resize, no alloc
|
||||
} else {
|
||||
var zero T
|
||||
s.Items = append(s.Items, zero) // append one item, mostly enlarge cap by more than one item
|
||||
}
|
||||
|
||||
copy(s.Items[i+1:], s.Items[i:])
|
||||
s.Items[i] = item
|
||||
}
|
||||
|
||||
// deleteItem at index i, shift the rest one pos left and clears the tail item
|
||||
//
|
||||
// It panics if i is out of range.
|
||||
func (s *Array[T]) deleteItem(i int) {
|
||||
var zero T
|
||||
|
||||
nl := len(s.Items) - 1 // new len
|
||||
copy(s.Items[i:], s.Items[i+1:]) // overwrite item at [i]
|
||||
|
||||
s.Items[nl] = zero // clear the tail item
|
||||
s.Items = s.Items[:nl] // new len, keep cap is unchanged
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package sparse provides a compact and efficient sparse array
|
||||
// implementation for addressable key ranges from [0..255].
|
||||
//
|
||||
// It is optimized for use in radix tries and lookup structures
|
||||
// where only a small subset of possible keys contain actual data.
|
||||
//
|
||||
// Internally, an Array256 combines a fixed-size BitSet256 with
|
||||
// a compact packed Items slice. The bitset tracks which key slots
|
||||
// are occupied and enables fast index mapping via popcount (Rank),
|
||||
// while the Items slice stores the associated payloads.
|
||||
//
|
||||
// Lookup, insertion, and deletion operate in O(1) time (worst-case O(n)
|
||||
// only for shifting/copying in extremely full arrays).
|
||||
//
|
||||
// This module avoids memory allocations where possible and provides
|
||||
// predictable performance even under heavy insert/delete workloads.
|
||||
package sparse
|
||||
|
||||
import (
|
||||
"github.com/gaissmai/bart/internal/bitset"
|
||||
)
|
||||
|
||||
// Array256 is a popcount-compressed sparse array for up to 256 item slots.
|
||||
//
|
||||
// Internally, it consists of:
|
||||
// - a BitSet256, where each set bit marks a present slot (i.e., key i used)
|
||||
// - a compact Items slice, which stores the payloads only for the set bits
|
||||
//
|
||||
// Each occupied index i ∈ [0..255] can be tested using .Test(i).
|
||||
// The actual index within the Items slice is computed via .Rank(i)-1.
|
||||
//
|
||||
// All insert/delete operations update both the bitset and item slice
|
||||
// to preserve this mapping invariant. Direct mutation of the underlying
|
||||
// bitset (via Set or Clear) is forbidden and will panic.
|
||||
//
|
||||
// This structure allows extremely fast and compact prefix-based routing,
|
||||
// highly efficient child-node containers in prefix tries, or memory-safe
|
||||
// alternatives to map[uint8]T with deterministic iteration.
|
||||
//
|
||||
// Example layout:
|
||||
//
|
||||
// BitSet256: [0 1 0 0 0 1 ...] // bits 1 and 5 set
|
||||
// Items: [T1, T2] // a.Items[0] ↔ key 1, a.Items[1] ↔ key 5
|
||||
type Array256[T any] struct {
|
||||
bitset.BitSet256
|
||||
Items []T
|
||||
}
|
||||
|
||||
// Set panics. The bitset is internally coupled with Items[].
|
||||
// Use InsertAt to add or overwrite at index i.
|
||||
func (a *Array256[T]) Set(uint8) {
|
||||
panic("forbidden, use InsertAt")
|
||||
}
|
||||
|
||||
// Clear panics. The bitset is internally coupled with Items[].
|
||||
func (a *Array256[T]) Clear(uint8) {
|
||||
panic("forbidden, use DeleteAt")
|
||||
}
|
||||
|
||||
// Get returns the value at index i and whether it exists.
|
||||
//
|
||||
// If the bit for i is not set, ok is false and value is the zero-value of T.
|
||||
//
|
||||
// example: a.Get(5) -> a.Items[1]
|
||||
//
|
||||
// ⬇
|
||||
// BitSet256: [0|0|1|0|0|1|0|...|1] <- 3 bits set
|
||||
// Items: [*|*|*] <- len(Items) = 3
|
||||
// ⬆
|
||||
//
|
||||
// BitSet256.Test(5): true
|
||||
// BitSet256.Rank(5): 2
|
||||
func (a *Array256[T]) Get(i uint8) (value T, ok bool) {
|
||||
if a.Test(i) {
|
||||
return a.Items[a.Rank(i)-1], true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MustGet returns the value at index i without checking if it exists.
|
||||
//
|
||||
// Use only after ensuring i is set (via Test(i)); otherwise it may return
|
||||
// an incorrect value or panic. Intended only for tight, validated loops.
|
||||
func (a *Array256[T]) MustGet(i uint8) T {
|
||||
return a.Items[a.Rank(i)-1]
|
||||
}
|
||||
|
||||
// Len returns the number of items in sparse array.
|
||||
func (a *Array256[T]) Len() int {
|
||||
return len(a.Items)
|
||||
}
|
||||
|
||||
// Copy returns a shallow copy of the Array.
|
||||
// The elements are copied using assignment, this is no deep clone.
|
||||
func (a *Array256[T]) Copy() *Array256[T] {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := &Array256[T]{
|
||||
BitSet256: a.BitSet256,
|
||||
Items: make([]T, len(a.Items)),
|
||||
}
|
||||
copy(c.Items, a.Items)
|
||||
return c
|
||||
}
|
||||
|
||||
// InsertAt inserts or overwrites the value at sparse index i.
|
||||
//
|
||||
// If the slot at i already exists, its value is overwritten in-place and
|
||||
// (rank0, true) is returned, where rank0 = Rank(i)-1 is the 0-based index
|
||||
// into Items[].
|
||||
//
|
||||
// If the slot is new, the bit for i is set, the value is inserted into Items[]
|
||||
// at the correct packed position, and (rank0, false) is returned.
|
||||
//
|
||||
// rank0 can be cached by the caller to directly access Items[rank0]
|
||||
// without a second Rank() call.
|
||||
func (a *Array256[T]) InsertAt(i uint8, value T) (rank0 int, exists bool) {
|
||||
|
||||
// slot exists, just overwrite value
|
||||
if a.Test(i) {
|
||||
rank0 = a.Rank(i) - 1
|
||||
a.Items[rank0] = value
|
||||
return rank0, true
|
||||
}
|
||||
|
||||
// Since i is not set yet, Rank(i) before Set(i) is exactly
|
||||
// the index where the new item should be inserted (equivalent to Rank(i)-1 after Set(i)).
|
||||
rank0 = a.Rank(i)
|
||||
|
||||
// new, insert into bitset ...
|
||||
a.BitSet256.Set(i)
|
||||
|
||||
// ... and insert value into slice
|
||||
a.insertItem(rank0, value)
|
||||
|
||||
return rank0, false
|
||||
}
|
||||
|
||||
// DeleteAt removes the value at index i from the sparse array,
|
||||
// shifting remaining items down in the slice and clearing the bit.
|
||||
//
|
||||
// If the entry exists, it is returned together with true.
|
||||
// If i is not present, the zero value and false are returned.
|
||||
func (a *Array256[T]) DeleteAt(i uint8) (value T, exists bool) {
|
||||
if a.Len() == 0 || !a.Test(i) {
|
||||
return value, exists
|
||||
}
|
||||
|
||||
rank0 := a.Rank(i) - 1
|
||||
value = a.Items[rank0]
|
||||
|
||||
// delete from slice
|
||||
a.deleteItem(rank0)
|
||||
|
||||
// delete from bitset
|
||||
a.BitSet256.Clear(i)
|
||||
|
||||
return value, true
|
||||
}
|
||||
|
||||
// insertItem inserts a new element at the given index position i in the Items slice,
|
||||
// shifting all following elements one position to the right to make space.
|
||||
//
|
||||
// This method must be called with the correct insertion index - that is,
|
||||
// the rank-0 value of the corresponding bit index i in BitSet256 (calculated before Set(i)).
|
||||
//
|
||||
// The slice will be extended by one element. If the capacity allows, this is done
|
||||
// without reallocation (fast path); otherwise slice growth occurs (slow path).
|
||||
//
|
||||
// Example (inserting at rank0 == 2):
|
||||
//
|
||||
// Items before: [A B C D]
|
||||
// After insertItem(2, X): [A B X C D]
|
||||
//
|
||||
// Panics if i is out of range (i < 0 or i > len(Items)).
|
||||
func (a *Array256[T]) insertItem(i int, item T) {
|
||||
if len(a.Items) < cap(a.Items) {
|
||||
a.Items = a.Items[:len(a.Items)+1] // fast resize, no alloc
|
||||
} else {
|
||||
var zero T
|
||||
a.Items = append(a.Items, zero) // append one item, mostly enlarge cap by more than one item
|
||||
}
|
||||
|
||||
_ = a.Items[i] // BCE
|
||||
copy(a.Items[i+1:], a.Items[i:]) // shift one slot right, starting at [i]
|
||||
a.Items[i] = item // insert new item at [i]
|
||||
}
|
||||
|
||||
// deleteItem removes the item at index i from the Items slice,
|
||||
// shifting all subsequent items one position to the left,
|
||||
// and clearing the final (now duplicate) slot.
|
||||
//
|
||||
// This is used to remove Items[i] in response to a BitSet256.Clear(i)
|
||||
// operation, where i is guaranteed to be set and valid.
|
||||
//
|
||||
// Example (deleting rank0 == 1):
|
||||
//
|
||||
// Items before: [A B C D]
|
||||
// After deleteItem(1): [A C D]
|
||||
//
|
||||
// The tail item is explicitly cleared (assigned the zero-value of T)
|
||||
// to avoid holding references (important for GC with pointer types).
|
||||
//
|
||||
// Panics if i is out of range (i < 0 or i >= len(Items)).
|
||||
func (a *Array256[T]) deleteItem(i int) {
|
||||
var zero T
|
||||
|
||||
_ = a.Items[i] // BCE
|
||||
copy(a.Items[i:], a.Items[i+1:]) // shift left, overwrite item at [i]
|
||||
|
||||
nl := len(a.Items) - 1 // new len
|
||||
|
||||
a.Items[nl] = zero // clear the tail item
|
||||
a.Items = a.Items[:nl] // new len, cap is unchanged
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package golden
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// Table is a simple and slow route table, implemented as a slice of prefixes
|
||||
// and values as a golden reference for bart.
|
||||
type Table[V any] []TableItem[V]
|
||||
|
||||
type TableItem[V any] struct {
|
||||
Pfx netip.Prefix
|
||||
Val V
|
||||
}
|
||||
|
||||
func (g TableItem[V]) String() string {
|
||||
return fmt.Sprintf("(%s, %v)", g.Pfx, g.Val)
|
||||
}
|
||||
|
||||
func (t *Table[V]) Insert(pfx netip.Prefix, val V) {
|
||||
pfx = pfx.Masked()
|
||||
for i, item := range *t {
|
||||
if item.Pfx == pfx {
|
||||
(*t)[i].Val = val // de-dupe
|
||||
return
|
||||
}
|
||||
}
|
||||
*t = append(*t, TableItem[V]{pfx, val})
|
||||
}
|
||||
|
||||
func (t *Table[V]) Delete(pfx netip.Prefix) (exists bool) {
|
||||
pfx = pfx.Masked()
|
||||
|
||||
for i, item := range *t {
|
||||
if item.Pfx == pfx {
|
||||
*t = slices.Delete(*t, i, i+1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t Table[V]) AllSorted() []netip.Prefix {
|
||||
var result []netip.Prefix
|
||||
|
||||
for _, item := range t {
|
||||
result = append(result, item.Pfx)
|
||||
}
|
||||
slices.SortFunc(result, cmpPrefix)
|
||||
return result
|
||||
}
|
||||
|
||||
func (t Table[V]) Get(pfx netip.Prefix) (val V, ok bool) {
|
||||
pfx = pfx.Masked()
|
||||
for _, item := range t {
|
||||
if item.Pfx == pfx {
|
||||
return item.Val, true
|
||||
}
|
||||
}
|
||||
return val, false
|
||||
}
|
||||
|
||||
func (t *Table[V]) Update(pfx netip.Prefix, cb func(V, bool) V) (val V) {
|
||||
pfx = pfx.Masked()
|
||||
for i, item := range *t {
|
||||
if item.Pfx == pfx {
|
||||
// update val
|
||||
val = cb(item.Val, true)
|
||||
(*t)[i].Val = val
|
||||
return val
|
||||
}
|
||||
}
|
||||
// new val
|
||||
val = cb(val, false)
|
||||
|
||||
*t = append(*t, TableItem[V]{pfx, val})
|
||||
return val
|
||||
}
|
||||
|
||||
func (ta *Table[V]) Union(tb *Table[V]) {
|
||||
for _, bItem := range *tb {
|
||||
var match bool
|
||||
for i, aItem := range *ta {
|
||||
if aItem.Pfx == bItem.Pfx {
|
||||
(*ta)[i] = bItem
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
*ta = append(*ta, bItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t Table[V]) Lookup(addr netip.Addr) (val V, ok bool) {
|
||||
bestLen := -1
|
||||
|
||||
for _, item := range t {
|
||||
if item.Pfx.Contains(addr) && item.Pfx.Bits() > bestLen {
|
||||
val = item.Val
|
||||
ok = true
|
||||
bestLen = item.Pfx.Bits()
|
||||
}
|
||||
}
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (t Table[V]) LookupPrefix(pfx netip.Prefix) (val V, ok bool) {
|
||||
pfx = pfx.Masked()
|
||||
bestLen := -1
|
||||
|
||||
for _, item := range t {
|
||||
if item.Pfx.Overlaps(pfx) && item.Pfx.Bits() <= pfx.Bits() && item.Pfx.Bits() > bestLen {
|
||||
val = item.Val
|
||||
ok = true
|
||||
bestLen = item.Pfx.Bits()
|
||||
}
|
||||
}
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (t Table[V]) LookupPrefixLPM(pfx netip.Prefix) (lpm netip.Prefix, val V, ok bool) {
|
||||
pfx = pfx.Masked()
|
||||
bestLen := -1
|
||||
|
||||
for _, item := range t {
|
||||
if item.Pfx.Overlaps(pfx) && item.Pfx.Bits() <= pfx.Bits() && item.Pfx.Bits() > bestLen {
|
||||
val = item.Val
|
||||
lpm = item.Pfx
|
||||
ok = true
|
||||
bestLen = item.Pfx.Bits()
|
||||
}
|
||||
}
|
||||
return lpm, val, ok
|
||||
}
|
||||
|
||||
func (t Table[V]) Subnets(pfx netip.Prefix) []netip.Prefix {
|
||||
pfx = pfx.Masked()
|
||||
var result []netip.Prefix
|
||||
|
||||
for _, item := range t {
|
||||
if pfx.Overlaps(item.Pfx) && pfx.Bits() <= item.Pfx.Bits() {
|
||||
result = append(result, item.Pfx)
|
||||
}
|
||||
}
|
||||
slices.SortFunc(result, cmpPrefix)
|
||||
return result
|
||||
}
|
||||
|
||||
func (t Table[V]) Supernets(pfx netip.Prefix) []netip.Prefix {
|
||||
pfx = pfx.Masked()
|
||||
var result []netip.Prefix
|
||||
|
||||
for _, item := range t {
|
||||
if item.Pfx.Overlaps(pfx) && item.Pfx.Bits() <= pfx.Bits() {
|
||||
result = append(result, item.Pfx)
|
||||
}
|
||||
}
|
||||
slices.SortFunc(result, cmpPrefix)
|
||||
slices.Reverse(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func (t Table[V]) OverlapsPrefix(pfx netip.Prefix) bool {
|
||||
pfx = pfx.Masked()
|
||||
for _, p := range t {
|
||||
if p.Pfx.Overlaps(pfx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ta *Table[V]) Overlaps(tb *Table[V]) bool {
|
||||
for _, aItem := range *ta {
|
||||
for _, bItem := range *tb {
|
||||
if aItem.Pfx.Overlaps(bItem.Pfx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Sort, inplace by netip.Prefix, all prefixes are in normalized form
|
||||
func (t *Table[V]) Sort() {
|
||||
slices.SortFunc(*t, func(a, b TableItem[V]) int {
|
||||
return cmpPrefix(a.Pfx, b.Pfx)
|
||||
})
|
||||
}
|
||||
|
||||
// cmpPrefix, helper function, compare func for prefix sort,
|
||||
// all cidrs are already normalized
|
||||
func cmpPrefix(a, b netip.Prefix) int {
|
||||
if cmpAddr := a.Addr().Compare(b.Addr()); cmpAddr != 0 {
|
||||
return cmpAddr
|
||||
}
|
||||
|
||||
return cmp.Compare(a.Bits(), b.Bits())
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package random
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// mpp is a abbreviation and panics on non masked prefixes.
|
||||
//
|
||||
//nolint:gochecknoglobals
|
||||
var mpp = func(s string) netip.Prefix {
|
||||
pfx := netip.MustParsePrefix(s)
|
||||
if pfx == pfx.Masked() {
|
||||
return pfx
|
||||
}
|
||||
panic(fmt.Sprintf("%s is not canonicalized as %s", s, pfx.Masked()))
|
||||
}
|
||||
|
||||
// Prefix returns a randomly generated prefix
|
||||
func Prefix(prng *rand.Rand) netip.Prefix {
|
||||
if prng.IntN(2) == 1 {
|
||||
return Prefix4(prng)
|
||||
}
|
||||
return Prefix6(prng)
|
||||
}
|
||||
|
||||
func Prefix4(prng *rand.Rand) netip.Prefix {
|
||||
bits := prng.IntN(33)
|
||||
return netip.PrefixFrom(IP4(prng), bits).Masked()
|
||||
}
|
||||
|
||||
func Prefix6(prng *rand.Rand) netip.Prefix {
|
||||
bits := prng.IntN(129)
|
||||
return netip.PrefixFrom(IP6(prng), bits).Masked()
|
||||
}
|
||||
|
||||
func IP4(prng *rand.Rand) netip.Addr {
|
||||
var b [4]byte
|
||||
for i := range b {
|
||||
//nolint:gosec // G115: integer overflow conversion uint -> byte
|
||||
b[i] = byte(prng.UintN(256))
|
||||
}
|
||||
return netip.AddrFrom4(b)
|
||||
}
|
||||
|
||||
func IP6(prng *rand.Rand) netip.Addr {
|
||||
var b [16]byte
|
||||
for i := range b {
|
||||
//nolint:gosec // G115: integer overflow conversion uint -> byte
|
||||
b[i] = byte(prng.UintN(256))
|
||||
}
|
||||
return netip.AddrFrom16(b)
|
||||
}
|
||||
|
||||
func IP(prng *rand.Rand) netip.Addr {
|
||||
if prng.IntN(2) == 1 {
|
||||
return IP4(prng)
|
||||
}
|
||||
return IP6(prng)
|
||||
}
|
||||
|
||||
func RealWorldPrefixes4(prng *rand.Rand, n int) []netip.Prefix {
|
||||
set := make(map[netip.Prefix]struct{})
|
||||
pfxs := make([]netip.Prefix, 0, n)
|
||||
|
||||
for len(set) < n {
|
||||
pfx := Prefix4(prng)
|
||||
|
||||
// skip too small or too big masks
|
||||
if pfx.Bits() < 8 || pfx.Bits() > 28 {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip reserved/experimental ranges (e.g., 240.0.0.0/8)
|
||||
if pfx.Overlaps(mpp("240.0.0.0/8")) {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := set[pfx]; !ok {
|
||||
set[pfx] = struct{}{}
|
||||
pfxs = append(pfxs, pfx)
|
||||
}
|
||||
}
|
||||
return pfxs
|
||||
}
|
||||
|
||||
func RealWorldPrefixes6(prng *rand.Rand, n int) []netip.Prefix {
|
||||
set := make(map[netip.Prefix]struct{})
|
||||
pfxs := make([]netip.Prefix, 0, n)
|
||||
|
||||
for len(set) < n {
|
||||
pfx := Prefix6(prng)
|
||||
|
||||
// skip too small or too big masks
|
||||
if pfx.Bits() < 16 || pfx.Bits() > 56 {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip non global routes seen in the real world
|
||||
if !pfx.Overlaps(mpp("2000::/3")) {
|
||||
continue
|
||||
}
|
||||
if pfx.Addr().Compare(mpp("2c0f::/16").Addr()) == 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := set[pfx]; !ok {
|
||||
set[pfx] = struct{}{}
|
||||
pfxs = append(pfxs, pfx)
|
||||
}
|
||||
}
|
||||
return pfxs
|
||||
}
|
||||
|
||||
func RealWorldPrefixes(prng *rand.Rand, n int) []netip.Prefix {
|
||||
pfxs := make([]netip.Prefix, 0, n)
|
||||
pfxs = append(pfxs, RealWorldPrefixes4(prng, n/2)...)
|
||||
pfxs = append(pfxs, RealWorldPrefixes6(prng, n-len(pfxs))...)
|
||||
|
||||
prng.Shuffle(len(pfxs), func(i, j int) {
|
||||
pfxs[i], pfxs[j] = pfxs[j], pfxs[i]
|
||||
})
|
||||
|
||||
return pfxs
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package value provides runtime utilities for working with generic type
|
||||
// parameters as payload values in bart data structures.
|
||||
//
|
||||
// The package offers three main categories of utilities:
|
||||
//
|
||||
// # V as type struct{} Detection
|
||||
//
|
||||
// IsEmptyStruct[V] detects whether a type parameter V is a `struct{}`. These
|
||||
// types carry no information in their values. Omitting them from dumps
|
||||
// and prints reduces line noise and improves readability.
|
||||
//
|
||||
// # Value Equality
|
||||
//
|
||||
// The [Equal] function enables custom equality logic for payload values.
|
||||
// When V implements an `Equal(V) bool` method, [Equal] uses that implementation,
|
||||
// avoiding the potentially expensive [reflect.DeepEqual] fallback.
|
||||
//
|
||||
// # Value Cloning
|
||||
//
|
||||
// The [CloneFnFactory] supports copying of payload values for persistent
|
||||
// operations. When V implements a `Clone() V` method,
|
||||
// bart methods like InsertPersist, DeletePersist, and UnionPersist use the
|
||||
// generated clone function to create independent copies.
|
||||
//
|
||||
// This is an internal package used by the bart data structure implementation.
|
||||
package value
|
||||
|
||||
// IsEmptyStruct reports whether type V is the unnamed empty struct type `struct{}`.
|
||||
func IsEmptyStruct[V any]() bool {
|
||||
var zero V
|
||||
_, ok := any(zero).(struct{})
|
||||
return ok
|
||||
}
|
||||
|
||||
// Equal compares two values of type V for equality.
|
||||
//
|
||||
// If V implements an `Equal(V) bool` method, its custom equality logic is used.
|
||||
// Otherwise, values are compared directly using the == operator.
|
||||
//
|
||||
// If V is not comparable at runtime (such as a slice or map without an Equal
|
||||
// method), a runtime panic will occur.
|
||||
//
|
||||
// Note: If V implements Equal(V) bool with a pointer receiver, the Equal
|
||||
// method should handle nil receivers gracefully.
|
||||
func Equal[V any](v1, v2 V) bool {
|
||||
if eq, ok := any(v1).(interface{ Equal(V) bool }); ok {
|
||||
return eq.Equal(v2)
|
||||
}
|
||||
|
||||
return any(v1) == any(v2)
|
||||
}
|
||||
|
||||
// CloneFnFactory returns a function that takes a value of type V and returns
|
||||
// a copy by calling its `Clone` method.
|
||||
//
|
||||
// If V does not implement a `Clone() V` method, it returns nil.
|
||||
//
|
||||
// Note: If V implements `Clone() V` with a pointer receiver, the `Clone`
|
||||
// method should handle nil receivers gracefully.
|
||||
func CloneFnFactory[V any]() func(V) V {
|
||||
var zero V
|
||||
|
||||
// Safely check if V implements the clone method using an inline interface.
|
||||
if _, ok := any(zero).(interface{ Clone() V }); ok {
|
||||
// Return an anonymous closure directly.
|
||||
// Since we already proved V implements the interface above,
|
||||
// the direct type assertion here is guaranteed to succeed.
|
||||
return func(val V) V {
|
||||
return any(val).(interface{ Clone() V }).Clone()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
package bart
|
||||
|
||||
// Copyright (c) 2024 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/netip"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// DumpListNode contains CIDR, value and list of subnets (tree childrens).
|
||||
type DumpListNode[V any] struct {
|
||||
CIDR netip.Prefix `json:"cidr"`
|
||||
Value V `json:"value"`
|
||||
Subnets []DumpListNode[V] `json:"subnets,omitempty"`
|
||||
}
|
||||
|
||||
// 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 *Table[V]) MarshalJSON() ([]byte, error) {
|
||||
if t == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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 custom json representation.
|
||||
func (t *Table[V]) DumpList4() []DumpListNode[V] {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
return t.root4.dumpListRec(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 *Table[V]) DumpList6() []DumpListNode[V] {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
return t.root6.dumpListRec(0, stridePath{}, 0, false)
|
||||
}
|
||||
|
||||
func (n *node[V]) dumpListRec(parentIdx uint, path stridePath, depth int, is4 bool) []DumpListNode[V] {
|
||||
// recursion stop condition
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
directKids := n.getKidsRec(parentIdx, path, depth, is4)
|
||||
slices.SortFunc(directKids, cmpKidByPrefix[V])
|
||||
|
||||
nodes := make([]DumpListNode[V], 0, len(directKids))
|
||||
for _, kid := range directKids {
|
||||
nodes = append(nodes, DumpListNode[V]{
|
||||
CIDR: kid.cidr,
|
||||
Value: kid.val,
|
||||
Subnets: kid.n.dumpListRec(kid.idx, kid.path, kid.depth, is4),
|
||||
})
|
||||
}
|
||||
|
||||
return nodes
|
||||
}
|
||||
+680
@@ -0,0 +1,680 @@
|
||||
// Copyright (c) 2026 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package bart
|
||||
|
||||
import (
|
||||
"io"
|
||||
"iter"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/gaissmai/bart/internal/art"
|
||||
"github.com/gaissmai/bart/internal/lpm"
|
||||
"github.com/gaissmai/bart/internal/nodes"
|
||||
)
|
||||
|
||||
// Lite follows the BART design but with no payload.
|
||||
// It is ideal for simple IP ACLs (access-control-lists) with plain
|
||||
// true/false results with the smallest memory consumption.
|
||||
//
|
||||
// The zero value is ready to use.
|
||||
//
|
||||
// A Lite table must not be copied by value; always pass by pointer.
|
||||
// Nil pointers as receivers or arguments are forbidden and will panic.
|
||||
//
|
||||
// Performance note: Do not pass IPv4-in-IPv6 addresses (e.g., ::ffff:192.0.2.1)
|
||||
// as input. The methods do not perform automatic unmapping to avoid unnecessary
|
||||
// overhead for the common case where native addresses are used.
|
||||
// Users should unmap IPv4-in-IPv6 addresses to their native IPv4 form
|
||||
// (e.g., 192.0.2.1) before calling these methods.
|
||||
type Lite struct {
|
||||
liteTable[struct{}]
|
||||
}
|
||||
|
||||
// BEGIN OF liteTable WRAPPER
|
||||
|
||||
// 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 is valid and exists, true is returned, otherwise false.
|
||||
//
|
||||
// For longest-prefix-match (LPM) lookups, use Contains(ip), Lookup(ip),
|
||||
// LookupPrefix(pfx) or LookupPrefixLPM(pfx) instead.
|
||||
func (l *Lite) Get(pfx netip.Prefix) bool {
|
||||
_, ok := l.liteTable.Get(pfx)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Lookup performs a longest-prefix-match (LPM) for addr.
|
||||
//
|
||||
// Note: Lite stores no payload values, so this method is rarely useful.
|
||||
// Prefer Contains(addr) to check whether any prefix matches the address.
|
||||
// For exact prefix existence use Get(pfx). For prefix-based LPM use
|
||||
// LookupPrefix or LookupPrefixLPM.
|
||||
//
|
||||
// Returns true if any prefix matches addr, otherwise false.
|
||||
func (l *Lite) Lookup(ip netip.Addr) bool {
|
||||
return l.Contains(ip)
|
||||
}
|
||||
|
||||
// LookupPrefix performs a longest prefix match lookup for any address within
|
||||
// the given prefix.
|
||||
//
|
||||
// Returns true if a matching prefix is found, otherwise false.
|
||||
func (l *Lite) LookupPrefix(pfx netip.Prefix) bool {
|
||||
_, ok := l.lookupPrefixLPM(pfx, false)
|
||||
return ok
|
||||
}
|
||||
|
||||
// LookupPrefixLPM performs a longest prefix match lookup for any address within
|
||||
// the given prefix. It finds the most specific routing table entry that would
|
||||
// match any address in the provided prefix range.
|
||||
//
|
||||
// This is functionally identical to LookupPrefix but returns the
|
||||
// matching LPM prefix itself.
|
||||
//
|
||||
// This method is slower than LookupPrefix and should only be used if the
|
||||
// matching lpm entry is also required for other reasons.
|
||||
//
|
||||
// Returns the matching prefix and true if found, otherwise the zero value and false.
|
||||
func (l *Lite) LookupPrefixLPM(pfx netip.Prefix) (lpmPfx netip.Prefix, ok bool) {
|
||||
return l.lookupPrefixLPM(pfx, true)
|
||||
}
|
||||
|
||||
// Insert adds a prefix to the routing table.
|
||||
// If the prefix already exists, it's a no-op; 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 (l *Lite) Insert(pfx netip.Prefix) {
|
||||
l.liteTable.Insert(pfx, struct{}{})
|
||||
}
|
||||
|
||||
// InsertPersist is similar to Insert but the receiver isn't modified.
|
||||
//
|
||||
// All nodes touched during insert are cloned and a new *Lite is returned.
|
||||
// This is not a full [Lite.Clone], all untouched nodes are still referenced
|
||||
// from both Tables.
|
||||
//
|
||||
// This is orders of magnitude slower than Insert,
|
||||
// typically taking μsec instead of nsec.
|
||||
//
|
||||
// The bulk table load could be done with [Lite.Insert] and then you can
|
||||
// use [Lite.InsertPersist], [Lite.ModifyPersist] and [Lite.DeletePersist]
|
||||
// for further lock-free ops.
|
||||
func (l *Lite) InsertPersist(pfx netip.Prefix) *Lite {
|
||||
lp := l.liteTable.InsertPersist(pfx, struct{}{})
|
||||
//nolint:govet // copy of *lp is here by intention
|
||||
return &Lite{*lp}
|
||||
}
|
||||
|
||||
// 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 *Lite reflecting the change.
|
||||
//
|
||||
// If the prefix is invalid or doesn't exist, the original table is
|
||||
// returned unchanged.
|
||||
//
|
||||
// Due to cloning overhead this is significantly slower than Delete,
|
||||
// typically taking μsec instead of nsec.
|
||||
func (l *Lite) DeletePersist(pfx netip.Prefix) *Lite {
|
||||
lp := l.liteTable.DeletePersist(pfx)
|
||||
if lp == &l.liteTable {
|
||||
// pfx is invalid or didn't exist
|
||||
return l
|
||||
}
|
||||
|
||||
//nolint:govet // copy of *lp is here by intention
|
||||
return &Lite{*lp}
|
||||
}
|
||||
|
||||
// Modify applies an insert, update, or delete for the given prefix.
|
||||
// The prefix is canonicalized (Masked) internally before the operation.
|
||||
// The operation is determined by the callback function, which is called with:
|
||||
//
|
||||
// true: the prefix is in table
|
||||
// false: the prefix is not in table
|
||||
//
|
||||
// The callback returns:
|
||||
//
|
||||
// true: delete the entry
|
||||
// false: insert or update
|
||||
//
|
||||
// Summary of callback semantics:
|
||||
//
|
||||
// | input | return | op |
|
||||
// ---------------------------
|
||||
// | false | true | no-op |
|
||||
// | false | false | insert |
|
||||
// | true | false | update |
|
||||
// | true | true | delete |
|
||||
// ---------------------------
|
||||
func (l *Lite) Modify(pfx netip.Prefix, cb func(exists bool) (del bool)) {
|
||||
// Adapt the callback to work with liteTable's signature
|
||||
adaptedCb := func(_ struct{}, exists bool) (_ struct{}, del bool) {
|
||||
return struct{}{}, cb(exists)
|
||||
}
|
||||
|
||||
l.liteTable.Modify(pfx, adaptedCb)
|
||||
}
|
||||
|
||||
// ModifyPersist is similar to Modify but the receiver isn't modified and
|
||||
// a new *Lite is returned.
|
||||
func (l *Lite) ModifyPersist(pfx netip.Prefix, cb func(exists bool) (del bool)) *Lite {
|
||||
// wrap callback to match the signature of liteTable.ModifyPersist
|
||||
cbWrapper := func(_ struct{}, exists bool) (_ struct{}, del bool) {
|
||||
return struct{}{}, cb(exists)
|
||||
}
|
||||
|
||||
lp := l.liteTable.ModifyPersist(pfx, cbWrapper)
|
||||
//nolint:govet // copy of *lp is here by intention
|
||||
return &Lite{*lp}
|
||||
}
|
||||
|
||||
// dropSeq2 converts a Seq2[netip.Prefix, V] into a Seq[netip.Prefix] by discarding the value.
|
||||
func dropSeq2[V any](seq2 iter.Seq2[netip.Prefix, V]) iter.Seq[netip.Prefix] {
|
||||
return func(yield func(netip.Prefix) bool) {
|
||||
seq2(func(p netip.Prefix, _ V) bool {
|
||||
return yield(p)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a copy of the routing table.
|
||||
func (l *Lite) Clone() *Lite {
|
||||
return &Lite{*l.liteTable.Clone()}
|
||||
}
|
||||
|
||||
// Union merges another routing table into the receiver table, modifying it in-place.
|
||||
//
|
||||
// All prefixes from the other table (o) are inserted into the receiver.
|
||||
func (l *Lite) Union(o *Lite) {
|
||||
l.liteTable.Union(&o.liteTable)
|
||||
}
|
||||
|
||||
// UnionPersist is similar to [Union] but the receiver isn't modified.
|
||||
//
|
||||
// All nodes touched during union are cloned and a new *Lite is returned.
|
||||
// If o is empty, no nodes are touched and the receiver may be
|
||||
// returned unchanged.
|
||||
func (l *Lite) UnionPersist(o *Lite) *Lite {
|
||||
lp := l.liteTable.UnionPersist(&o.liteTable)
|
||||
if lp == &l.liteTable {
|
||||
return l
|
||||
}
|
||||
//nolint:govet // copy of *lp is here by intention
|
||||
return &Lite{*lp}
|
||||
}
|
||||
|
||||
// All returns an iterator over all prefixes in the table.
|
||||
//
|
||||
// The iteration order is unspecified and may vary between calls; for a stable order,
|
||||
// use [Lite.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 (l *Lite) All() iter.Seq[netip.Prefix] {
|
||||
return dropSeq2(l.liteTable.All())
|
||||
}
|
||||
|
||||
// All4 is like [Lite.All] but only for the v4 routing table.
|
||||
func (l *Lite) All4() iter.Seq[netip.Prefix] {
|
||||
return dropSeq2(l.liteTable.All4())
|
||||
}
|
||||
|
||||
// All6 is like [Lite.All] but only for the v6 routing table.
|
||||
func (l *Lite) All6() iter.Seq[netip.Prefix] {
|
||||
return dropSeq2(l.liteTable.All6())
|
||||
}
|
||||
|
||||
// AllSorted is like [Lite.All] but the iteration is ordered in canonical
|
||||
// CIDR prefix sort order.
|
||||
func (l *Lite) AllSorted() iter.Seq[netip.Prefix] {
|
||||
return dropSeq2(l.liteTable.AllSorted())
|
||||
}
|
||||
|
||||
// AllSorted4 is like [Lite.AllSorted] but only for the v4 routing table.
|
||||
func (l *Lite) AllSorted4() iter.Seq[netip.Prefix] {
|
||||
return dropSeq2(l.liteTable.AllSorted4())
|
||||
}
|
||||
|
||||
// AllSorted6 is like [Lite.AllSorted] but only for the v6 routing table.
|
||||
func (l *Lite) AllSorted6() iter.Seq[netip.Prefix] {
|
||||
return dropSeq2(l.liteTable.AllSorted6())
|
||||
}
|
||||
|
||||
// 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 := range table.Subnets(netip.MustParsePrefix("10.0.0.0/8")) {
|
||||
// fmt.Println("Covered:", sub)
|
||||
// }
|
||||
//
|
||||
// The iteration can be stopped early by breaking from the range loop.
|
||||
// Returns an empty iterator if the prefix is invalid.
|
||||
func (l *Lite) Subnets(pfx netip.Prefix) iter.Seq[netip.Prefix] {
|
||||
return dropSeq2(l.liteTable.Subnets(pfx))
|
||||
}
|
||||
|
||||
// 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
|
||||
// overlap or 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.
|
||||
//
|
||||
// The search is protocol-specific (IPv4 or IPv6) and stops immediately if the yield
|
||||
// function returns false. If pfx is invalid, the function silently returns.
|
||||
//
|
||||
// This can be used to enumerate all covering supernet routes in routing-based
|
||||
// policy engines, diagnostics tools, or fallback resolution logic.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// for supernet := range table.Supernets(netip.MustParsePrefix("192.0.2.128/25")) {
|
||||
// fmt.Println("Matched covering route:", supernet)
|
||||
// }
|
||||
func (l *Lite) Supernets(pfx netip.Prefix) iter.Seq[netip.Prefix] {
|
||||
return dropSeq2(l.liteTable.Supernets(pfx))
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// It is intentionally not nil-receiver safe: calling with a nil
|
||||
// receiver will panic by design.
|
||||
func (l *Lite) Overlaps(o *Lite) bool {
|
||||
return l.liteTable.Overlaps(&o.liteTable)
|
||||
}
|
||||
|
||||
// Overlaps4 is like [Lite.Overlaps] but for the v4 routing table only.
|
||||
func (l *Lite) Overlaps4(o *Lite) bool {
|
||||
return l.liteTable.Overlaps4(&o.liteTable)
|
||||
}
|
||||
|
||||
// Overlaps6 is like [Lite.Overlaps] but for the v6 routing table only.
|
||||
func (l *Lite) Overlaps6(o *Lite) bool {
|
||||
return l.liteTable.Overlaps6(&o.liteTable)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Note: Lite has no payload values, so this only checks structural equality.
|
||||
func (l *Lite) Equal(o *Lite) bool {
|
||||
return l.liteTable.Equal(&o.liteTable)
|
||||
}
|
||||
|
||||
// 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 (l *Lite) DumpList4() []DumpListNode[struct{}] {
|
||||
return l.liteTable.DumpList4()
|
||||
}
|
||||
|
||||
// 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 (l *Lite) DumpList6() []DumpListNode[struct{}] {
|
||||
return l.liteTable.DumpList6()
|
||||
}
|
||||
|
||||
// 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 (l *Lite) Fprint(w io.Writer) error {
|
||||
return l.liteTable.Fprint(w)
|
||||
}
|
||||
|
||||
// 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 (l *Lite) MarshalJSON() ([]byte, error) {
|
||||
return l.liteTable.MarshalJSON()
|
||||
}
|
||||
|
||||
// MarshalText implements the [encoding.TextMarshaler] interface,
|
||||
// just a wrapper for [liteTable.Fprint].
|
||||
func (l *Lite) MarshalText() ([]byte, error) {
|
||||
return l.liteTable.MarshalText()
|
||||
}
|
||||
|
||||
// END OF liteTable WRAPPER
|
||||
|
||||
// liteTable follows the BART design but with no payload.
|
||||
// It is ideal for simple IP ACLs (access-control-lists) with plain
|
||||
// true/false results with the smallest memory consumption.
|
||||
//
|
||||
// Performance note: Do not pass IPv4-in-IPv6 addresses (e.g., ::ffff:192.0.2.1)
|
||||
// as input. The methods do not perform automatic unmapping to avoid unnecessary
|
||||
// overhead for the common case where native addresses are used.
|
||||
// Users should unmap IPv4-in-IPv6 addresses to their native IPv4 form
|
||||
// (e.g., 192.0.2.1) before calling these methods.
|
||||
type liteTable[V any] struct {
|
||||
// used by -copylocks checker from `go vet`.
|
||||
_ [0]sync.Mutex
|
||||
|
||||
root4 nodes.LiteNode[V]
|
||||
root6 nodes.LiteNode[V]
|
||||
|
||||
// the number of prefixes in the routing table
|
||||
size4 int
|
||||
size6 int
|
||||
}
|
||||
|
||||
// rootNodeByVersion, root node getter for ip version.
|
||||
func (l *liteTable[V]) rootNodeByVersion(is4 bool) *nodes.LiteNode[V] {
|
||||
if is4 {
|
||||
return &l.root4
|
||||
}
|
||||
return &l.root6
|
||||
}
|
||||
|
||||
// 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 (l *liteTable[V]) Insert(pfx netip.Prefix, val V) {
|
||||
l.insert(pfx, val)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Due to cloning overhead this is significantly slower than Insert,
|
||||
// typically taking μsec instead of nsec.
|
||||
func (l *liteTable[V]) InsertPersist(pfx netip.Prefix, val V) *liteTable[V] {
|
||||
return l.insertPersist(pfx, val)
|
||||
}
|
||||
|
||||
// Modify applies an insert, update, or delete operation for the value
|
||||
// associated with the given prefix. The supplied callback decides the
|
||||
// operation: it is called with the current value (or zero if not found)
|
||||
// and a boolean indicating whether the prefix exists. The callback must
|
||||
// return a new value and a delete flag: del == false inserts or updates,
|
||||
// del == true deletes the entry if it exists (otherwise no-op).
|
||||
//
|
||||
// The operation is determined by the callback function, which is called with:
|
||||
//
|
||||
// val: the current value (or zero value if not found)
|
||||
// found: true if the prefix currently exists, false otherwise
|
||||
//
|
||||
// The callback returns:
|
||||
//
|
||||
// val: the new value to insert or update (ignored if del == true)
|
||||
// del: true to delete the entry, false to insert or update
|
||||
//
|
||||
// Summary of callback semantics:
|
||||
//
|
||||
// | cb-input | cb-return | Ops |
|
||||
// ------------------------------------- --------
|
||||
// | (zero, false) | (_, true) | no-op |
|
||||
// | (zero, false) | (newVal, false) | insert |
|
||||
// | (oldVal, true) | (newVal, false) | update |
|
||||
// | (oldVal, true) | (_, true) | delete |
|
||||
// ------------------------------------- --------
|
||||
func (t *liteTable[V]) Modify(pfx netip.Prefix, cb func(_ V, ok bool) (_ V, del bool)) {
|
||||
if !pfx.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
// canonicalize prefix
|
||||
pfx = pfx.Masked()
|
||||
|
||||
is4 := pfx.Addr().Is4()
|
||||
|
||||
n := t.rootNodeByVersion(is4)
|
||||
|
||||
delta := n.Modify(pfx, cb)
|
||||
t.sizeUpdate(is4, delta)
|
||||
}
|
||||
|
||||
// Contains reports whether any stored prefix covers the given IP address.
|
||||
// Returns false for invalid IP addresses.
|
||||
//
|
||||
// This performs longest-prefix matching and returns true if any prefix
|
||||
// in the routing table contains the IP address, regardless of the associated value.
|
||||
//
|
||||
// It does not return the value nor the prefix of the matching item,
|
||||
// but as a test against an allow-/deny-list it's often sufficient
|
||||
// and even few nanoseconds faster than [Table.Lookup].
|
||||
func (l *liteTable[V]) Contains(ip netip.Addr) bool {
|
||||
// speed is top priority: no explicit test for ip.IsValid
|
||||
// if ip is invalid, AsSlice() returns nil, Contains returns false.
|
||||
is4 := ip.Is4()
|
||||
n := l.rootNodeByVersion(is4)
|
||||
|
||||
for _, octet := range ip.AsSlice() {
|
||||
// for contains, any lpm match is good enough, no backtracking needed
|
||||
if n.Prefixes.Count != 0 && n.Contains(art.OctetToIdx(octet)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// stop traversing?
|
||||
if !n.Children.Test(octet) {
|
||||
return false
|
||||
}
|
||||
kid := n.MustGetChild(octet)
|
||||
|
||||
// kid is node or leaf or fringe at octet
|
||||
switch kid := kid.(type) {
|
||||
case *nodes.LiteNode[V]:
|
||||
n = kid // descend down to next trie level
|
||||
|
||||
case *nodes.FringeNode[V]:
|
||||
// fringe is the default-route for all possible octets below
|
||||
return true
|
||||
|
||||
case *nodes.LeafNode[V]:
|
||||
return kid.Prefix.Contains(ip)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Lookup is just a wrapper for Contains.
|
||||
// Returns the zero value V and true if a prefix matches ip, otherwise zero value and false.
|
||||
// This method exists to provide a consistent interface for code generation.
|
||||
func (l *liteTable[V]) Lookup(ip netip.Addr) (val V, exists bool) {
|
||||
return val, l.Contains(ip)
|
||||
}
|
||||
|
||||
// LookupPrefix performs a longest prefix match lookup for any address within
|
||||
// the given prefix. It finds the most specific routing table entry that would
|
||||
// match any address in the provided prefix range.
|
||||
//
|
||||
// This is functionally identical to LookupPrefixLPM but returns only the
|
||||
// associated value, not the matching prefix itself.
|
||||
//
|
||||
// Returns the zero value and true if a matching prefix is found.
|
||||
// Returns zero value and false if no match exists.
|
||||
func (l *liteTable[V]) LookupPrefix(pfx netip.Prefix) (val V, exists bool) {
|
||||
_, exists = l.lookupPrefixLPM(pfx, false)
|
||||
return
|
||||
}
|
||||
|
||||
// LookupPrefixLPM performs a longest prefix match lookup for any address within
|
||||
// the given prefix. It finds the most specific routing table entry that would
|
||||
// match any address in the provided prefix range.
|
||||
//
|
||||
// This is functionally identical to LookupPrefix but additionally returns the
|
||||
// matching LPM prefix itself along with the value.
|
||||
//
|
||||
// This method is slower than LookupPrefix and should only be used if the
|
||||
// matching lpm entry is also required for other reasons.
|
||||
//
|
||||
// Returns the matching prefix, the zero value, and true if found.
|
||||
// Returns zero values and false if no match exists.
|
||||
func (l *liteTable[V]) LookupPrefixLPM(pfx netip.Prefix) (lpm netip.Prefix, val V, exists bool) {
|
||||
lpm, exists = l.lookupPrefixLPM(pfx, true)
|
||||
return
|
||||
}
|
||||
|
||||
// lookupPrefixLPM performs a longest prefix match lookup for any address within
|
||||
// the given prefix. It finds the most specific routing table entry that would
|
||||
// match any address in the provided prefix range. If withLPM is true, it also
|
||||
// returns the matching longest prefix.
|
||||
func (l *liteTable[V]) lookupPrefixLPM(pfx netip.Prefix, withLPM bool) (lpmPfx netip.Prefix, ok bool) {
|
||||
if !pfx.IsValid() {
|
||||
return lpmPfx, ok
|
||||
}
|
||||
|
||||
// canonicalize the prefix
|
||||
pfx = pfx.Masked()
|
||||
|
||||
ip := pfx.Addr()
|
||||
pfxLen := pfx.Bits()
|
||||
is4 := ip.Is4()
|
||||
octets := ip.AsSlice()
|
||||
strideCount, modBits := nodes.DivMod8(pfxLen)
|
||||
|
||||
n := l.rootNodeByVersion(is4)
|
||||
|
||||
// record path to leaf node
|
||||
stack := [nodes.MaxTreeDepth]*nodes.LiteNode[V]{}
|
||||
|
||||
var depth int
|
||||
var octet byte
|
||||
|
||||
LOOP:
|
||||
// find the last node on the octets path in the trie,
|
||||
for depth, octet = range octets {
|
||||
depth &= nodes.DepthMask // BCE
|
||||
|
||||
// stepped one past the last stride of interest; back up to last and break
|
||||
if depth > strideCount {
|
||||
depth--
|
||||
break
|
||||
}
|
||||
// push current node on stack
|
||||
stack[depth] = n
|
||||
|
||||
// go down in tight loop to leaf node
|
||||
if !n.Children.Test(octet) {
|
||||
break LOOP
|
||||
}
|
||||
kid := n.MustGetChild(octet)
|
||||
|
||||
// kid is node or leaf or fringe at octet
|
||||
switch kid := kid.(type) {
|
||||
case *nodes.LiteNode[V]:
|
||||
n = kid
|
||||
continue LOOP // descend down to next trie level
|
||||
|
||||
case *nodes.LeafNode[V]:
|
||||
// reached a path compressed prefix, stop traversing
|
||||
if kid.Prefix.Bits() > pfxLen || !kid.Prefix.Contains(ip) {
|
||||
break LOOP
|
||||
}
|
||||
return kid.Prefix, true
|
||||
|
||||
case *nodes.FringeNode[V]:
|
||||
// the bits of the fringe are defined by the depth
|
||||
// maybe the LPM isn't needed, saves some cycles
|
||||
fringeBits := (depth + 1) << 3
|
||||
if fringeBits > pfxLen {
|
||||
break LOOP
|
||||
}
|
||||
|
||||
// the LPM isn't needed, saves some cycles
|
||||
if !withLPM {
|
||||
return netip.Prefix{}, true
|
||||
}
|
||||
|
||||
// get the LPM prefix back from ip and depth
|
||||
// it's a fringe, bits are always /8, /16, /24, ...
|
||||
fringePfx, _ := ip.Prefix((depth + 1) << 3)
|
||||
return fringePfx, true
|
||||
}
|
||||
}
|
||||
|
||||
// start backtracking, unwind the stack
|
||||
for ; depth >= 0; depth-- {
|
||||
depth &= nodes.DepthMask // BCE
|
||||
|
||||
n = stack[depth]
|
||||
|
||||
// longest prefix match, skip if node has no prefixes
|
||||
if n.PrefixCount() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var idx uint8
|
||||
octet = octets[depth]
|
||||
|
||||
// only the final stride may have a different prefix len
|
||||
// all others are just host routes
|
||||
if depth == strideCount {
|
||||
idx = art.PfxToIdx(octet, modBits)
|
||||
} else {
|
||||
idx = art.OctetToIdx(octet)
|
||||
}
|
||||
|
||||
// manually inlined: lookupIdx(idx)
|
||||
var topIdx uint8
|
||||
if topIdx, ok = n.Prefixes.IntersectionTop(&lpm.LookupTbl[idx]); ok {
|
||||
// called from LookupPrefix
|
||||
if !withLPM {
|
||||
return netip.Prefix{}, ok
|
||||
}
|
||||
|
||||
// called from LookupPrefixLPM
|
||||
|
||||
// get the bits from depth and top idx
|
||||
pfxBits := int(art.PfxBits(depth, topIdx))
|
||||
|
||||
// calculate the lpmPfx from incoming ip and new mask
|
||||
// netip.Addr.Prefix canonicalizes. Invariant: art.PfxBits(depth, topIdx)
|
||||
// yields a valid mask (v4: 0..32, v6: 0..128), so error is impossible.
|
||||
lpmPfx, _ = ip.Prefix(pfxBits)
|
||||
return lpmPfx, ok
|
||||
}
|
||||
}
|
||||
|
||||
return lpmPfx, ok
|
||||
}
|
||||
+731
@@ -0,0 +1,731 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
-538
@@ -1,538 +0,0 @@
|
||||
package bart
|
||||
|
||||
import "github.com/gaissmai/bart/internal/bitset"
|
||||
|
||||
// lpmLookupTbl is the backtracking sequence in the complete binary tree as bitstring.
|
||||
//
|
||||
// for idx := 1; idx > 0; idx >>= 1 { b.Set(idx) }
|
||||
//
|
||||
// allows a one shot bitset intersection algorithm:
|
||||
//
|
||||
// func (n *node[V]) lpmTest(idx uint) bool {
|
||||
// return n.prefixes.IntersectsAny(lpmLookupTbl[idx])
|
||||
// }
|
||||
//
|
||||
// instead of a sequence of single bitset tests:
|
||||
//
|
||||
// func (n *node[V]) lpmTest(idx uint) bool {
|
||||
// for ; idx > 0; idx >>= 1 {
|
||||
// if n.prefixes.Test(idx) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
var lpmLookupTbl = [512]bitset.BitSet{
|
||||
/* idx: 0 */ {}, // invalid
|
||||
/* idx: 1 */ {0x2}, // 0b0000_0010
|
||||
/* idx: 2 */ {0x6}, // 0b0000_0110
|
||||
/* idx: 3 */ {0xa}, // 0b0000_1010
|
||||
/* idx: 4 */ {0x16}, // ...
|
||||
/* idx: 5 */ {0x26},
|
||||
/* idx: 6 */ {0x4a},
|
||||
/* idx: 7 */ {0x8a},
|
||||
/* idx: 8 */ {0x116},
|
||||
/* idx: 9 */ {0x216},
|
||||
/* idx: 10 */ {0x426},
|
||||
/* idx: 11 */ {0x826},
|
||||
/* idx: 12 */ {0x104a},
|
||||
/* idx: 13 */ {0x204a},
|
||||
/* idx: 14 */ {0x408a},
|
||||
/* idx: 15 */ {0x808a},
|
||||
/* idx: 16 */ {0x10116},
|
||||
/* idx: 17 */ {0x20116},
|
||||
/* idx: 18 */ {0x40216},
|
||||
/* idx: 19 */ {0x80216},
|
||||
/* idx: 20 */ {0x100426},
|
||||
/* idx: 21 */ {0x200426},
|
||||
/* idx: 22 */ {0x400826},
|
||||
/* idx: 23 */ {0x800826},
|
||||
/* idx: 24 */ {0x100104a},
|
||||
/* idx: 25 */ {0x200104a},
|
||||
/* idx: 26 */ {0x400204a},
|
||||
/* idx: 27 */ {0x800204a},
|
||||
/* idx: 28 */ {0x1000408a},
|
||||
/* idx: 29 */ {0x2000408a},
|
||||
/* idx: 30 */ {0x4000808a},
|
||||
/* idx: 31 */ {0x8000808a},
|
||||
/* idx: 32 */ {0x100010116},
|
||||
/* idx: 33 */ {0x200010116},
|
||||
/* idx: 34 */ {0x400020116},
|
||||
/* idx: 35 */ {0x800020116},
|
||||
/* idx: 36 */ {0x1000040216},
|
||||
/* idx: 37 */ {0x2000040216},
|
||||
/* idx: 38 */ {0x4000080216},
|
||||
/* idx: 39 */ {0x8000080216},
|
||||
/* idx: 40 */ {0x10000100426},
|
||||
/* idx: 41 */ {0x20000100426},
|
||||
/* idx: 42 */ {0x40000200426},
|
||||
/* idx: 43 */ {0x80000200426},
|
||||
/* idx: 44 */ {0x100000400826},
|
||||
/* idx: 45 */ {0x200000400826},
|
||||
/* idx: 46 */ {0x400000800826},
|
||||
/* idx: 47 */ {0x800000800826},
|
||||
/* idx: 48 */ {0x100000100104a},
|
||||
/* idx: 49 */ {0x200000100104a},
|
||||
/* idx: 50 */ {0x400000200104a},
|
||||
/* idx: 51 */ {0x800000200104a},
|
||||
/* idx: 52 */ {0x1000000400204a},
|
||||
/* idx: 53 */ {0x2000000400204a},
|
||||
/* idx: 54 */ {0x4000000800204a},
|
||||
/* idx: 55 */ {0x8000000800204a},
|
||||
/* idx: 56 */ {0x10000001000408a},
|
||||
/* idx: 57 */ {0x20000001000408a},
|
||||
/* idx: 58 */ {0x40000002000408a},
|
||||
/* idx: 59 */ {0x80000002000408a},
|
||||
/* idx: 60 */ {0x100000004000808a},
|
||||
/* idx: 61 */ {0x200000004000808a},
|
||||
/* idx: 62 */ {0x400000008000808a},
|
||||
/* idx: 63 */ {0x800000008000808a},
|
||||
/* idx: 64 */ {0x100010116, 0x1},
|
||||
/* idx: 65 */ {0x100010116, 0x2},
|
||||
/* idx: 66 */ {0x200010116, 0x4},
|
||||
/* idx: 67 */ {0x200010116, 0x8},
|
||||
/* idx: 68 */ {0x400020116, 0x10},
|
||||
/* idx: 69 */ {0x400020116, 0x20},
|
||||
/* idx: 70 */ {0x800020116, 0x40},
|
||||
/* idx: 71 */ {0x800020116, 0x80},
|
||||
/* idx: 72 */ {0x1000040216, 0x100},
|
||||
/* idx: 73 */ {0x1000040216, 0x200},
|
||||
/* idx: 74 */ {0x2000040216, 0x400},
|
||||
/* idx: 75 */ {0x2000040216, 0x800},
|
||||
/* idx: 76 */ {0x4000080216, 0x1000},
|
||||
/* idx: 77 */ {0x4000080216, 0x2000},
|
||||
/* idx: 78 */ {0x8000080216, 0x4000},
|
||||
/* idx: 79 */ {0x8000080216, 0x8000},
|
||||
/* idx: 80 */ {0x10000100426, 0x10000},
|
||||
/* idx: 81 */ {0x10000100426, 0x20000},
|
||||
/* idx: 82 */ {0x20000100426, 0x40000},
|
||||
/* idx: 83 */ {0x20000100426, 0x80000},
|
||||
/* idx: 84 */ {0x40000200426, 0x100000},
|
||||
/* idx: 85 */ {0x40000200426, 0x200000},
|
||||
/* idx: 86 */ {0x80000200426, 0x400000},
|
||||
/* idx: 87 */ {0x80000200426, 0x800000},
|
||||
/* idx: 88 */ {0x100000400826, 0x1000000},
|
||||
/* idx: 89 */ {0x100000400826, 0x2000000},
|
||||
/* idx: 90 */ {0x200000400826, 0x4000000},
|
||||
/* idx: 91 */ {0x200000400826, 0x8000000},
|
||||
/* idx: 92 */ {0x400000800826, 0x10000000},
|
||||
/* idx: 93 */ {0x400000800826, 0x20000000},
|
||||
/* idx: 94 */ {0x800000800826, 0x40000000},
|
||||
/* idx: 95 */ {0x800000800826, 0x80000000},
|
||||
/* idx: 96 */ {0x100000100104a, 0x100000000},
|
||||
/* idx: 97 */ {0x100000100104a, 0x200000000},
|
||||
/* idx: 98 */ {0x200000100104a, 0x400000000},
|
||||
/* idx: 99 */ {0x200000100104a, 0x800000000},
|
||||
/* idx: 100 */ {0x400000200104a, 0x1000000000},
|
||||
/* idx: 101 */ {0x400000200104a, 0x2000000000},
|
||||
/* idx: 102 */ {0x800000200104a, 0x4000000000},
|
||||
/* idx: 103 */ {0x800000200104a, 0x8000000000},
|
||||
/* idx: 104 */ {0x1000000400204a, 0x10000000000},
|
||||
/* idx: 105 */ {0x1000000400204a, 0x20000000000},
|
||||
/* idx: 106 */ {0x2000000400204a, 0x40000000000},
|
||||
/* idx: 107 */ {0x2000000400204a, 0x80000000000},
|
||||
/* idx: 108 */ {0x4000000800204a, 0x100000000000},
|
||||
/* idx: 109 */ {0x4000000800204a, 0x200000000000},
|
||||
/* idx: 110 */ {0x8000000800204a, 0x400000000000},
|
||||
/* idx: 111 */ {0x8000000800204a, 0x800000000000},
|
||||
/* idx: 112 */ {0x10000001000408a, 0x1000000000000},
|
||||
/* idx: 113 */ {0x10000001000408a, 0x2000000000000},
|
||||
/* idx: 114 */ {0x20000001000408a, 0x4000000000000},
|
||||
/* idx: 115 */ {0x20000001000408a, 0x8000000000000},
|
||||
/* idx: 116 */ {0x40000002000408a, 0x10000000000000},
|
||||
/* idx: 117 */ {0x40000002000408a, 0x20000000000000},
|
||||
/* idx: 118 */ {0x80000002000408a, 0x40000000000000},
|
||||
/* idx: 119 */ {0x80000002000408a, 0x80000000000000},
|
||||
/* idx: 120 */ {0x100000004000808a, 0x100000000000000},
|
||||
/* idx: 121 */ {0x100000004000808a, 0x200000000000000},
|
||||
/* idx: 122 */ {0x200000004000808a, 0x400000000000000},
|
||||
/* idx: 123 */ {0x200000004000808a, 0x800000000000000},
|
||||
/* idx: 124 */ {0x400000008000808a, 0x1000000000000000},
|
||||
/* idx: 125 */ {0x400000008000808a, 0x2000000000000000},
|
||||
/* idx: 126 */ {0x800000008000808a, 0x4000000000000000},
|
||||
/* idx: 127 */ {0x800000008000808a, 0x8000000000000000},
|
||||
/* idx: 128 */ {0x100010116, 0x1, 0x1},
|
||||
/* idx: 129 */ {0x100010116, 0x1, 0x2},
|
||||
/* idx: 130 */ {0x100010116, 0x2, 0x4},
|
||||
/* idx: 131 */ {0x100010116, 0x2, 0x8},
|
||||
/* idx: 132 */ {0x200010116, 0x4, 0x10},
|
||||
/* idx: 133 */ {0x200010116, 0x4, 0x20},
|
||||
/* idx: 134 */ {0x200010116, 0x8, 0x40},
|
||||
/* idx: 135 */ {0x200010116, 0x8, 0x80},
|
||||
/* idx: 136 */ {0x400020116, 0x10, 0x100},
|
||||
/* idx: 137 */ {0x400020116, 0x10, 0x200},
|
||||
/* idx: 138 */ {0x400020116, 0x20, 0x400},
|
||||
/* idx: 139 */ {0x400020116, 0x20, 0x800},
|
||||
/* idx: 140 */ {0x800020116, 0x40, 0x1000},
|
||||
/* idx: 141 */ {0x800020116, 0x40, 0x2000},
|
||||
/* idx: 142 */ {0x800020116, 0x80, 0x4000},
|
||||
/* idx: 143 */ {0x800020116, 0x80, 0x8000},
|
||||
/* idx: 144 */ {0x1000040216, 0x100, 0x10000},
|
||||
/* idx: 145 */ {0x1000040216, 0x100, 0x20000},
|
||||
/* idx: 146 */ {0x1000040216, 0x200, 0x40000},
|
||||
/* idx: 147 */ {0x1000040216, 0x200, 0x80000},
|
||||
/* idx: 148 */ {0x2000040216, 0x400, 0x100000},
|
||||
/* idx: 149 */ {0x2000040216, 0x400, 0x200000},
|
||||
/* idx: 150 */ {0x2000040216, 0x800, 0x400000},
|
||||
/* idx: 151 */ {0x2000040216, 0x800, 0x800000},
|
||||
/* idx: 152 */ {0x4000080216, 0x1000, 0x1000000},
|
||||
/* idx: 153 */ {0x4000080216, 0x1000, 0x2000000},
|
||||
/* idx: 154 */ {0x4000080216, 0x2000, 0x4000000},
|
||||
/* idx: 155 */ {0x4000080216, 0x2000, 0x8000000},
|
||||
/* idx: 156 */ {0x8000080216, 0x4000, 0x10000000},
|
||||
/* idx: 157 */ {0x8000080216, 0x4000, 0x20000000},
|
||||
/* idx: 158 */ {0x8000080216, 0x8000, 0x40000000},
|
||||
/* idx: 159 */ {0x8000080216, 0x8000, 0x80000000},
|
||||
/* idx: 160 */ {0x10000100426, 0x10000, 0x100000000},
|
||||
/* idx: 161 */ {0x10000100426, 0x10000, 0x200000000},
|
||||
/* idx: 162 */ {0x10000100426, 0x20000, 0x400000000},
|
||||
/* idx: 163 */ {0x10000100426, 0x20000, 0x800000000},
|
||||
/* idx: 164 */ {0x20000100426, 0x40000, 0x1000000000},
|
||||
/* idx: 165 */ {0x20000100426, 0x40000, 0x2000000000},
|
||||
/* idx: 166 */ {0x20000100426, 0x80000, 0x4000000000},
|
||||
/* idx: 167 */ {0x20000100426, 0x80000, 0x8000000000},
|
||||
/* idx: 168 */ {0x40000200426, 0x100000, 0x10000000000},
|
||||
/* idx: 169 */ {0x40000200426, 0x100000, 0x20000000000},
|
||||
/* idx: 170 */ {0x40000200426, 0x200000, 0x40000000000},
|
||||
/* idx: 171 */ {0x40000200426, 0x200000, 0x80000000000},
|
||||
/* idx: 172 */ {0x80000200426, 0x400000, 0x100000000000},
|
||||
/* idx: 173 */ {0x80000200426, 0x400000, 0x200000000000},
|
||||
/* idx: 174 */ {0x80000200426, 0x800000, 0x400000000000},
|
||||
/* idx: 175 */ {0x80000200426, 0x800000, 0x800000000000},
|
||||
/* idx: 176 */ {0x100000400826, 0x1000000, 0x1000000000000},
|
||||
/* idx: 177 */ {0x100000400826, 0x1000000, 0x2000000000000},
|
||||
/* idx: 178 */ {0x100000400826, 0x2000000, 0x4000000000000},
|
||||
/* idx: 179 */ {0x100000400826, 0x2000000, 0x8000000000000},
|
||||
/* idx: 180 */ {0x200000400826, 0x4000000, 0x10000000000000},
|
||||
/* idx: 181 */ {0x200000400826, 0x4000000, 0x20000000000000},
|
||||
/* idx: 182 */ {0x200000400826, 0x8000000, 0x40000000000000},
|
||||
/* idx: 183 */ {0x200000400826, 0x8000000, 0x80000000000000},
|
||||
/* idx: 184 */ {0x400000800826, 0x10000000, 0x100000000000000},
|
||||
/* idx: 185 */ {0x400000800826, 0x10000000, 0x200000000000000},
|
||||
/* idx: 186 */ {0x400000800826, 0x20000000, 0x400000000000000},
|
||||
/* idx: 187 */ {0x400000800826, 0x20000000, 0x800000000000000},
|
||||
/* idx: 188 */ {0x800000800826, 0x40000000, 0x1000000000000000},
|
||||
/* idx: 189 */ {0x800000800826, 0x40000000, 0x2000000000000000},
|
||||
/* idx: 190 */ {0x800000800826, 0x80000000, 0x4000000000000000},
|
||||
/* idx: 191 */ {0x800000800826, 0x80000000, 0x8000000000000000},
|
||||
/* idx: 192 */ {0x100000100104a, 0x100000000, 0x0, 0x1},
|
||||
/* idx: 193 */ {0x100000100104a, 0x100000000, 0x0, 0x2},
|
||||
/* idx: 194 */ {0x100000100104a, 0x200000000, 0x0, 0x4},
|
||||
/* idx: 195 */ {0x100000100104a, 0x200000000, 0x0, 0x8},
|
||||
/* idx: 196 */ {0x200000100104a, 0x400000000, 0x0, 0x10},
|
||||
/* idx: 197 */ {0x200000100104a, 0x400000000, 0x0, 0x20},
|
||||
/* idx: 198 */ {0x200000100104a, 0x800000000, 0x0, 0x40},
|
||||
/* idx: 199 */ {0x200000100104a, 0x800000000, 0x0, 0x80},
|
||||
/* idx: 200 */ {0x400000200104a, 0x1000000000, 0x0, 0x100},
|
||||
/* idx: 201 */ {0x400000200104a, 0x1000000000, 0x0, 0x200},
|
||||
/* idx: 202 */ {0x400000200104a, 0x2000000000, 0x0, 0x400},
|
||||
/* idx: 203 */ {0x400000200104a, 0x2000000000, 0x0, 0x800},
|
||||
/* idx: 204 */ {0x800000200104a, 0x4000000000, 0x0, 0x1000},
|
||||
/* idx: 205 */ {0x800000200104a, 0x4000000000, 0x0, 0x2000},
|
||||
/* idx: 206 */ {0x800000200104a, 0x8000000000, 0x0, 0x4000},
|
||||
/* idx: 207 */ {0x800000200104a, 0x8000000000, 0x0, 0x8000},
|
||||
/* idx: 208 */ {0x1000000400204a, 0x10000000000, 0x0, 0x10000},
|
||||
/* idx: 209 */ {0x1000000400204a, 0x10000000000, 0x0, 0x20000},
|
||||
/* idx: 210 */ {0x1000000400204a, 0x20000000000, 0x0, 0x40000},
|
||||
/* idx: 211 */ {0x1000000400204a, 0x20000000000, 0x0, 0x80000},
|
||||
/* idx: 212 */ {0x2000000400204a, 0x40000000000, 0x0, 0x100000},
|
||||
/* idx: 213 */ {0x2000000400204a, 0x40000000000, 0x0, 0x200000},
|
||||
/* idx: 214 */ {0x2000000400204a, 0x80000000000, 0x0, 0x400000},
|
||||
/* idx: 215 */ {0x2000000400204a, 0x80000000000, 0x0, 0x800000},
|
||||
/* idx: 216 */ {0x4000000800204a, 0x100000000000, 0x0, 0x1000000},
|
||||
/* idx: 217 */ {0x4000000800204a, 0x100000000000, 0x0, 0x2000000},
|
||||
/* idx: 218 */ {0x4000000800204a, 0x200000000000, 0x0, 0x4000000},
|
||||
/* idx: 219 */ {0x4000000800204a, 0x200000000000, 0x0, 0x8000000},
|
||||
/* idx: 220 */ {0x8000000800204a, 0x400000000000, 0x0, 0x10000000},
|
||||
/* idx: 221 */ {0x8000000800204a, 0x400000000000, 0x0, 0x20000000},
|
||||
/* idx: 222 */ {0x8000000800204a, 0x800000000000, 0x0, 0x40000000},
|
||||
/* idx: 223 */ {0x8000000800204a, 0x800000000000, 0x0, 0x80000000},
|
||||
/* idx: 224 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x100000000},
|
||||
/* idx: 225 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x200000000},
|
||||
/* idx: 226 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x400000000},
|
||||
/* idx: 227 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x800000000},
|
||||
/* idx: 228 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x1000000000},
|
||||
/* idx: 229 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x2000000000},
|
||||
/* idx: 230 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x4000000000},
|
||||
/* idx: 231 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x8000000000},
|
||||
/* idx: 232 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x10000000000},
|
||||
/* idx: 233 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x20000000000},
|
||||
/* idx: 234 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x40000000000},
|
||||
/* idx: 235 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x80000000000},
|
||||
/* idx: 236 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x100000000000},
|
||||
/* idx: 237 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x200000000000},
|
||||
/* idx: 238 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x400000000000},
|
||||
/* idx: 239 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x800000000000},
|
||||
/* idx: 240 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x1000000000000},
|
||||
/* idx: 241 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x2000000000000},
|
||||
/* idx: 242 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x4000000000000},
|
||||
/* idx: 243 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x8000000000000},
|
||||
/* idx: 244 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x10000000000000},
|
||||
/* idx: 245 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x20000000000000},
|
||||
/* idx: 246 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x40000000000000},
|
||||
/* idx: 247 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x80000000000000},
|
||||
/* idx: 248 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x100000000000000},
|
||||
/* idx: 249 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x200000000000000},
|
||||
/* idx: 250 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x400000000000000},
|
||||
/* idx: 251 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x800000000000000},
|
||||
/* idx: 252 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x1000000000000000},
|
||||
/* idx: 253 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x2000000000000000},
|
||||
/* idx: 254 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x4000000000000000},
|
||||
/* idx: 255 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x8000000000000000},
|
||||
/* idx: 256 */ {0x100010116, 0x1, 0x1, 0x0, 0x1},
|
||||
/* idx: 257 */ {0x100010116, 0x1, 0x1, 0x0, 0x2},
|
||||
/* idx: 258 */ {0x100010116, 0x1, 0x2, 0x0, 0x4},
|
||||
/* idx: 259 */ {0x100010116, 0x1, 0x2, 0x0, 0x8},
|
||||
/* idx: 260 */ {0x100010116, 0x2, 0x4, 0x0, 0x10},
|
||||
/* idx: 261 */ {0x100010116, 0x2, 0x4, 0x0, 0x20},
|
||||
/* idx: 262 */ {0x100010116, 0x2, 0x8, 0x0, 0x40},
|
||||
/* idx: 263 */ {0x100010116, 0x2, 0x8, 0x0, 0x80},
|
||||
/* idx: 264 */ {0x200010116, 0x4, 0x10, 0x0, 0x100},
|
||||
/* idx: 265 */ {0x200010116, 0x4, 0x10, 0x0, 0x200},
|
||||
/* idx: 266 */ {0x200010116, 0x4, 0x20, 0x0, 0x400},
|
||||
/* idx: 267 */ {0x200010116, 0x4, 0x20, 0x0, 0x800},
|
||||
/* idx: 268 */ {0x200010116, 0x8, 0x40, 0x0, 0x1000},
|
||||
/* idx: 269 */ {0x200010116, 0x8, 0x40, 0x0, 0x2000},
|
||||
/* idx: 270 */ {0x200010116, 0x8, 0x80, 0x0, 0x4000},
|
||||
/* idx: 271 */ {0x200010116, 0x8, 0x80, 0x0, 0x8000},
|
||||
/* idx: 272 */ {0x400020116, 0x10, 0x100, 0x0, 0x10000},
|
||||
/* idx: 273 */ {0x400020116, 0x10, 0x100, 0x0, 0x20000},
|
||||
/* idx: 274 */ {0x400020116, 0x10, 0x200, 0x0, 0x40000},
|
||||
/* idx: 275 */ {0x400020116, 0x10, 0x200, 0x0, 0x80000},
|
||||
/* idx: 276 */ {0x400020116, 0x20, 0x400, 0x0, 0x100000},
|
||||
/* idx: 277 */ {0x400020116, 0x20, 0x400, 0x0, 0x200000},
|
||||
/* idx: 278 */ {0x400020116, 0x20, 0x800, 0x0, 0x400000},
|
||||
/* idx: 279 */ {0x400020116, 0x20, 0x800, 0x0, 0x800000},
|
||||
/* idx: 280 */ {0x800020116, 0x40, 0x1000, 0x0, 0x1000000},
|
||||
/* idx: 281 */ {0x800020116, 0x40, 0x1000, 0x0, 0x2000000},
|
||||
/* idx: 282 */ {0x800020116, 0x40, 0x2000, 0x0, 0x4000000},
|
||||
/* idx: 283 */ {0x800020116, 0x40, 0x2000, 0x0, 0x8000000},
|
||||
/* idx: 284 */ {0x800020116, 0x80, 0x4000, 0x0, 0x10000000},
|
||||
/* idx: 285 */ {0x800020116, 0x80, 0x4000, 0x0, 0x20000000},
|
||||
/* idx: 286 */ {0x800020116, 0x80, 0x8000, 0x0, 0x40000000},
|
||||
/* idx: 287 */ {0x800020116, 0x80, 0x8000, 0x0, 0x80000000},
|
||||
/* idx: 288 */ {0x1000040216, 0x100, 0x10000, 0x0, 0x100000000},
|
||||
/* idx: 289 */ {0x1000040216, 0x100, 0x10000, 0x0, 0x200000000},
|
||||
/* idx: 290 */ {0x1000040216, 0x100, 0x20000, 0x0, 0x400000000},
|
||||
/* idx: 291 */ {0x1000040216, 0x100, 0x20000, 0x0, 0x800000000},
|
||||
/* idx: 292 */ {0x1000040216, 0x200, 0x40000, 0x0, 0x1000000000},
|
||||
/* idx: 293 */ {0x1000040216, 0x200, 0x40000, 0x0, 0x2000000000},
|
||||
/* idx: 294 */ {0x1000040216, 0x200, 0x80000, 0x0, 0x4000000000},
|
||||
/* idx: 295 */ {0x1000040216, 0x200, 0x80000, 0x0, 0x8000000000},
|
||||
/* idx: 296 */ {0x2000040216, 0x400, 0x100000, 0x0, 0x10000000000},
|
||||
/* idx: 297 */ {0x2000040216, 0x400, 0x100000, 0x0, 0x20000000000},
|
||||
/* idx: 298 */ {0x2000040216, 0x400, 0x200000, 0x0, 0x40000000000},
|
||||
/* idx: 299 */ {0x2000040216, 0x400, 0x200000, 0x0, 0x80000000000},
|
||||
/* idx: 300 */ {0x2000040216, 0x800, 0x400000, 0x0, 0x100000000000},
|
||||
/* idx: 301 */ {0x2000040216, 0x800, 0x400000, 0x0, 0x200000000000},
|
||||
/* idx: 302 */ {0x2000040216, 0x800, 0x800000, 0x0, 0x400000000000},
|
||||
/* idx: 303 */ {0x2000040216, 0x800, 0x800000, 0x0, 0x800000000000},
|
||||
/* idx: 304 */ {0x4000080216, 0x1000, 0x1000000, 0x0, 0x1000000000000},
|
||||
/* idx: 305 */ {0x4000080216, 0x1000, 0x1000000, 0x0, 0x2000000000000},
|
||||
/* idx: 306 */ {0x4000080216, 0x1000, 0x2000000, 0x0, 0x4000000000000},
|
||||
/* idx: 307 */ {0x4000080216, 0x1000, 0x2000000, 0x0, 0x8000000000000},
|
||||
/* idx: 308 */ {0x4000080216, 0x2000, 0x4000000, 0x0, 0x10000000000000},
|
||||
/* idx: 309 */ {0x4000080216, 0x2000, 0x4000000, 0x0, 0x20000000000000},
|
||||
/* idx: 310 */ {0x4000080216, 0x2000, 0x8000000, 0x0, 0x40000000000000},
|
||||
/* idx: 311 */ {0x4000080216, 0x2000, 0x8000000, 0x0, 0x80000000000000},
|
||||
/* idx: 312 */ {0x8000080216, 0x4000, 0x10000000, 0x0, 0x100000000000000},
|
||||
/* idx: 313 */ {0x8000080216, 0x4000, 0x10000000, 0x0, 0x200000000000000},
|
||||
/* idx: 314 */ {0x8000080216, 0x4000, 0x20000000, 0x0, 0x400000000000000},
|
||||
/* idx: 315 */ {0x8000080216, 0x4000, 0x20000000, 0x0, 0x800000000000000},
|
||||
/* idx: 316 */ {0x8000080216, 0x8000, 0x40000000, 0x0, 0x1000000000000000},
|
||||
/* idx: 317 */ {0x8000080216, 0x8000, 0x40000000, 0x0, 0x2000000000000000},
|
||||
/* idx: 318 */ {0x8000080216, 0x8000, 0x80000000, 0x0, 0x4000000000000000},
|
||||
/* idx: 319 */ {0x8000080216, 0x8000, 0x80000000, 0x0, 0x8000000000000000},
|
||||
/* idx: 320 */ {0x10000100426, 0x10000, 0x100000000, 0x0, 0x0, 0x1},
|
||||
/* idx: 321 */ {0x10000100426, 0x10000, 0x100000000, 0x0, 0x0, 0x2},
|
||||
/* idx: 322 */ {0x10000100426, 0x10000, 0x200000000, 0x0, 0x0, 0x4},
|
||||
/* idx: 323 */ {0x10000100426, 0x10000, 0x200000000, 0x0, 0x0, 0x8},
|
||||
/* idx: 324 */ {0x10000100426, 0x20000, 0x400000000, 0x0, 0x0, 0x10},
|
||||
/* idx: 325 */ {0x10000100426, 0x20000, 0x400000000, 0x0, 0x0, 0x20},
|
||||
/* idx: 326 */ {0x10000100426, 0x20000, 0x800000000, 0x0, 0x0, 0x40},
|
||||
/* idx: 327 */ {0x10000100426, 0x20000, 0x800000000, 0x0, 0x0, 0x80},
|
||||
/* idx: 328 */ {0x20000100426, 0x40000, 0x1000000000, 0x0, 0x0, 0x100},
|
||||
/* idx: 329 */ {0x20000100426, 0x40000, 0x1000000000, 0x0, 0x0, 0x200},
|
||||
/* idx: 330 */ {0x20000100426, 0x40000, 0x2000000000, 0x0, 0x0, 0x400},
|
||||
/* idx: 331 */ {0x20000100426, 0x40000, 0x2000000000, 0x0, 0x0, 0x800},
|
||||
/* idx: 332 */ {0x20000100426, 0x80000, 0x4000000000, 0x0, 0x0, 0x1000},
|
||||
/* idx: 333 */ {0x20000100426, 0x80000, 0x4000000000, 0x0, 0x0, 0x2000},
|
||||
/* idx: 334 */ {0x20000100426, 0x80000, 0x8000000000, 0x0, 0x0, 0x4000},
|
||||
/* idx: 335 */ {0x20000100426, 0x80000, 0x8000000000, 0x0, 0x0, 0x8000},
|
||||
/* idx: 336 */ {0x40000200426, 0x100000, 0x10000000000, 0x0, 0x0, 0x10000},
|
||||
/* idx: 337 */ {0x40000200426, 0x100000, 0x10000000000, 0x0, 0x0, 0x20000},
|
||||
/* idx: 338 */ {0x40000200426, 0x100000, 0x20000000000, 0x0, 0x0, 0x40000},
|
||||
/* idx: 339 */ {0x40000200426, 0x100000, 0x20000000000, 0x0, 0x0, 0x80000},
|
||||
/* idx: 340 */ {0x40000200426, 0x200000, 0x40000000000, 0x0, 0x0, 0x100000},
|
||||
/* idx: 341 */ {0x40000200426, 0x200000, 0x40000000000, 0x0, 0x0, 0x200000},
|
||||
/* idx: 342 */ {0x40000200426, 0x200000, 0x80000000000, 0x0, 0x0, 0x400000},
|
||||
/* idx: 343 */ {0x40000200426, 0x200000, 0x80000000000, 0x0, 0x0, 0x800000},
|
||||
/* idx: 344 */ {0x80000200426, 0x400000, 0x100000000000, 0x0, 0x0, 0x1000000},
|
||||
/* idx: 345 */ {0x80000200426, 0x400000, 0x100000000000, 0x0, 0x0, 0x2000000},
|
||||
/* idx: 346 */ {0x80000200426, 0x400000, 0x200000000000, 0x0, 0x0, 0x4000000},
|
||||
/* idx: 347 */ {0x80000200426, 0x400000, 0x200000000000, 0x0, 0x0, 0x8000000},
|
||||
/* idx: 348 */ {0x80000200426, 0x800000, 0x400000000000, 0x0, 0x0, 0x10000000},
|
||||
/* idx: 349 */ {0x80000200426, 0x800000, 0x400000000000, 0x0, 0x0, 0x20000000},
|
||||
/* idx: 350 */ {0x80000200426, 0x800000, 0x800000000000, 0x0, 0x0, 0x40000000},
|
||||
/* idx: 351 */ {0x80000200426, 0x800000, 0x800000000000, 0x0, 0x0, 0x80000000},
|
||||
/* idx: 352 */ {0x100000400826, 0x1000000, 0x1000000000000, 0x0, 0x0, 0x100000000},
|
||||
/* idx: 353 */ {0x100000400826, 0x1000000, 0x1000000000000, 0x0, 0x0, 0x200000000},
|
||||
/* idx: 354 */ {0x100000400826, 0x1000000, 0x2000000000000, 0x0, 0x0, 0x400000000},
|
||||
/* idx: 355 */ {0x100000400826, 0x1000000, 0x2000000000000, 0x0, 0x0, 0x800000000},
|
||||
/* idx: 356 */ {0x100000400826, 0x2000000, 0x4000000000000, 0x0, 0x0, 0x1000000000},
|
||||
/* idx: 357 */ {0x100000400826, 0x2000000, 0x4000000000000, 0x0, 0x0, 0x2000000000},
|
||||
/* idx: 358 */ {0x100000400826, 0x2000000, 0x8000000000000, 0x0, 0x0, 0x4000000000},
|
||||
/* idx: 359 */ {0x100000400826, 0x2000000, 0x8000000000000, 0x0, 0x0, 0x8000000000},
|
||||
/* idx: 360 */ {0x200000400826, 0x4000000, 0x10000000000000, 0x0, 0x0, 0x10000000000},
|
||||
/* idx: 361 */ {0x200000400826, 0x4000000, 0x10000000000000, 0x0, 0x0, 0x20000000000},
|
||||
/* idx: 362 */ {0x200000400826, 0x4000000, 0x20000000000000, 0x0, 0x0, 0x40000000000},
|
||||
/* idx: 363 */ {0x200000400826, 0x4000000, 0x20000000000000, 0x0, 0x0, 0x80000000000},
|
||||
/* idx: 364 */ {0x200000400826, 0x8000000, 0x40000000000000, 0x0, 0x0, 0x100000000000},
|
||||
/* idx: 365 */ {0x200000400826, 0x8000000, 0x40000000000000, 0x0, 0x0, 0x200000000000},
|
||||
/* idx: 366 */ {0x200000400826, 0x8000000, 0x80000000000000, 0x0, 0x0, 0x400000000000},
|
||||
/* idx: 367 */ {0x200000400826, 0x8000000, 0x80000000000000, 0x0, 0x0, 0x800000000000},
|
||||
/* idx: 368 */ {0x400000800826, 0x10000000, 0x100000000000000, 0x0, 0x0, 0x1000000000000},
|
||||
/* idx: 369 */ {0x400000800826, 0x10000000, 0x100000000000000, 0x0, 0x0, 0x2000000000000},
|
||||
/* idx: 370 */ {0x400000800826, 0x10000000, 0x200000000000000, 0x0, 0x0, 0x4000000000000},
|
||||
/* idx: 371 */ {0x400000800826, 0x10000000, 0x200000000000000, 0x0, 0x0, 0x8000000000000},
|
||||
/* idx: 372 */ {0x400000800826, 0x20000000, 0x400000000000000, 0x0, 0x0, 0x10000000000000},
|
||||
/* idx: 373 */ {0x400000800826, 0x20000000, 0x400000000000000, 0x0, 0x0, 0x20000000000000},
|
||||
/* idx: 374 */ {0x400000800826, 0x20000000, 0x800000000000000, 0x0, 0x0, 0x40000000000000},
|
||||
/* idx: 375 */ {0x400000800826, 0x20000000, 0x800000000000000, 0x0, 0x0, 0x80000000000000},
|
||||
/* idx: 376 */ {0x800000800826, 0x40000000, 0x1000000000000000, 0x0, 0x0, 0x100000000000000},
|
||||
/* idx: 377 */ {0x800000800826, 0x40000000, 0x1000000000000000, 0x0, 0x0, 0x200000000000000},
|
||||
/* idx: 378 */ {0x800000800826, 0x40000000, 0x2000000000000000, 0x0, 0x0, 0x400000000000000},
|
||||
/* idx: 379 */ {0x800000800826, 0x40000000, 0x2000000000000000, 0x0, 0x0, 0x800000000000000},
|
||||
/* idx: 380 */ {0x800000800826, 0x80000000, 0x4000000000000000, 0x0, 0x0, 0x1000000000000000},
|
||||
/* idx: 381 */ {0x800000800826, 0x80000000, 0x4000000000000000, 0x0, 0x0, 0x2000000000000000},
|
||||
/* idx: 382 */ {0x800000800826, 0x80000000, 0x8000000000000000, 0x0, 0x0, 0x4000000000000000},
|
||||
/* idx: 383 */ {0x800000800826, 0x80000000, 0x8000000000000000, 0x0, 0x0, 0x8000000000000000},
|
||||
/* idx: 384 */ {0x100000100104a, 0x100000000, 0x0, 0x1, 0x0, 0x0, 0x1},
|
||||
/* idx: 385 */ {0x100000100104a, 0x100000000, 0x0, 0x1, 0x0, 0x0, 0x2},
|
||||
/* idx: 386 */ {0x100000100104a, 0x100000000, 0x0, 0x2, 0x0, 0x0, 0x4},
|
||||
/* idx: 387 */ {0x100000100104a, 0x100000000, 0x0, 0x2, 0x0, 0x0, 0x8},
|
||||
/* idx: 388 */ {0x100000100104a, 0x200000000, 0x0, 0x4, 0x0, 0x0, 0x10},
|
||||
/* idx: 389 */ {0x100000100104a, 0x200000000, 0x0, 0x4, 0x0, 0x0, 0x20},
|
||||
/* idx: 390 */ {0x100000100104a, 0x200000000, 0x0, 0x8, 0x0, 0x0, 0x40},
|
||||
/* idx: 391 */ {0x100000100104a, 0x200000000, 0x0, 0x8, 0x0, 0x0, 0x80},
|
||||
/* idx: 392 */ {0x200000100104a, 0x400000000, 0x0, 0x10, 0x0, 0x0, 0x100},
|
||||
/* idx: 393 */ {0x200000100104a, 0x400000000, 0x0, 0x10, 0x0, 0x0, 0x200},
|
||||
/* idx: 394 */ {0x200000100104a, 0x400000000, 0x0, 0x20, 0x0, 0x0, 0x400},
|
||||
/* idx: 395 */ {0x200000100104a, 0x400000000, 0x0, 0x20, 0x0, 0x0, 0x800},
|
||||
/* idx: 396 */ {0x200000100104a, 0x800000000, 0x0, 0x40, 0x0, 0x0, 0x1000},
|
||||
/* idx: 397 */ {0x200000100104a, 0x800000000, 0x0, 0x40, 0x0, 0x0, 0x2000},
|
||||
/* idx: 398 */ {0x200000100104a, 0x800000000, 0x0, 0x80, 0x0, 0x0, 0x4000},
|
||||
/* idx: 399 */ {0x200000100104a, 0x800000000, 0x0, 0x80, 0x0, 0x0, 0x8000},
|
||||
/* idx: 400 */ {0x400000200104a, 0x1000000000, 0x0, 0x100, 0x0, 0x0, 0x10000},
|
||||
/* idx: 401 */ {0x400000200104a, 0x1000000000, 0x0, 0x100, 0x0, 0x0, 0x20000},
|
||||
/* idx: 402 */ {0x400000200104a, 0x1000000000, 0x0, 0x200, 0x0, 0x0, 0x40000},
|
||||
/* idx: 403 */ {0x400000200104a, 0x1000000000, 0x0, 0x200, 0x0, 0x0, 0x80000},
|
||||
/* idx: 404 */ {0x400000200104a, 0x2000000000, 0x0, 0x400, 0x0, 0x0, 0x100000},
|
||||
/* idx: 405 */ {0x400000200104a, 0x2000000000, 0x0, 0x400, 0x0, 0x0, 0x200000},
|
||||
/* idx: 406 */ {0x400000200104a, 0x2000000000, 0x0, 0x800, 0x0, 0x0, 0x400000},
|
||||
/* idx: 407 */ {0x400000200104a, 0x2000000000, 0x0, 0x800, 0x0, 0x0, 0x800000},
|
||||
/* idx: 408 */ {0x800000200104a, 0x4000000000, 0x0, 0x1000, 0x0, 0x0, 0x1000000},
|
||||
/* idx: 409 */ {0x800000200104a, 0x4000000000, 0x0, 0x1000, 0x0, 0x0, 0x2000000},
|
||||
/* idx: 410 */ {0x800000200104a, 0x4000000000, 0x0, 0x2000, 0x0, 0x0, 0x4000000},
|
||||
/* idx: 411 */ {0x800000200104a, 0x4000000000, 0x0, 0x2000, 0x0, 0x0, 0x8000000},
|
||||
/* idx: 412 */ {0x800000200104a, 0x8000000000, 0x0, 0x4000, 0x0, 0x0, 0x10000000},
|
||||
/* idx: 413 */ {0x800000200104a, 0x8000000000, 0x0, 0x4000, 0x0, 0x0, 0x20000000},
|
||||
/* idx: 414 */ {0x800000200104a, 0x8000000000, 0x0, 0x8000, 0x0, 0x0, 0x40000000},
|
||||
/* idx: 415 */ {0x800000200104a, 0x8000000000, 0x0, 0x8000, 0x0, 0x0, 0x80000000},
|
||||
/* idx: 416 */ {0x1000000400204a, 0x10000000000, 0x0, 0x10000, 0x0, 0x0, 0x100000000},
|
||||
/* idx: 417 */ {0x1000000400204a, 0x10000000000, 0x0, 0x10000, 0x0, 0x0, 0x200000000},
|
||||
/* idx: 418 */ {0x1000000400204a, 0x10000000000, 0x0, 0x20000, 0x0, 0x0, 0x400000000},
|
||||
/* idx: 419 */ {0x1000000400204a, 0x10000000000, 0x0, 0x20000, 0x0, 0x0, 0x800000000},
|
||||
/* idx: 420 */ {0x1000000400204a, 0x20000000000, 0x0, 0x40000, 0x0, 0x0, 0x1000000000},
|
||||
/* idx: 421 */ {0x1000000400204a, 0x20000000000, 0x0, 0x40000, 0x0, 0x0, 0x2000000000},
|
||||
/* idx: 422 */ {0x1000000400204a, 0x20000000000, 0x0, 0x80000, 0x0, 0x0, 0x4000000000},
|
||||
/* idx: 423 */ {0x1000000400204a, 0x20000000000, 0x0, 0x80000, 0x0, 0x0, 0x8000000000},
|
||||
/* idx: 424 */ {0x2000000400204a, 0x40000000000, 0x0, 0x100000, 0x0, 0x0, 0x10000000000},
|
||||
/* idx: 425 */ {0x2000000400204a, 0x40000000000, 0x0, 0x100000, 0x0, 0x0, 0x20000000000},
|
||||
/* idx: 426 */ {0x2000000400204a, 0x40000000000, 0x0, 0x200000, 0x0, 0x0, 0x40000000000},
|
||||
/* idx: 427 */ {0x2000000400204a, 0x40000000000, 0x0, 0x200000, 0x0, 0x0, 0x80000000000},
|
||||
/* idx: 428 */ {0x2000000400204a, 0x80000000000, 0x0, 0x400000, 0x0, 0x0, 0x100000000000},
|
||||
/* idx: 429 */ {0x2000000400204a, 0x80000000000, 0x0, 0x400000, 0x0, 0x0, 0x200000000000},
|
||||
/* idx: 430 */ {0x2000000400204a, 0x80000000000, 0x0, 0x800000, 0x0, 0x0, 0x400000000000},
|
||||
/* idx: 431 */ {0x2000000400204a, 0x80000000000, 0x0, 0x800000, 0x0, 0x0, 0x800000000000},
|
||||
/* idx: 432 */ {0x4000000800204a, 0x100000000000, 0x0, 0x1000000, 0x0, 0x0, 0x1000000000000},
|
||||
/* idx: 433 */ {0x4000000800204a, 0x100000000000, 0x0, 0x1000000, 0x0, 0x0, 0x2000000000000},
|
||||
/* idx: 434 */ {0x4000000800204a, 0x100000000000, 0x0, 0x2000000, 0x0, 0x0, 0x4000000000000},
|
||||
/* idx: 435 */ {0x4000000800204a, 0x100000000000, 0x0, 0x2000000, 0x0, 0x0, 0x8000000000000},
|
||||
/* idx: 436 */ {0x4000000800204a, 0x200000000000, 0x0, 0x4000000, 0x0, 0x0, 0x10000000000000},
|
||||
/* idx: 437 */ {0x4000000800204a, 0x200000000000, 0x0, 0x4000000, 0x0, 0x0, 0x20000000000000},
|
||||
/* idx: 438 */ {0x4000000800204a, 0x200000000000, 0x0, 0x8000000, 0x0, 0x0, 0x40000000000000},
|
||||
/* idx: 439 */ {0x4000000800204a, 0x200000000000, 0x0, 0x8000000, 0x0, 0x0, 0x80000000000000},
|
||||
/* idx: 440 */ {0x8000000800204a, 0x400000000000, 0x0, 0x10000000, 0x0, 0x0, 0x100000000000000},
|
||||
/* idx: 441 */ {0x8000000800204a, 0x400000000000, 0x0, 0x10000000, 0x0, 0x0, 0x200000000000000},
|
||||
/* idx: 442 */ {0x8000000800204a, 0x400000000000, 0x0, 0x20000000, 0x0, 0x0, 0x400000000000000},
|
||||
/* idx: 443 */ {0x8000000800204a, 0x400000000000, 0x0, 0x20000000, 0x0, 0x0, 0x800000000000000},
|
||||
/* idx: 444 */ {0x8000000800204a, 0x800000000000, 0x0, 0x40000000, 0x0, 0x0, 0x1000000000000000},
|
||||
/* idx: 445 */ {0x8000000800204a, 0x800000000000, 0x0, 0x40000000, 0x0, 0x0, 0x2000000000000000},
|
||||
/* idx: 446 */ {0x8000000800204a, 0x800000000000, 0x0, 0x80000000, 0x0, 0x0, 0x4000000000000000},
|
||||
/* idx: 447 */ {0x8000000800204a, 0x800000000000, 0x0, 0x80000000, 0x0, 0x0, 0x8000000000000000},
|
||||
/* idx: 448 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x1},
|
||||
/* idx: 449 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x2},
|
||||
/* idx: 450 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x200000000, 0x0, 0x0, 0x0, 0x4},
|
||||
/* idx: 451 */ {0x10000001000408a, 0x1000000000000, 0x0, 0x200000000, 0x0, 0x0, 0x0, 0x8},
|
||||
/* idx: 452 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x400000000, 0x0, 0x0, 0x0, 0x10},
|
||||
/* idx: 453 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x400000000, 0x0, 0x0, 0x0, 0x20},
|
||||
/* idx: 454 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x800000000, 0x0, 0x0, 0x0, 0x40},
|
||||
/* idx: 455 */ {0x10000001000408a, 0x2000000000000, 0x0, 0x800000000, 0x0, 0x0, 0x0, 0x80},
|
||||
/* idx: 456 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x1000000000, 0x0, 0x0, 0x0, 0x100},
|
||||
/* idx: 457 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x1000000000, 0x0, 0x0, 0x0, 0x200},
|
||||
/* idx: 458 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x2000000000, 0x0, 0x0, 0x0, 0x400},
|
||||
/* idx: 459 */ {0x20000001000408a, 0x4000000000000, 0x0, 0x2000000000, 0x0, 0x0, 0x0, 0x800},
|
||||
/* idx: 460 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x4000000000, 0x0, 0x0, 0x0, 0x1000},
|
||||
/* idx: 461 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x4000000000, 0x0, 0x0, 0x0, 0x2000},
|
||||
/* idx: 462 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x8000000000, 0x0, 0x0, 0x0, 0x4000},
|
||||
/* idx: 463 */ {0x20000001000408a, 0x8000000000000, 0x0, 0x8000000000, 0x0, 0x0, 0x0, 0x8000},
|
||||
/* idx: 464 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x10000000000, 0x0, 0x0, 0x0, 0x10000},
|
||||
/* idx: 465 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x10000000000, 0x0, 0x0, 0x0, 0x20000},
|
||||
/* idx: 466 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x20000000000, 0x0, 0x0, 0x0, 0x40000},
|
||||
/* idx: 467 */ {0x40000002000408a, 0x10000000000000, 0x0, 0x20000000000, 0x0, 0x0, 0x0, 0x80000},
|
||||
/* idx: 468 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x40000000000, 0x0, 0x0, 0x0, 0x100000},
|
||||
/* idx: 469 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x40000000000, 0x0, 0x0, 0x0, 0x200000},
|
||||
/* idx: 470 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x80000000000, 0x0, 0x0, 0x0, 0x400000},
|
||||
/* idx: 471 */ {0x40000002000408a, 0x20000000000000, 0x0, 0x80000000000, 0x0, 0x0, 0x0, 0x800000},
|
||||
/* idx: 472 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x100000000000, 0x0, 0x0, 0x0, 0x1000000},
|
||||
/* idx: 473 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x100000000000, 0x0, 0x0, 0x0, 0x2000000},
|
||||
/* idx: 474 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x200000000000, 0x0, 0x0, 0x0, 0x4000000},
|
||||
/* idx: 475 */ {0x80000002000408a, 0x40000000000000, 0x0, 0x200000000000, 0x0, 0x0, 0x0, 0x8000000},
|
||||
/* idx: 476 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x400000000000, 0x0, 0x0, 0x0, 0x10000000},
|
||||
/* idx: 477 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x400000000000, 0x0, 0x0, 0x0, 0x20000000},
|
||||
/* idx: 478 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x800000000000, 0x0, 0x0, 0x0, 0x40000000},
|
||||
/* idx: 479 */ {0x80000002000408a, 0x80000000000000, 0x0, 0x800000000000, 0x0, 0x0, 0x0, 0x80000000},
|
||||
/* idx: 480 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x1000000000000, 0x0, 0x0, 0x0, 0x100000000},
|
||||
/* idx: 481 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x1000000000000, 0x0, 0x0, 0x0, 0x200000000},
|
||||
/* idx: 482 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x2000000000000, 0x0, 0x0, 0x0, 0x400000000},
|
||||
/* idx: 483 */ {0x100000004000808a, 0x100000000000000, 0x0, 0x2000000000000, 0x0, 0x0, 0x0, 0x800000000},
|
||||
/* idx: 484 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x4000000000000, 0x0, 0x0, 0x0, 0x1000000000},
|
||||
/* idx: 485 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x4000000000000, 0x0, 0x0, 0x0, 0x2000000000},
|
||||
/* idx: 486 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x8000000000000, 0x0, 0x0, 0x0, 0x4000000000},
|
||||
/* idx: 487 */ {0x100000004000808a, 0x200000000000000, 0x0, 0x8000000000000, 0x0, 0x0, 0x0, 0x8000000000},
|
||||
/* idx: 488 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x10000000000000, 0x0, 0x0, 0x0, 0x10000000000},
|
||||
/* idx: 489 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x10000000000000, 0x0, 0x0, 0x0, 0x20000000000},
|
||||
/* idx: 490 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x40000000000},
|
||||
/* idx: 491 */ {0x200000004000808a, 0x400000000000000, 0x0, 0x20000000000000, 0x0, 0x0, 0x0, 0x80000000000},
|
||||
/* idx: 492 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x40000000000000, 0x0, 0x0, 0x0, 0x100000000000},
|
||||
/* idx: 493 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x40000000000000, 0x0, 0x0, 0x0, 0x200000000000},
|
||||
/* idx: 494 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x80000000000000, 0x0, 0x0, 0x0, 0x400000000000},
|
||||
/* idx: 495 */ {0x200000004000808a, 0x800000000000000, 0x0, 0x80000000000000, 0x0, 0x0, 0x0, 0x800000000000},
|
||||
/* idx: 496 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x100000000000000, 0x0, 0x0, 0x0, 0x1000000000000},
|
||||
/* idx: 497 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x100000000000000, 0x0, 0x0, 0x0, 0x2000000000000},
|
||||
/* idx: 498 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x200000000000000, 0x0, 0x0, 0x0, 0x4000000000000},
|
||||
/* idx: 499 */ {0x400000008000808a, 0x1000000000000000, 0x0, 0x200000000000000, 0x0, 0x0, 0x0, 0x8000000000000},
|
||||
/* idx: 500 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x400000000000000, 0x0, 0x0, 0x0, 0x10000000000000},
|
||||
/* idx: 501 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x400000000000000, 0x0, 0x0, 0x0, 0x20000000000000},
|
||||
/* idx: 502 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x800000000000000, 0x0, 0x0, 0x0, 0x40000000000000},
|
||||
/* idx: 503 */ {0x400000008000808a, 0x2000000000000000, 0x0, 0x800000000000000, 0x0, 0x0, 0x0, 0x80000000000000},
|
||||
/* idx: 504 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x1000000000000000, 0x0, 0x0, 0x0, 0x100000000000000},
|
||||
/* idx: 505 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x1000000000000000, 0x0, 0x0, 0x0, 0x200000000000000},
|
||||
/* idx: 506 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x2000000000000000, 0x0, 0x0, 0x0, 0x400000000000000},
|
||||
/* idx: 507 */ {0x800000008000808a, 0x4000000000000000, 0x0, 0x2000000000000000, 0x0, 0x0, 0x0, 0x800000000000000},
|
||||
/* idx: 508 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x4000000000000000, 0x0, 0x0, 0x0, 0x1000000000000000},
|
||||
/* idx: 509 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x4000000000000000, 0x0, 0x0, 0x0, 0x2000000000000000},
|
||||
/* idx: 510 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x8000000000000000, 0x0, 0x0, 0x0, 0x4000000000000000},
|
||||
/* idx: 511 */ {0x800000008000808a, 0x8000000000000000, 0x0, 0x8000000000000000, 0x0, 0x0, 0x0, 0x8000000000000000},
|
||||
}
|
||||
-729
@@ -1,729 +0,0 @@
|
||||
// Copyright (c) 2024 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package bart
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"github.com/gaissmai/bart/internal/sparse"
|
||||
)
|
||||
|
||||
const (
|
||||
strideLen = 8 // octet
|
||||
maxTreeDepth = 16 // 16 for IPv6
|
||||
maxNodeChildren = 256 // 256
|
||||
maxNodePrefixes = 512 // 512
|
||||
)
|
||||
|
||||
// stridePath, max 16 octets deep
|
||||
type stridePath [maxTreeDepth]uint8
|
||||
|
||||
// node is a level node in the multibit-trie.
|
||||
// A node has prefixes and children, forming the multibit trie.
|
||||
//
|
||||
// The prefixes form a complete binary tree, see the artlookup.pdf
|
||||
// paper in the doc folder to understand the data structure.
|
||||
//
|
||||
// In contrast to the ART algorithm, sparse arrays
|
||||
// (popcount-compressed slices) are used instead of fixed-size arrays.
|
||||
//
|
||||
// The array slots are also not pre-allocated (alloted) as described
|
||||
// in the ART algorithm, fast backtracking with a bitset vector is used
|
||||
// to get the longest-prefix-match.
|
||||
//
|
||||
// The sparse child array recursively spans the trie with a branching factor of 256
|
||||
// and also records path-compressed leaves in the free node slots.
|
||||
type node[V any] struct {
|
||||
// prefixes contains the routes indexed as a complete binary tree with payload V
|
||||
// with the help of the baseIndex function from the ART algorithm.
|
||||
prefixes sparse.Array[V]
|
||||
|
||||
// children, recursively spans the trie with a branching factor of 256.
|
||||
//
|
||||
// Sorry, here we have to use a mixture of generics and interfaces:
|
||||
//
|
||||
// Without path compression, the definition would naturally be:
|
||||
// children sparse.Array[*node[V]]
|
||||
//
|
||||
// ... but with path compression the child can now be a node or a path compressed leaf.
|
||||
//
|
||||
// With path compression we could define:
|
||||
// type noder[V any] interface {
|
||||
// isLeaf[V]() bool
|
||||
// }
|
||||
//
|
||||
// and:
|
||||
// children sparse.Array[noder[V]]
|
||||
//
|
||||
// But we use the empty interface{} instead, by intention, see below!
|
||||
//
|
||||
// The empty interface{} consumes less memory and type assertions are faster than
|
||||
// indirect method calls like node.isLeaf()
|
||||
children sparse.Array[interface{}]
|
||||
}
|
||||
|
||||
// isEmpty returns true if node has neither prefixes nor children
|
||||
func (n *node[V]) isEmpty() bool {
|
||||
return n.prefixes.Len() == 0 && n.children.Len() == 0
|
||||
}
|
||||
|
||||
// leaf is a prefix with value, used as a path compressed child
|
||||
type leaf[V any] struct {
|
||||
prefix netip.Prefix
|
||||
value V
|
||||
}
|
||||
|
||||
// cloneOrCopy, helper function,
|
||||
// deep copy if v implements the Cloner interface.
|
||||
func cloneOrCopy[V any](val V) V {
|
||||
if cloner, ok := any(val).(Cloner[V]); ok {
|
||||
return cloner.Clone()
|
||||
}
|
||||
// just a shallow copy
|
||||
return val
|
||||
}
|
||||
|
||||
// cloneLeaf returns a clone of the leaf
|
||||
// if the value implements the Cloner interface.
|
||||
func (l *leaf[V]) cloneLeaf() *leaf[V] {
|
||||
return &leaf[V]{l.prefix, cloneOrCopy(l.value)}
|
||||
}
|
||||
|
||||
// insertAtDepth insert a prefix/val into a node tree at depth.
|
||||
// n must not be nil, prefix must be valid and already in canonical form.
|
||||
//
|
||||
// If a path compression has to be resolved because a new value is added
|
||||
// that collides with a leaf, the compressed leaf is then reinserted
|
||||
// one depth down in the node trie.
|
||||
func (n *node[V]) insertAtDepth(pfx netip.Prefix, val V, depth int) (exists bool) {
|
||||
ip := pfx.Addr()
|
||||
bits := pfx.Bits()
|
||||
|
||||
lastIdx, lastBits := lastOctetIdxAndBits(bits)
|
||||
|
||||
octets := ip.AsSlice()
|
||||
octets = octets[:lastIdx+1]
|
||||
|
||||
// find the proper trie node to insert prefix
|
||||
// start with prefix octet at depth
|
||||
for ; depth < len(octets); depth++ {
|
||||
octet := octets[depth]
|
||||
addr := uint(octet)
|
||||
|
||||
// last significant octet: insert/override prefix/val into node
|
||||
if depth == lastIdx {
|
||||
return n.prefixes.InsertAt(pfxToIdx(octet, lastBits), val)
|
||||
}
|
||||
|
||||
if !n.children.Test(addr) {
|
||||
// insert prefix path compressed
|
||||
return n.children.InsertAt(addr, &leaf[V]{pfx, val})
|
||||
}
|
||||
|
||||
// get the child: node or leaf
|
||||
switch kid := n.children.MustGet(addr).(type) {
|
||||
case *node[V]:
|
||||
n = kid
|
||||
continue // descend down to next trie level
|
||||
|
||||
case *leaf[V]:
|
||||
// reached a path compressed prefix
|
||||
// override value in slot if prefixes are equal
|
||||
if kid.prefix == pfx {
|
||||
kid.value = val
|
||||
// exists
|
||||
return true
|
||||
}
|
||||
|
||||
// create new node
|
||||
// push the leaf down
|
||||
// insert new child at current leaf position (addr)
|
||||
// descend down, replace n with new child
|
||||
newNode := new(node[V])
|
||||
newNode.insertAtDepth(kid.prefix, kid.value, depth+1)
|
||||
|
||||
n.children.InsertAt(addr, newNode)
|
||||
n = newNode
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// insertAtDepthPersist is the immutable version of insertAtDepth.
|
||||
// All visited nodes are cloned during insertion.
|
||||
func (n *node[V]) insertAtDepthPersist(pfx netip.Prefix, val V, depth int) (exists bool) {
|
||||
ip := pfx.Addr()
|
||||
bits := pfx.Bits()
|
||||
|
||||
lastIdx, lastBits := lastOctetIdxAndBits(bits)
|
||||
|
||||
octets := ip.AsSlice()
|
||||
octets = octets[:lastIdx+1]
|
||||
|
||||
// find the proper trie node to insert prefix
|
||||
// start with prefix octet at depth
|
||||
for ; depth < len(octets); depth++ {
|
||||
|
||||
octet := octets[depth]
|
||||
addr := uint(octet)
|
||||
|
||||
// last significant octet: insert/override prefix/val into node
|
||||
if depth == lastIdx {
|
||||
return n.prefixes.InsertAt(pfxToIdx(octet, lastBits), val)
|
||||
}
|
||||
|
||||
if !n.children.Test(addr) {
|
||||
// insert new prefix path compressed
|
||||
return n.children.InsertAt(addr, &leaf[V]{pfx, val})
|
||||
}
|
||||
|
||||
// get the child: node or leaf, but clone the path down
|
||||
switch kid := n.children.MustGet(addr).(type) {
|
||||
case *node[V]:
|
||||
// proceed to next level
|
||||
kid = kid.cloneFlat()
|
||||
n.children.InsertAt(addr, kid)
|
||||
n = kid
|
||||
continue // descend down to next trie level
|
||||
|
||||
case *leaf[V]:
|
||||
kid = kid.cloneLeaf()
|
||||
|
||||
// override value in slot if prefixes are equal
|
||||
if kid.prefix == pfx {
|
||||
// exists
|
||||
return n.children.InsertAt(addr, &leaf[V]{pfx, val})
|
||||
}
|
||||
|
||||
// create new node
|
||||
// push the leaf down
|
||||
// insert new child at current leaf position (addr)
|
||||
// descend down, replace n with new child
|
||||
newNode := new(node[V])
|
||||
newNode.insertAtDepth(kid.prefix, kid.value, depth+1)
|
||||
|
||||
n.children.InsertAt(addr, newNode)
|
||||
n = newNode
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// purgeAndCompress, purge empty nodes or compress nodes with single prefix or leaf.
|
||||
func (n *node[V]) purgeAndCompress(parentStack []*node[V], childPath []uint8, is4 bool) {
|
||||
// unwind the stack
|
||||
for i := len(parentStack) - 1; i >= 0; i-- {
|
||||
parent := parentStack[i]
|
||||
addr := uint(childPath[i])
|
||||
|
||||
pfxCount := n.prefixes.Len()
|
||||
childCount := n.children.Len()
|
||||
|
||||
switch {
|
||||
case n.isEmpty():
|
||||
// purge empty node
|
||||
parent.children.DeleteAt(addr)
|
||||
|
||||
case pfxCount == 1 && childCount == 0:
|
||||
// make leaf from prefix idx, shift leaf one level up
|
||||
// and override current node with new leaf
|
||||
idx, _ := n.prefixes.FirstSet()
|
||||
val := n.prefixes.Items[0]
|
||||
|
||||
path := stridePath{}
|
||||
copy(path[:], childPath)
|
||||
pfx := cidrFromPath(path, i+1, is4, idx)
|
||||
|
||||
parent.children.InsertAt(addr, &leaf[V]{pfx, val})
|
||||
|
||||
case pfxCount == 0 && childCount == 1:
|
||||
// if single child is a leaf, shift it up one level
|
||||
// and override current node with this leaf
|
||||
if leafPtr, ok := n.children.Items[0].(*leaf[V]); ok {
|
||||
parent.children.InsertAt(addr, leafPtr)
|
||||
}
|
||||
}
|
||||
|
||||
n = parent
|
||||
}
|
||||
}
|
||||
|
||||
// lpmGet does a route lookup for idx in the 8-bit (stride) routing table
|
||||
// at this depth and returns (baseIdx, value, true) if a matching
|
||||
// longest prefix exists, or ok=false otherwise.
|
||||
//
|
||||
// The prefixes in the stride form a complete binary tree (CBT) using the baseIndex function.
|
||||
// In contrast to the ART algorithm, I do not use an allotment approach but map
|
||||
// the backtracking in the CBT by a bitset operation with a precalculated backtracking path
|
||||
// for the respective idx.
|
||||
func (n *node[V]) lpmGet(idx uint) (baseIdx uint, val V, ok bool) {
|
||||
// top is the idx of the longest-prefix-match
|
||||
if top, ok := n.prefixes.IntersectionTop(lpmLookupTbl[idx]); ok {
|
||||
return top, n.prefixes.MustGet(top), true
|
||||
}
|
||||
|
||||
// not found (on this level)
|
||||
return 0, val, false
|
||||
}
|
||||
|
||||
// lpmTest for faster lpm tests without value returns.
|
||||
func (n *node[V]) lpmTest(idx uint) bool {
|
||||
return n.prefixes.IntersectsAny(lpmLookupTbl[idx])
|
||||
}
|
||||
|
||||
// cloneRec, clones the node recursive.
|
||||
func (n *node[V]) cloneRec() *node[V] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := new(node[V])
|
||||
if n.isEmpty() {
|
||||
return c
|
||||
}
|
||||
|
||||
// shallow
|
||||
c.prefixes = *(n.prefixes.Copy())
|
||||
|
||||
_, isCloner := any(*new(V)).(Cloner[V])
|
||||
|
||||
// deep copy if V implements Cloner[V]
|
||||
if isCloner {
|
||||
for i, val := range c.prefixes.Items {
|
||||
c.prefixes.Items[i] = cloneOrCopy(val)
|
||||
}
|
||||
}
|
||||
|
||||
// shallow
|
||||
c.children = *(n.children.Copy())
|
||||
|
||||
// deep copy of nodes and leaves
|
||||
for i, kidAny := range c.children.Items {
|
||||
switch kid := kidAny.(type) {
|
||||
case *node[V]:
|
||||
// clone the child node rec-descent
|
||||
c.children.Items[i] = kid.cloneRec()
|
||||
case *leaf[V]:
|
||||
// deep copy if V implements Cloner[V]
|
||||
c.children.Items[i] = kid.cloneLeaf()
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// cloneFlat, copies the node and clone the values in prefixes and path compressed leaves
|
||||
// if V implements Cloner. Used in the various ...Persist functions.
|
||||
func (n *node[V]) cloneFlat() *node[V] {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := new(node[V])
|
||||
if n.isEmpty() {
|
||||
return c
|
||||
}
|
||||
|
||||
// shallow copy
|
||||
c.prefixes = *(n.prefixes.Copy())
|
||||
c.children = *(n.children.Copy())
|
||||
|
||||
if _, ok := any(*new(V)).(Cloner[V]); !ok {
|
||||
// if V doesn't implement Cloner[V], return early
|
||||
return c
|
||||
}
|
||||
|
||||
// deep copy of values in prefixes
|
||||
for i, val := range c.prefixes.Items {
|
||||
c.prefixes.Items[i] = cloneOrCopy(val)
|
||||
}
|
||||
|
||||
// deep copy of values in path compressed leaves
|
||||
for i, kidAny := range c.children.Items {
|
||||
if kidLeaf, ok := kidAny.(*leaf[V]); ok {
|
||||
c.children.Items[i] = kidLeaf.cloneLeaf()
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// allRec runs recursive the trie, starting at this node and
|
||||
// the yield function is called for each route entry with prefix and value.
|
||||
// If the yield function returns false the recursion ends prematurely and the
|
||||
// false value is propagated.
|
||||
//
|
||||
// The iteration order is not defined, just the simplest and fastest recursive implementation.
|
||||
func (n *node[V]) allRec(path stridePath, depth int, is4 bool, yield func(netip.Prefix, V) bool) bool {
|
||||
// for all prefixes in this node do ...
|
||||
allIndices := n.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes))
|
||||
for _, idx := range allIndices {
|
||||
cidr := cidrFromPath(path, depth, is4, idx)
|
||||
|
||||
// callback for this prefix and val
|
||||
if !yield(cidr, n.prefixes.MustGet(idx)) {
|
||||
// early exit
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// for all children (nodes and leaves) in this node do ...
|
||||
allChildAddrs := n.children.AsSlice(make([]uint, 0, maxNodeChildren))
|
||||
for i, addr := range allChildAddrs {
|
||||
switch kid := n.children.Items[i].(type) {
|
||||
case *node[V]:
|
||||
// rec-descent with this node
|
||||
path[depth] = byte(addr)
|
||||
if !kid.allRec(path, depth+1, is4, yield) {
|
||||
// early exit
|
||||
return false
|
||||
}
|
||||
case *leaf[V]:
|
||||
// callback for this leaf
|
||||
if !yield(kid.prefix, kid.value) {
|
||||
// early exit
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// allRecSorted runs recursive the trie, starting at node and
|
||||
// the yield function is called for each route entry with prefix and value.
|
||||
// The iteration is in prefix sort order.
|
||||
//
|
||||
// If the yield function returns false the recursion ends prematurely and the
|
||||
// false value is propagated.
|
||||
func (n *node[V]) allRecSorted(path stridePath, depth int, is4 bool, yield func(netip.Prefix, V) bool) bool {
|
||||
// get slice of all child octets, sorted by addr
|
||||
allChildAddrs := n.children.AsSlice(make([]uint, 0, maxNodeChildren))
|
||||
|
||||
// get slice of all indexes, sorted by idx
|
||||
allIndices := n.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes))
|
||||
|
||||
// sort indices in CIDR sort order
|
||||
slices.SortFunc(allIndices, cmpIndexRank)
|
||||
|
||||
childCursor := 0
|
||||
|
||||
// yield indices and childs in CIDR sort order
|
||||
for _, pfxIdx := range allIndices {
|
||||
pfxOctet, _ := idxToPfx(pfxIdx)
|
||||
|
||||
// yield all childs before idx
|
||||
for j := childCursor; j < len(allChildAddrs); j++ {
|
||||
childAddr := allChildAddrs[j]
|
||||
|
||||
if childAddr >= uint(pfxOctet) {
|
||||
break
|
||||
}
|
||||
|
||||
// yield the node (rec-descent) or leaf
|
||||
switch kid := n.children.Items[j].(type) {
|
||||
case *node[V]:
|
||||
path[depth] = byte(childAddr)
|
||||
if !kid.allRecSorted(path, depth+1, is4, yield) {
|
||||
return false
|
||||
}
|
||||
case *leaf[V]:
|
||||
if !yield(kid.prefix, kid.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
|
||||
childCursor++
|
||||
}
|
||||
|
||||
// yield the prefix for this idx
|
||||
cidr := cidrFromPath(path, depth, is4, pfxIdx)
|
||||
// n.prefixes.Items[i] not possible after sorting allIndices
|
||||
if !yield(cidr, n.prefixes.MustGet(pfxIdx)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// yield the rest of leaves and nodes (rec-descent)
|
||||
for j := childCursor; j < len(allChildAddrs); j++ {
|
||||
addr := allChildAddrs[j]
|
||||
switch kid := n.children.Items[j].(type) {
|
||||
case *node[V]:
|
||||
path[depth] = byte(addr)
|
||||
if !kid.allRecSorted(path, depth+1, is4, yield) {
|
||||
return false
|
||||
}
|
||||
case *leaf[V]:
|
||||
if !yield(kid.prefix, kid.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// unionRec combines two nodes, changing the receiver node.
|
||||
// If there are duplicate entries, the value is taken from the other node.
|
||||
// Count duplicate entries to adjust the t.size struct members.
|
||||
func (n *node[V]) unionRec(o *node[V], depth int) (duplicates int) {
|
||||
// for all prefixes in other node do ...
|
||||
allIndices := o.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes))
|
||||
for i, oIdx := range allIndices {
|
||||
// insert/overwrite prefix/value from oNode to nNode
|
||||
exists := n.prefixes.InsertAt(oIdx, o.prefixes.Items[i])
|
||||
|
||||
// this prefix is duplicate in n and o
|
||||
if exists {
|
||||
duplicates++
|
||||
}
|
||||
}
|
||||
|
||||
// for all child addrs in other node do ...
|
||||
allOtherChildAddrs := o.children.AsSlice(make([]uint, 0, maxNodeChildren))
|
||||
LOOP:
|
||||
for i, addr := range allOtherChildAddrs {
|
||||
// 6 possible combinations for this child and other child child
|
||||
//
|
||||
// THIS, OTHER:
|
||||
// ----------
|
||||
// NULL, node <-- easy, insert at cloned node
|
||||
// NULL, leaf <-- easy, insert at cloned leaf
|
||||
// node, node <-- easy, union rec-descent
|
||||
// node, leaf <-- easy, insert other cloned leaf at depth+1
|
||||
// leaf, node <-- complex, push this leaf down, union rec-descent
|
||||
// leaf, leaf <-- complex, push this leaf down, insert other cloned leaf at depth+1
|
||||
//
|
||||
// try to get child at same addr from n
|
||||
thisChild, thisExists := n.children.Get(addr)
|
||||
if !thisExists {
|
||||
switch otherChild := o.children.Items[i].(type) {
|
||||
|
||||
case *node[V]: // NULL, node
|
||||
if !thisExists {
|
||||
n.children.InsertAt(addr, otherChild.cloneRec())
|
||||
continue LOOP
|
||||
}
|
||||
|
||||
case *leaf[V]: // NULL, leaf
|
||||
if !thisExists {
|
||||
n.children.InsertAt(addr, otherChild.cloneLeaf())
|
||||
continue LOOP
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
switch otherChild := o.children.Items[i].(type) {
|
||||
|
||||
case *node[V]:
|
||||
switch this := thisChild.(type) {
|
||||
|
||||
case *node[V]: // node, node
|
||||
// both childs have node in octet, call union rec-descent on child nodes
|
||||
duplicates += this.unionRec(otherChild, depth+1)
|
||||
continue LOOP
|
||||
|
||||
case *leaf[V]: // leaf, node
|
||||
// create new node
|
||||
nc := new(node[V])
|
||||
|
||||
// push this leaf down
|
||||
nc.insertAtDepth(this.prefix, this.value, depth+1)
|
||||
|
||||
// insert new node at current addr
|
||||
n.children.InsertAt(addr, nc)
|
||||
|
||||
// union rec-descent new node with other node
|
||||
duplicates += nc.unionRec(otherChild, depth+1)
|
||||
continue LOOP
|
||||
}
|
||||
|
||||
case *leaf[V]:
|
||||
switch this := thisChild.(type) {
|
||||
|
||||
case *node[V]: // node, leaf
|
||||
clonedLeaf := otherChild.cloneLeaf()
|
||||
if this.insertAtDepth(clonedLeaf.prefix, clonedLeaf.value, depth+1) {
|
||||
duplicates++
|
||||
}
|
||||
continue LOOP
|
||||
|
||||
case *leaf[V]: // leaf, leaf
|
||||
// create new node
|
||||
nc := new(node[V])
|
||||
|
||||
// push this leaf down
|
||||
nc.insertAtDepth(this.prefix, this.value, depth+1)
|
||||
|
||||
// insert at depth cloned leaf
|
||||
clonedLeaf := otherChild.cloneLeaf()
|
||||
if nc.insertAtDepth(clonedLeaf.prefix, clonedLeaf.value, depth+1) {
|
||||
duplicates++
|
||||
}
|
||||
|
||||
// insert the new node at current addr
|
||||
n.children.InsertAt(addr, nc)
|
||||
continue LOOP
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return duplicates
|
||||
}
|
||||
|
||||
// eachLookupPrefix does an all prefix match in the 8-bit (stride) routing table
|
||||
// at this depth and calls yield() for any matching CIDR.
|
||||
func (n *node[V]) eachLookupPrefix(octets []byte, depth int, is4 bool, pfxLen int, yield func(netip.Prefix, V) bool) (ok bool) {
|
||||
if n.prefixes.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// octets as array, needed below more than once
|
||||
var path stridePath
|
||||
copy(path[:], octets)
|
||||
|
||||
// backtracking the CBT
|
||||
for idx := pfxToIdx(octets[depth], pfxLen); idx > 0; idx >>= 1 {
|
||||
if n.prefixes.Test(idx) {
|
||||
val := n.prefixes.MustGet(idx)
|
||||
cidr := cidrFromPath(path, depth, is4, idx)
|
||||
|
||||
if !yield(cidr, val) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// eachSubnet calls yield() for any covered CIDR by parent prefix in natural CIDR sort order.
|
||||
func (n *node[V]) eachSubnet(octets []byte, depth int, is4 bool, pfxLen int, yield func(netip.Prefix, V) bool) bool {
|
||||
// octets as array, needed below more than once
|
||||
var path stridePath
|
||||
copy(path[:], octets)
|
||||
|
||||
pfxFirstAddr := uint(octets[depth])
|
||||
pfxLastAddr := uint(octets[depth] | ^netMask(pfxLen))
|
||||
|
||||
// 1. collect all indices in n covered by prefix
|
||||
|
||||
allCoveredIndices := make([]uint, 0, maxNodePrefixes)
|
||||
for _, idx := range n.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes)) {
|
||||
thisOctet, thisPfxLen := idxToPfx(idx)
|
||||
|
||||
thisFirstAddr := uint(thisOctet)
|
||||
thisLastAddr := uint(thisOctet | ^netMask(thisPfxLen))
|
||||
|
||||
if thisFirstAddr >= pfxFirstAddr && thisLastAddr <= pfxLastAddr {
|
||||
allCoveredIndices = append(allCoveredIndices, idx)
|
||||
}
|
||||
}
|
||||
|
||||
// sort indices in CIDR sort order
|
||||
slices.SortFunc(allCoveredIndices, cmpIndexRank)
|
||||
|
||||
// 2. collect all covered child addrs by prefix
|
||||
|
||||
allCoveredChildAddrs := make([]uint, 0, maxNodeChildren)
|
||||
for _, addr := range n.children.AsSlice(make([]uint, 0, maxNodeChildren)) {
|
||||
if addr >= pfxFirstAddr && addr <= pfxLastAddr {
|
||||
allCoveredChildAddrs = append(allCoveredChildAddrs, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. yield covered indices, pathcomp prefixes and childs in CIDR sort order
|
||||
|
||||
addrCursor := 0
|
||||
|
||||
// yield indices and childs in CIDR sort order
|
||||
for _, pfxIdx := range allCoveredIndices {
|
||||
pfxOctet, _ := idxToPfx(pfxIdx)
|
||||
|
||||
// yield all childs before idx
|
||||
for j := addrCursor; j < len(allCoveredChildAddrs); j++ {
|
||||
addr := allCoveredChildAddrs[j]
|
||||
if addr >= uint(pfxOctet) {
|
||||
break
|
||||
}
|
||||
|
||||
// yield the node or leaf?
|
||||
switch kid := n.children.MustGet(addr).(type) {
|
||||
|
||||
case *node[V]:
|
||||
path[depth] = byte(addr)
|
||||
if !kid.allRecSorted(path, depth+1, is4, yield) {
|
||||
return false
|
||||
}
|
||||
|
||||
case *leaf[V]:
|
||||
if !yield(kid.prefix, kid.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
|
||||
addrCursor++
|
||||
}
|
||||
|
||||
// yield the prefix for this idx
|
||||
cidr := cidrFromPath(path, depth, is4, pfxIdx)
|
||||
// n.prefixes.Items[i] not possible after sorting allIndices
|
||||
if !yield(cidr, n.prefixes.MustGet(pfxIdx)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// yield the rest of leaves and nodes (rec-descent)
|
||||
for _, addr := range allCoveredChildAddrs[addrCursor:] {
|
||||
// yield the node or leaf?
|
||||
switch kid := n.children.MustGet(addr).(type) {
|
||||
|
||||
case *node[V]:
|
||||
path[depth] = byte(addr)
|
||||
if !kid.allRecSorted(path, depth+1, is4, yield) {
|
||||
return false
|
||||
}
|
||||
|
||||
case *leaf[V]:
|
||||
if !yield(kid.prefix, kid.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
-305
@@ -1,305 +0,0 @@
|
||||
package bart
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/gaissmai/bart/internal/bitset"
|
||||
)
|
||||
|
||||
// overlaps returns true if any IP in the nodes n or o overlaps.
|
||||
func (n *node[V]) overlaps(o *node[V], depth int) bool {
|
||||
nPfxCount := n.prefixes.Len()
|
||||
oPfxCount := o.prefixes.Len()
|
||||
|
||||
nChildCount := n.children.Len()
|
||||
oChildCount := o.children.Len()
|
||||
|
||||
// ##############################
|
||||
// 1. Test if any routes overlaps
|
||||
// ##############################
|
||||
|
||||
// full cross check
|
||||
if nPfxCount > 0 && oPfxCount > 0 {
|
||||
if n.overlapsRoutes(o) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ####################################
|
||||
// 2. Test if routes overlaps any child
|
||||
// ####################################
|
||||
|
||||
// swap nodes to help chance on its way,
|
||||
// if the first call to expensive overlapsChildrenIn() is already true,
|
||||
// if both orders are false it doesn't help either
|
||||
if nChildCount > oChildCount {
|
||||
n, o = o, n
|
||||
|
||||
nPfxCount = n.prefixes.Len()
|
||||
oPfxCount = o.prefixes.Len()
|
||||
|
||||
nChildCount = n.children.Len()
|
||||
oChildCount = o.children.Len()
|
||||
}
|
||||
|
||||
if nPfxCount > 0 && oChildCount > 0 {
|
||||
if n.overlapsChildrenIn(o) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// symmetric reverse
|
||||
if oPfxCount > 0 && nChildCount > 0 {
|
||||
if o.overlapsChildrenIn(n) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ###########################################
|
||||
// 3. childs with same octet in nodes n and o
|
||||
// ###########################################
|
||||
|
||||
// stop condition, n or o have no childs
|
||||
if nChildCount == 0 || oChildCount == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// stop condition, no child with identical octet in n and o
|
||||
if !n.children.IntersectsAny(o.children.BitSet) {
|
||||
return false
|
||||
}
|
||||
|
||||
return n.overlapsSameChildren(o, depth)
|
||||
}
|
||||
|
||||
// overlapsRoutes, test if n overlaps o prefixes and vice versa
|
||||
func (n *node[V]) overlapsRoutes(o *node[V]) bool {
|
||||
// some prefixes are identical, trivial overlap
|
||||
if n.prefixes.IntersectsAny(o.prefixes.BitSet) {
|
||||
return true
|
||||
}
|
||||
|
||||
// get the lowest idx (biggest prefix)
|
||||
nFirstIdx, _ := n.prefixes.FirstSet()
|
||||
oFirstIdx, _ := o.prefixes.FirstSet()
|
||||
|
||||
// start with other min value, see ART algo
|
||||
nIdx := oFirstIdx
|
||||
oIdx := nFirstIdx
|
||||
|
||||
// make full cross check
|
||||
nOK := true
|
||||
oOK := true
|
||||
|
||||
// zip, range over n and o together to help chance on its way
|
||||
for nOK || oOK {
|
||||
if nOK {
|
||||
// does any route in o overlap this prefix from n
|
||||
if nIdx, nOK = n.prefixes.NextSet(nIdx); nOK {
|
||||
if o.lpmTest(nIdx) {
|
||||
return true
|
||||
}
|
||||
|
||||
nIdx++
|
||||
}
|
||||
}
|
||||
|
||||
if oOK {
|
||||
// does any route in n overlap this prefix from o
|
||||
if oIdx, oOK = o.prefixes.NextSet(oIdx); oOK {
|
||||
if n.lpmTest(oIdx) {
|
||||
return true
|
||||
}
|
||||
|
||||
oIdx++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// overlapsChildrenIn, test if prefixes in n overlaps child octets in o.
|
||||
func (n *node[V]) overlapsChildrenIn(o *node[V]) bool {
|
||||
pfxCount := n.prefixes.Len()
|
||||
childCount := o.children.Len()
|
||||
|
||||
// heuristic, compare benchmarks
|
||||
// when will we range over the children and when will we do bitset calc?
|
||||
magicNumber := 15
|
||||
doRange := childCount < magicNumber || pfxCount > magicNumber
|
||||
|
||||
// do range over, not so many childs and maybe to many prefixes for other algo below
|
||||
if doRange {
|
||||
lowerBound, _ := n.prefixes.FirstSet()
|
||||
for _, addr := range o.children.AsSlice(make([]uint, 0, maxNodeChildren)) {
|
||||
idx := hostIndex(addr)
|
||||
if idx < lowerBound { // lpm match impossible
|
||||
continue
|
||||
}
|
||||
if n.lpmTest(idx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// do bitset intersection, alloted route table with child octets
|
||||
// maybe to many childs for range over or not so many prefixes to
|
||||
// build the alloted routing table from them
|
||||
|
||||
// make allot table with prefixes as bitsets, bitsets are precalculated
|
||||
// just union the bitsets to one bitset (allot table) for all prefixes
|
||||
// in this node
|
||||
prefixRoutes := bitset.BitSet(make([]uint64, 8))
|
||||
|
||||
allIndices := n.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes))
|
||||
|
||||
for _, idx := range allIndices {
|
||||
// get pre alloted bitset for idx
|
||||
prefixRoutes.InPlaceUnion(allotLookupTbl[idx])
|
||||
}
|
||||
|
||||
// clone a bitset without heap allocation
|
||||
// shift-right children bitset by 256 (firstHostIndex)
|
||||
c8 := make([]uint64, 8)
|
||||
copy(c8[4:], o.children.BitSet) // 4*64= 256
|
||||
hostRoutes := bitset.BitSet(c8)
|
||||
|
||||
return prefixRoutes.IntersectsAny(hostRoutes)
|
||||
}
|
||||
|
||||
// overlapsSameChildren, find same octets with bitset intersection.
|
||||
func (n *node[V]) overlapsSameChildren(o *node[V], depth int) bool {
|
||||
// clone a bitset without heap allocation
|
||||
c4 := [4]uint64{}
|
||||
copy(c4[:], n.children.BitSet)
|
||||
nChildrenBitsetCloned := bitset.BitSet(c4[:])
|
||||
|
||||
// intersect in place the cloned child bitset from n with o
|
||||
nChildrenBitsetCloned.InPlaceIntersection(o.children.BitSet)
|
||||
|
||||
allCommonChildren := nChildrenBitsetCloned.AsSlice(make([]uint, 0, maxNodeChildren))
|
||||
|
||||
// range over all child addrs, common in n and o
|
||||
for _, addr := range allCommonChildren {
|
||||
nChild := n.children.MustGet(addr)
|
||||
oChild := o.children.MustGet(addr)
|
||||
|
||||
if overlapsTwoChilds[V](nChild, oChild, depth+1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// overlapsTwoChilds, childs can be node or leaf.
|
||||
func overlapsTwoChilds[V any](nChild, oChild any, depth int) bool {
|
||||
// 4 possible different combinations for n and o
|
||||
//
|
||||
// node, node --> overlapsRec
|
||||
// node, leaf --> overlapsPrefixAtDepth
|
||||
// leaf, node --> overlapsPrefixAtDepth
|
||||
// leaf, leaf --> netip.Prefix.Overlaps
|
||||
//
|
||||
switch nKind := nChild.(type) {
|
||||
|
||||
case *node[V]:
|
||||
switch oKind := oChild.(type) {
|
||||
case *node[V]: // node, node
|
||||
return nKind.overlaps(oKind, depth)
|
||||
case *leaf[V]: // node, leaf
|
||||
return nKind.overlapsPrefixAtDepth(oKind.prefix, depth)
|
||||
}
|
||||
|
||||
case *leaf[V]:
|
||||
switch oKind := oChild.(type) {
|
||||
case *node[V]: // leaf, node
|
||||
return oKind.overlapsPrefixAtDepth(nKind.prefix, depth)
|
||||
case *leaf[V]: // leaf, leaf
|
||||
return oKind.prefix.Overlaps(nKind.prefix)
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// overlapsPrefixAtDepth, returns true if node overlaps with prefix
|
||||
// starting with prefix octet at depth.
|
||||
//
|
||||
// Needed for path compressed prefix some level down in the node trie.
|
||||
func (n *node[V]) overlapsPrefixAtDepth(pfx netip.Prefix, depth int) bool {
|
||||
ip := pfx.Addr()
|
||||
bits := pfx.Bits()
|
||||
|
||||
lastIdx, lastBits := lastOctetIdxAndBits(bits)
|
||||
|
||||
octets := ip.AsSlice()
|
||||
octets = octets[:lastIdx+1]
|
||||
|
||||
for ; depth < len(octets); depth++ {
|
||||
octet := octets[depth]
|
||||
addr := uint(octet)
|
||||
|
||||
// full octet path in node trie, check overlap with last prefix octet
|
||||
if depth == lastIdx {
|
||||
return n.overlapsIdx(pfxToIdx(octet, lastBits))
|
||||
}
|
||||
|
||||
// test if any route overlaps prefix´ so far
|
||||
// no best match needed, forward tests without backtracking
|
||||
if n.prefixes.Len() != 0 && n.lpmTest(hostIndex(addr)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if !n.children.Test(addr) {
|
||||
return false
|
||||
}
|
||||
|
||||
// next child, node or leaf
|
||||
switch kid := n.children.MustGet(addr).(type) {
|
||||
case *node[V]:
|
||||
n = kid
|
||||
continue
|
||||
case *leaf[V]:
|
||||
return kid.prefix.Overlaps(pfx)
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreachable: " + pfx.String())
|
||||
}
|
||||
|
||||
// overlapsIdx returns true if node overlaps with prefix.
|
||||
func (n *node[V]) overlapsIdx(idx uint) bool {
|
||||
// 1. Test if any route in this node overlaps prefix?
|
||||
if n.lpmTest(idx) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 2. Test if prefix overlaps any route in this node
|
||||
|
||||
// use bitset intersections instead of range loops
|
||||
// shallow copy pre alloted bitset for idx
|
||||
allotedPrefixRoutes := allotLookupTbl[idx]
|
||||
if allotedPrefixRoutes.IntersectsAny(n.prefixes.BitSet) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 3. Test if prefix overlaps any child in this node
|
||||
|
||||
// shift-right children bitset by 256 (firstHostIndex)
|
||||
c8 := [8]uint64{}
|
||||
copy(c8[4:], n.children.BitSet) // 4*64= 256
|
||||
hostRoutes := bitset.BitSet(c8[:])
|
||||
|
||||
// use bitsets intersection instead of range loops
|
||||
return allotedPrefixRoutes.IntersectsAny(hostRoutes)
|
||||
}
|
||||
-263
@@ -1,263 +0,0 @@
|
||||
// Copyright (c) 2024 Karl Gaissmaier
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package bart
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// kid, a node has no path information about its predecessors,
|
||||
// we collect this during the recursive descent.
|
||||
// The path/depth/idx is needed to get the CIDR back.
|
||||
type kid[V any] struct {
|
||||
// for traversing
|
||||
n *node[V]
|
||||
is4 bool
|
||||
path stridePath
|
||||
depth int
|
||||
idx uint
|
||||
|
||||
// for printing
|
||||
cidr netip.Prefix
|
||||
val V
|
||||
}
|
||||
|
||||
// MarshalText implements the [encoding.TextMarshaler] interface,
|
||||
// just a wrapper for [Table.Fprint].
|
||||
func (t *Table[V]) MarshalText() ([]byte, error) {
|
||||
w := new(bytes.Buffer)
|
||||
if err := t.Fprint(w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return w.Bytes(), nil
|
||||
}
|
||||
|
||||
// String returns a hierarchical tree diagram of the ordered CIDRs
|
||||
// as string, just a wrapper for [Table.Fprint].
|
||||
// If Fprint returns an error, String panics.
|
||||
func (t *Table[V]) String() string {
|
||||
w := new(strings.Builder)
|
||||
if err := t.Fprint(w); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return w.String()
|
||||
}
|
||||
|
||||
// Fprint writes a hierarchical tree diagram of the ordered CIDRs
|
||||
// with default formatted payload V to w. If w is nil, Fprint panics.
|
||||
//
|
||||
// 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 *Table[V]) Fprint(w io.Writer) error {
|
||||
// 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 *Table[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
|
||||
}
|
||||
|
||||
startKid := kid[V]{
|
||||
n: nil,
|
||||
idx: 0,
|
||||
path: stridePath{},
|
||||
is4: is4,
|
||||
}
|
||||
|
||||
if err := n.fprintRec(w, startKid, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fprintRec, the output is a hierarchical CIDR tree starting with this kid.
|
||||
func (n *node[V]) fprintRec(w io.Writer, parent kid[V], pad string) error {
|
||||
// recursion stop condition
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get direct childs for this kid ...
|
||||
directKids := n.getKidsRec(parent.idx, parent.path, parent.depth, parent.is4)
|
||||
|
||||
// sort them by netip.Prefix, not by baseIndex
|
||||
slices.SortFunc(directKids, cmpKidByPrefix[V])
|
||||
|
||||
// symbols used in tree
|
||||
glyphe := "├─ "
|
||||
spacer := "│ "
|
||||
|
||||
// for all direct kids under this node ...
|
||||
for i, kid := range directKids {
|
||||
// ... treat last kid special
|
||||
if i == len(directKids)-1 {
|
||||
glyphe = "└─ "
|
||||
spacer = " "
|
||||
}
|
||||
|
||||
// print prefix and val, padded with glyphe
|
||||
if _, err := fmt.Fprintf(w, "%s%s (%v)\n", pad+glyphe, kid.cidr, kid.val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rec-descent with this prefix as parentIdx.
|
||||
// hierarchical nested tree view, two rec-descent functions
|
||||
// work together to spoil the reader.
|
||||
if err := kid.n.fprintRec(w, kid, pad+spacer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getKidsRec, returns the direct kids below path and parentIdx.
|
||||
// It's a recursive monster together with printRec,
|
||||
// you have to know the data structure by heart to understand this function!
|
||||
//
|
||||
// See the artlookup.pdf paper in the doc folder,
|
||||
// the baseIndex function is the key.
|
||||
func (n *node[V]) getKidsRec(parentIdx uint, path stridePath, depth int, is4 bool) []kid[V] {
|
||||
// recursion stop condition
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var directKids []kid[V]
|
||||
|
||||
for _, idx := range n.prefixes.All() {
|
||||
// parent or self, handled alreday in an upper stack frame.
|
||||
if idx <= parentIdx {
|
||||
continue
|
||||
}
|
||||
|
||||
// check if lpmIdx for this idx' parent is equal to parentIdx
|
||||
lpmIdx, _, _ := n.lpmGet(idx >> 1)
|
||||
|
||||
// if idx is directKid?
|
||||
if lpmIdx == parentIdx {
|
||||
cidr := cidrFromPath(path, depth, is4, idx)
|
||||
|
||||
kid := kid[V]{
|
||||
n: n,
|
||||
is4: is4,
|
||||
path: path,
|
||||
depth: depth,
|
||||
idx: idx,
|
||||
cidr: cidr,
|
||||
val: n.prefixes.MustGet(idx),
|
||||
}
|
||||
|
||||
directKids = append(directKids, kid)
|
||||
}
|
||||
}
|
||||
|
||||
// the node may have childs and leaves, the rec-descent monster starts
|
||||
for i, addr := range n.children.All() {
|
||||
// do a longest-prefix-match
|
||||
lpmIdx, _, _ := n.lpmGet(hostIndex(addr))
|
||||
if lpmIdx == parentIdx {
|
||||
switch k := n.children.Items[i].(type) {
|
||||
case *node[V]:
|
||||
path[depth] = byte(addr)
|
||||
|
||||
// traverse, rec-descent call with next child node
|
||||
directKids = append(directKids, k.getKidsRec(0, path, depth+1, is4)...)
|
||||
case *leaf[V]:
|
||||
kid := kid[V]{
|
||||
n: nil, // path compressed item, stop recursion
|
||||
is4: is4,
|
||||
cidr: k.prefix,
|
||||
val: k.value,
|
||||
}
|
||||
|
||||
directKids = append(directKids, kid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return directKids
|
||||
}
|
||||
|
||||
// cmpKidByPrefix, all prefixes are already normalized (Masked).
|
||||
func cmpKidByPrefix[V any](a, b kid[V]) int {
|
||||
return cmpPrefix(a.cidr, b.cidr)
|
||||
}
|
||||
|
||||
// cmpPrefix, compare func for prefix sort,
|
||||
// all cidrs are already normalized
|
||||
func cmpPrefix(a, b netip.Prefix) int {
|
||||
if cmp := a.Addr().Compare(b.Addr()); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
|
||||
return cmp.Compare(a.Bits(), b.Bits())
|
||||
}
|
||||
|
||||
// cidrFromPath, get prefix back from byte path, depth, octet and pfxLen.
|
||||
func cidrFromPath(path stridePath, depth int, is4 bool, idx uint) netip.Prefix {
|
||||
octet, pfxLen := idxToPfx(idx)
|
||||
|
||||
// set masked byte in path at depth
|
||||
path[depth] = octet
|
||||
|
||||
// zero/mask the bytes after prefix bits
|
||||
clear(path[depth+1:])
|
||||
|
||||
// make ip addr from octets
|
||||
var ip netip.Addr
|
||||
if is4 {
|
||||
ip = netip.AddrFrom4([4]byte(path[:4]))
|
||||
} else {
|
||||
ip = netip.AddrFrom16(path)
|
||||
}
|
||||
|
||||
// calc bits with pathLen and pfxLen
|
||||
bits := depth<<3 + pfxLen
|
||||
|
||||
// return a normalized prefix from ip/bits
|
||||
return netip.PrefixFrom(ip, bits)
|
||||
}
|
||||
-1131
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user