Skip to content

Commit

Permalink
prefixes with recursive algo: faster, 0 allocs
Browse files Browse the repository at this point in the history
  • Loading branch information
gaissmai committed Aug 3, 2022
1 parent 8152dd2 commit 478667d
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 90 deletions.
88 changes: 42 additions & 46 deletions extnetip.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ func Range(p netip.Prefix) (first, last netip.Addr) {
}

// peek the internals, do math in uint128
exhib := peek(p.Addr())
z := exhib.z
pa := peek(p.Addr())
z := pa.z

bits := p.Bits()
if z == z4 {
bits += 96
}
mask := mask6(bits)

first128 := exhib.addr.and(mask)
first128 := pa.ip.and(mask)
last128 := first128.or(mask.not())

// convert back to netip.Addr
first = back(exhibType{first128, z})
last = back(exhibType{last128, z})
first = back(addr{first128, z})
last = back(addr{last128, z})

return
}
Expand All @@ -52,21 +52,21 @@ func Prefix(first, last netip.Addr) (prefix netip.Prefix, ok bool) {
}

// peek the internals, do math in uint128
exhibFirst := peek(first)
exhibLast := peek(last)
pFirst := peek(first)
pLast := peek(last)

// IP versions differ?
if exhibFirst.z != exhibLast.z {
if pFirst.z != pLast.z {
return
}

// do math in uint128
bits, ok := exhibFirst.addr.prefixOK(exhibLast.addr)
bits, ok := pFirst.ip.prefixOK(pLast.ip)
if !ok {
return
}

if exhibFirst.z == z4 {
if pFirst.z == z4 {
bits -= 96
}

Expand Down Expand Up @@ -97,53 +97,49 @@ func PrefixesAppend(dst []netip.Prefix, first, last netip.Addr) []netip.Prefix {
}

// peek the internals, do math in uint128
exhibFirst := peek(first)
exhibLast := peek(last)
pFirst := peek(first)
pLast := peek(last)

// different IP versions
if exhibFirst.z != exhibLast.z {
if pFirst.z != pLast.z {
return nil
}

// no recursion, use an iterative algo with stack
var stack []exhibType

// push, params are the starting point
stack = append(stack, exhibFirst, exhibLast)

for len(stack) > 0 {

// pop two addresses
exhibLast := stack[len(stack)-1]
exhibFirst := stack[len(stack)-2]
stack = stack[:len(stack)-2]

// are first-last already representing a prefix?
bits, ok := exhibFirst.addr.prefixOK(exhibLast.addr)
if ok {
if exhibFirst.z == z4 {
bits -= 96
}
// convert back to netip
pfx := netip.PrefixFrom(back(exhibFirst), bits)
return prefixesAppendRec(dst, pFirst, pLast)
}

dst = append(dst, pfx)
continue
// append prefix if (first, last) represents a whole CIDR, like 10.0.0.0/8
// (first being 10.0.0.0 and last being 10.255.255.255)
//
// Otherwise recursively do both halves.
//
// Recursion is here faster than an iterative algo, no bounds checking and no heap escape and
// btw. the recursion level is max. 254 deep, for IP range: ::1-ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe
func prefixesAppendRec(dst []netip.Prefix, first, last addr) []netip.Prefix {
// are first-last already representing a prefix?
bits, ok := first.ip.prefixOK(last.ip)
if ok {
if first.z == z4 {
bits -= 96
}
// convert back to netip
pfx := netip.PrefixFrom(back(first), bits)

// Otherwise split the range, make two halves and push it on the stack
mask := mask6(bits + 1)
return append(dst, pfx)
}

// make middle last, set hostbits
exhibMidOne := exhibType{exhibFirst.addr.or(mask.not()), exhibFirst.z}
// otherwise split the range, make two halves and do both halves recursively
mask := mask6(bits + 1)

// make middle first, clear hostbits
exhibMidTwo := exhibType{exhibLast.addr.and(mask), exhibFirst.z}
// make middle last, set hostbits
midOne := addr{first.ip.or(mask.not()), first.z}

// push both halves (in reverse order, prefixes are then sorted)
stack = append(stack, exhibMidTwo, exhibLast)
stack = append(stack, exhibFirst, exhibMidOne)
}
// make middle next, clear hostbits
midTwo := addr{last.ip.and(mask), first.z}

// ... do both halves recursively
dst = prefixesAppendRec(dst, first, midOne)
dst = prefixesAppendRec(dst, midTwo, last)

return dst
}
221 changes: 190 additions & 31 deletions extnetip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,40 +244,199 @@ func TestPrefixes(t *testing.T) {
{mustAddr("10.0.0.0"), mustAddr("11.10.255.255"), pfxSlice("10.0.0.0/8", "11.0.0.0/13", "11.8.0.0/15", "11.10.0.0/16")},
{mustAddr("fe80::"), mustAddr("fe80::8"), pfxSlice("fe80::/125", "fe80::8/128")},

{mustAddr("1.2.3.5"), mustAddr("5.6.7.8"), pfxSlice(
"1.2.3.5/32",
"1.2.3.6/31",
"1.2.3.8/29",
"1.2.3.16/28",
"1.2.3.32/27",
"1.2.3.64/26",
"1.2.3.128/25",
"1.2.4.0/22",
"1.2.8.0/21",
"1.2.16.0/20",
"1.2.32.0/19",
"1.2.64.0/18",
"1.2.128.0/17",
"1.3.0.0/16",
"1.4.0.0/14",
"1.8.0.0/13",
"1.16.0.0/12",
"1.32.0.0/11",
"1.64.0.0/10",
"1.128.0.0/9",
{mustAddr("0.0.0.1"), mustAddr("255.255.255.254"), pfxSlice(
"0.0.0.1/32",
"0.0.0.2/31",
"0.0.0.4/30",
"0.0.0.8/29",
"0.0.0.16/28",
"0.0.0.32/27",
"0.0.0.64/26",
"0.0.0.128/25",
"0.0.1.0/24",
"0.0.2.0/23",
"0.0.4.0/22",
"0.0.8.0/21",
"0.0.16.0/20",
"0.0.32.0/19",
"0.0.64.0/18",
"0.0.128.0/17",
"0.1.0.0/16",
"0.2.0.0/15",
"0.4.0.0/14",
"0.8.0.0/13",
"0.16.0.0/12",
"0.32.0.0/11",
"0.64.0.0/10",
"0.128.0.0/9",
"1.0.0.0/8",
"2.0.0.0/7",
"4.0.0.0/8",
"5.0.0.0/14",
"5.4.0.0/15",
"5.6.0.0/22",
"5.6.4.0/23",
"5.6.6.0/24",
"5.6.7.0/29",
"5.6.7.8/32",
"4.0.0.0/6",
"8.0.0.0/5",
"16.0.0.0/4",
"32.0.0.0/3",
"64.0.0.0/2",
"128.0.0.0/2",
"192.0.0.0/3",
"224.0.0.0/4",
"240.0.0.0/5",
"248.0.0.0/6",
"252.0.0.0/7",
"254.0.0.0/8",
"255.0.0.0/9",
"255.128.0.0/10",
"255.192.0.0/11",
"255.224.0.0/12",
"255.240.0.0/13",
"255.248.0.0/14",
"255.252.0.0/15",
"255.254.0.0/16",
"255.255.0.0/17",
"255.255.128.0/18",
"255.255.192.0/19",
"255.255.224.0/20",
"255.255.240.0/21",
"255.255.248.0/22",
"255.255.252.0/23",
"255.255.254.0/24",
"255.255.255.0/25",
"255.255.255.128/26",
"255.255.255.192/27",
"255.255.255.224/28",
"255.255.255.240/29",
"255.255.255.248/30",
"255.255.255.252/31",
"255.255.255.254/32",
)},

{mustAddr("::"), mustAddr("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"), pfxSlice(
"::/1",
{mustAddr("::1"), mustAddr("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"), pfxSlice(
"::1/128",
"::2/127",
"::4/126",
"::8/125",
"::10/124",
"::20/123",
"::40/122",
"::80/121",
"::100/120",
"::200/119",
"::400/118",
"::800/117",
"::1000/116",
"::2000/115",
"::4000/114",
"::8000/113",
"::1:0/112",
"::2:0/111",
"::4:0/110",
"::8:0/109",
"::10:0/108",
"::20:0/107",
"::40:0/106",
"::80:0/105",
"::100:0/104",
"::200:0/103",
"::400:0/102",
"::800:0/101",
"::1000:0/100",
"::2000:0/99",
"::4000:0/98",
"::8000:0/97",
"::1:0:0/96",
"::2:0:0/95",
"::4:0:0/94",
"::8:0:0/93",
"::10:0:0/92",
"::20:0:0/91",
"::40:0:0/90",
"::80:0:0/89",
"::100:0:0/88",
"::200:0:0/87",
"::400:0:0/86",
"::800:0:0/85",
"::1000:0:0/84",
"::2000:0:0/83",
"::4000:0:0/82",
"::8000:0:0/81",
"::1:0:0:0/80",
"::2:0:0:0/79",
"::4:0:0:0/78",
"::8:0:0:0/77",
"::10:0:0:0/76",
"::20:0:0:0/75",
"::40:0:0:0/74",
"::80:0:0:0/73",
"::100:0:0:0/72",
"::200:0:0:0/71",
"::400:0:0:0/70",
"::800:0:0:0/69",
"::1000:0:0:0/68",
"::2000:0:0:0/67",
"::4000:0:0:0/66",
"::8000:0:0:0/65",
"0:0:0:1::/64",
"0:0:0:2::/63",
"0:0:0:4::/62",
"0:0:0:8::/61",
"0:0:0:10::/60",
"0:0:0:20::/59",
"0:0:0:40::/58",
"0:0:0:80::/57",
"0:0:0:100::/56",
"0:0:0:200::/55",
"0:0:0:400::/54",
"0:0:0:800::/53",
"0:0:0:1000::/52",
"0:0:0:2000::/51",
"0:0:0:4000::/50",
"0:0:0:8000::/49",
"0:0:1::/48",
"0:0:2::/47",
"0:0:4::/46",
"0:0:8::/45",
"0:0:10::/44",
"0:0:20::/43",
"0:0:40::/42",
"0:0:80::/41",
"0:0:100::/40",
"0:0:200::/39",
"0:0:400::/38",
"0:0:800::/37",
"0:0:1000::/36",
"0:0:2000::/35",
"0:0:4000::/34",
"0:0:8000::/33",
"0:1::/32",
"0:2::/31",
"0:4::/30",
"0:8::/29",
"0:10::/28",
"0:20::/27",
"0:40::/26",
"0:80::/25",
"0:100::/24",
"0:200::/23",
"0:400::/22",
"0:800::/21",
"0:1000::/20",
"0:2000::/19",
"0:4000::/18",
"0:8000::/17",
"1::/16",
"2::/15",
"4::/14",
"8::/13",
"10::/12",
"20::/11",
"40::/10",
"80::/9",
"100::/8",
"200::/7",
"400::/6",
"800::/5",
"1000::/4",
"2000::/3",
"4000::/2",
"8000::/2",
"c000::/3",
"e000::/4",
Expand Down
Empty file added go.sum
Empty file.
Loading

0 comments on commit 478667d

Please sign in to comment.