Skip to content

Commit

Permalink
some examples and maybe better doc
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Dec 23, 2023
1 parent ad484eb commit 30d509e
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 76 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

`package cidrtree` is a datastructure for IP routing tables (IPv4/IPv6) with fast lookup (longest prefix match).

The implementation is based on treaps, which have been augmented here for CIDRs. Treaps are randomized, self-balancing binary search trees. Due to the nature of treaps, the lookups (readers) and updates (writers) can be decoupled without causing delayed rebalancing, which is a perfect fit for a software router or firewall.
The implementation is based on treaps, which have been augmented here for CIDRs. Treaps are randomized, self-balancing binary search trees. Due to the nature of treaps, the lookups (readers) and updates (writers) can be decoupled, which is a perfect fit for a software router or firewall.

This package is a specialization of the more generic [interval package] of the same author,
but explicit for CIDRs. It has a narrow focus with a specialized API for IP routing tables.
Expand Down
75 changes: 75 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package cidrtree_test

import (
"fmt"
"net/netip"
"os"

"github.com/gaissmai/cidrtree"
)

var input = []cidrtree.Route{
{netip.MustParsePrefix("fe80::/10"), nil},
{netip.MustParsePrefix("172.16.0.0/12"), nil},
{netip.MustParsePrefix("10.0.0.0/24"), nil},
{netip.MustParsePrefix("::1/128"), nil},
{netip.MustParsePrefix("192.168.0.0/16"), nil},
{netip.MustParsePrefix("10.0.0.0/8"), nil},
{netip.MustParsePrefix("::/0"), nil},
{netip.MustParsePrefix("10.0.1.0/24"), nil},
{netip.MustParsePrefix("169.254.0.0/16"), nil},
{netip.MustParsePrefix("2000::/3"), nil},
{netip.MustParsePrefix("2001:db8::/32"), nil},
{netip.MustParsePrefix("127.0.0.0/8"), nil},
{netip.MustParsePrefix("127.0.0.1/32"), nil},
{netip.MustParsePrefix("192.168.1.0/24"), nil},
}

func ExampleTree_Fprint() {
tree := cidrtree.New(input...)
tree.Fprint(os.Stdout)

// Output:
// ▼
// ├─ 10.0.0.0/8 (<nil>)
// │ ├─ 10.0.0.0/24 (<nil>)
// │ └─ 10.0.1.0/24 (<nil>)
// ├─ 127.0.0.0/8 (<nil>)
// │ └─ 127.0.0.1/32 (<nil>)
// ├─ 169.254.0.0/16 (<nil>)
// ├─ 172.16.0.0/12 (<nil>)
// └─ 192.168.0.0/16 (<nil>)
// └─ 192.168.1.0/24 (<nil>)
// ▼
// └─ ::/0 (<nil>)
// ├─ ::1/128 (<nil>)
// ├─ 2000::/3 (<nil>)
// │ └─ 2001:db8::/32 (<nil>)
// └─ fe80::/10 (<nil>)
}

