Skip to content

Commit

Permalink
fix: overlaps bug, slipped in with path compression
Browse files Browse the repository at this point in the history
and a lot of naming and formatting
  • Loading branch information
gaissmai committed Feb 9, 2025
1 parent 83f2077 commit d508a5b
Show file tree
Hide file tree
Showing 16 changed files with 299 additions and 217 deletions.
28 changes: 20 additions & 8 deletions dumper.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ func (t *Table[V]) dump(w io.Writer) {
if t.size4 > 0 {
fmt.Fprintln(w)
fmt.Fprintf(w, "### IPv4: size(%d), nodes(%d)", t.size4, t.root4.nodeStatsRec().nodes)
t.root4.dumpRec(w, zeroPath, 0, true)
t.root4.dumpRec(w, stridePath{}, 0, true)
}

if t.size6 > 0 {
fmt.Fprintln(w)
fmt.Fprintf(w, "### IPv6: size(%d), nodes(%d)", t.size6, t.root6.nodeStatsRec().nodes)
t.root6.dumpRec(w, zeroPath, 0, false)
t.root6.dumpRec(w, stridePath{}, 0, false)
}
}

// dumpRec, rec-descent the trie.
func (n *node[V]) dumpRec(w io.Writer, path [16]byte, depth int, is4 bool) {
func (n *node[V]) dumpRec(w io.Writer, path stridePath, depth int, is4 bool) {
// dump this node
n.dump(w, path, depth, is4)

Expand All @@ -68,7 +68,7 @@ func (n *node[V]) dumpRec(w io.Writer, path [16]byte, depth int, is4 bool) {
}

// dump the node to w.
func (n *node[V]) dump(w io.Writer, path [16]byte, depth int, is4 bool) {
func (n *node[V]) dump(w io.Writer, path stridePath, depth int, is4 bool) {
bits := depth * strideLen
indent := strings.Repeat(".", depth)

Expand Down Expand Up @@ -114,8 +114,12 @@ func (n *node[V]) dump(w io.Writer, path [16]byte, depth int, is4 bool) {
case *node[V]:
nodeAddrs = append(nodeAddrs, addr)
continue

case *leaf[V]:
leafAddrs = append(leafAddrs, addr)

default:
panic("logic error, wrong node type")
}
}

Expand Down Expand Up @@ -179,7 +183,7 @@ func octetFmt(octet byte, is4 bool) string {
//
// 127.0.0
// 2001:0d
func ipStridePath(path [16]byte, depth int, is4 bool) string {
func ipStridePath(path stridePath, depth int, is4 bool) string {
buf := new(strings.Builder)

if is4 {
Expand Down Expand Up @@ -240,10 +244,15 @@ func (n *node[V]) nodeStats() stats {
switch n.children.Items[i].(type) {
case *node[V]:
s.nodes++

case *leaf[V]:
s.leaves++

default:
panic("logic error, wrong node type")
}
}

return s
}

Expand All @@ -259,11 +268,11 @@ func (n *node[V]) nodeStatsRec() stats {
s.nodes = 1 // this node
s.leaves = 0

for _, c := range n.children.Items {
switch k := c.(type) {
for _, kidAny := range n.children.Items {
switch kid := kidAny.(type) {
case *node[V]:
// rec-descent
rs := k.nodeStatsRec()
rs := kid.nodeStatsRec()

s.pfxs += rs.pfxs
s.childs += rs.childs
Expand All @@ -272,6 +281,9 @@ func (n *node[V]) nodeStatsRec() stats {

case *leaf[V]:
s.leaves++

default:
panic("logic error, wrong node type")
}
}

Expand Down
62 changes: 44 additions & 18 deletions fulltable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,17 +252,17 @@ func BenchmarkFullTableOverlapsV4(b *testing.B) {
rt.Insert(route.CIDR, i)
}

for i := 1; i <= 1024; i *= 2 {
inter := new(Table[int])
for j := 0; j <= i; j++ {
pfx := randomPrefix4()
inter.Insert(pfx, j)
for i := 1; i <= 1<<20; i *= 2 {
rt2 := new(Table[int])
for j, pfx := range randomRealWorldPrefixes4(i) {
rt2.Insert(pfx, j)
}
b.Log(rt2.String())

b.Run(fmt.Sprintf("With_%4d", i), func(b *testing.B) {
b.ResetTimer()
for range b.N {
boolSink = rt.Overlaps(inter)
boolSink = rt.Overlaps(rt2)
}
})
}
Expand All @@ -275,17 +275,17 @@ func BenchmarkFullTableOverlapsV6(b *testing.B) {
rt.Insert(route.CIDR, i)
}

for i := 1; i <= 1024; i *= 2 {
inter := new(Table[int])
for j := 0; j <= i; j++ {
pfx := randomPrefix6()
inter.Insert(pfx, j)
for i := 1; i <= 1<<20; i *= 2 {
rt2 := new(Table[int])
for j, pfx := range randomRealWorldPrefixes6(i) {
rt2.Insert(pfx, j)
}
b.Log(rt2.String())

b.Run(fmt.Sprintf("With_%4d", i), func(b *testing.B) {
b.ResetTimer()
for range b.N {
boolSink = rt.Overlaps(inter)
boolSink = rt.Overlaps(rt2)
}
})
}
Expand All @@ -298,7 +298,7 @@ func BenchmarkFullTableOverlapsPrefix(b *testing.B) {
rt.Insert(route.CIDR, i)
}

pfx := randomPrefix()
pfx := randomRealWorldPrefixes(1)[0]

b.ResetTimer()
for range b.N {
Expand Down Expand Up @@ -466,29 +466,55 @@ func fillRouteTables() {

// #########################################################

func gimmeRandomPrefixes4(n int) []netip.Prefix {
func randomRealWorldPrefixes4(n int) []netip.Prefix {
set := map[netip.Prefix]netip.Prefix{}
pfxs := make([]netip.Prefix, 0, n)

for {
pfx := randomPrefix4()

// skip too small or too big masks
if pfx.Bits() < 8 && pfx.Bits() > 28 {
continue
}

// skip multicast ...
if pfx.Overlaps(mpp("240.0.0.0/8")) {
continue
}

if _, ok := set[pfx]; !ok {
set[pfx] = pfx
pfxs = append(pfxs, pfx)
}

if len(set) >= n {
break
}
}
return pfxs
}

func gimmeRandomPrefixes6(n int) []netip.Prefix {
func randomRealWorldPrefixes6(n int) []netip.Prefix {
set := map[netip.Prefix]netip.Prefix{}
pfxs := make([]netip.Prefix, 0, n)

for {
pfx := randomPrefix6()

// 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] = pfx
pfxs = append(pfxs, pfx)
Expand All @@ -500,10 +526,10 @@ func gimmeRandomPrefixes6(n int) []netip.Prefix {
return pfxs
}

func gimmeRandomPrefixes(n int) []netip.Prefix {
func randomRealWorldPrefixes(n int) []netip.Prefix {
pfxs := make([]netip.Prefix, 0, n)
pfxs = append(pfxs, gimmeRandomPrefixes4(n/2)...)
pfxs = append(pfxs, gimmeRandomPrefixes6(n-len(pfxs))...)
pfxs = append(pfxs, randomRealWorldPrefixes4(n/2)...)
pfxs = append(pfxs, randomRealWorldPrefixes6(n-len(pfxs))...)

prng.Shuffle(n, func(i, j int) {
pfxs[i], pfxs[j] = pfxs[j], pfxs[i]
Expand Down
4 changes: 2 additions & 2 deletions gold_stride_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ type goldStrideItem[V any] struct {
}

func (t *goldStrideTbl[V]) insertMany(strides []goldStrideItem[V]) *goldStrideTbl[V] {
cast := goldStrideTbl[V](strides)
t = &cast
conv := goldStrideTbl[V](strides)
t = &conv
return t
}

Expand Down
4 changes: 2 additions & 2 deletions gold_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ func (t *goldTable[V]) insert(pfx netip.Prefix, val V) {
}

func (t *goldTable[V]) insertMany(pfxs []goldTableItem[V]) *goldTable[V] {
cast := goldTable[V](pfxs)
t = &cast
conv := goldTable[V](pfxs)
t = &conv
return t
}

Expand Down
9 changes: 2 additions & 7 deletions internal/bitset/bitset.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// can inline BitSet.Set with cost 60
// can inline BitSet.Test with cost 26
// can inline BitSet.Clear with cost 24
// can inline BitSet.Clone with cost 20
// 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
Expand Down Expand Up @@ -94,12 +94,7 @@ func (b BitSet) Test(i uint) (ok bool) {

// Clone this BitSet, returning a new BitSet that has the same bits set.
func (b BitSet) Clone() BitSet {
if b == nil {
return nil
}
c := BitSet(make([]uint64, len(b), cap(b)))
copy(c, b)
return c
return append(b[:0:0], b...)
}

// Compact, preserve all set bits, while minimizing memory usage.
Expand Down
11 changes: 2 additions & 9 deletions internal/sparse/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,9 @@ func (s *Array[T]) Copy() *Array[T] {
return nil
}

var items []T

if s.Items != nil {
items = make([]T, len(s.Items), cap(s.Items))
copy(items, s.Items) // shallow
}

return &Array[T]{
s.BitSet.Clone(),
items,
BitSet: s.BitSet.Clone(),
Items: append(s.Items[:0:0], s.Items...),
}
}

Expand Down
6 changes: 3 additions & 3 deletions jsonify.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (t *Table[V]) DumpList4() []DumpListNode[V] {
if t == nil {
return nil
}
return t.root4.dumpListRec(0, zeroPath, 0, true)
return t.root4.dumpListRec(0, stridePath{}, 0, true)
}

// DumpList6 dumps the ipv6 tree into a list of roots and their subnets.
Expand All @@ -54,10 +54,10 @@ func (t *Table[V]) DumpList6() []DumpListNode[V] {
if t == nil {
return nil
}
return t.root6.dumpListRec(0, zeroPath, 0, false)
return t.root6.dumpListRec(0, stridePath{}, 0, false)
}

func (n *node[V]) dumpListRec(parentIdx uint, path [16]byte, depth int, is4 bool) []DumpListNode[V] {
func (n *node[V]) dumpListRec(parentIdx uint, path stridePath, depth int, is4 bool) []DumpListNode[V] {
// recursion stop condition
if n == nil {
return nil
Expand Down
20 changes: 0 additions & 20 deletions nocopy.go

This file was deleted.

Loading

0 comments on commit d508a5b

Please sign in to comment.