Update dependencies
This commit is contained in:
305
vendor/github.com/gaissmai/bart/overlaps.go
generated
vendored
Normal file
305
vendor/github.com/gaissmai/bart/overlaps.go
generated
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
package bart
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/gaissmai/bart/internal/bitset"
|
||||
)
|
||||
|
||||
// overlaps returns true if any IP in the nodes n or o overlaps.
|
||||
func (n *node[V]) overlaps(o *node[V], depth int) bool {
|
||||
nPfxCount := n.prefixes.Len()
|
||||
oPfxCount := o.prefixes.Len()
|
||||
|
||||
nChildCount := n.children.Len()
|
||||
oChildCount := o.children.Len()
|
||||
|
||||
// ##############################
|
||||
// 1. Test if any routes overlaps
|
||||
// ##############################
|
||||
|
||||
// full cross check
|
||||
if nPfxCount > 0 && oPfxCount > 0 {
|
||||
if n.overlapsRoutes(o) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ####################################
|
||||
// 2. Test if routes overlaps any child
|
||||
// ####################################
|
||||
|
||||
// swap nodes to help chance on its way,
|
||||
// if the first call to expensive overlapsChildrenIn() is already true,
|
||||
// if both orders are false it doesn't help either
|
||||
if nChildCount > oChildCount {
|
||||
n, o = o, n
|
||||
|
||||
nPfxCount = n.prefixes.Len()
|
||||
oPfxCount = o.prefixes.Len()
|
||||
|
||||
nChildCount = n.children.Len()
|
||||
oChildCount = o.children.Len()
|
||||
}
|
||||
|
||||
if nPfxCount > 0 && oChildCount > 0 {
|
||||
if n.overlapsChildrenIn(o) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// symmetric reverse
|
||||
if oPfxCount > 0 && nChildCount > 0 {
|
||||
if o.overlapsChildrenIn(n) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ###########################################
|
||||
// 3. childs with same octet in nodes n and o
|
||||
// ###########################################
|
||||
|
||||
// stop condition, n or o have no childs
|
||||
if nChildCount == 0 || oChildCount == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// stop condition, no child with identical octet in n and o
|
||||
if !n.children.IntersectsAny(o.children.BitSet) {
|
||||
return false
|
||||
}
|
||||
|
||||
return n.overlapsSameChildren(o, depth)
|
||||
}
|
||||
|
||||
// overlapsRoutes, test if n overlaps o prefixes and vice versa
|
||||
func (n *node[V]) overlapsRoutes(o *node[V]) bool {
|
||||
// some prefixes are identical, trivial overlap
|
||||
if n.prefixes.IntersectsAny(o.prefixes.BitSet) {
|
||||
return true
|
||||
}
|
||||
|
||||
// get the lowest idx (biggest prefix)
|
||||
nFirstIdx, _ := n.prefixes.FirstSet()
|
||||
oFirstIdx, _ := o.prefixes.FirstSet()
|
||||
|
||||
// start with other min value, see ART algo
|
||||
nIdx := oFirstIdx
|
||||
oIdx := nFirstIdx
|
||||
|
||||
// make full cross check
|
||||
nOK := true
|
||||
oOK := true
|
||||
|
||||
// zip, range over n and o together to help chance on its way
|
||||
for nOK || oOK {
|
||||
if nOK {
|
||||
// does any route in o overlap this prefix from n
|
||||
if nIdx, nOK = n.prefixes.NextSet(nIdx); nOK {
|
||||
if o.lpmTest(nIdx) {
|
||||
return true
|
||||
}
|
||||
|
||||
nIdx++
|
||||
}
|
||||
}
|
||||
|
||||
if oOK {
|
||||
// does any route in n overlap this prefix from o
|
||||
if oIdx, oOK = o.prefixes.NextSet(oIdx); oOK {
|
||||
if n.lpmTest(oIdx) {
|
||||
return true
|
||||
}
|
||||
|
||||
oIdx++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// overlapsChildrenIn, test if prefixes in n overlaps child octets in o.
|
||||
func (n *node[V]) overlapsChildrenIn(o *node[V]) bool {
|
||||
pfxCount := n.prefixes.Len()
|
||||
childCount := o.children.Len()
|
||||
|
||||
// heuristic, compare benchmarks
|
||||
// when will we range over the children and when will we do bitset calc?
|
||||
magicNumber := 15
|
||||
doRange := childCount < magicNumber || pfxCount > magicNumber
|
||||
|
||||
// do range over, not so many childs and maybe to many prefixes for other algo below
|
||||
if doRange {
|
||||
lowerBound, _ := n.prefixes.FirstSet()
|
||||
for _, addr := range o.children.AsSlice(make([]uint, 0, maxNodeChildren)) {
|
||||
idx := hostIndex(addr)
|
||||
if idx < lowerBound { // lpm match impossible
|
||||
continue
|
||||
}
|
||||
if n.lpmTest(idx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// do bitset intersection, alloted route table with child octets
|
||||
// maybe to many childs for range over or not so many prefixes to
|
||||
// build the alloted routing table from them
|
||||
|
||||
// make allot table with prefixes as bitsets, bitsets are precalculated
|
||||
// just union the bitsets to one bitset (allot table) for all prefixes
|
||||
// in this node
|
||||
prefixRoutes := bitset.BitSet(make([]uint64, 8))
|
||||
|
||||
allIndices := n.prefixes.AsSlice(make([]uint, 0, maxNodePrefixes))
|
||||
|
||||
for _, idx := range allIndices {
|
||||
// get pre alloted bitset for idx
|
||||
prefixRoutes.InPlaceUnion(allotLookupTbl[idx])
|
||||
}
|
||||
|
||||
// clone a bitset without heap allocation
|
||||
// shift-right children bitset by 256 (firstHostIndex)
|
||||
c8 := make([]uint64, 8)
|
||||
copy(c8[4:], o.children.BitSet) // 4*64= 256
|
||||
hostRoutes := bitset.BitSet(c8)
|
||||
|
||||
return prefixRoutes.IntersectsAny(hostRoutes)
|
||||
}
|
||||
|
||||
// overlapsSameChildren, find same octets with bitset intersection.
|
||||
func (n *node[V]) overlapsSameChildren(o *node[V], depth int) bool {
|
||||
// clone a bitset without heap allocation
|
||||
c4 := [4]uint64{}
|
||||
copy(c4[:], n.children.BitSet)
|
||||
nChildrenBitsetCloned := bitset.BitSet(c4[:])
|
||||
|
||||
// intersect in place the cloned child bitset from n with o
|
||||
nChildrenBitsetCloned.InPlaceIntersection(o.children.BitSet)
|
||||
|
||||
allCommonChildren := nChildrenBitsetCloned.AsSlice(make([]uint, 0, maxNodeChildren))
|
||||
|
||||
// range over all child addrs, common in n and o
|
||||
for _, addr := range allCommonChildren {
|
||||
nChild := n.children.MustGet(addr)
|
||||
oChild := o.children.MustGet(addr)
|
||||
|
||||
if overlapsTwoChilds[V](nChild, oChild, depth+1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// overlapsTwoChilds, childs can be node or leaf.
|
||||
func overlapsTwoChilds[V any](nChild, oChild any, depth int) bool {
|
||||
// 4 possible different combinations for n and o
|
||||
//
|
||||
// node, node --> overlapsRec
|
||||
// node, leaf --> overlapsPrefixAtDepth
|
||||
// leaf, node --> overlapsPrefixAtDepth
|
||||
// leaf, leaf --> netip.Prefix.Overlaps
|
||||
//
|
||||
switch nKind := nChild.(type) {
|
||||
|
||||
case *node[V]:
|
||||
switch oKind := oChild.(type) {
|
||||
case *node[V]: // node, node
|
||||
return nKind.overlaps(oKind, depth)
|
||||
case *leaf[V]: // node, leaf
|
||||
return nKind.overlapsPrefixAtDepth(oKind.prefix, depth)
|
||||
}
|
||||
|
||||
case *leaf[V]:
|
||||
switch oKind := oChild.(type) {
|
||||
case *node[V]: // leaf, node
|
||||
return oKind.overlapsPrefixAtDepth(nKind.prefix, depth)
|
||||
case *leaf[V]: // leaf, leaf
|
||||
return oKind.prefix.Overlaps(nKind.prefix)
|
||||
}
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// overlapsPrefixAtDepth, returns true if node overlaps with prefix
|
||||
// starting with prefix octet at depth.
|
||||
//
|
||||
// Needed for path compressed prefix some level down in the node trie.
|
||||
func (n *node[V]) overlapsPrefixAtDepth(pfx netip.Prefix, depth int) bool {
|
||||
ip := pfx.Addr()
|
||||
bits := pfx.Bits()
|
||||
|
||||
lastIdx, lastBits := lastOctetIdxAndBits(bits)
|
||||
|
||||
octets := ip.AsSlice()
|
||||
octets = octets[:lastIdx+1]
|
||||
|
||||
for ; depth < len(octets); depth++ {
|
||||
octet := octets[depth]
|
||||
addr := uint(octet)
|
||||
|
||||
// full octet path in node trie, check overlap with last prefix octet
|
||||
if depth == lastIdx {
|
||||
return n.overlapsIdx(pfxToIdx(octet, lastBits))
|
||||
}
|
||||
|
||||
// test if any route overlaps prefix´ so far
|
||||
// no best match needed, forward tests without backtracking
|
||||
if n.prefixes.Len() != 0 && n.lpmTest(hostIndex(addr)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if !n.children.Test(addr) {
|
||||
return false
|
||||
}
|
||||
|
||||
// next child, node or leaf
|
||||
switch kid := n.children.MustGet(addr).(type) {
|
||||
case *node[V]:
|
||||
n = kid
|
||||
continue
|
||||
case *leaf[V]:
|
||||
return kid.prefix.Overlaps(pfx)
|
||||
|
||||
default:
|
||||
panic("logic error, wrong node type")
|
||||
}
|
||||
}
|
||||
|
||||
panic("unreachable: " + pfx.String())
|
||||
}
|
||||
|
||||
// overlapsIdx returns true if node overlaps with prefix.
|
||||
func (n *node[V]) overlapsIdx(idx uint) bool {
|
||||
// 1. Test if any route in this node overlaps prefix?
|
||||
if n.lpmTest(idx) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 2. Test if prefix overlaps any route in this node
|
||||
|
||||
// use bitset intersections instead of range loops
|
||||
// shallow copy pre alloted bitset for idx
|
||||
allotedPrefixRoutes := allotLookupTbl[idx]
|
||||
if allotedPrefixRoutes.IntersectsAny(n.prefixes.BitSet) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 3. Test if prefix overlaps any child in this node
|
||||
|
||||
// shift-right children bitset by 256 (firstHostIndex)
|
||||
c8 := [8]uint64{}
|
||||
copy(c8[4:], n.children.BitSet) // 4*64= 256
|
||||
hostRoutes := bitset.BitSet(c8[:])
|
||||
|
||||
// use bitsets intersection instead of range loops
|
||||
return allotedPrefixRoutes.IntersectsAny(hostRoutes)
|
||||
}
|
||||
Reference in New Issue
Block a user