From b72b2c3bd5964390e34f71c09a10734aec5c3c96 Mon Sep 17 00:00:00 2001 From: Karl Gaissmaier Date: Sun, 26 Jan 2025 18:17:38 +0100 Subject: [PATCH] simplify --- dumper.go | 34 +++++++++++++++------------------- internal/bitset/bitset.go | 13 ++++++++----- internal/bitset/bitset_test.go | 16 +++++----------- stringify.go | 8 ++------ 4 files changed, 30 insertions(+), 41 deletions(-) diff --git a/dumper.go b/dumper.go index ebbcd06..02564a3 100644 --- a/dumper.go +++ b/dumper.go @@ -14,9 +14,9 @@ type nodeType byte const ( nullNode nodeType = iota // empty node - fullNode // prefixes and (children or PC) - leafNode // no children, only prefixes or PC - intermediateNode // only children, no prefix nor PC, + fullNode // prefixes and children or path-compressed prefixes + leafNode // no children, only prefixes or path-compressed prefixes + intermediateNode // only children, no prefix nor path-compressed prefixes UNKNOWN // logic error ) @@ -38,34 +38,30 @@ func (t *Table[V]) dump(w io.Writer) { return } - if t.Size4() > 0 { + if t.size4 > 0 { fmt.Fprintln(w) - fmt.Fprintf(w, "### IPv4: size(%d), nodes(%d)", t.Size4(), t.root4.nodeStatsRec().nodes) + fmt.Fprintf(w, "### IPv4: size(%d), nodes(%d)", t.size4, t.root4.nodeStatsRec().nodes) t.root4.dumpRec(w, zeroPath, 0, true) } - if t.Size6() > 0 { + if t.size6 > 0 { fmt.Fprintln(w) - fmt.Fprintf(w, "### IPv6: size(%d), nodes(%d)", t.Size6(), t.root6.nodeStatsRec().nodes) + fmt.Fprintf(w, "### IPv6: size(%d), nodes(%d)", t.size6, t.root6.nodeStatsRec().nodes) t.root6.dumpRec(w, zeroPath, 0, false) } } // dumpRec, rec-descent the trie. func (n *node[V]) dumpRec(w io.Writer, path [16]byte, depth int, is4 bool) { + // dump this node n.dump(w, path, depth, is4) - // no heap allocs - allChildAddrs := n.children.AsSlice(make([]uint, 0, maxNodeChildren)) - - // the node may have childs, the rec-descent monster starts - for i, addr := range allChildAddrs { + // the node may have childs, rec-descent down + for i, addr := range n.children.All() { octet := byte(addr) path[depth] = octet - switch child := n.children.Items[i].(type) { - case *leaf[V]: - continue - case *node[V]: + + if child, ok := n.children.Items[i].(*node[V]); ok { child.dumpRec(w, path, depth+1, is4) } } @@ -82,7 +78,7 @@ func (n *node[V]) dump(w io.Writer, path [16]byte, depth int, is4 bool) { if nPfxCount := n.prefixes.Len(); nPfxCount != 0 { // no heap allocs - allIndices := n.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes)) + allIndices := n.prefixes.All() // print the baseIndices for this node. fmt.Fprintf(w, "%sindexs(#%d): %v\n", indent, nPfxCount, allIndices) @@ -113,7 +109,7 @@ func (n *node[V]) dump(w io.Writer, path [16]byte, depth int, is4 bool) { leafAddrs := make([]uint, 0, maxNodeChildren) // the node has recursive child nodes or path-compressed leaves - for i, addr := range n.children.AsSlice(make([]uint, 0, maxNodeChildren)) { + for i, addr := range n.children.All() { switch n.children.Items[i].(type) { case *node[V]: nodeAddrs = append(nodeAddrs, addr) @@ -240,7 +236,7 @@ func (n *node[V]) nodeStats() stats { s.pfxs = n.prefixes.Len() s.childs = n.children.Len() - for i := range n.children.AsSlice(make([]uint, 0, maxNodeChildren)) { + for i := range n.children.All() { switch n.children.Items[i].(type) { case *node[V]: s.nodes++ diff --git a/internal/bitset/bitset.go b/internal/bitset/bitset.go index f6d3b54..a4b6ec0 100644 --- a/internal/bitset/bitset.go +++ b/internal/bitset/bitset.go @@ -135,7 +135,7 @@ func (b BitSet) NextSet(i uint) (uint, bool) { // AsSlice returns all set bits as slice of uint without // heap allocations. // -// This is faster than AppendTo, but also more dangerous, +// 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 @@ -155,12 +155,15 @@ func (b BitSet) AsSlice(buf []uint) []uint { return buf } -// AppendTo appends all set bits to buf and returns the (maybe extended) buf. -// If the capacity of buf is < b.Size() new memory is allocated. -func (b BitSet) AppendTo(buf []uint) []uint { +// 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 = append(buf, uint(idx<<6+bits.TrailingZeros64(word))) + buf[slot] = uint(idx<<6 + bits.TrailingZeros64(word)) + slot++ // clear the rightmost set bit word &= word - 1 diff --git a/internal/bitset/bitset_test.go b/internal/bitset/bitset_test.go index 16de100..d05d793 100644 --- a/internal/bitset/bitset_test.go +++ b/internal/bitset/bitset_test.go @@ -45,7 +45,7 @@ func TestNil(t *testing.T) { b.AsSlice(nil) b = BitSet(nil) - b.AppendTo(nil) + b.All() b = BitSet(nil) c := BitSet(nil) @@ -100,7 +100,7 @@ func TestZeroValue(t *testing.T) { b.AsSlice(nil) b = BitSet{} - b.AppendTo(nil) + b.All() b = BitSet{} c := BitSet{} @@ -383,7 +383,7 @@ func TestNextSet(t *testing.T) { } } -func TestAppendTo(t *testing.T) { +func TestAll(t *testing.T) { t.Parallel() testCases := []struct { name string @@ -391,42 +391,36 @@ func TestAppendTo(t *testing.T) { set []uint del []uint // - buf []uint wantData []uint }{ { name: "null", set: []uint{}, del: []uint{}, - buf: nil, wantData: []uint{}, }, { name: "zero", set: []uint{0}, del: []uint{}, - buf: nil, wantData: []uint{0}, // bit #0 is set }, { name: "1,5", set: []uint{1, 5}, del: []uint{}, - buf: nil, wantData: []uint{1, 5}, }, { name: "many", set: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, del: []uint{}, - buf: nil, wantData: []uint{1, 65, 130, 190, 250, 300, 380, 420, 480, 511}, }, { name: "special, last return", set: []uint{1}, del: []uint{1}, // delete without compact - buf: nil, wantData: []uint{}, }, } @@ -441,10 +435,10 @@ func TestAppendTo(t *testing.T) { b = b.Clear(u) // without compact } - buf := b.AppendTo(tc.buf) + buf := b.All() if !slices.Equal(buf, tc.wantData) { - t.Errorf("AppendTo, %s: returned buf is not equal as expected:\ngot: %v\nwant: %v", + t.Errorf("All, %s: returned buf is not equal as expected:\ngot: %v\nwant: %v", tc.name, buf, tc.wantData) } } diff --git a/stringify.go b/stringify.go index 341b4a8..5ccbac7 100644 --- a/stringify.go +++ b/stringify.go @@ -168,9 +168,7 @@ func (n *node[V]) getKidsRec(parentIdx uint, path [16]byte, depth int, is4 bool) var directKids []kid[V] - allIndices := n.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes)) - - for _, idx := range allIndices { + for _, idx := range n.prefixes.All() { // parent or self, handled alreday in an upper stack frame. if idx <= parentIdx { continue @@ -198,9 +196,7 @@ func (n *node[V]) getKidsRec(parentIdx uint, path [16]byte, depth int, is4 bool) } // the node may have childs and leaves, the rec-descent monster starts - - allChildAddrs := n.children.AsSlice(make([]uint, 0, maxNodeChildren)) - for i, addr := range allChildAddrs { + for i, addr := range n.children.All() { // do a longest-prefix-match lpmIdx, _, _ := n.lpmGet(hostIndex(addr)) if lpmIdx == parentIdx {