Skip to content

Commit

Permalink
new API, Insert with value, duplicate handling
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Dec 28, 2023
1 parent d040508 commit 8513809
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 46 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ but explicit for CIDRs. It has a narrow focus with a specialized API for IP rout
type Table struct { // Has unexported fields. }
Table is an IPv4 and IPv6 routing table. The zero value is ready to use.

func New() *Table

func (t Table) LookupIP(ip netip.Addr) (lpm netip.Prefix, value any, ok bool)
func (t Table) LookupCIDR(pfx netip.Prefix) (lpm netip.Prefix, value any, ok bool)

Expand Down
18 changes: 9 additions & 9 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func cloneAndShuffleRoutes(n int) []netip.Prefix {

func BenchmarkLookupIP(b *testing.B) {
for k := 1; k <= 1_000_000; k *= 10 {
rt := cidrtree.New()
rt := new(cidrtree.Table)
cidrs := cloneAndShuffleRoutes(k)
for _, cidr := range cidrs {
rt.InsertMutable(cidr, nil)
Expand All @@ -91,7 +91,7 @@ func BenchmarkLookupIP(b *testing.B) {

func BenchmarkLookupCIDR(b *testing.B) {
for k := 1; k <= 1_000_000; k *= 10 {
rt := cidrtree.New()
rt := new(cidrtree.Table)
cidrs := cloneAndShuffleRoutes(k)
for _, cidr := range cidrs {
rt.InsertMutable(cidr, nil)
Expand All @@ -113,7 +113,7 @@ func BenchmarkNew(b *testing.B) {
cidrs := cloneAndShuffleRoutes(k)
b.Run(intMap[k], func(b *testing.B) {
for n := 0; n < b.N; n++ {
rt := cidrtree.New()
rt := new(cidrtree.Table)
for i := range cidrs {
rt = rt.Insert(cidrs[i], nil)
}
Expand All @@ -124,7 +124,7 @@ func BenchmarkNew(b *testing.B) {

func BenchmarkClone(b *testing.B) {
for k := 1; k <= 1_000_000; k *= 10 {
rt := cidrtree.New()
rt := new(cidrtree.Table)
for _, cidr := range cloneAndShuffleRoutes(k) {
rt = rt.Insert(cidr, nil)
}
Expand All @@ -140,7 +140,7 @@ func BenchmarkClone(b *testing.B) {

func BenchmarkInsert(b *testing.B) {
for k := 1; k <= 1_000_000; k *= 10 {
rt := cidrtree.New()
rt := new(cidrtree.Table)
cidrs := cloneAndShuffleRoutes(k)
for _, cidr := range cidrs {
rt = rt.Insert(cidr, 0)
Expand All @@ -158,7 +158,7 @@ func BenchmarkInsert(b *testing.B) {

func BenchmarkInsertMutable(b *testing.B) {
for k := 1; k <= 1_000_000; k *= 10 {
rt := cidrtree.New()
rt := new(cidrtree.Table)
cidrs := cloneAndShuffleRoutes(k)
for _, cidr := range cidrs {
rt = rt.Insert(cidr, 0)
Expand All @@ -176,7 +176,7 @@ func BenchmarkInsertMutable(b *testing.B) {

func BenchmarkDelete(b *testing.B) {
for k := 1; k <= 1_000_000; k *= 10 {
rt := cidrtree.New()
rt := new(cidrtree.Table)
cidrs := cloneAndShuffleRoutes(k)
for _, cidr := range cidrs {
rt = rt.Insert(cidr, nil)
Expand All @@ -195,7 +195,7 @@ func BenchmarkDelete(b *testing.B) {

func BenchmarkMutableDelete(b *testing.B) {
for k := 1; k <= 1_000_000; k *= 10 {
rt := cidrtree.New()
rt := new(cidrtree.Table)
cidrs := cloneAndShuffleRoutes(k)
for _, cidr := range cidrs {
rt = rt.Insert(cidr, nil)
Expand All @@ -214,7 +214,7 @@ func BenchmarkMutableDelete(b *testing.B) {

func BenchmarkWalk(b *testing.B) {
for k := 1; k <= 1_000_000; k *= 10 {
rt := cidrtree.New()
rt := new(cidrtree.Table)
cidrs := cloneAndShuffleRoutes(k)
for _, cidr := range cidrs {
rt.InsertMutable(cidr, nil)
Expand Down
4 changes: 2 additions & 2 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var input = []netip.Prefix{
}

func ExampleTable_Fprint() {
rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, cidr := range input {
rtbl.InsertMutable(cidr, nil)
}
Expand Down Expand Up @@ -57,7 +57,7 @@ func ExampleTable_Walk() {
return true
}

rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, cidr := range input {
rtbl.InsertMutable(cidr, nil)
}
Expand Down
30 changes: 15 additions & 15 deletions treap.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,8 @@ type (
}
)

// New returns a pointer to the zero value of Table.
func New() *Table {
return &Table{}
}

// Insert routes into the table, returns the new table.
// Duplicate prefixes are just skipped.
// Insert adds pfx to the table with value val, returning a new table.
// If pfx is already present in the table, its value is set to val.
func (t Table) Insert(pfx netip.Prefix, val any) *Table {
if pfx.Addr().Is4() {
t.root4 = t.root4.insert(makeNode(pfx, val), true)
Expand All @@ -52,9 +47,8 @@ func (t Table) Insert(pfx netip.Prefix, val any) *Table {
return &t
}

// InsertMutable insert routes into the table, changing the original table.
// Duplicate prefixes are just skipped.
// If the original table does not need to be preserved then this is much faster than the immutable insert.
// InsertMutable adds pfx to the table with value val, changing the original table.
// If pfx is already present in the table, its value is set to val.
func (t *Table) InsertMutable(pfx netip.Prefix, val any) {
if pfx.Addr().Is4() {
t.root4 = t.root4.insert(makeNode(pfx, val), false)
Expand All @@ -65,6 +59,7 @@ func (t *Table) InsertMutable(pfx netip.Prefix, val any) {

// insert into treap, changing nodes are copied, new treap is returned,
// old treap is modified if immutable is false.
// If node is already present in the table, its value is set to val.
func (n *node) insert(m *node, immutable bool) *node {
if n == nil {
// recursion stop condition
Expand All @@ -87,7 +82,9 @@ func (n *node) insert(m *node, immutable bool) *node {
//
l, _, r := n.split(m.cidr, immutable)

// no duplicate handling, take m as new root
// take m as new root, no duplicate handling
// the prio is different, m and n can not be equal
// since prio is defined by the hash of the cidr.
//
// m
// / \
Expand Down Expand Up @@ -119,7 +116,9 @@ func (n *node) insert(m *node, immutable bool) *node {
// l r
//
default:
// cmp == 0, skip duplicate
// duplicate cidr, prio is the hash of the cidr, same priority, just store the new value
// n and m are at the same position in the treap (somde cidr -> same priority)
n.value = m.value
}

n.recalc() // n has changed, recalc
Expand Down Expand Up @@ -176,7 +175,7 @@ func (t *Table) DeleteMutable(cidr netip.Prefix) bool {
return m != nil
}

// Union combines any two tables. The tables tables are not changed.
// Union combines any two tables immutable and returns the combined table.
// Duplicates are skipped.
func (t Table) Union(other *Table) *Table {
t.root4 = t.root4.union(other.root4, true)
Expand Down Expand Up @@ -223,9 +222,9 @@ func (n *node) union(b *node, immutable bool) *node {
}

// Walk iterates the cidrtree in ascending order.
// The callback function is called with the Route struct of the respective node.
// The callback function is called with the prefix and value of the respective node.
// If callback returns `false`, the iteration is aborted.
func (t *Table) Walk(cb func(pfx netip.Prefix, val any) bool) {
func (t Table) Walk(cb func(pfx netip.Prefix, val any) bool) {
if !t.root4.walk(cb) {
return
}
Expand Down Expand Up @@ -259,6 +258,7 @@ func (n *node) walk(cb func(pfy netip.Prefix, val any) bool) bool {

// LookupIP returns the longest-prefix-match (lpm) for given ip.
// If the ip isn't covered by any CIDR, the zero value and false is returned.
//
// LookupIP does not allocate memory.
//
// example:
Expand Down
52 changes: 34 additions & 18 deletions treap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func TestZeroValue(t *testing.T) {

func TestInsert(t *testing.T) {
t.Parallel()
rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)

for _, route := range routes {
rtbl = rtbl.Insert(route.cidr, route.nextHop)
Expand All @@ -142,7 +142,7 @@ func TestInsert(t *testing.T) {

func TestDupInsert(t *testing.T) {
t.Parallel()
rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)

for _, route := range routes {
rtbl = rtbl.Insert(route.cidr, route.nextHop)
Expand All @@ -163,11 +163,27 @@ func TestDupInsert(t *testing.T) {
if rtbl.String() != asTopoStr {
t.Errorf("Fprint()\nwant:\n%sgot:\n%s", asTopoStr, rtbl.String())
}

cidr := routes[0].cidr
_, _, ok := rtbl.LookupCIDR(routes[0].cidr)
if !ok {
t.Errorf("LookupCIDR(%v), expect %v, got %v", routes[0].cidr, true, ok)
}
// overwrite value for this cidr
rtbl.InsertMutable(cidr, "overwrite value")

_, value, ok := rtbl.LookupCIDR(cidr)
if !ok {
t.Errorf("LookupCIDR(%v), expect %v, got %v", routes[0].cidr, true, ok)
}
if value != "overwrite value" {
t.Errorf("InsertMutable duplicate, expect %q, got %q", "overwrite value", value)
}
}

func TestInsertMutable(t *testing.T) {
t.Parallel()
rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)

for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
Expand All @@ -181,7 +197,7 @@ func TestInsertMutable(t *testing.T) {
func TestImmutable(t *testing.T) {
t.Parallel()

rtbl1 := cidrtree.New()
rtbl1 := new(cidrtree.Table)
for _, route := range routes {
rtbl1.InsertMutable(route.cidr, route.nextHop)
}
Expand Down Expand Up @@ -219,7 +235,7 @@ func TestImmutable(t *testing.T) {
}

func TestMutable(t *testing.T) {
rtbl1 := cidrtree.New()
rtbl1 := new(cidrtree.Table)
for _, route := range routes {
rtbl1.InsertMutable(route.cidr, route.nextHop)
}
Expand All @@ -237,7 +253,7 @@ func TestMutable(t *testing.T) {
}

// reset table1, table2
rtbl1 = cidrtree.New()
rtbl1 = new(cidrtree.Table)
for _, route := range routes {
rtbl1.InsertMutable(route.cidr, route.nextHop)
}
Expand All @@ -258,7 +274,7 @@ func TestMutable(t *testing.T) {
func TestDelete(t *testing.T) {
t.Parallel()

rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
}
Expand All @@ -283,7 +299,7 @@ func TestDelete(t *testing.T) {
func TestDeleteMutable(t *testing.T) {
t.Parallel()

rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
}
Expand All @@ -306,7 +322,7 @@ func TestDeleteMutable(t *testing.T) {
func TestLookup(t *testing.T) {
t.Parallel()

rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
}
Expand Down Expand Up @@ -390,7 +406,7 @@ func TestLookup(t *testing.T) {
// ##########################################

tc := cloneAndShuffleRoutes(100_000)
rtbl2 := cidrtree.New()
rtbl2 := new(cidrtree.Table)
for _, cidr := range tc {
rtbl2.InsertMutable(cidr, nil)
}
Expand All @@ -412,7 +428,7 @@ func TestLookup(t *testing.T) {
func TestLookupCIDR(t *testing.T) {
t.Parallel()

rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
}
Expand Down Expand Up @@ -497,7 +513,7 @@ func TestLookupCIDR(t *testing.T) {

tc := cloneAndShuffleRoutes(100_000)

rtbl2 := cidrtree.New()
rtbl2 := new(cidrtree.Table)
for _, cidr := range tc {
rtbl2.InsertMutable(cidr, nil)
}
Expand All @@ -510,7 +526,7 @@ func TestLookupCIDR(t *testing.T) {

func TestUnion(t *testing.T) {
t.Parallel()
rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
}
Expand All @@ -520,8 +536,8 @@ func TestUnion(t *testing.T) {
t.Fatal("Clone isn't deep equal to original table.")
}

rtbl2 := cidrtree.New()
rtbl3 := cidrtree.New()
rtbl2 := new(cidrtree.Table)
rtbl3 := new(cidrtree.Table)
for _, route := range routes {
rtbl3.InsertMutable(route.cidr, route.nextHop)
rtbl2.UnionMutable(rtbl3)
Expand All @@ -541,7 +557,7 @@ func TestUnion(t *testing.T) {

func TestFprint(t *testing.T) {
t.Parallel()
rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
}
Expand All @@ -558,7 +574,7 @@ func TestFprint(t *testing.T) {

func TestWalk(t *testing.T) {
t.Parallel()
rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
}
Expand All @@ -577,7 +593,7 @@ func TestWalk(t *testing.T) {

func TestWalkStartStop(t *testing.T) {
t.Parallel()
rtbl := cidrtree.New()
rtbl := new(cidrtree.Table)
for _, route := range routes {
rtbl.InsertMutable(route.cidr, route.nextHop)
}
Expand Down

0 comments on commit 8513809

Please sign in to comment.