Update dependencies

This commit is contained in:
bluepython508
2024-11-01 17:33:34 +00:00
parent 033ac0b400
commit 5cdfab398d
3596 changed files with 1033483 additions and 259 deletions

1
vendor/github.com/gaissmai/bart/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1 @@
Contributions are welcome, but please open an issue for discussion before sending a pull request.

21
vendor/github.com/gaissmai/bart/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

123
vendor/github.com/gaissmai/bart/README.md generated vendored Normal file
View File

@@ -0,0 +1,123 @@
# package bart
[![Go Reference](https://pkg.go.dev/badge/github.com/gaissmai/bart.svg)](https://pkg.go.dev/github.com/gaissmai/bart#section-documentation)
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/gaissmai/bart)
[![CI](https://github.com/gaissmai/bart/actions/workflows/go.yml/badge.svg)](https://github.com/gaissmai/bart/actions/workflows/go.yml)
[![Coverage Status](https://coveralls.io/repos/github/gaissmai/bart/badge.svg)](https://coveralls.io/github/gaissmai/bart)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
## Overview
`package bart` provides a Balanced-Routing-Table (BART).
BART is balanced in terms of memory consumption versus
lookup time.
The lookup time is by a factor of ~2 slower on average as the
routing algorithms ART, SMART, CPE, ... but reduces the memory
consumption by an order of magnitude in comparison.
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.
The second key factor is popcount array compression at each stride level
of the CBT prefix tree and backtracking along the CBT in O(k).
The CBT is implemented as a bitvector, backtracking is just
a matter of fast cache friendly bitmask operations.
The child array at each stride level is also popcount compressed.
## API
The API changes in v0.4.2, 0.5.3, v0.6.3, v0.10.1, v0.11.0
```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.
The Table is safe for concurrent readers but not for concurrent readers
and/or writers.
func (t *Table[V]) Insert(pfx netip.Prefix, val V)
func (t *Table[V]) Delete(pfx netip.Prefix)
func (t *Table[V]) Get(pfx netip.Prefix) (val V, ok bool)
func (t *Table[V]) Update(pfx netip.Prefix, cb func(val V, ok bool) V) (newVal V)
func (t *Table[V]) Union(o *Table[V])
func (t *Table[V]) Clone() *Table[V]
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]) EachLookupPrefix(pfx netip.Prefix, yield func(pfx netip.Prefix, val V) bool)
func (t *Table[V]) EachSubnet(pfx netip.Prefix, yield func(pfx netip.Prefix, val V) bool)
func (t *Table[V]) OverlapsPrefix(pfx 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]) Size() int
func (t *Table[V]) Size4() int
func (t *Table[V]) Size6() int
func (t *Table[V]) All(yield func(pfx netip.Prefix, val V) bool)
func (t *Table[V]) All4(yield func(pfx netip.Prefix, val V) bool)
func (t *Table[V]) All6(yield func(pfx netip.Prefix, val V) bool)
func (t *Table[V]) AllSorted(yield func(pfx netip.Prefix, val V) bool)
func (t *Table[V]) All4Sorted(yield func(pfx netip.Prefix, val V) bool)
func (t *Table[V]) All6Sorted(yield func(pfx netip.Prefix, val V) bool)
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]) DumpList4() []DumpListNode[V]
func (t *Table[V]) DumpList6() []DumpListNode[V]
```
## benchmarks
Please see the extensive [benchmarks](https://github.com/gaissmai/iprbench) comparing `bart` with other IP routing table implementations.
Just a teaser, LPM lookups against the full Internet routing table with random probes:
```
goos: linux
goarch: amd64
pkg: github.com/gaissmai/bart
cpu: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
BenchmarkFullMatchV4/Lookup 24484828 49.03 ns/op
BenchmarkFullMatchV6/Lookup 17098262 70.15 ns/op
BenchmarkFullMissV4/Lookup 24480925 49.15 ns/op
BenchmarkFullMissV6/Lookup 54955310 21.79 ns/op
```
## CONTRIBUTION
Please open an issue for discussion before sending a pull request.
## CREDIT
Credits for many inspirations go to the clever guys at tailscale,
to Daniel Lemire for the super-fast bitset package and
to Donald E. Knuth for the **ART** routing algorithm and
all the rest of his *Art* and for keeping important algorithms
in the public domain!
## LICENSE
MIT

621
vendor/github.com/gaissmai/bart/base_index.go generated vendored Normal file
View File

@@ -0,0 +1,621 @@
// Copyright (c) 2024 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
import "cmp"
// Please read the ART paper ./doc/artlookup.pdf
// to understand the baseIndex algorithm.
// hostMasks as lookup table
var hostMasks = []uint8{
0b1111_1111, // bits == 0
0b0111_1111, // bits == 1
0b0011_1111, // bits == 2
0b0001_1111, // bits == 3
0b0000_1111, // bits == 4
0b0000_0111, // bits == 5
0b0000_0011, // bits == 6
0b0000_0001, // bits == 7
0b0000_0000, // bits == 8
}
func netMask(mask int) uint8 {
return ^hostMasks[uint8(mask)]
}
const (
// baseIndex of the first host route: prefixToBaseIndex(0,8)
firstHostIndex = 0b1_0000_0000 // 256
// baseIndex of the last host route: prefixToBaseIndex(255,8)
//nolint:unused
lastHostIndex = 0b1_1111_1111 // 511
)
// prefixToBaseIndex, maps a prefix table as a 'complete binary tree'.
// This is the so-called baseIndex a.k.a heapFunc:
func prefixToBaseIndex(octet byte, prefixLen int) uint {
return uint(octet>>(strideLen-prefixLen)) + (1 << prefixLen)
}
// octetToBaseIndex, just prefixToBaseIndex(octet, 8), a.k.a host routes
// but faster, use it for host routes in Lookup.
func octetToBaseIndex(octet byte) uint {
return uint(octet) + firstHostIndex // just: octet + 256
}
// baseIndexToPrefixLen, calc the bits from baseIndex and octect depth
func baseIndexToPrefixLen(baseIdx uint, depth int) int {
_, pfxLen := baseIndexToPrefix(baseIdx)
return depth*strideLen + pfxLen
}
// hostRoutesByIndex, get range of host routes for this idx.
//
// idx: 72
// prefix: 32/6
// lower: 256 + 32 = 288
// upper: 256 + (32 | 0b0000_0011) = 291
//
// Use the pre computed lookup table.
//
// func hostRoutesByIndex(idx uint) (uint, uint) {
// octet, bits := baseIndexToPrefix(idx)
// return octetToBaseIndex(octet), octetToBaseIndex(octet | hostMasks[bits])
// }
func hostRoutesByIndex(idx uint) (uint, uint) {
item := baseIdxLookupTbl[idx]
return uint(item.lower), uint(item.upper)
}
// baseIndexToPrefix returns the octet and prefix len of baseIdx.
// It's the inverse to prefixToBaseIndex.
//
// Use the pre computed lookup table, bits.LeadingZeros is too slow.
//
// func baseIndexToPrefix(baseIdx uint) (octet byte, pfxLen int) {
// nlz := bits.LeadingZeros(baseIdx)
// pfxLen = strconv.IntSize - nlz - 1
// octet = (baseIdx & (0xFF >> (8 - pfxLen))) << (8 - pfxLen)
// return octet, pfxLen
// }
func baseIndexToPrefix(baseIdx uint) (octet byte, pfxLen int) {
item := baseIdxLookupTbl[baseIdx]
return item.octet, int(item.bits)
}
// cmpIndexRank, compare SortFunc to sort indexes in prefix sort order.
func cmpIndexRank(a, b uint) int {
return cmp.Compare(baseIdxLookupTbl[a].rank, baseIdxLookupTbl[b].rank)
}
// baseIdxLookupTbl
//
// octet, bits,
// host route boundaries,
// prefix sort rank
//
// as lookup table.
var baseIdxLookupTbl = [512]struct {
octet byte
bits int8
lower uint16 // host route lower bound
upper uint16 // host route upper bound
rank uint16 // prefix sort rank
}{
{0, -1, 0, 0, 0}, // idx == 0 invalid!
{0, 0, 256, 511, 1}, // idx == 1
{0, 1, 256, 383, 2}, // idx == 2
{128, 1, 384, 511, 257}, // idx == 3
{0, 2, 256, 319, 3}, // idx == 4
{64, 2, 320, 383, 130}, // idx == 5
{128, 2, 384, 447, 258}, // idx == 6
{192, 2, 448, 511, 385}, // idx == 7
{0, 3, 256, 287, 4}, // idx == 8
{32, 3, 288, 319, 67}, // idx == 9
{64, 3, 320, 351, 131}, // idx == 10
{96, 3, 352, 383, 194}, // idx == 11
{128, 3, 384, 415, 259}, // idx == 12
{160, 3, 416, 447, 322}, // idx == 13
{192, 3, 448, 479, 386}, // idx == 14
{224, 3, 480, 511, 449}, // idx == 15
{0, 4, 256, 271, 5}, // idx == 16
{16, 4, 272, 287, 36}, // idx == 17
{32, 4, 288, 303, 68}, // idx == 18
{48, 4, 304, 319, 99}, // idx == 19
{64, 4, 320, 335, 132}, // idx == 20
{80, 4, 336, 351, 163}, // idx == 21
{96, 4, 352, 367, 195}, // idx == 22
{112, 4, 368, 383, 226}, // idx == 23
{128, 4, 384, 399, 260}, // idx == 24
{144, 4, 400, 415, 291}, // idx == 25
{160, 4, 416, 431, 323}, // idx == 26
{176, 4, 432, 447, 354}, // idx == 27
{192, 4, 448, 463, 387}, // idx == 28
{208, 4, 464, 479, 418}, // idx == 29
{224, 4, 480, 495, 450}, // idx == 30
{240, 4, 496, 511, 481}, // idx == 31
{0, 5, 256, 263, 6}, // idx == 32
{8, 5, 264, 271, 21}, // idx == 33
{16, 5, 272, 279, 37}, // idx == 34
{24, 5, 280, 287, 52}, // idx == 35
{32, 5, 288, 295, 69}, // idx == 36
{40, 5, 296, 303, 84}, // idx == 37
{48, 5, 304, 311, 100}, // idx == 38
{56, 5, 312, 319, 115}, // idx == 39
{64, 5, 320, 327, 133}, // idx == 40
{72, 5, 328, 335, 148}, // idx == 41
{80, 5, 336, 343, 164}, // idx == 42
{88, 5, 344, 351, 179}, // idx == 43
{96, 5, 352, 359, 196}, // idx == 44
{104, 5, 360, 367, 211}, // idx == 45
{112, 5, 368, 375, 227}, // idx == 46
{120, 5, 376, 383, 242}, // idx == 47
{128, 5, 384, 391, 261}, // idx == 48
{136, 5, 392, 399, 276}, // idx == 49
{144, 5, 400, 407, 292}, // idx == 50
{152, 5, 408, 415, 307}, // idx == 51
{160, 5, 416, 423, 324}, // idx == 52
{168, 5, 424, 431, 339}, // idx == 53
{176, 5, 432, 439, 355}, // idx == 54
{184, 5, 440, 447, 370}, // idx == 55
{192, 5, 448, 455, 388}, // idx == 56
{200, 5, 456, 463, 403}, // idx == 57
{208, 5, 464, 471, 419}, // idx == 58
{216, 5, 472, 479, 434}, // idx == 59
{224, 5, 480, 487, 451}, // idx == 60
{232, 5, 488, 495, 466}, // idx == 61
{240, 5, 496, 503, 482}, // idx == 62
{248, 5, 504, 511, 497}, // idx == 63
{0, 6, 256, 259, 7}, // idx == 64
{4, 6, 260, 263, 14}, // idx == 65
{8, 6, 264, 267, 22}, // idx == 66
{12, 6, 268, 271, 29}, // idx == 67
{16, 6, 272, 275, 38}, // idx == 68
{20, 6, 276, 279, 45}, // idx == 69
{24, 6, 280, 283, 53}, // idx == 70
{28, 6, 284, 287, 60}, // idx == 71
{32, 6, 288, 291, 70}, // idx == 72
{36, 6, 292, 295, 77}, // idx == 73
{40, 6, 296, 299, 85}, // idx == 74
{44, 6, 300, 303, 92}, // idx == 75
{48, 6, 304, 307, 101}, // idx == 76
{52, 6, 308, 311, 108}, // idx == 77
{56, 6, 312, 315, 116}, // idx == 78
{60, 6, 316, 319, 123}, // idx == 79
{64, 6, 320, 323, 134}, // idx == 80
{68, 6, 324, 327, 141}, // idx == 81
{72, 6, 328, 331, 149}, // idx == 82
{76, 6, 332, 335, 156}, // idx == 83
{80, 6, 336, 339, 165}, // idx == 84
{84, 6, 340, 343, 172}, // idx == 85
{88, 6, 344, 347, 180}, // idx == 86
{92, 6, 348, 351, 187}, // idx == 87
{96, 6, 352, 355, 197}, // idx == 88
{100, 6, 356, 359, 204}, // idx == 89
{104, 6, 360, 363, 212}, // idx == 90
{108, 6, 364, 367, 219}, // idx == 91
{112, 6, 368, 371, 228}, // idx == 92
{116, 6, 372, 375, 235}, // idx == 93
{120, 6, 376, 379, 243}, // idx == 94
{124, 6, 380, 383, 250}, // idx == 95
{128, 6, 384, 387, 262}, // idx == 96
{132, 6, 388, 391, 269}, // idx == 97
{136, 6, 392, 395, 277}, // idx == 98
{140, 6, 396, 399, 284}, // idx == 99
{144, 6, 400, 403, 293}, // idx == 100
{148, 6, 404, 407, 300}, // idx == 101
{152, 6, 408, 411, 308}, // idx == 102
{156, 6, 412, 415, 315}, // idx == 103
{160, 6, 416, 419, 325}, // idx == 104
{164, 6, 420, 423, 332}, // idx == 105
{168, 6, 424, 427, 340}, // idx == 106
{172, 6, 428, 431, 347}, // idx == 107
{176, 6, 432, 435, 356}, // idx == 108
{180, 6, 436, 439, 363}, // idx == 109
{184, 6, 440, 443, 371}, // idx == 110
{188, 6, 444, 447, 378}, // idx == 111
{192, 6, 448, 451, 389}, // idx == 112
{196, 6, 452, 455, 396}, // idx == 113
{200, 6, 456, 459, 404}, // idx == 114
{204, 6, 460, 463, 411}, // idx == 115
{208, 6, 464, 467, 420}, // idx == 116
{212, 6, 468, 471, 427}, // idx == 117
{216, 6, 472, 475, 435}, // idx == 118
{220, 6, 476, 479, 442}, // idx == 119
{224, 6, 480, 483, 452}, // idx == 120
{228, 6, 484, 487, 459}, // idx == 121
{232, 6, 488, 491, 467}, // idx == 122
{236, 6, 492, 495, 474}, // idx == 123
{240, 6, 496, 499, 483}, // idx == 124
{244, 6, 500, 503, 490}, // idx == 125
{248, 6, 504, 507, 498}, // idx == 126
{252, 6, 508, 511, 505}, // idx == 127
{0, 7, 256, 257, 8}, // idx == 128
{2, 7, 258, 259, 11}, // idx == 129
{4, 7, 260, 261, 15}, // idx == 130
{6, 7, 262, 263, 18}, // idx == 131
{8, 7, 264, 265, 23}, // idx == 132
{10, 7, 266, 267, 26}, // idx == 133
{12, 7, 268, 269, 30}, // idx == 134
{14, 7, 270, 271, 33}, // idx == 135
{16, 7, 272, 273, 39}, // idx == 136
{18, 7, 274, 275, 42}, // idx == 137
{20, 7, 276, 277, 46}, // idx == 138
{22, 7, 278, 279, 49}, // idx == 139
{24, 7, 280, 281, 54}, // idx == 140
{26, 7, 282, 283, 57}, // idx == 141
{28, 7, 284, 285, 61}, // idx == 142
{30, 7, 286, 287, 64}, // idx == 143
{32, 7, 288, 289, 71}, // idx == 144
{34, 7, 290, 291, 74}, // idx == 145
{36, 7, 292, 293, 78}, // idx == 146
{38, 7, 294, 295, 81}, // idx == 147
{40, 7, 296, 297, 86}, // idx == 148
{42, 7, 298, 299, 89}, // idx == 149
{44, 7, 300, 301, 93}, // idx == 150
{46, 7, 302, 303, 96}, // idx == 151
{48, 7, 304, 305, 102}, // idx == 152
{50, 7, 306, 307, 105}, // idx == 153
{52, 7, 308, 309, 109}, // idx == 154
{54, 7, 310, 311, 112}, // idx == 155
{56, 7, 312, 313, 117}, // idx == 156
{58, 7, 314, 315, 120}, // idx == 157
{60, 7, 316, 317, 124}, // idx == 158
{62, 7, 318, 319, 127}, // idx == 159
{64, 7, 320, 321, 135}, // idx == 160
{66, 7, 322, 323, 138}, // idx == 161
{68, 7, 324, 325, 142}, // idx == 162
{70, 7, 326, 327, 145}, // idx == 163
{72, 7, 328, 329, 150}, // idx == 164
{74, 7, 330, 331, 153}, // idx == 165
{76, 7, 332, 333, 157}, // idx == 166
{78, 7, 334, 335, 160}, // idx == 167
{80, 7, 336, 337, 166}, // idx == 168
{82, 7, 338, 339, 169}, // idx == 169
{84, 7, 340, 341, 173}, // idx == 170
{86, 7, 342, 343, 176}, // idx == 171
{88, 7, 344, 345, 181}, // idx == 172
{90, 7, 346, 347, 184}, // idx == 173
{92, 7, 348, 349, 188}, // idx == 174
{94, 7, 350, 351, 191}, // idx == 175
{96, 7, 352, 353, 198}, // idx == 176
{98, 7, 354, 355, 201}, // idx == 177
{100, 7, 356, 357, 205}, // idx == 178
{102, 7, 358, 359, 208}, // idx == 179
{104, 7, 360, 361, 213}, // idx == 180
{106, 7, 362, 363, 216}, // idx == 181
{108, 7, 364, 365, 220}, // idx == 182
{110, 7, 366, 367, 223}, // idx == 183
{112, 7, 368, 369, 229}, // idx == 184
{114, 7, 370, 371, 232}, // idx == 185
{116, 7, 372, 373, 236}, // idx == 186
{118, 7, 374, 375, 239}, // idx == 187
{120, 7, 376, 377, 244}, // idx == 188
{122, 7, 378, 379, 247}, // idx == 189
{124, 7, 380, 381, 251}, // idx == 190
{126, 7, 382, 383, 254}, // idx == 191
{128, 7, 384, 385, 263}, // idx == 192
{130, 7, 386, 387, 266}, // idx == 193
{132, 7, 388, 389, 270}, // idx == 194
{134, 7, 390, 391, 273}, // idx == 195
{136, 7, 392, 393, 278}, // idx == 196
{138, 7, 394, 395, 281}, // idx == 197
{140, 7, 396, 397, 285}, // idx == 198
{142, 7, 398, 399, 288}, // idx == 199
{144, 7, 400, 401, 294}, // idx == 200
{146, 7, 402, 403, 297}, // idx == 201
{148, 7, 404, 405, 301}, // idx == 202
{150, 7, 406, 407, 304}, // idx == 203
{152, 7, 408, 409, 309}, // idx == 204
{154, 7, 410, 411, 312}, // idx == 205
{156, 7, 412, 413, 316}, // idx == 206
{158, 7, 414, 415, 319}, // idx == 207
{160, 7, 416, 417, 326}, // idx == 208
{162, 7, 418, 419, 329}, // idx == 209
{164, 7, 420, 421, 333}, // idx == 210
{166, 7, 422, 423, 336}, // idx == 211
{168, 7, 424, 425, 341}, // idx == 212
{170, 7, 426, 427, 344}, // idx == 213
{172, 7, 428, 429, 348}, // idx == 214
{174, 7, 430, 431, 351}, // idx == 215
{176, 7, 432, 433, 357}, // idx == 216
{178, 7, 434, 435, 360}, // idx == 217
{180, 7, 436, 437, 364}, // idx == 218
{182, 7, 438, 439, 367}, // idx == 219
{184, 7, 440, 441, 372}, // idx == 220
{186, 7, 442, 443, 375}, // idx == 221
{188, 7, 444, 445, 379}, // idx == 222
{190, 7, 446, 447, 382}, // idx == 223
{192, 7, 448, 449, 390}, // idx == 224
{194, 7, 450, 451, 393}, // idx == 225
{196, 7, 452, 453, 397}, // idx == 226
{198, 7, 454, 455, 400}, // idx == 227
{200, 7, 456, 457, 405}, // idx == 228
{202, 7, 458, 459, 408}, // idx == 229
{204, 7, 460, 461, 412}, // idx == 230
{206, 7, 462, 463, 415}, // idx == 231
{208, 7, 464, 465, 421}, // idx == 232
{210, 7, 466, 467, 424}, // idx == 233
{212, 7, 468, 469, 428}, // idx == 234
{214, 7, 470, 471, 431}, // idx == 235
{216, 7, 472, 473, 436}, // idx == 236
{218, 7, 474, 475, 439}, // idx == 237
{220, 7, 476, 477, 443}, // idx == 238
{222, 7, 478, 479, 446}, // idx == 239
{224, 7, 480, 481, 453}, // idx == 240
{226, 7, 482, 483, 456}, // idx == 241
{228, 7, 484, 485, 460}, // idx == 242
{230, 7, 486, 487, 463}, // idx == 243
{232, 7, 488, 489, 468}, // idx == 244
{234, 7, 490, 491, 471}, // idx == 245
{236, 7, 492, 493, 475}, // idx == 246
{238, 7, 494, 495, 478}, // idx == 247
{240, 7, 496, 497, 484}, // idx == 248
{242, 7, 498, 499, 487}, // idx == 249
{244, 7, 500, 501, 491}, // idx == 250
{246, 7, 502, 503, 494}, // idx == 251
{248, 7, 504, 505, 499}, // idx == 252
{250, 7, 506, 507, 502}, // idx == 253
{252, 7, 508, 509, 506}, // idx == 254
{254, 7, 510, 511, 509}, // idx == 255
{0, 8, 256, 256, 9}, // idx == 256 -- first host route
{1, 8, 257, 257, 10}, // idx == 257
{2, 8, 258, 258, 12}, // idx == 258
{3, 8, 259, 259, 13}, // idx == 259
{4, 8, 260, 260, 16}, // idx == 260
{5, 8, 261, 261, 17}, // idx == 261
{6, 8, 262, 262, 19}, // idx == 262
{7, 8, 263, 263, 20}, // idx == 263
{8, 8, 264, 264, 24}, // idx == 264
{9, 8, 265, 265, 25}, // idx == 265
{10, 8, 266, 266, 27}, // idx == 266
{11, 8, 267, 267, 28}, // idx == 267
{12, 8, 268, 268, 31}, // idx == 268
{13, 8, 269, 269, 32}, // idx == 269
{14, 8, 270, 270, 34}, // idx == 270
{15, 8, 271, 271, 35}, // idx == 271
{16, 8, 272, 272, 40}, // idx == 272
{17, 8, 273, 273, 41}, // idx == 273
{18, 8, 274, 274, 43}, // idx == 274
{19, 8, 275, 275, 44}, // idx == 275
{20, 8, 276, 276, 47}, // idx == 276
{21, 8, 277, 277, 48}, // idx == 277
{22, 8, 278, 278, 50}, // idx == 278
{23, 8, 279, 279, 51}, // idx == 279
{24, 8, 280, 280, 55}, // idx == 280
{25, 8, 281, 281, 56}, // idx == 281
{26, 8, 282, 282, 58}, // idx == 282
{27, 8, 283, 283, 59}, // idx == 283
{28, 8, 284, 284, 62}, // idx == 284
{29, 8, 285, 285, 63}, // idx == 285
{30, 8, 286, 286, 65}, // idx == 286
{31, 8, 287, 287, 66}, // idx == 287
{32, 8, 288, 288, 72}, // idx == 288
{33, 8, 289, 289, 73}, // idx == 289
{34, 8, 290, 290, 75}, // idx == 290
{35, 8, 291, 291, 76}, // idx == 291
{36, 8, 292, 292, 79}, // idx == 292
{37, 8, 293, 293, 80}, // idx == 293
{38, 8, 294, 294, 82}, // idx == 294
{39, 8, 295, 295, 83}, // idx == 295
{40, 8, 296, 296, 87}, // idx == 296
{41, 8, 297, 297, 88}, // idx == 297
{42, 8, 298, 298, 90}, // idx == 298
{43, 8, 299, 299, 91}, // idx == 299
{44, 8, 300, 300, 94}, // idx == 300
{45, 8, 301, 301, 95}, // idx == 301
{46, 8, 302, 302, 97}, // idx == 302
{47, 8, 303, 303, 98}, // idx == 303
{48, 8, 304, 304, 103}, // idx == 304
{49, 8, 305, 305, 104}, // idx == 305
{50, 8, 306, 306, 106}, // idx == 306
{51, 8, 307, 307, 107}, // idx == 307
{52, 8, 308, 308, 110}, // idx == 308
{53, 8, 309, 309, 111}, // idx == 309
{54, 8, 310, 310, 113}, // idx == 310
{55, 8, 311, 311, 114}, // idx == 311
{56, 8, 312, 312, 118}, // idx == 312
{57, 8, 313, 313, 119}, // idx == 313
{58, 8, 314, 314, 121}, // idx == 314
{59, 8, 315, 315, 122}, // idx == 315
{60, 8, 316, 316, 125}, // idx == 316
{61, 8, 317, 317, 126}, // idx == 317
{62, 8, 318, 318, 128}, // idx == 318
{63, 8, 319, 319, 129}, // idx == 319
{64, 8, 320, 320, 136}, // idx == 320
{65, 8, 321, 321, 137}, // idx == 321
{66, 8, 322, 322, 139}, // idx == 322
{67, 8, 323, 323, 140}, // idx == 323
{68, 8, 324, 324, 143}, // idx == 324
{69, 8, 325, 325, 144}, // idx == 325
{70, 8, 326, 326, 146}, // idx == 326
{71, 8, 327, 327, 147}, // idx == 327
{72, 8, 328, 328, 151}, // idx == 328
{73, 8, 329, 329, 152}, // idx == 329
{74, 8, 330, 330, 154}, // idx == 330
{75, 8, 331, 331, 155}, // idx == 331
{76, 8, 332, 332, 158}, // idx == 332
{77, 8, 333, 333, 159}, // idx == 333
{78, 8, 334, 334, 161}, // idx == 334
{79, 8, 335, 335, 162}, // idx == 335
{80, 8, 336, 336, 167}, // idx == 336
{81, 8, 337, 337, 168}, // idx == 337
{82, 8, 338, 338, 170}, // idx == 338
{83, 8, 339, 339, 171}, // idx == 339
{84, 8, 340, 340, 174}, // idx == 340
{85, 8, 341, 341, 175}, // idx == 341
{86, 8, 342, 342, 177}, // idx == 342
{87, 8, 343, 343, 178}, // idx == 343
{88, 8, 344, 344, 182}, // idx == 344
{89, 8, 345, 345, 183}, // idx == 345
{90, 8, 346, 346, 185}, // idx == 346
{91, 8, 347, 347, 186}, // idx == 347
{92, 8, 348, 348, 189}, // idx == 348
{93, 8, 349, 349, 190}, // idx == 349
{94, 8, 350, 350, 192}, // idx == 350
{95, 8, 351, 351, 193}, // idx == 351
{96, 8, 352, 352, 199}, // idx == 352
{97, 8, 353, 353, 200}, // idx == 353
{98, 8, 354, 354, 202}, // idx == 354
{99, 8, 355, 355, 203}, // idx == 355
{100, 8, 356, 356, 206}, // idx == 356
{101, 8, 357, 357, 207}, // idx == 357
{102, 8, 358, 358, 209}, // idx == 358
{103, 8, 359, 359, 210}, // idx == 359
{104, 8, 360, 360, 214}, // idx == 360
{105, 8, 361, 361, 215}, // idx == 361
{106, 8, 362, 362, 217}, // idx == 362
{107, 8, 363, 363, 218}, // idx == 363
{108, 8, 364, 364, 221}, // idx == 364
{109, 8, 365, 365, 222}, // idx == 365
{110, 8, 366, 366, 224}, // idx == 366
{111, 8, 367, 367, 225}, // idx == 367
{112, 8, 368, 368, 230}, // idx == 368
{113, 8, 369, 369, 231}, // idx == 369
{114, 8, 370, 370, 233}, // idx == 370
{115, 8, 371, 371, 234}, // idx == 371
{116, 8, 372, 372, 237}, // idx == 372
{117, 8, 373, 373, 238}, // idx == 373
{118, 8, 374, 374, 240}, // idx == 374
{119, 8, 375, 375, 241}, // idx == 375
{120, 8, 376, 376, 245}, // idx == 376
{121, 8, 377, 377, 246}, // idx == 377
{122, 8, 378, 378, 248}, // idx == 378
{123, 8, 379, 379, 249}, // idx == 379
{124, 8, 380, 380, 252}, // idx == 380
{125, 8, 381, 381, 253}, // idx == 381
{126, 8, 382, 382, 255}, // idx == 382
{127, 8, 383, 383, 256}, // idx == 383
{128, 8, 384, 384, 264}, // idx == 384
{129, 8, 385, 385, 265}, // idx == 385
{130, 8, 386, 386, 267}, // idx == 386
{131, 8, 387, 387, 268}, // idx == 387
{132, 8, 388, 388, 271}, // idx == 388
{133, 8, 389, 389, 272}, // idx == 389
{134, 8, 390, 390, 274}, // idx == 390
{135, 8, 391, 391, 275}, // idx == 391
{136, 8, 392, 392, 279}, // idx == 392
{137, 8, 393, 393, 280}, // idx == 393
{138, 8, 394, 394, 282}, // idx == 394
{139, 8, 395, 395, 283}, // idx == 395
{140, 8, 396, 396, 286}, // idx == 396
{141, 8, 397, 397, 287}, // idx == 397
{142, 8, 398, 398, 289}, // idx == 398
{143, 8, 399, 399, 290}, // idx == 399
{144, 8, 400, 400, 295}, // idx == 400
{145, 8, 401, 401, 296}, // idx == 401
{146, 8, 402, 402, 298}, // idx == 402
{147, 8, 403, 403, 299}, // idx == 403
{148, 8, 404, 404, 302}, // idx == 404
{149, 8, 405, 405, 303}, // idx == 405
{150, 8, 406, 406, 305}, // idx == 406
{151, 8, 407, 407, 306}, // idx == 407
{152, 8, 408, 408, 310}, // idx == 408
{153, 8, 409, 409, 311}, // idx == 409
{154, 8, 410, 410, 313}, // idx == 410
{155, 8, 411, 411, 314}, // idx == 411
{156, 8, 412, 412, 317}, // idx == 412
{157, 8, 413, 413, 318}, // idx == 413
{158, 8, 414, 414, 320}, // idx == 414
{159, 8, 415, 415, 321}, // idx == 415
{160, 8, 416, 416, 327}, // idx == 416
{161, 8, 417, 417, 328}, // idx == 417
{162, 8, 418, 418, 330}, // idx == 418
{163, 8, 419, 419, 331}, // idx == 419
{164, 8, 420, 420, 334}, // idx == 420
{165, 8, 421, 421, 335}, // idx == 421
{166, 8, 422, 422, 337}, // idx == 422
{167, 8, 423, 423, 338}, // idx == 423
{168, 8, 424, 424, 342}, // idx == 424
{169, 8, 425, 425, 343}, // idx == 425
{170, 8, 426, 426, 345}, // idx == 426
{171, 8, 427, 427, 346}, // idx == 427
{172, 8, 428, 428, 349}, // idx == 428
{173, 8, 429, 429, 350}, // idx == 429
{174, 8, 430, 430, 352}, // idx == 430
{175, 8, 431, 431, 353}, // idx == 431
{176, 8, 432, 432, 358}, // idx == 432
{177, 8, 433, 433, 359}, // idx == 433
{178, 8, 434, 434, 361}, // idx == 434
{179, 8, 435, 435, 362}, // idx == 435
{180, 8, 436, 436, 365}, // idx == 436
{181, 8, 437, 437, 366}, // idx == 437
{182, 8, 438, 438, 368}, // idx == 438
{183, 8, 439, 439, 369}, // idx == 439
{184, 8, 440, 440, 373}, // idx == 440
{185, 8, 441, 441, 374}, // idx == 441
{186, 8, 442, 442, 376}, // idx == 442
{187, 8, 443, 443, 377}, // idx == 443
{188, 8, 444, 444, 380}, // idx == 444
{189, 8, 445, 445, 381}, // idx == 445
{190, 8, 446, 446, 383}, // idx == 446
{191, 8, 447, 447, 384}, // idx == 447
{192, 8, 448, 448, 391}, // idx == 448
{193, 8, 449, 449, 392}, // idx == 449
{194, 8, 450, 450, 394}, // idx == 450
{195, 8, 451, 451, 395}, // idx == 451
{196, 8, 452, 452, 398}, // idx == 452
{197, 8, 453, 453, 399}, // idx == 453
{198, 8, 454, 454, 401}, // idx == 454
{199, 8, 455, 455, 402}, // idx == 455
{200, 8, 456, 456, 406}, // idx == 456
{201, 8, 457, 457, 407}, // idx == 457
{202, 8, 458, 458, 409}, // idx == 458
{203, 8, 459, 459, 410}, // idx == 459
{204, 8, 460, 460, 413}, // idx == 460
{205, 8, 461, 461, 414}, // idx == 461
{206, 8, 462, 462, 416}, // idx == 462
{207, 8, 463, 463, 417}, // idx == 463
{208, 8, 464, 464, 422}, // idx == 464
{209, 8, 465, 465, 423}, // idx == 465
{210, 8, 466, 466, 425}, // idx == 466
{211, 8, 467, 467, 426}, // idx == 467
{212, 8, 468, 468, 429}, // idx == 468
{213, 8, 469, 469, 430}, // idx == 469
{214, 8, 470, 470, 432}, // idx == 470
{215, 8, 471, 471, 433}, // idx == 471
{216, 8, 472, 472, 437}, // idx == 472
{217, 8, 473, 473, 438}, // idx == 473
{218, 8, 474, 474, 440}, // idx == 474
{219, 8, 475, 475, 441}, // idx == 475
{220, 8, 476, 476, 444}, // idx == 476
{221, 8, 477, 477, 445}, // idx == 477
{222, 8, 478, 478, 447}, // idx == 478
{223, 8, 479, 479, 448}, // idx == 479
{224, 8, 480, 480, 454}, // idx == 480
{225, 8, 481, 481, 455}, // idx == 481
{226, 8, 482, 482, 457}, // idx == 482
{227, 8, 483, 483, 458}, // idx == 483
{228, 8, 484, 484, 461}, // idx == 484
{229, 8, 485, 485, 462}, // idx == 485
{230, 8, 486, 486, 464}, // idx == 486
{231, 8, 487, 487, 465}, // idx == 487
{232, 8, 488, 488, 469}, // idx == 488
{233, 8, 489, 489, 470}, // idx == 489
{234, 8, 490, 490, 472}, // idx == 490
{235, 8, 491, 491, 473}, // idx == 491
{236, 8, 492, 492, 476}, // idx == 492
{237, 8, 493, 493, 477}, // idx == 493
{238, 8, 494, 494, 479}, // idx == 494
{239, 8, 495, 495, 480}, // idx == 495
{240, 8, 496, 496, 485}, // idx == 496
{241, 8, 497, 497, 486}, // idx == 497
{242, 8, 498, 498, 488}, // idx == 498
{243, 8, 499, 499, 489}, // idx == 499
{244, 8, 500, 500, 492}, // idx == 500
{245, 8, 501, 501, 493}, // idx == 501
{246, 8, 502, 502, 495}, // idx == 502
{247, 8, 503, 503, 496}, // idx == 503
{248, 8, 504, 504, 500}, // idx == 504
{249, 8, 505, 505, 501}, // idx == 505
{250, 8, 506, 506, 503}, // idx == 506
{251, 8, 507, 507, 504}, // idx == 507
{252, 8, 508, 508, 507}, // idx == 508
{253, 8, 509, 509, 508}, // idx == 509
{254, 8, 510, 510, 510}, // idx == 510
{255, 8, 511, 511, 511}, // idx == 511
}

205
vendor/github.com/gaissmai/bart/dumper.go generated vendored Normal file
View File

@@ -0,0 +1,205 @@
// 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
leafNode // only prefixes
intermediateNode // only children
)
// ##################################################
// 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.
//
// Output:
//
// [FULL] depth: 0 path: [] / 0
// indexs(#6): 1 66 128 133 266 383
// prefxs(#6): 0/0 8/6 0/7 10/7 10/8 127/8
// childs(#3): 10 127 192
//
// .[IMED] depth: 1 path: [10] / 8
// .childs(#1): 0
//
// ..[LEAF] depth: 2 path: [10.0] / 16
// ..indexs(#2): 256 257
// ..prefxs(#2): 0/8 1/8
//
// .[IMED] depth: 1 path: [127] / 8
// .childs(#1): 0
//
// ..[IMED] depth: 2 path: [127.0] / 16
// ..childs(#1): 0
//
// ...[LEAF] depth: 3 path: [127.0.0] / 24
// ...indexs(#1): 257
// ...prefxs(#1): 1/8
//
// ...
func (t *Table[V]) dump(w io.Writer) {
t.init()
fmt.Fprint(w, "### IPv4:")
t.rootV4.dumpRec(w, zeroPath, 0, true)
fmt.Fprint(w, "### IPv6:")
t.rootV6.dumpRec(w, zeroPath, 0, false)
}
// dumpRec, rec-descent the trie.
func (n *node[V]) dumpRec(w io.Writer, path [16]byte, depth int, is4 bool) {
n.dump(w, path, depth, is4)
// make backing arrays, no heap allocs
addrBackingArray := [maxNodeChildren]uint{}
// the node may have childs, the rec-descent monster starts
for i, addr := range n.allChildAddrs(addrBackingArray[:]) {
octet := byte(addr)
child := n.children[i]
path[depth] = octet
child.dumpRec(w, path, depth+1, is4)
}
}
// dump the node to w.
func (n *node[V]) dump(w io.Writer, path [16]byte, 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 nPfxLen := len(n.prefixes); nPfxLen != 0 {
// make backing arrays, no heap allocs
idxBackingArray := [maxNodePrefixes]uint{}
allIndices := n.allStrideIndexes(idxBackingArray[:])
// print the baseIndices for this node.
fmt.Fprintf(w, "%sindexs(#%d): %v\n", indent, nPfxLen, allIndices)
// print the prefixes for this node
fmt.Fprintf(w, "%sprefxs(#%d):", indent, nPfxLen)
for _, idx := range allIndices {
octet, bits := baseIndexToPrefix(idx)
fmt.Fprintf(w, " %s/%d", octetFmt(octet, is4), bits)
}
fmt.Fprintln(w)
// print the values for this node
fmt.Fprintf(w, "%svalues(#%d):", indent, nPfxLen)
for _, val := range n.prefixes {
fmt.Fprintf(w, " %v", val)
}
fmt.Fprintln(w)
}
if childs := len(n.children); childs != 0 {
// print the childs for this node
fmt.Fprintf(w, "%schilds(#%d):", indent, childs)
addrBackingArray := [maxNodeChildren]uint{}
for _, addr := range n.allChildAddrs(addrBackingArray[:]) {
octet := byte(addr)
fmt.Fprintf(w, " %s", octetFmt(octet, is4))
}
fmt.Fprintln(w)
}
}
// 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 [16]byte, 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"
}
panic("unreachable")
}
// hasType returns the nodeType.
func (n *node[V]) hasType() nodeType {
lenPefixes := len(n.prefixes)
lenChilds := len(n.children)
if lenPefixes == 0 && lenChilds != 0 {
return intermediateNode
}
if lenPefixes == 0 && lenChilds == 0 {
return nullNode
}
if lenPefixes != 0 && lenChilds == 0 {
return leafNode
}
if lenPefixes != 0 && lenChilds != 0 {
return fullNode
}
panic("unreachable")
}

74
vendor/github.com/gaissmai/bart/jsonify.go generated vendored Normal file
View File

@@ -0,0 +1,74 @@
// Copyright (c) 2024 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
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) {
t.init()
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] {
t.init()
if t.rootV4 == nil {
return nil
}
return t.rootV4.dumpListRec(0, zeroPath, 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] {
t.init()
if t.rootV6 == nil {
return nil
}
return t.rootV6.dumpListRec(0, zeroPath, 0, false)
}
func (n *node[V]) dumpListRec(parentIdx uint, path [16]byte, depth int, is4 bool) []DumpListNode[V] {
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
}

800
vendor/github.com/gaissmai/bart/node.go generated vendored Normal file
View File

@@ -0,0 +1,800 @@
// Copyright (c) 2024 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
import (
"cmp"
"net/netip"
"slices"
"github.com/bits-and-blooms/bitset"
)
const (
strideLen = 8 // octet
maxTreeDepth = 128 / strideLen // 16
maxNodeChildren = 1 << strideLen // 256
maxNodePrefixes = 1 << (strideLen + 1) // 512
)
// zero value, used manifold
var zeroPath [16]byte
// node is a level node in the multibit-trie.
// A node has prefixes and children.
//
// 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, popcount-compressed slices are used
// instead of fixed-size arrays.
//
// The array slots are also not pre-allocated as in the ART algorithm,
// but backtracking is used for the longest-prefix-match.
//
// The lookup is then slower by a factor of about 2, but this is
// the intended trade-off to prevent memory consumption from exploding.
type node[V any] struct {
prefixesBitset *bitset.BitSet
childrenBitset *bitset.BitSet
// popcount compressed slices
prefixes []V
children []*node[V]
}
// newNode, BitSets have to be initialized.
func newNode[V any]() *node[V] {
return &node[V]{
prefixesBitset: bitset.New(0), // init BitSet
childrenBitset: bitset.New(0), // init BitSet
}
}
// isEmpty returns true if node has neither prefixes nor children.
func (n *node[V]) isEmpty() bool {
return len(n.prefixes) == 0 && len(n.children) == 0
}
// ################## prefixes ################################
// prefixRank, Rank() is the key of the popcount compression algorithm,
// mapping between bitset index and slice index.
func (n *node[V]) prefixRank(baseIdx uint) int {
// adjust offset by one to slice index
return int(n.prefixesBitset.Rank(baseIdx)) - 1
}
// insertPrefix adds the route for baseIdx, with value val.
// If the value already exists, overwrite it with val and return false.
func (n *node[V]) insertPrefix(baseIdx uint, val V) (ok bool) {
// prefix exists, overwrite val
if n.prefixesBitset.Test(baseIdx) {
n.prefixes[n.prefixRank(baseIdx)] = val
return false
}
// new, insert into bitset and slice
n.prefixesBitset.Set(baseIdx)
n.prefixes = slices.Insert(n.prefixes, n.prefixRank(baseIdx), val)
return true
}
// deletePrefix removes the route octet/prefixLen.
// Returns false if there was no prefix to delete.
func (n *node[V]) deletePrefix(octet byte, prefixLen int) (ok bool) {
baseIdx := prefixToBaseIndex(octet, prefixLen)
// no route entry
if !n.prefixesBitset.Test(baseIdx) {
return false
}
rnk := n.prefixRank(baseIdx)
// delete from slice
n.prefixes = slices.Delete(n.prefixes, rnk, rnk+1)
// delete from bitset, followed by Compact to reduce memory consumption
n.prefixesBitset.Clear(baseIdx)
n.prefixesBitset.Compact()
return true
}
// updatePrefix, update or set the value at prefix via callback. The new value returned
// and a bool wether the prefix was already present in the node.
func (n *node[V]) updatePrefix(octet byte, prefixLen int, cb func(V, bool) V) (newVal V, wasPresent bool) {
// calculate idx once
baseIdx := prefixToBaseIndex(octet, prefixLen)
var rnk int
// if prefix is set, get current value
var oldVal V
if wasPresent = n.prefixesBitset.Test(baseIdx); wasPresent {
rnk = n.prefixRank(baseIdx)
oldVal = n.prefixes[rnk]
}
// callback function to get updated or new value
newVal = cb(oldVal, wasPresent)
// prefix is already set, update and return value
if wasPresent {
n.prefixes[rnk] = newVal
return
}
// new prefix, insert into bitset ...
n.prefixesBitset.Set(baseIdx)
// bitset has changed, recalc rank
rnk = n.prefixRank(baseIdx)
// ... and insert value into slice
n.prefixes = slices.Insert(n.prefixes, rnk, newVal)
return
}
// lpm 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.
//
// backtracking is fast, it's just a bitset test and, if found, one popcount.
// max steps in backtracking is the stride length.
func (n *node[V]) lpm(idx uint) (baseIdx uint, val V, ok bool) {
for baseIdx = idx; baseIdx > 0; baseIdx >>= 1 {
if n.prefixesBitset.Test(baseIdx) {
// longest prefix match
return baseIdx, n.prefixes[n.prefixRank(baseIdx)], true
}
}
// not found (on this level)
return 0, val, false
}
// lpmTest for internal use in overlap tests, just return true or false, no value needed.
func (n *node[V]) lpmTest(baseIdx uint) bool {
for idx := baseIdx; idx > 0; idx >>= 1 {
if n.prefixesBitset.Test(idx) {
return true
}
}
return false
}
// getValue for baseIdx.
func (n *node[V]) getValue(baseIdx uint) (val V, ok bool) {
if n.prefixesBitset.Test(baseIdx) {
return n.prefixes[n.prefixRank(baseIdx)], true
}
return
}
// allStrideIndexes returns all baseIndexes set in this stride node in ascending order.
func (n *node[V]) allStrideIndexes(buffer []uint) []uint {
if len(n.prefixes) > len(buffer) {
panic("logic error, buffer is too small")
}
_, buffer = n.prefixesBitset.NextSetMany(0, buffer)
return buffer
}
// ################## children ################################
// childRank, Rank() is the key of the popcount compression algorithm,
// mapping between bitset index and slice index.
func (n *node[V]) childRank(octet byte) int {
// adjust offset by one to slice index
return int(n.childrenBitset.Rank(uint(octet))) - 1
}
// insertChild, insert the child
func (n *node[V]) insertChild(octet byte, child *node[V]) {
// child exists, overwrite it
if n.childrenBitset.Test(uint(octet)) {
n.children[n.childRank(octet)] = child
return
}
// new insert into bitset and slice
n.childrenBitset.Set(uint(octet))
n.children = slices.Insert(n.children, n.childRank(octet), child)
}
// deleteChild, delete the child at octet. It is valid to delete a non-existent child.
func (n *node[V]) deleteChild(octet byte) {
if !n.childrenBitset.Test(uint(octet)) {
return
}
rnk := n.childRank(octet)
// delete from slice
n.children = slices.Delete(n.children, rnk, rnk+1)
// delete from bitset, followed by Compact to reduce memory consumption
n.childrenBitset.Clear(uint(octet))
n.childrenBitset.Compact()
}
// getChild returns the child pointer for octet, or nil if none.
func (n *node[V]) getChild(octet byte) *node[V] {
if !n.childrenBitset.Test(uint(octet)) {
return nil
}
return n.children[n.childRank(octet)]
}
// allChildAddrs fills the buffer with the octets of all child nodes in ascending order,
// panics if the buffer isn't big enough.
func (n *node[V]) allChildAddrs(buffer []uint) []uint {
if len(n.children) > len(buffer) {
panic("logic error, buffer is too small")
}
_, buffer = n.childrenBitset.NextSetMany(0, buffer)
return buffer
}
// #################### nodes #############################################
// 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(path [16]byte, depth int, is4 bool, octet byte, bits int, yield func(pfx netip.Prefix, val V) bool) bool {
for idx := prefixToBaseIndex(octet, bits); idx > 0; idx >>= 1 {
if n.prefixesBitset.Test(idx) {
cidr, _ := cidrFromPath(path, depth, is4, idx)
val, _ := n.getValue(idx)
if !yield(cidr, val) {
// early exit
return false
}
}
}
return true
}
// overlapsRec returns true if any IP in the nodes n or o overlaps.
func (n *node[V]) overlapsRec(o *node[V]) bool {
// ##############################
// 1. test if any routes overlaps
// ##############################
nPfxLen := len(n.prefixes)
oPfxLen := len(o.prefixes)
if oPfxLen > 0 && nPfxLen > 0 {
// some prefixes are identical
if n.prefixesBitset.IntersectionCardinality(o.prefixesBitset) > 0 {
return true
}
var nIdx, oIdx uint
nOK := nPfxLen > 0
oOK := oPfxLen > 0
// zip, range over n and o at the same time 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.prefixesBitset.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.prefixesBitset.NextSet(oIdx); oOK {
if n.lpmTest(oIdx) {
return true
}
}
oIdx++
}
}
}
// ####################################
// 2. test if routes overlaps any child
// ####################################
nChildLen := len(n.children)
oChildLen := len(o.children)
var nAddr, oAddr uint
nOK := nChildLen > 0 && oPfxLen > 0 // test the childs in n against the routes in o
oOK := oChildLen > 0 && nPfxLen > 0 // test the childs in o against the routes in n
// zip, range over n and o at the same time to help chance on its way
for nOK || oOK {
if nOK {
// does any route in o overlap this child from n
if nAddr, nOK = n.childrenBitset.NextSet(nAddr); nOK {
if o.lpmTest(octetToBaseIndex(byte(nAddr))) {
return true
}
}
nAddr++
}
if oOK {
// does any route in n overlap this child from o
if oAddr, oOK = o.childrenBitset.NextSet(oAddr); oOK {
if n.lpmTest(octetToBaseIndex(byte(oAddr))) {
return true
}
}
oAddr++
}
}
// ################################################################
// 3. rec-descent call for childs with same octet in nodes n and o
// ################################################################
// stop condition, n or o have no childs
if nChildLen == 0 || oChildLen == 0 {
return false
}
// stop condition, no child with identical octet in n and o
if n.childrenBitset.IntersectionCardinality(o.childrenBitset) == 0 {
return false
}
// swap the nodes, range over shorter bitset
if nChildLen > oChildLen {
n, o = o, n
}
addrBackingArray := [maxNodeChildren]uint{}
for i, addr := range n.allChildAddrs(addrBackingArray[:]) {
oChild := o.getChild(byte(addr))
if oChild == nil {
// no child in o with this octet
continue
}
// we have the slice index for n
nChild := n.children[i]
// rec-descent
if nChild.overlapsRec(oChild) {
return true
}
}
return false
}
// overlapsPrefix returns true if node overlaps with prefix.
func (n *node[V]) overlapsPrefix(octet byte, pfxLen int) bool {
// ##################################################
// 1. test if any route in this node overlaps prefix?
// ##################################################
pfxIdx := prefixToBaseIndex(octet, pfxLen)
if n.lpmTest(pfxIdx) {
return true
}
// #################################################
// 2. test if prefix overlaps any route in this node
// #################################################
// lower/upper boundary for host routes
pfxLower, pfxUpper := hostRoutesByIndex(pfxIdx)
// increment to 'next' routeIdx for start in bitset search
// since pfxIdx already testet by lpm in other direction
routeIdx := pfxIdx * 2
var ok bool
for {
if routeIdx, ok = n.prefixesBitset.NextSet(routeIdx); !ok {
break
}
routeLower, routeUpper := hostRoutesByIndex(routeIdx)
if routeLower >= pfxLower && routeUpper <= pfxUpper {
return true
}
// next route
routeIdx++
}
// #################################################
// 3. test if prefix overlaps any child in this node
// #################################################
// set start octet in bitset search with prefix octet
addr := uint(octet)
for {
if addr, ok = n.childrenBitset.NextSet(addr); !ok {
break
}
idx := addr + firstHostIndex
if idx >= pfxLower && idx <= pfxUpper {
return true
}
// next round
addr++
}
return false
}
// eachSubnet calls yield() for any covered CIDR by parent prefix.
func (n *node[V]) eachSubnet(path [16]byte, depth int, is4 bool, parentOctet byte, pfxLen int, yield func(pfx netip.Prefix, val V) bool) bool {
// collect all routes covered by this pfx
// see also algorithm in overlapsPrefix
// can't use lpm, search prefix has no node
parentIdx := prefixToBaseIndex(parentOctet, pfxLen)
parentLower, parentUpper := hostRoutesByIndex(parentIdx)
// start bitset search at parentIdx
idx := parentIdx
var ok bool
for {
if idx, ok = n.prefixesBitset.NextSet(idx); !ok {
break
}
// can't use lpm, search prefix has no node
lower, upper := hostRoutesByIndex(idx)
// idx is covered by parentIdx?
if lower >= parentLower && upper <= parentUpper {
val, _ := n.getValue(idx)
cidr, _ := cidrFromPath(path, depth, is4, idx)
if !yield(cidr, val) {
// early exit
return false
}
}
idx++
}
// collect all children covered
var addr uint
for {
if addr, ok = n.childrenBitset.NextSet(addr); !ok {
break
}
octet := byte(addr)
// make host route for comparison with lower, upper
idx := octetToBaseIndex(octet)
// is child covered?
if idx >= parentLower && idx <= parentUpper {
c := n.getChild(octet)
// add (set) this octet to path
path[depth] = octet
// all cidrs under this child are covered by pfx
if !c.allRec(path, depth+1, is4, yield) {
// early exit
return false
}
}
addr++
}
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]) (duplicates int) {
// make backing arrays, no heap allocs
idxBackingArray := [maxNodePrefixes]uint{}
// for all prefixes in other node do ...
for _, oIdx := range o.allStrideIndexes(idxBackingArray[:]) {
// insert/overwrite prefix/value from oNode to nNode
oVal, _ := o.getValue(oIdx)
if !n.insertPrefix(oIdx, oVal) {
duplicates++
}
}
// make backing arrays, no heap allocs
addrBackingArray := [maxNodeChildren]uint{}
// for all children in other node do ...
for i, oOctet := range o.allChildAddrs(addrBackingArray[:]) {
octet := byte(oOctet)
// we know the slice index, faster as o.getChild(octet)
oc := o.children[i]
// get n child with same octet,
// we don't know the slice index in n.children
nc := n.getChild(octet)
if nc == nil {
// insert cloned child from oNode into nNode
n.insertChild(octet, oc.cloneRec())
} else {
// both nodes have child with octet, call union rec-descent
duplicates += nc.unionRec(oc)
}
}
return duplicates
}
// cloneRec, clones the node recursive.
func (n *node[V]) cloneRec() *node[V] {
c := newNode[V]()
if n.isEmpty() {
return c
}
c.prefixesBitset = n.prefixesBitset.Clone() // deep
c.prefixes = slices.Clone(n.prefixes) // shallow values
c.childrenBitset = n.childrenBitset.Clone() // deep
c.children = slices.Clone(n.children) // shallow
// now clone the children deep
for i, child := range c.children {
c.children[i] = child.cloneRec()
}
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 [16]byte, depth int, is4 bool, yield func(netip.Prefix, V) bool) bool {
// for all prefixes in this node do ...
idxBackingArray := [maxNodePrefixes]uint{}
for _, idx := range n.allStrideIndexes(idxBackingArray[:]) {
val, _ := n.getValue(idx)
cidr, _ := cidrFromPath(path, depth, is4, idx)
// make the callback for this prefix
if !yield(cidr, val) {
// early exit
return false
}
}
// for all children in this node do ...
addrBackingArray := [maxNodeChildren]uint{}
for i, addr := range n.allChildAddrs(addrBackingArray[:]) {
child := n.children[i]
path[depth] = byte(addr)
if !child.allRec(path, depth+1, is4, yield) {
// early exit
return false
}
}
return true
}
// allRecSorted runs recursive the trie, starting at 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 is in prefix sort order, it's a very complex implemenation compared with allRec.
func (n *node[V]) allRecSorted(path [16]byte, depth int, is4 bool, yield func(netip.Prefix, V) bool) bool {
// make backing arrays, no heap allocs
addrBackingArray := [maxNodeChildren]uint{}
idxBackingArray := [maxNodePrefixes]uint{}
// get slice of all child octets, sorted by addr
childAddrs := n.allChildAddrs(addrBackingArray[:])
childCursor := 0
// get slice of all indexes, sorted by idx
allIndices := n.allStrideIndexes(idxBackingArray[:])
// re-sort indexes by prefix in place
slices.SortFunc(allIndices, cmpIndexRank)
// example for entry with root node:
//
// ▼
// ├─ 0.0.0.1/32 <-- FOOTNOTE A: child 0 in first node
// ├─ 10.0.0.0/7 <-- FOOTNOTE B: prefix 10/7 in first node
// │ └─ 10.0.0.0/8 <-- FOOTNOTE C: prefix 10/8 in first node
// │ └─ 10.0.0.1/32 <-- FOOTNOTE D: child 10 in first node
// ├─ 127.0.0.0/8 <-- FOOTNOTE E: prefix 127/8 in first node
// └─ 192.168.0.0/16 <-- FOOTNOTE F: child 192 in first node
// range over all indexes in this node, now in prefix sort order
// FOOTNOTE: B, C, E
for i, idx := range allIndices {
// get the host routes for this index
lower, upper := hostRoutesByIndex(idx)
// adjust host routes for this idx in case the host routes
// of the following idx overlaps
// FOOTNOTE: B and C have overlaps in host routes
// FOOTNOTE: C, E don't overlap in host routes
// FOOTNOTE: E has no following prefix in this node
if i+1 < len(allIndices) {
lower, upper = adjustHostRoutes(idx, allIndices[i+1])
}
// handle childs before the host routes of idx
// FOOTNOTE: A
for j := childCursor; j < len(childAddrs); j++ {
addr := childAddrs[j]
octet := byte(addr)
if octetToBaseIndex(octet) >= lower {
// lower border of host routes
break
}
// we know the slice index, faster as n.getChild(octet)
c := n.children[j]
// add (set) this octet to path
path[depth] = octet
if !c.allRecSorted(path, depth+1, is4, yield) {
// early exit
return false
}
childCursor++
}
// FOOTNOTE: B, C, F
// now handle prefix for idx
val, _ := n.getValue(idx)
cidr, _ := cidrFromPath(path, depth, is4, idx)
if !yield(cidr, val) {
// early exit
return false
}
// handle the children in host routes for this prefix
// FOOTNOTE: D
for j := childCursor; j < len(childAddrs); j++ {
addr := childAddrs[j]
octet := byte(addr)
if octetToBaseIndex(octet) > upper {
// out of host routes
break
}
// we know the slice index, faster as n.getChild(octet)
c := n.children[j]
// add (set) this octet to path
path[depth] = octet
if !c.allRecSorted(path, depth+1, is4, yield) {
// early exit
return false
}
childCursor++
}
}
// FOOTNOTE: F
// handle all the rest of the children
for j := childCursor; j < len(childAddrs); j++ {
addr := childAddrs[j]
octet := byte(addr)
// we know the slice index, faster as n.getChild(octet)
c := n.children[j]
// add (set) this octet to path
path[depth] = octet
if !c.allRecSorted(path, depth+1, is4, yield) {
// early exit
return false
}
}
return true
}
// adjustHostRoutes, helper function to adjust the lower, upper bounds of the
// host routes in case the host routes of the next idx overlaps
func adjustHostRoutes(idx, next uint) (lower, upper uint) {
lower, upper = hostRoutesByIndex(idx)
// get the lower host route border of the next idx
nextLower, _ := hostRoutesByIndex(next)
// is there an overlap?
switch {
case nextLower == lower:
upper = 0
// [------------] idx
// [-----] next
// make host routes for this idx invalid
//
// ][ idx
// [-----]^^^^^^] next
//
// these ^^^^^^ children are handled before next prefix
//
// sorry, I know, it's completely confusing
case nextLower <= upper:
upper = nextLower - 1
// [------------] idx
// [------] next
//
// shrink host routes for this idx
// [----][------] idx, next
// ^
}
return lower, upper
}
// numPrefixesRec, calculate the number of prefixes under n.
func (n *node[V]) numPrefixesRec() int {
size := len(n.prefixes) // this node
for _, c := range n.children {
size += c.numPrefixesRec()
}
return size
}
// numNodesRec, calculate the number of nodes under n.
func (n *node[V]) numNodesRec() int {
size := 1 // this node
for _, c := range n.children {
size += c.numNodesRec()
}
return size
}
// 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())
}

