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