quorate/packages/oci-l4-relay-publisher/ranges.go

110 lines
2.8 KiB
Go
Raw Permalink Normal View History

package main
import (
"encoding/binary"
"net/netip"
"sort"
)
type addressRange struct {
start uint32
end uint32
}
func rangeFromPrefix(value string) (addressRange, bool) {
prefix, err := netip.ParsePrefix(value)
if err != nil || !prefix.Addr().Is4() {
return addressRange{}, false
}
address := prefix.Masked().Addr().As4()
start := binary.BigEndian.Uint32(address[:])
hostBits := 32 - prefix.Bits()
end := start
if hostBits == 32 {
end = ^uint32(0)
} else if hostBits > 0 {
end |= (uint32(1) << hostBits) - 1
}
return addressRange{start: start, end: end}, true
}
func mergeRanges(ranges []addressRange) []addressRange {
if len(ranges) == 0 {
return nil
}
sort.Slice(ranges, func(i, j int) bool {
if ranges[i].start == ranges[j].start {
return ranges[i].end < ranges[j].end
}
return ranges[i].start < ranges[j].start
})
merged := []addressRange{ranges[0]}
for _, candidate := range ranges[1:] {
current := &merged[len(merged)-1]
adjacent := current.end != ^uint32(0) && candidate.start == current.end+1
if candidate.start <= current.end || adjacent {
if candidate.end > current.end {
current.end = candidate.end
}
continue
}
merged = append(merged, candidate)
}
return merged
}
func intersectRanges(left, right []addressRange) []addressRange {
left = mergeRanges(left)
right = mergeRanges(right)
var result []addressRange
for leftIndex, rightIndex := 0, 0; leftIndex < len(left) && rightIndex < len(right); {
start := max(left[leftIndex].start, right[rightIndex].start)
end := min(left[leftIndex].end, right[rightIndex].end)
if start <= end {
result = append(result, addressRange{start: start, end: end})
}
if left[leftIndex].end < right[rightIndex].end {
leftIndex++
} else {
rightIndex++
}
}
return mergeRanges(result)
}
func subtractRanges(base, denied []addressRange) []addressRange {
base = mergeRanges(base)
denied = mergeRanges(denied)
var result []addressRange
denyIndex := 0
for _, candidate := range base {
cursor := uint64(candidate.start)
end := uint64(candidate.end)
for denyIndex < len(denied) && uint64(denied[denyIndex].end) < cursor {
denyIndex++
}
for index := denyIndex; index < len(denied) && uint64(denied[index].start) <= end; index++ {
if uint64(denied[index].start) > cursor {
result = append(result, addressRange{start: uint32(cursor), end: denied[index].start - 1})
}
if uint64(denied[index].end)+1 > cursor {
cursor = uint64(denied[index].end) + 1
}
if cursor > end {
break
}
}
if cursor <= end {
result = append(result, addressRange{start: uint32(cursor), end: uint32(end)})
}
}
return mergeRanges(result)
}
func addressCount(ranges []addressRange) uint64 {
var result uint64
for _, current := range mergeRanges(ranges) {
result += uint64(current.end) - uint64(current.start) + 1
}
return result
}