func ExampleTree_Walk() {
cb := func(r cidrtree.Route) bool {
fmt.Printf("%v (%v)\n", r.CIDR, r.Value)
return true
}

tree := cidrtree.New(input...)
tree.Walk(cb)

// Output:
// 10.0.0.0/8 (<nil>)
// 10.0.0.0/24 (<nil>)
// 10.0.1.0/24 (<nil>)
// 127.0.0.0/8 (<nil>)
// 127.0.0.1/32 (<nil>)
// 169.254.0.0/16 (<nil>)
// 172.16.0.0/12 (<nil>)
// 192.168.0.0/16 (<nil>)
// 192.168.1.0/24 (<nil>)
// ::/0 (<nil>)
// ::1/128 (<nil>)
// 2000::/3 (<nil>)
// 2001:db8::/32 (<nil>)
// fe80::/10 (<nil>)
}
22 changes: 0 additions & 22 deletions stringify.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,6 @@ func (t Tree) String() string {
//
// The order from top to bottom is in ascending order of the start address
// and the subtree structure is determined by the CIDRs coverage.
//
// ▼
// └─ 0.0.0.0/0 (value)
// ├─ 10.0.0.0/8 (value)
// │ ├─ 10.0.0.0/24 (value)
// │ └─ 10.0.1.0/24 (value)
// ├─ 127.0.0.0/8 (value)
// │ └─ 127.0.0.1/32 (value)
// ├─ 169.254.0.0/16 (value)
// ├─ 172.16.0.0/12 (value)
// └─ 192.168.0.0/16 (value)
// └─ 192.168.1.0/24 (value)
// ▼
// └─ ::/0 (value)
// ├─ ::1/128 (value)
// ├─ 2000::/3 (value)
// │ └─ 2001:db8::/32 (value)
// ├─ fc00::/7 (value)
// ├─ fe80::/10 (value)
// └─ ff00::/8 (value)
//
func (t Tree) Fprint(w io.Writer) error {
if err := t.root4.fprint(w); err != nil {
return err
Expand Down Expand Up @@ -113,7 +92,6 @@ func (n *node) walkAndStringify(w io.Writer, pcm parentChildsMap, pad string) er
// parentChildsMap, needed for hierarchical tree printing, this is not BST printing!
//
// CIDR tree, parent->childs relation printed. A parent CIDR covers a child CIDR.
//
type parentChildsMap struct {
pcMap map[*node][]*node // parent -> []child map
stack []*node // just needed for the algo
Expand Down
107 changes: 54 additions & 53 deletions treap.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Package cidrtree provides fast IP to CIDR lookup (longest prefix match).
// Package cidrtree implements fast lookup (longest-prefix-match) for IP routing tables (IPv4/IPv6).
//
// This package is a specialization of the more generic [interval package] of the same author,
// but explicit for CIDRs. It has a narrow focus with a smaller and simpler API.
// The implementation is based on treaps, which have been augmented here for CIDRs.
//
// [interval package]: https://github.com/gaissmai/interval
// Treaps are randomized, self-balancing binary search trees. Due to the nature of treaps,
// the lookups (readers) and updates (writers) can be decoupled,
// which is a perfect fit for a software router or firewall.
package cidrtree

import (
Expand Down Expand Up @@ -327,33 +328,32 @@ func (n *node) walk(cb func(r Route) bool) bool {

// LookupIP returns the longest-prefix-match for given ip.
// If the ip isn't covered by any CIDR, the zero value and false is returned.
// The algorithm for LookupIP does not allocate memory.
// LookupIP does not allocate memory.
//
// example:
// example:
//
// ▼
// ├─ 10.0.0.0/8
// │ ├─ 10.0.0.0/24
// │ └─ 10.0.1.0/24
// ├─ 127.0.0.0/8
// │ └─ 127.0.0.1/32
// ├─ 169.254.0.0/16
// ├─ 172.16.0.0/12
// └─ 192.168.0.0/16
// └─ 192.168.1.0/24
// ▼
// └─ ::/0
// ├─ ::1/128
// ├─ 2000::/3
// │ └─ 2001:db8::/32
// ├─ fc00::/7
// ├─ fe80::/10
// └─ ff00::/8
//
// tree.LookupIP(42.0.0.0) returns (netip.Prefix{}, <nil>, false)
// tree.LookupIP(10.0.1.17) returns (10.0.1.0/24, <value>, true)
// tree.LookupIP(2001:7c0:3100:1::111) returns (2000::/3, <value>, true)
// ▼
// ├─ 10.0.0.0/8
// │ ├─ 10.0.0.0/24
// │ └─ 10.0.1.0/24
// ├─ 127.0.0.0/8
// │ └─ 127.0.0.1/32
// ├─ 169.254.0.0/16
// ├─ 172.16.0.0/12
// └─ 192.168.0.0/16
// └─ 192.168.1.0/24
// ▼
// └─ ::/0
// ├─ ::1/128
// ├─ 2000::/3
// │ └─ 2001:db8::/32
// ├─ fc00::/7
// ├─ fe80::/10
// └─ ff00::/8
//
// tree.LookupIP(42.0.0.0) returns (netip.Prefix{}, <nil>, false)
// tree.LookupIP(10.0.1.17) returns (10.0.1.0/24, <value>, true)
// tree.LookupIP(2001:7c0:3100:1::111) returns (2000::/3, <value>, true)
func (t Tree) LookupIP(key netip.Addr) (cidr netip.Prefix, value any, ok bool) {
if key.Is4() {
return t.root4.lookupIP(key)
Expand Down Expand Up @@ -398,35 +398,36 @@ func (n *node) lookupIP(ip netip.Addr) (cidr netip.Prefix, value any, ok bool) {
return n.left.lookupIP(ip)
}

// LookupCIDR returns the longest-prefix-match for given prefix.
// If the prefix isn't equal or covered by any CIDR in the tree, the zero value and false is returned.
// The algorithm for LookupCIDR does not allocate memory.
// LookupCIDR returns the longest-prefix-match for given key.
// If the key isn't equal or covered by any CIDR in the tree, the zero value and false is returned.
// LookupCIDR can also be used to test whether a specific route exists in the tree.
//
// example:
// LookupCIDR does not allocate memory.
//
// ▼
// ├─ 10.0.0.0/8
// │ ├─ 10.0.0.0/24
// │ └─ 10.0.1.0/24
// ├─ 127.0.0.0/8
// │ └─ 127.0.0.1/32
// ├─ 169.254.0.0/16
// ├─ 172.16.0.0/12
// └─ 192.168.0.0/16
// └─ 192.168.1.0/24
// ▼
// └─ ::/0
// ├─ ::1/128
// ├─ 2000::/3
// │ └─ 2001:db8::/32
// ├─ fc00::/7
// ├─ fe80::/10
// └─ ff00::/8
// example:
//
// tree.LookupCIDR(42.0.0.0/8) returns (netip.Prefix{}, <nil>, false)
// tree.LookupCIDR(10.0.1.0/29) returns (10.0.1.0/24, <value>, true)
// tree.LookupCIDR(2001:7c0:3100::/40) returns (2000::/3, <value>, true)
// ▼
// ├─ 10.0.0.0/8
// │ ├─ 10.0.0.0/24
// │ └─ 10.0.1.0/24
// ├─ 127.0.0.0/8
// │ └─ 127.0.0.1/32
// ├─ 169.254.0.0/16
// ├─ 172.16.0.0/12
// └─ 192.168.0.0/16
// └─ 192.168.1.0/24
// ▼
// └─ ::/0
// ├─ ::1/128
// ├─ 2000::/3
// │ └─ 2001:db8::/32
// ├─ fc00::/7
// ├─ fe80::/10
// └─ ff00::/8
//
// tree.LookupCIDR(42.0.0.0/8) returns (netip.Prefix{}, <nil>, false)
// tree.LookupCIDR(10.0.1.0/29) returns (10.0.1.0/24, <value>, true)
// tree.LookupCIDR(2001:7c0:3100::/40) returns (2000::/3, <value>, true)
func (t Tree) LookupCIDR(key netip.Prefix) (cidr netip.Prefix, value any, ok bool) {
if key.Addr().Is4() {
return t.root4.lookupCIDR(key)
Expand Down

0 comments on commit 30d509e

Please sign in to comment.