Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+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
|
||||
}
|
||||
Reference in new issue
Block a user