Skip to content

Commit

Permalink
minor
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Jan 10, 2024
1 parent 3fd646f commit c71cf80
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 180 deletions.
33 changes: 16 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,41 @@
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## !!! ATTENTION, API HAS CHANGED
## !!! ATTENTION

The API has changed from v0.3.0 to v0.4.0.
API is currently not stable!

## Overview

`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 the update (writer) can be easily decoupled. This is the 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.
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.

[interval package]: https://github.com/gaissmai/interval

## API
```go
import "github.com/gaissmai/cidrtree"

type Table struct { // Has unexported fields. }
type Table[V any] struct { // Has unexported fields. }
Table is an IPv4 and IPv6 routing table. The zero value is ready to use.

func (t Table) Lookup(ip netip.Addr) (lpm netip.Prefix, value any, ok bool)
func (t Table) LookupPrefix(pfx netip.Prefix) (lpm netip.Prefix, value any, ok bool)
func (t Table[V]) Lookup(ip netip.Addr) (lpm netip.Prefix, value V, ok bool)
func (t Table[V]) LookupPrefix(pfx netip.Prefix) (lpm netip.Prefix, value V, ok bool)

func (t *Table) Insert(pfx netip.Prefix, val any)
func (t *Table) Delete(pfx netip.Prefix) bool
func (t *Table) Union(other Table)
func (t *Table[V]) Insert(pfx netip.Prefix, value V)
func (t *Table[V]) Delete(pfx netip.Prefix) bool
func (t *Table[V]) Union(other Table[V])

func (t Table) InsertImmutable(pfx netip.Prefix, val any) *Table
func (t Table) DeleteImmutable(pfx netip.Prefix) (*Table, bool)
func (t Table) UnionImmutable(other Table) *Table
func (t Table) Clone() *Table
func (t Table[V]) InsertImmutable(pfx netip.Prefix, value V) *Table[V]
func (t Table[V]) DeleteImmutable(pfx netip.Prefix) (*Table[V], bool)
func (t Table[V]) UnionImmutable(other Table[V]) *Table[V]
func (t Table[V]) Clone() *Table[V]

func (t Table) String() string
func (t Table) Fprint(w io.Writer) error
func (t Table[V]) String() string
func (t Table[V]) Fprint(w io.Writer) error

func (t Table) Walk(cb func(pfx netip.Prefix, val any) bool)
func (t Table[V]) Walk(cb func(pfx netip.Prefix, value V) bool)
```
8 changes: 4 additions & 4 deletions debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// fprintBST writes a horizontal tree diagram of the binary search tree (BST) to w.
//
// Note: This is for debugging purposes only.
func (t Table[T]) fprintBST(w io.Writer) error {
func (t Table[V]) fprintBST(w io.Writer) error {
if t.root4 != nil {
if _, err := fmt.Fprint(w, "R "); err != nil {
return err
Expand All @@ -33,7 +33,7 @@ func (t Table[T]) fprintBST(w io.Writer) error {
}

// fprintBST recursive helper.
func (n *node[T]) fprintBST(w io.Writer, pad string) error {
func (n *node[V]) fprintBST(w io.Writer, pad string) error {
// stringify this node
_, err := fmt.Fprintf(w, "%v [prio:%.4g] [subtree maxUpper: %v]\n", n.cidr, float64(n.prio)/math.MaxUint64, n.maxUpper.cidr)
if err != nil {
Expand Down Expand Up @@ -80,7 +80,7 @@ func (n *node[T]) fprintBST(w io.Writer, pad string) error {
// If the skip function is not nil, a true return value defines which nodes must be skipped in the statistics.
//
// Note: This is for debugging and testing purposes only during development.
func (t Table[T]) statistics(skip func(netip.Prefix, any, int) bool) (size int, maxDepth int, average, deviation float64) {
func (t Table[V]) statistics(skip func(netip.Prefix, any, int) bool) (size int, maxDepth int, average, deviation float64) {
// key is depth, value is the sum of nodes with this depth
depths := make(map[int]int)

Expand Down Expand Up @@ -120,7 +120,7 @@ func (t Table[T]) statistics(skip func(netip.Prefix, any, int) bool) (size int,
}

// walkWithDepth in ascending prefix order.
func (n *node[T]) walkWithDepth(cb func(netip.Prefix, any, int) bool, depth int) bool {
func (n *node[V]) walkWithDepth(cb func(netip.Prefix, any, int) bool, depth int) bool {
if n == nil {
return true
}
Expand Down
18 changes: 9 additions & 9 deletions stringify.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

// String returns a hierarchical tree diagram of the ordered CIDRs as string, just a wrapper for [Tree.Fprint].
func (t Table[T]) String() string {
func (t Table[V]) String() string {
w := new(strings.Builder)
_ = t.Fprint(w)
return w.String()
Expand All @@ -17,7 +17,7 @@ func (t Table[T]) 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.
func (t Table[T]) Fprint(w io.Writer) error {
func (t Table[V]) Fprint(w io.Writer) error {
if err := t.root4.fprint(w); err != nil {
return err
}
Expand All @@ -27,16 +27,16 @@ func (t Table[T]) Fprint(w io.Writer) error {
return nil
}

func (n *node[T]) fprint(w io.Writer) error {
func (n *node[V]) fprint(w io.Writer) error {
if n == nil {
return nil
}

// pcm = parent-child-mapping
var pcm parentChildsMap[T]
var pcm parentChildsMap[V]

// init map
pcm.pcMap = make(map[*node[T]][]*node[T])
pcm.pcMap = make(map[*node[V]][]*node[V])

pcm = n.buildParentChildsMap(pcm)

Expand All @@ -50,11 +50,11 @@ func (n *node[T]) fprint(w io.Writer) error {
}

// start recursion with root and empty padding
var root *node[T]
var root *node[V]
return root.walkAndStringify(w, pcm, "")
}

func (n *node[T]) walkAndStringify(w io.Writer, pcm parentChildsMap[T], pad string) error {
func (n *node[V]) walkAndStringify(w io.Writer, pcm parentChildsMap[V], pad string) error {
// the prefix (pad + glyphe) is already printed on the line on upper level
if n != nil {
if _, err := fmt.Fprintf(w, "%v (%v)\n", n.cidr, n.value); err != nil {
Expand Down Expand Up @@ -98,7 +98,7 @@ type parentChildsMap[T any] struct {
}

// buildParentChildsMap, in-order traversal
func (n *node[T]) buildParentChildsMap(pcm parentChildsMap[T]) parentChildsMap[T] {
func (n *node[V]) buildParentChildsMap(pcm parentChildsMap[V]) parentChildsMap[V] {
if n == nil {
return pcm
}
Expand All @@ -114,7 +114,7 @@ func (n *node[T]) buildParentChildsMap(pcm parentChildsMap[T]) parentChildsMap[T
}

// pcmForNode, find parent in stack, remove cidrs from stack, put this cidr on stack.
func (n *node[T]) pcmForNode(pcm parentChildsMap[T]) parentChildsMap[T] {
func (n *node[V]) pcmForNode(pcm parentChildsMap[V]) parentChildsMap[V] {
// if this cidr is covered by a prev cidr on stack
for j := len(pcm.stack) - 1; j >= 0; j-- {
that := pcm.stack[j]
Expand Down

0 comments on commit c71cf80

Please sign in to comment.