Skip to content

Commit

Permalink
Merge pull request #2 from Khalid-Nowaf/cidr-trie-type
Browse files Browse the repository at this point in the history
chore: create type CidrTrie allies for BinaryTrie[Metadata]
  • Loading branch information
Khalid-Nowaf committed Jul 1, 2024
2 parents ca78c1c + 0b577a6 commit 3958b4b
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 62 deletions.
4 changes: 2 additions & 2 deletions cmd/supernet/main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package main

import (
"github.com/khalid_nowaf/supernet/pkg/cli"
"github.com/khalid_nowaf/supernet/pkg/supernet"
"github.com/khalid-nowaf/supernet/pkg/cli"
"github.com/khalid-nowaf/supernet/pkg/supernet"
)

func main() {
Expand Down
22 changes: 11 additions & 11 deletions pkg/supernet/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

type Action interface {
Execute(newCidr *trie.BinaryTrie[Metadata], conflictedPoint *trie.BinaryTrie[Metadata], targetNode *trie.BinaryTrie[Metadata], remainingPath []int) *ActionResult
Execute(newCidr *CidrTrie, conflictedPoint *CidrTrie, targetNode *CidrTrie, remainingPath []int) *ActionResult
String() string
}

Expand All @@ -17,7 +17,7 @@ type (
SplitExistingCIDR struct{} // split the existing CIDR `on` specific node
)

func (action IgnoreInsertion) Execute(_ *trie.BinaryTrie[Metadata], _ *trie.BinaryTrie[Metadata], _ *trie.BinaryTrie[Metadata], _ []int) *ActionResult {
func (action IgnoreInsertion) Execute(_ *CidrTrie, _ *CidrTrie, _ *CidrTrie, _ []int) *ActionResult {
return &ActionResult{
Action: action,
}
Expand All @@ -27,7 +27,7 @@ func (_ IgnoreInsertion) String() string {
return "Ignore Insertion"
}

func (action InsertNewCIDR) Execute(newCidr *trie.BinaryTrie[Metadata], conflictedPoint *trie.BinaryTrie[Metadata], _ *trie.BinaryTrie[Metadata], remainingPath []int) *ActionResult {
func (action InsertNewCIDR) Execute(newCidr *CidrTrie, conflictedPoint *CidrTrie, _ *CidrTrie, remainingPath []int) *ActionResult {

actionResult := &ActionResult{
Action: action,
Expand Down Expand Up @@ -60,7 +60,7 @@ func (_ InsertNewCIDR) String() string {
return "Insert New CIDR"
}

func (action RemoveExistingCIDR) Execute(newCidr *trie.BinaryTrie[Metadata], _ *trie.BinaryTrie[Metadata], targetNode *trie.BinaryTrie[Metadata], _ []int) *ActionResult {
func (action RemoveExistingCIDR) Execute(newCidr *CidrTrie, _ *CidrTrie, targetNode *CidrTrie, _ []int) *ActionResult {

actionResult := &ActionResult{
Action: action,
Expand All @@ -83,7 +83,7 @@ func (_ RemoveExistingCIDR) String() string {
return "Remove Existing CIDR"
}

func (action SplitInsertedCIDR) Execute(newCidr *trie.BinaryTrie[Metadata], conflictedPoint *trie.BinaryTrie[Metadata], targetNode *trie.BinaryTrie[Metadata], _ []int) *ActionResult {
func (action SplitInsertedCIDR) Execute(newCidr *CidrTrie, conflictedPoint *CidrTrie, targetNode *CidrTrie, _ []int) *ActionResult {

actionResult := &ActionResult{
Action: action,
Expand All @@ -103,7 +103,7 @@ func (_ SplitInsertedCIDR) String() string {
return "Split Inserted CIDR"
}

func (action SplitExistingCIDR) Execute(newCidr *trie.BinaryTrie[Metadata], conflictedPoint *trie.BinaryTrie[Metadata], targetNode *trie.BinaryTrie[Metadata], _ []int) *ActionResult {
func (action SplitExistingCIDR) Execute(newCidr *CidrTrie, conflictedPoint *CidrTrie, targetNode *CidrTrie, _ []int) *ActionResult {
// init inserted result
actionResult := &ActionResult{
Action: action,
Expand All @@ -123,23 +123,23 @@ func (_ SplitExistingCIDR) String() string {
}

// to keep track of all removed CIDRs from resolving a conflict.
func (ar *ActionResult) appendRemovedCidr(cidr *trie.BinaryTrie[Metadata]) {
func (ar *ActionResult) appendRemovedCidr(cidr *CidrTrie) {
ar.RemoveCidrs = append(ar.RemoveCidrs, *cidr)
}

// The function traverses from the sub-CIDR node upwards, attempting to insert a sibling node at each step.
// If a sibling node at a given position does not exist, it is created and added. The traversal and modifications
// stop when reaching the depth of the super-CIDR node.
func splitAround(sub *trie.BinaryTrie[Metadata], newCidrMetadata *Metadata, limitDepth int) []*trie.BinaryTrie[Metadata] {
func splitAround(sub *CidrTrie, newCidrMetadata *Metadata, limitDepth int) []*CidrTrie {
splittedCidrMetadata := newCidrMetadata

if splittedCidrMetadata == nil {
panic("[BUG] splitAround: Metadata is required to split a supernet")
}

var splittedCidrs []*trie.BinaryTrie[Metadata]
var splittedCidrs []*CidrTrie

sub.ForEachStepUp(func(current *trie.BinaryTrie[Metadata]) {
sub.ForEachStepUp(func(current *CidrTrie) {

// Create a new trie node with the same metadata as the splittedCidrMetadata.
newCidr := trie.NewTrieWithMetadata(&Metadata{
Expand All @@ -156,7 +156,7 @@ func splitAround(sub *trie.BinaryTrie[Metadata], newCidrMetadata *Metadata, limi
splittedCidrs = append(splittedCidrs, added)
} else {
}
}, func(nextNode *trie.BinaryTrie[Metadata]) bool {
}, func(nextNode *CidrTrie) bool {
// Stop propagation when reaching the depth of the super-CIDR.
return nextNode.Depth() > limitDepth
})
Expand Down
18 changes: 7 additions & 11 deletions pkg/supernet/conflict.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package supernet

import (
"github.com/khalid-nowaf/supernet/pkg/trie"
)

type ConflictType interface {
String() string
Resolve(conflictedCidr *trie.BinaryTrie[Metadata], newCidr *trie.BinaryTrie[Metadata], comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan
Resolve(conflictedCidr *CidrTrie, newCidr *CidrTrie, comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan
}

type (
Expand All @@ -16,7 +12,7 @@ type (
SubCIDR struct{} // the new CIDR is a sub CIDR of an existing super CIDR
)

func (_ NoConflict) Resolve(at *trie.BinaryTrie[Metadata], newCidr *trie.BinaryTrie[Metadata], comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan {
func (_ NoConflict) Resolve(at *CidrTrie, newCidr *CidrTrie, comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan {
plan := &ResolutionPlan{}
plan.AddAction(InsertNewCIDR{}, at)
return plan
Expand All @@ -26,7 +22,7 @@ func (_ NoConflict) String() string {
return "No Conflict"
}

func (_ EqualCIDR) Resolve(conflictedCidr *trie.BinaryTrie[Metadata], newCidr *trie.BinaryTrie[Metadata], comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan {
func (_ EqualCIDR) Resolve(conflictedCidr *CidrTrie, newCidr *CidrTrie, comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan {
plan := &ResolutionPlan{}
plan.Conflicts = append(plan.Conflicts, *conflictedCidr)
if comparator(newCidr.Metadata(), conflictedCidr.Metadata()) {
Expand All @@ -44,15 +40,15 @@ func (_ EqualCIDR) String() string {
return "Equal CIDR"
}

func (_ SuperCIDR) Resolve(conflictPoint *trie.BinaryTrie[Metadata], newSuperCidr *trie.BinaryTrie[Metadata], comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan {
func (_ SuperCIDR) Resolve(conflictPoint *CidrTrie, newSuperCidr *CidrTrie, comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan {
plan := &ResolutionPlan{}

// since this is a super, we do not know how many subcidrs yet conflicting with this super
// let us get all subCidrs
conflictedSubCidrs := conflictPoint.Leafs()

subCidrsWithLowPriority := []*trie.BinaryTrie[Metadata]{}
subCidrsWithHighPriority := []*trie.BinaryTrie[Metadata]{}
subCidrsWithLowPriority := []*CidrTrie{}
subCidrsWithHighPriority := []*CidrTrie{}

for _, conflictedSubCidr := range conflictedSubCidrs {
plan.Conflicts = append(plan.Conflicts, *conflictedSubCidr)
Expand Down Expand Up @@ -86,7 +82,7 @@ func (_ SuperCIDR) String() string {
return "Super CIDR"
}

func (_ SubCIDR) Resolve(existingSuperCidr *trie.BinaryTrie[Metadata], newSubCidr *trie.BinaryTrie[Metadata], comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan {
func (_ SubCIDR) Resolve(existingSuperCidr *CidrTrie, newSubCidr *CidrTrie, comparator func(a *Metadata, b *Metadata) bool) *ResolutionPlan {
plan := &ResolutionPlan{}
plan.Conflicts = append(plan.Conflicts, *existingSuperCidr)
// since this is a SubCidr, we have 2 option
Expand Down
6 changes: 2 additions & 4 deletions pkg/supernet/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package supernet

import (
"fmt"

"github.com/khalid-nowaf/supernet/pkg/trie"
)

type Option func(*Supernet) *Supernet
Expand All @@ -12,8 +10,8 @@ type LoggerOption func(*InsertionResult)

func DefaultOptions() *Supernet {
return &Supernet{
ipv4Cidrs: &trie.BinaryTrie[Metadata]{},
ipv6Cidrs: &trie.BinaryTrie[Metadata]{},
ipv4Cidrs: &CidrTrie{},
ipv6Cidrs: &CidrTrie{},
comparator: DefaultComparator,
logger: func(ir *InsertionResult) {},
}
Expand Down
8 changes: 3 additions & 5 deletions pkg/supernet/plan.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package supernet

import "github.com/khalid-nowaf/supernet/pkg/trie"

type PlanStep struct {
Action Action
TargetNode *trie.BinaryTrie[Metadata]
TargetNode *CidrTrie
}
type ResolutionPlan struct {
Conflicts []trie.BinaryTrie[Metadata]
Conflicts []CidrTrie
Steps []*PlanStep
}

func (plan *ResolutionPlan) AddAction(action Action, on *trie.BinaryTrie[Metadata]) {
func (plan *ResolutionPlan) AddAction(action Action, on *CidrTrie) {
plan.Steps = append(plan.Steps, &PlanStep{
Action: action,
TargetNode: on,
Expand Down
16 changes: 7 additions & 9 deletions pkg/supernet/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ package supernet
import (
"fmt"
"net"

"github.com/khalid-nowaf/supernet/pkg/trie"
)

// records the outcome of attempting to insert a CIDR for reporting
type InsertionResult struct {
CIDR *net.IPNet // CIDR was attempted to be inserted.
actions []*ActionResult // the result of each action is taken
ConflictedWith []trie.BinaryTrie[Metadata] // array of conflicting nodes
ConflictType // the type of the conflict
CIDR *net.IPNet // CIDR was attempted to be inserted.
actions []*ActionResult // the result of each action is taken
ConflictedWith []CidrTrie // array of conflicting nodes
ConflictType // the type of the conflict
}

func (ir *InsertionResult) String() string {
Expand All @@ -36,8 +34,8 @@ func (ir *InsertionResult) String() string {

type ActionResult struct {
Action Action
AddedCidrs []trie.BinaryTrie[Metadata]
RemoveCidrs []trie.BinaryTrie[Metadata]
AddedCidrs []CidrTrie
RemoveCidrs []CidrTrie
}

func (ar ActionResult) String() string {
Expand All @@ -56,6 +54,6 @@ func (ar ActionResult) String() string {
}

// to keep track of all the added CIDRs from resolving a conflict.
func (ar *ActionResult) appendAddedCidr(cidr *trie.BinaryTrie[Metadata]) {
func (ar *ActionResult) appendAddedCidr(cidr *CidrTrie) {
ar.AddedCidrs = append(ar.AddedCidrs, *cidr)
}
18 changes: 10 additions & 8 deletions pkg/supernet/supernet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/khalid-nowaf/supernet/pkg/trie"
)

type CidrTrie = trie.BinaryTrie[Metadata]

// holds the properties for a CIDR node
type Metadata struct {
originCIDR *net.IPNet // copy of the CIDR, to track it, if it get splitted later due to conflict resolution
Expand All @@ -30,8 +32,8 @@ func NewMetadata(ipnet *net.IPNet) *Metadata {

// Supernet represents a structure containing both IPv4 and IPv6 CIDRs, each stored in a separate trie.
type Supernet struct {
ipv4Cidrs *trie.BinaryTrie[Metadata]
ipv6Cidrs *trie.BinaryTrie[Metadata]
ipv4Cidrs *CidrTrie
ipv6Cidrs *CidrTrie
comparator ComparatorOption
logger LoggerOption
}
Expand Down Expand Up @@ -114,7 +116,7 @@ func (super *Supernet) LookupIP(ip string) (*net.IPNet, error) {
}

// retrieves all CIDRs from the specified IPv4 or IPv6 trie within a supernet.
func (super *Supernet) AllCIDRS(forV6 bool) []*trie.BinaryTrie[Metadata] {
func (super *Supernet) AllCIDRS(forV6 bool) []*CidrTrie {
supernet := super.ipv4Cidrs
if forV6 {
supernet = super.ipv6Cidrs
Expand All @@ -136,12 +138,12 @@ func (super *Supernet) AllCidrsString(forV6 bool) []string {
}

// creates a new trie node intended for path utilization without any associated metadata.
func newPathNode() *trie.BinaryTrie[Metadata] {
return &trie.BinaryTrie[Metadata]{}
func newPathNode() *CidrTrie {
return &CidrTrie{}
}

// build the CIDR path, and report any conflict
func buildPath(root *trie.BinaryTrie[Metadata], path []int) (lastNode *trie.BinaryTrie[Metadata], conflict ConflictType, remainingPath []int) {
func buildPath(root *CidrTrie, path []int) (lastNode *CidrTrie, conflict ConflictType, remainingPath []int) {
currentNode := root
for currentDepth, bit := range path {
// add a pathNode, if the current node is nil
Expand All @@ -158,7 +160,7 @@ func buildPath(root *trie.BinaryTrie[Metadata], path []int) (lastNode *trie.Bina
}

// try to build the CIDR path, and handle any conflict if any
func (super Supernet) insertLeaf(root *trie.BinaryTrie[Metadata], path []int, newCidrNode *trie.BinaryTrie[Metadata]) *InsertionResult {
func (super Supernet) insertLeaf(root *CidrTrie, path []int, newCidrNode *CidrTrie) *InsertionResult {
insertionResults := &InsertionResult{
CIDR: newCidrNode.Metadata().originCIDR,
}
Expand All @@ -183,7 +185,7 @@ func (super Supernet) insertLeaf(root *trie.BinaryTrie[Metadata], path []int, ne
}

// CIDR conflict detection, it check the current node if it conflicts with other CIDRS
func isThereAConflict(currentNode *trie.BinaryTrie[Metadata], targetedDepth int) ConflictType {
func isThereAConflict(currentNode *CidrTrie, targetedDepth int) ConflictType {
// Check if the current node is a new or path node without specific metadata.
if currentNode.Metadata() == nil {
// Determine if the current node is a supernet of the targeted CIDR.
Expand Down
4 changes: 1 addition & 3 deletions pkg/supernet/uitls.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package supernet

import (
"net"

"github.com/khalid-nowaf/supernet/pkg/trie"
)

// BitsToCidr converts a slice of binary bits into a net.IPNet structure that represents a CIDR.
Expand Down Expand Up @@ -75,7 +73,7 @@ func BitsToCidr(bits []int, ipV6 bool) *net.IPNet {
//
// Given a trie node representing an IP address with metadata, this function will output the address in CIDR format,
// like "192.168.1.0/24" for IPv4 or "2001:db8::/32" for IPv6.
func NodeToCidr(t *trie.BinaryTrie[Metadata]) string {
func NodeToCidr(t *CidrTrie) string {
if t.Metadata() == nil {
panic("[Bug] NodeToCidr: Cannot convert a trie path node to CIDR, metadata is missing")
}
Expand Down
11 changes: 2 additions & 9 deletions pkg/trie/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,17 +173,10 @@ func (t *BinaryTrie[T]) ForEachChild(f func(t *BinaryTrie[T])) *BinaryTrie[T] {
// if no conation is needed you can pass nil as while parameter
// will return the original node t
func (t *BinaryTrie[T]) ForEachStepDown(f func(t *BinaryTrie[T]), while func(t *BinaryTrie[T]) bool) *BinaryTrie[T] {
t.forEachStepDown(f, while)
return t
}

// is a helper for ForEachStepDown to implement recursive traversal.
// will return the original node t
func (t *BinaryTrie[T]) forEachStepDown(f func(t *BinaryTrie[T]), while func(t *BinaryTrie[T]) bool) *BinaryTrie[T] {
t.ForEachChild(func(child *BinaryTrie[T]) {
if while == nil || while(t) {
f(child)
child.forEachStepDown(f, while)
child.ForEachStepDown(f, while)
}
})
return t
Expand Down Expand Up @@ -239,7 +232,7 @@ func (root *BinaryTrie[T]) LeafsPaths() [][]int {
}

func (t *BinaryTrie[T]) String(printOnLeaf func(*BinaryTrie[T]) string) {
t.forEachStepDown(func(node *BinaryTrie[T]) {
t.ForEachStepDown(func(node *BinaryTrie[T]) {
if node.IsLeaf() {
extra := ""
if printOnLeaf != nil {
Expand Down

0 comments on commit 3958b4b

Please sign in to comment.