213
vendor/github.com/gaissmai/bart/stringify.go generated vendored Normal file
View File

@@ -0,0 +1,213 @@
// Copyright (c) 2024 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart
import (
"bytes"
"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]
path [16]byte
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) {
t.init()
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 {
t.init()
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 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 (9.9.9.9)
// │ ├─ 10.0.0.0/24 (8.8.8.8)
// │ └─ 10.0.1.0/24 (10.0.0.0)
// ├─ 127.0.0.0/8 (127.0.0.1)
// │ └─ 127.0.0.1/32 (127.0.0.1)
// ├─ 169.254.0.0/16 (10.0.0.0)
// ├─ 172.16.0.0/12 (8.8.8.8)
// └─ 192.168.0.0/16 (9.9.9.9)
// └─ 192.168.1.0/24 (127.0.0.1)
// ▼
// └─ ::/0 (2001:db8::1)
// ├─ ::1/128 (::1%lo)
// ├─ 2000::/3 (2001:db8::1)
// │ └─ 2001:db8::/32 (2001:db8::1)
// └─ fe80::/10 (::1%eth0)
func (t *Table[V]) Fprint(w io.Writer) error {
t.init()
if err := t.fprint(w, true); err != nil {
return err
}
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
}
if err := n.fprintRec(w, 0, zeroPath, 0, is4, ""); err != nil {
return err
}
return nil
}
// fprintRec, the output is a hierarchical CIDR tree starting with parentIdx and byte path.
func (n *node[V]) fprintRec(w io.Writer, parentIdx uint, path [16]byte, depth int, is4 bool, pad string) error {
// get direct childs for this parentIdx ...
directKids := n.getKidsRec(parentIdx, path, depth, 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.idx, kid.path, kid.depth, is4, 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 [16]byte, depth int, is4 bool) []kid[V] {
directKids := []kid[V]{}
// make backing arrays, no heap allocs
idxBackingArray := [maxNodePrefixes]uint{}
for _, idx := range n.allStrideIndexes(idxBackingArray[:]) {
// 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.lpm(idx >> 1)
if lpmIdx == parentIdx {
// idx is directKid
val, _ := n.getValue(idx)
cidr, _ := cidrFromPath(path, depth, is4, idx)
directKids = append(directKids, kid[V]{n, path, depth, idx, cidr, val})
}
}
// the node may have childs, the rec-descent monster starts
addrBackingArray := [maxNodeChildren]uint{}
for i, addr := range n.allChildAddrs(addrBackingArray[:]) {
octet := byte(addr)
// do a longest-prefix-match
lpmIdx, _, _ := n.lpm(octetToBaseIndex(octet))
if lpmIdx == parentIdx {
c := n.children[i]
path[depth] = octet
// traverse, rec-descent call with next child node
directKids = append(directKids, c.getKidsRec(0, path, depth+1, is4)...)
}
}
return directKids
}
// cidrFromPath, get prefix back from byte path, depth, octet and pfxLen.
func cidrFromPath(path [16]byte, depth int, is4 bool, idx uint) (netip.Prefix, error) {
octet, pfxLen := baseIndexToPrefix(idx)
// set (partially) masked byte in path at depth
path[depth] = octet
// make ip addr from octets
var ip netip.Addr
if is4 {
b4 := [4]byte{}
copy(b4[:], path[:4])
ip = netip.AddrFrom4(b4)
} else {
ip = netip.AddrFrom16(path)
}
// calc bits with pathLen and pfxLen
bits := depth*strideLen + pfxLen
// make a normalized prefix from ip/bits
return ip.Prefix(bits)
}
// cmpKidByPrefix, all prefixes are already normalized (Masked).
func cmpKidByPrefix[V any](a, b kid[V]) int {
return cmpPrefix(a.cidr, b.cidr)
}

821
vendor/github.com/gaissmai/bart/table.go generated vendored Normal file
View File

@@ -0,0 +1,821 @@
// Copyright (c) 2024 Karl Gaissmaier
// SPDX-License-Identifier: MIT
// package bart provides a Balanced-Routing-Table (BART).
//
// BART is balanced in terms of memory consumption versus
// lookup time.
//
// The lookup time is by a factor of ~2 slower on average as the
// routing algorithms ART, SMART, CPE, ... but reduces the memory
// consumption by an order of magnitude in comparison.
//
// 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.
//
// The second key factor is popcount array compression at each stride level
// of the CBT prefix tree and backtracking along the CBT in O(k).
//
// The CBT is implemented as a bitvector, backtracking is just
// a matter of fast cache friendly bitmask operations.
//
// The child array at each stride level is also popcount compressed.
package bart
import (
"net/netip"
"sync"
)
// Table is an IPv4 and IPv6 routing table with payload V.
// The zero value is ready to use.
//
// The Table is safe for concurrent readers but not for
// concurrent readers and/or writers.
type Table[V any] struct {
rootV4 *node[V]
rootV6 *node[V]
size4 int
size6 int
// BitSets have to be initialized.
initOnce sync.Once
}
// init BitSets once, so no constructor is needed
func (t *Table[V]) init() {
// upfront nil test, faster than the atomic load in sync.Once.Do
// this makes bulk inserts 5% faster and the table is not safe
// for concurrent writers anyway
if t.rootV6 != nil {
return
}
t.initOnce.Do(func() {
t.rootV4 = newNode[V]()
t.rootV6 = newNode[V]()
})
}
// rootNodeByVersion, select root node for ip version.
func (t *Table[V]) rootNodeByVersion(is4 bool) *node[V] {
if is4 {
return t.rootV4
}
return t.rootV6
}
// Insert adds pfx to the tree, with value val.
// If pfx is already present in the tree, its value is set to val.
func (t *Table[V]) Insert(pfx netip.Prefix, val V) {
t.init()
if !pfx.IsValid() {
return
}
// values derived from pfx
ip := pfx.Addr()
is4 := ip.Is4()
bits := pfx.Bits()
// get the root node of the routing table
n := t.rootNodeByVersion(is4)
// Do not allocate!
// As16() is inlined, the prefered AsSlice() is too complex for inlining.
// starting with go1.23 we can use AsSlice(),
// see https://github.com/golang/go/issues/56136
a16 := ip.As16()
octets := a16[:]
if is4 {
octets = octets[12:]
}
// 10.0.0.0/8 -> 0
// 10.12.0.0/15 -> 1
// 10.12.0.0/16 -> 1
// 10.12.10.9/32 -> 3
lastOctetIdx := (bits - 1) / strideLen
// 10.0.0.0/8 -> 10
// 10.12.0.0/15 -> 12
// 10.12.0.0/16 -> 12
// 10.12.10.9/32 -> 9
lastOctet := octets[lastOctetIdx]
// 10.0.0.0/8 -> 8
// 10.12.0.0/15 -> 7
// 10.12.0.0/16 -> 8
// 10.12.10.9/32 -> 8
lastOctetBits := bits - (lastOctetIdx * strideLen)
// mask the prefix, this is faster than netip.Prefix.Masked()
lastOctet = lastOctet & netMask(lastOctetBits)
// find the proper trie node to insert prefix
for _, octet := range octets[:lastOctetIdx] {
// descend down to next trie level
c := n.getChild(octet)
if c == nil {
// create and insert missing intermediate child
c = newNode[V]()
n.insertChild(octet, c)
}
// proceed with next level
n = c
}
// insert prefix/val into node
if n.insertPrefix(prefixToBaseIndex(lastOctet, lastOctetBits), val) {
t.sizeUpdate(is4, 1)
}
}
// Update or set the value at pfx with a callback function.
// The callback function is called with (value, ok) and returns a new value..
//
// If the pfx does not already exist, it is set with the new value.
func (t *Table[V]) Update(pfx netip.Prefix, cb func(val V, ok bool) V) (newVal V) {
t.init()
if !pfx.IsValid() {
var zero V
return zero
}
// values derived from pfx
ip := pfx.Addr()
is4 := ip.Is4()
bits := pfx.Bits()
n := t.rootNodeByVersion(is4)
// do not allocate
a16 := ip.As16()
octets := a16[:]
if is4 {
octets = octets[12:]
}
// see comment in Insert()
lastOctetIdx := (bits - 1) / strideLen
lastOctet := octets[lastOctetIdx]
lastOctetBits := bits - (lastOctetIdx * strideLen)
// mask the prefix
lastOctet = lastOctet & netMask(lastOctetBits)
// find the proper trie node to update prefix
for _, octet := range octets[:lastOctetIdx] {
// descend down to next trie level
c := n.getChild(octet)
if c == nil {
// create and insert missing intermediate child
c = newNode[V]()
n.insertChild(octet, c)
}
// proceed with next level
n = c
}
// update/insert prefix into node
var wasPresent bool
newVal, wasPresent = n.updatePrefix(lastOctet, lastOctetBits, cb)
if !wasPresent {
t.sizeUpdate(is4, 1)
}
return newVal
}
// Get returns the associated payload for prefix and true, or false if
// prefix is not set in the routing table.
func (t *Table[V]) Get(pfx netip.Prefix) (val V, ok bool) {
if !pfx.IsValid() {
return
}
// values derived from pfx
ip := pfx.Addr()
is4 := ip.Is4()
bits := pfx.Bits()
n := t.rootNodeByVersion(is4)
if n == nil {
return
}
// do not allocate
a16 := ip.As16()
octets := a16[:]
if is4 {
octets = octets[12:]
}
// see comment in Insert()
lastOctetIdx := (bits - 1) / strideLen
lastOctet := octets[lastOctetIdx]
lastOctetBits := bits - (lastOctetIdx * strideLen)
// mask the prefix
lastOctet = lastOctet & netMask(lastOctetBits)
// find the proper trie node
for _, octet := range octets[:lastOctetIdx] {
c := n.getChild(octet)
if c == nil {
// not found
return
}
n = c
}
return n.getValue(prefixToBaseIndex(lastOctet, lastOctetBits))
}
// Delete removes pfx from the tree, pfx does not have to be present.
func (t *Table[V]) Delete(pfx netip.Prefix) {
if !pfx.IsValid() {
return
}
// values derived from pfx
ip := pfx.Addr()
is4 := ip.Is4()
bits := pfx.Bits()
n := t.rootNodeByVersion(is4)
if n == nil {
return
}
// do not allocate
a16 := ip.As16()
octets := a16[:]
if is4 {
octets = octets[12:]
}
// see comment in Insert()
lastOctetIdx := (bits - 1) / strideLen
lastOctet := octets[lastOctetIdx]
lastOctetBits := bits - (lastOctetIdx * strideLen)
// mask the prefix
lastOctet = lastOctet & netMask(lastOctetBits)
octets[lastOctetIdx] = lastOctet
// record path to deleted node
stack := [maxTreeDepth]*node[V]{}
// run variable as stackPointer, see below
var i int
// find the trie node
for i = range octets {
// push current node on stack for path recording
stack[i] = n
if i == lastOctetIdx {
break
}
// descend down to next level
c := n.getChild(octets[i])
if c == nil {
return
}
n = c
}
// try to delete prefix in trie node
if !n.deletePrefix(lastOctet, lastOctetBits) {
// nothing deleted
return
}
t.sizeUpdate(is4, -1)
// purge dangling nodes after successful deletion
for i > 0 {
if n.isEmpty() {
// purge empty node from parents children
parent := stack[i-1]
parent.deleteChild(octets[i-1])
}
// unwind the stack
i--
n = stack[i]
}
}
// Lookup does a route lookup (longest prefix match) for IP and
// returns the associated value and true, or false if no route matched.
func (t *Table[V]) Lookup(ip netip.Addr) (val V, ok bool) {
if !ip.IsValid() {
return
}
is4 := ip.Is4()
n := t.rootNodeByVersion(is4)
if n == nil {
return
}
// do not allocate
a16 := ip.As16()
octets := a16[:]
if is4 {
octets = octets[12:]
}
// stack of the traversed nodes for fast backtracking, if needed
stack := [maxTreeDepth]*node[V]{}
// run variable, used after for loop
var i int
var octet byte
// find leaf node
for i, octet = range octets {
// push current node on stack for fast backtracking
stack[i] = n
// go down in tight loop to leaf node
c := n.getChild(octet)
if c == nil {
break
}
n = c
}
// start backtracking, unwind the stack
for depth := i; depth >= 0; depth-- {
n = stack[depth]
octet = octets[depth]
// longest prefix match
// micro benchmarking: skip if node has no prefixes
if len(n.prefixes) != 0 {
if _, val, ok := n.lpm(octetToBaseIndex(octet)); ok {
return val, true
}
}
}
return
}
// LookupPrefix does a route lookup (longest prefix match) for pfx and
// returns the associated value and true, or false if no route matched.
func (t *Table[V]) LookupPrefix(pfx netip.Prefix) (val V, ok bool) {
_, _, val, ok = t.lpmPrefix(pfx)
return val, ok
}
// LookupPrefixLPM is similar to [Table.LookupPrefix],
// but it returns the lpm prefix in addition to value,ok.
//
// This method is about 20-30% slower than LookupPrefix and should only
// be used if the matching lpm entry is also required for other reasons.
//
// If LookupPrefixLPM is to be used for IP addresses,
// they must be converted to /32 or /128 prefixes.
func (t *Table[V]) LookupPrefixLPM(pfx netip.Prefix) (lpm netip.Prefix, val V, ok bool) {
depth, baseIdx, val, ok := t.lpmPrefix(pfx)
if ok {
// calculate the mask from baseIdx and depth
mask := baseIndexToPrefixLen(baseIdx, depth)
// calculate the lpm from ip and mask
lpm, _ = pfx.Addr().Prefix(mask)
}
return lpm, val, ok
}
func (t *Table[V]) lpmPrefix(pfx netip.Prefix) (depth int, baseIdx uint, val V, ok bool) {
if !pfx.IsValid() {
return
}
// values derived from pfx
ip := pfx.Addr()
is4 := ip.Is4()
bits := pfx.Bits()
n := t.rootNodeByVersion(is4)
if n == nil {
return
}
// do not allocate
a16 := ip.As16()
octets := a16[:]
if is4 {
octets = octets[12:]
}
// see comment in Insert()
lastOctetIdx := (bits - 1) / strideLen
lastOctet := octets[lastOctetIdx]
lastOctetBits := bits - (lastOctetIdx * strideLen)
// mask the prefix
lastOctet = lastOctet & netMask(lastOctetBits)
octets[lastOctetIdx] = lastOctet
var i int
var octet byte
// record path to leaf node
stack := [maxTreeDepth]*node[V]{}
// find the node
for i, octet = range octets[:lastOctetIdx+1] {
// push current node on stack
stack[i] = n
// go down in tight loop
c := n.getChild(octet)
if c == nil {
break
}
n = c
}
// start backtracking, unwind the stack
for depth = i; depth >= 0; depth-- {
n = stack[depth]
octet = octets[depth]
// longest prefix match
// micro benchmarking: skip if node has no prefixes
if len(n.prefixes) != 0 {
// only the lastOctet may have a different prefix len
// all others are just host routes
idx := uint(0)
if depth == lastOctetIdx {
idx = prefixToBaseIndex(octet, lastOctetBits)
} else {
idx = octetToBaseIndex(octet)
}
baseIdx, val, ok = n.lpm(idx)
if ok {
return depth, baseIdx, val, ok
}
}
}
return
}
// EachLookupPrefix calls yield() for each CIDR covering pfx
// in reverse CIDR sort order, from longest-prefix-match to
// shortest-prefix-match.
//
// If the yield function returns false, the iteration ends prematurely.
func (t *Table[V]) EachLookupPrefix(pfx netip.Prefix, yield func(pfx netip.Prefix, val V) bool) {
if !pfx.IsValid() {
return
}
// values derived from pfx
ip := pfx.Addr()
is4 := ip.Is4()
bits := pfx.Bits()
n := t.rootNodeByVersion(is4)
if n == nil {
return
}
// do not allocate
path := ip.As16()
octets := path[:]
if is4 {
octets = octets[12:]
}
copy(path[:], octets[:])
// see comment in Insert()
lastOctetIdx := (bits - 1) / strideLen
lastOctet := octets[lastOctetIdx]
lastOctetBits := bits - (lastOctetIdx * strideLen)
// mask the prefix
lastOctet = lastOctet & netMask(lastOctetBits)
octets[lastOctetIdx] = lastOctet
// stack of the traversed nodes for reverse ordering of supernets
stack := [maxTreeDepth]*node[V]{}
// run variable, used after for loop
var i int
var octet byte
// find last node
for i, octet = range octets[:lastOctetIdx+1] {
// push current node on stack
stack[i] = n
// go down in tight loop
c := n.getChild(octet)
if c == nil {
break
}
n = c
}
// start backtracking, unwind the stack
for depth := i; depth >= 0; depth-- {
n = stack[depth]
// microbenchmarking
if len(n.prefixes) == 0 {
continue
}
// only the lastOctet may have a different prefix len
if depth == lastOctetIdx {
if !n.eachLookupPrefix(path, depth, is4, lastOctet, lastOctetBits, yield) {
// early exit
return
}
continue
}
// all others are just host routes
if !n.eachLookupPrefix(path, depth, is4, octets[depth], strideLen, yield) {
// early exit
return
}
}
}
// EachSubnet calls yield() for each CIDR covered by pfx.
// If the yield function returns false, the iteration ends prematurely.
//
// The sort order is undefined and you must not rely on it!
func (t *Table[V]) EachSubnet(pfx netip.Prefix, yield func(pfx netip.Prefix, val V) bool) {
if !pfx.IsValid() {
return
}
// values derived from pfx
ip := pfx.Addr()
is4 := ip.Is4()
bits := pfx.Bits()
n := t.rootNodeByVersion(is4)
if n == nil {
return
}
// do not allocate
path := ip.As16()
octets := path[:]
if is4 {
octets = octets[12:]
}
copy(path[:], octets[:])
// see comment in Insert()
lastOctetIdx := (bits - 1) / strideLen
lastOctet := octets[lastOctetIdx]
lastOctetBits := bits - (lastOctetIdx * strideLen)
// mask the prefix
lastOctet = lastOctet & netMask(lastOctetBits)
octets[lastOctetIdx] = lastOctet
// find the trie node
for i, octet := range octets {
if i == lastOctetIdx {
_ = n.eachSubnet(path, i, is4, lastOctet, lastOctetBits, yield)
return
}
c := n.getChild(octet)
if c == nil {
break
}
n = c
}
}
// OverlapsPrefix reports whether any IP in pfx matches a route in the table.
func (t *Table[V]) OverlapsPrefix(pfx netip.Prefix) bool {
if !pfx.IsValid() {
return false
}
// values derived from pfx
ip := pfx.Addr()
is4 := ip.Is4()
bits := pfx.Bits()
// get the root node of the routing table
n := t.rootNodeByVersion(is4)
if n == nil {
return false
}
// do not allocate
a16 := ip.As16()
octets := a16[:]
if is4 {
octets = octets[12:]
}
// see comment in Insert()
lastOctetIdx := (bits - 1) / strideLen
lastOctet := octets[lastOctetIdx]
lastOctetBits := bits - (lastOctetIdx * strideLen)
// mask the prefix
lastOctet = lastOctet & netMask(lastOctetBits)
for _, octet := range octets[:lastOctetIdx] {
// test if any route overlaps prefix´ so far
if n.lpmTest(octetToBaseIndex(octet)) {
return true
}
// no overlap so far, go down to next c
c := n.getChild(octet)
if c == nil {
return false
}
n = c
}
return n.overlapsPrefix(lastOctet, lastOctetBits)
}
// Overlaps reports whether any IP in the table matches a route in the
// other table.
func (t *Table[V]) Overlaps(o *Table[V]) bool {
t.init()
o.init()
// t is empty
if t.Size() == 0 {
return false
}
// o is empty
if o.Size() == 0 {
return false
}
// at least one v4 is empty
if t.size4 == 0 || o.size4 == 0 {
return t.Overlaps6(o)
}
// at least one v6 is empty
if t.size6 == 0 || o.size6 == 0 {
return t.Overlaps4(o)
}
return t.Overlaps4(o) || t.Overlaps6(o)
}
// Overlaps4 reports whether any IPv4 in the table matches a route in the
// other table.
func (t *Table[V]) Overlaps4(o *Table[V]) bool {
t.init()
o.init()
return t.rootV4.overlapsRec(o.rootV4)
}
// Overlaps6 reports whether any IPv6 in the table matches a route in the
// other table.
func (t *Table[V]) Overlaps6(o *Table[V]) bool {
t.init()
o.init()
return t.rootV6.overlapsRec(o.rootV6)
}
// Union combines two tables, changing the receiver table.
// If there are duplicate entries, the value is taken from the other table.
func (t *Table[V]) Union(o *Table[V]) {
t.init()
o.init()
dup4 := t.rootV4.unionRec(o.rootV4)
dup6 := t.rootV6.unionRec(o.rootV6)
t.size4 += o.size4 - dup4
t.size6 += o.size6 - dup6
}
// Clone returns a copy of the routing table.
// The payloads V are copied using assignment, so this is a shallow clone.
func (t *Table[V]) Clone() *Table[V] {
t.init()
c := new(Table[V])
c.init()
c.rootV4 = t.rootV4.cloneRec()
c.rootV6 = t.rootV6.cloneRec()
c.size4 = t.size4
c.size6 = t.size6
return c
}
// All may be used in a for/range loop to iterate
// through all the prefixes.
// The sort order is undefined and you must not rely on it!
//
// Prefixes must not be inserted or deleted during iteration, otherwise
// the behavior is undefined. However, value updates are permitted.
//
// If the yield function returns false, the iteration ends prematurely.
func (t *Table[V]) All(yield func(pfx netip.Prefix, val V) bool) {
t.init()
// respect early exit
_ = t.rootV4.allRec(zeroPath, 0, true, yield) &&
t.rootV6.allRec(zeroPath, 0, false, yield)
}
// All4, like [Table.All] but only for the v4 routing table.
func (t *Table[V]) All4(yield func(pfx netip.Prefix, val V) bool) {
t.init()
t.rootV4.allRec(zeroPath, 0, true, yield)
}
// All6, like [Table.All] but only for the v6 routing table.
func (t *Table[V]) All6(yield func(pfx netip.Prefix, val V) bool) {
t.init()
t.rootV6.allRec(zeroPath, 0, false, yield)
}
// AllSorted may be used in a for/range loop to iterate
// through all the prefixes in natural CIDR sort order.
//
// Prefixes must not be inserted or deleted during iteration, otherwise
// the behavior is undefined. However, value updates are permitted.
//
// If the yield function returns false, the iteration ends prematurely.
func (t *Table[V]) AllSorted(yield func(pfx netip.Prefix, val V) bool) {
t.init()
// respect early exit
_ = t.rootV4.allRecSorted(zeroPath, 0, true, yield) &&
t.rootV6.allRecSorted(zeroPath, 0, false, yield)
}
// All4Sorted, like [Table.AllSorted] but only for the v4 routing table.
func (t *Table[V]) All4Sorted(yield func(pfx netip.Prefix, val V) bool) {
t.init()
t.rootV4.allRecSorted(zeroPath, 0, true, yield)
}
// All6Sorted, like [Table.AllSorted] but only for the v6 routing table.
func (t *Table[V]) All6Sorted(yield func(pfx netip.Prefix, val V) bool) {
t.init()
t.rootV6.allRecSorted(zeroPath, 0, false, yield)
}
func (t *Table[V]) sizeUpdate(is4 bool, n int) {
switch is4 {
case true:
t.size4 += n
case false:
t.size6 += n
}
}
// 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
}
// nodes, calculates the IPv4 and IPv6 nodes and returns the sum.
func (t *Table[V]) nodes() int {
t.init()
return t.rootV4.numNodesRec() + t.rootV6.numNodesRec()
}