refactor(matcher): replace cidranger with zero-alloc trie, migrate to netip
Replace cidranger third-party dependency with a custom binary trie for zero-allocation CIDR containment lookups. Migrate ipMatcher from string-keyed map to netip.Addr for correct IPv4/IPv6 normalization. Extract shared splitHostPort helper to eliminate repeated host:port parsing boilerplate across all matchers. Add comprehensive benchmarks.
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// cidrTrie is a binary trie for zero-allocation netip.Prefix containment lookups.
|
||||
// IPv4 and IPv6 prefixes are stored in separate sub-tries.
|
||||
type cidrTrie struct {
|
||||
root4 *cidrNode
|
||||
root6 *cidrNode
|
||||
}
|
||||
|
||||
type cidrNode struct {
|
||||
hasEntry bool
|
||||
children [2]*cidrNode
|
||||
}
|
||||
|
||||
func newCIDRTrie() *cidrTrie {
|
||||
return &cidrTrie{}
|
||||
}
|
||||
|
||||
// insert adds prefix to the trie.
|
||||
func (t *cidrTrie) insert(prefix netip.Prefix) {
|
||||
addr := prefix.Addr()
|
||||
bits := prefix.Bits()
|
||||
if bits == 0 {
|
||||
// Match everything for this address family.
|
||||
root := t.root(addr)
|
||||
if root == nil {
|
||||
root = &cidrNode{hasEntry: true}
|
||||
t.setRoot(addr, root)
|
||||
} else {
|
||||
root.hasEntry = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
root := t.rootOrCreate(addr)
|
||||
b := addr.As16()
|
||||
maxBits := 128
|
||||
if addr.Is4() || addr.Is4In6() {
|
||||
maxBits = 32
|
||||
}
|
||||
if bits > maxBits {
|
||||
bits = maxBits
|
||||
}
|
||||
|
||||
node := root
|
||||
for i := 0; i < bits; i++ {
|
||||
bit := bitAt(b, i, addr.Is4() || addr.Is4In6())
|
||||
if node.children[bit] == nil {
|
||||
node.children[bit] = &cidrNode{}
|
||||
}
|
||||
node = node.children[bit]
|
||||
}
|
||||
node.hasEntry = true
|
||||
}
|
||||
|
||||
// contains reports whether any inserted prefix covers addr.
|
||||
func (t *cidrTrie) contains(addr netip.Addr) bool {
|
||||
root := t.root(addr)
|
||||
if root == nil {
|
||||
return false
|
||||
}
|
||||
if root.hasEntry {
|
||||
return true
|
||||
}
|
||||
|
||||
b := addr.As16()
|
||||
is4 := addr.Is4() || addr.Is4In6()
|
||||
maxBits := 128
|
||||
if is4 {
|
||||
maxBits = 32
|
||||
}
|
||||
|
||||
node := root
|
||||
for i := 0; i < maxBits; i++ {
|
||||
bit := bitAt(b, i, is4)
|
||||
child := node.children[bit]
|
||||
if child == nil {
|
||||
return false
|
||||
}
|
||||
if child.hasEntry {
|
||||
return true
|
||||
}
|
||||
node = child
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *cidrTrie) root(addr netip.Addr) *cidrNode {
|
||||
if addr.Is4() || addr.Is4In6() {
|
||||
return t.root4
|
||||
}
|
||||
return t.root6
|
||||
}
|
||||
|
||||
func (t *cidrTrie) setRoot(addr netip.Addr, n *cidrNode) {
|
||||
if addr.Is4() || addr.Is4In6() {
|
||||
t.root4 = n
|
||||
} else {
|
||||
t.root6 = n
|
||||
}
|
||||
}
|
||||
|
||||
func (t *cidrTrie) rootOrCreate(addr netip.Addr) *cidrNode {
|
||||
if addr.Is4() || addr.Is4In6() {
|
||||
if t.root4 == nil {
|
||||
t.root4 = &cidrNode{}
|
||||
}
|
||||
return t.root4
|
||||
}
|
||||
if t.root6 == nil {
|
||||
t.root6 = &cidrNode{}
|
||||
}
|
||||
return t.root6
|
||||
}
|
||||
|
||||
// bitAt returns the i-th bit (MSB-first) from a 16-byte address representation.
|
||||
// For IPv4, bits start at byte index 12 (the last 4 bytes of the 16-byte form).
|
||||
func bitAt(b [16]byte, i int, is4 bool) uint8 {
|
||||
offset := 0
|
||||
if is4 {
|
||||
offset = 12
|
||||
}
|
||||
byteIdx := offset + i/8
|
||||
bitIdx := 7 - i%8
|
||||
return (b[byteIdx] >> bitIdx) & 1
|
||||
}
|
||||
|
||||
// ipNetToPrefix converts a net.IPNet to a netip.Prefix.
|
||||
// Returns ok=false if conversion fails.
|
||||
func ipNetToPrefix(inet *net.IPNet) (netip.Prefix, bool) {
|
||||
addr, ok := netip.AddrFromSlice(inet.IP)
|
||||
if !ok {
|
||||
return netip.Prefix{}, false
|
||||
}
|
||||
addr = addr.Unmap()
|
||||
ones, _ := inet.Mask.Size()
|
||||
bits := addr.BitLen()
|
||||
if ones > bits {
|
||||
ones = bits
|
||||
}
|
||||
return netip.PrefixFrom(addr, ones), true
|
||||
}
|
||||
Reference in New Issue
Block a user