quality / commitlint (push) Skipped
quality / changes (push) Successful in 9s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 46s
quality / web (push) Successful in 1m16s
quality / go (push) Successful in 2m42s
quality / bird2 (push) Successful in 16s
CD / quality (push) Successful in 5m19s
CD / publish (push) Successful in 7m19s
- Deleted unused components: `DashboardActivityTimeline`, `DashboardFramePanel`, `DashboardModulesGrid`, `DashboardRecentJobsGrid`, and `DashboardRecentRevisionsGrid` to streamline the dashboard. - Updated `DashboardKpiGrid` to improve KPI display logic, including progress indicators and enhanced badge functionality. - Refactored `DashboardNetworkHealth` to provide better status representation based on loading states and network conditions. - Introduced new properties for KPI cards to support progress tracking and improved visual feedback. This cleanup aims to enhance performance and maintainability of the dashboard while providing a better user experience.
174 lines
4.8 KiB
Go
174 lines
4.8 KiB
Go
package pipeline
|
|
|
|
import (
|
|
"net/netip"
|
|
"sort"
|
|
|
|
"evobgp/internal/store"
|
|
)
|
|
|
|
// Prefix collapse replaces the former prune+merge fixed-point loop (O(n²) per pass,
|
|
// multiple passes) with a single sort + linear stack pass: O(n log n) overall.
|
|
//
|
|
// Classic trie-collapse without building a trie:
|
|
// 1. Sort prefixes by (address, mask len ascending).
|
|
// 2. Walk left to right keeping a stack of "open" prefixes:
|
|
// - while the stack top contains the new prefix -> the top covers it, drop the new one (prune);
|
|
// - else, while the stack top is a sibling of the new prefix (same mask, XOR of the
|
|
// network bit equals the parent block) and merging is allowed for that mask length
|
|
// -> pop the sibling, replace the new prefix with the parent and re-check;
|
|
// - otherwise push the new prefix.
|
|
//
|
|
// A parent absorbs its sibling children only when both halves are present, which matches
|
|
// the previous merge-to-fixed-point semantics, including the guard that forbids merging
|
|
// above a floor mask (IPv4 /8, IPv6 /16).
|
|
|
|
type collapseItem struct {
|
|
row store.PrefixRow
|
|
pfx netip.Prefix
|
|
}
|
|
|
|
// collapsePrefixGroup collapses one (community, source) group of same-family prefixes.
|
|
// All rows must be masked; family and floor are enforced by minBits.
|
|
func collapsePrefixGroup(rows []store.PrefixRow, is4 bool) []store.PrefixRow {
|
|
if len(rows) <= 1 {
|
|
return rows
|
|
}
|
|
items := make([]collapseItem, 0, len(rows))
|
|
seen := make(map[string]struct{}, len(rows))
|
|
for _, row := range rows {
|
|
if _, dup := seen[row.Prefix]; dup {
|
|
continue
|
|
}
|
|
seen[row.Prefix] = struct{}{}
|
|
pfx, err := netip.ParsePrefix(row.Prefix)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
pfx = pfx.Masked()
|
|
if is4 != pfx.Addr().Is4() {
|
|
continue
|
|
}
|
|
items = append(items, collapseItem{row: row, pfx: pfx})
|
|
}
|
|
if len(items) <= 1 {
|
|
out := make([]store.PrefixRow, 0, len(items))
|
|
for _, it := range items {
|
|
out = append(out, it.row)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Sort by (address, mask len): a parent always sorts before its children, and a
|
|
// shorter sibling sorts before a longer one within the same parent block.
|
|
sort.Slice(items, func(i, j int) bool {
|
|
a, b := items[i].pfx, items[j].pfx
|
|
if a.Addr() != b.Addr() {
|
|
return lessAddr(a.Addr(), b.Addr())
|
|
}
|
|
return a.Bits() < b.Bits()
|
|
})
|
|
|
|
minBits := 8
|
|
if !is4 {
|
|
minBits = 16
|
|
}
|
|
|
|
stack := make([]collapseItem, 0, len(items))
|
|
for _, it := range items {
|
|
cur := it
|
|
dropped := false
|
|
for len(stack) > 0 {
|
|
top := stack[len(stack)-1]
|
|
if top.pfx.Contains(cur.pfx.Addr()) && top.pfx.Bits() <= cur.pfx.Bits() {
|
|
// Covered by an existing prefix: drop (prune).
|
|
dropped = true
|
|
break
|
|
}
|
|
if cur.pfx.Bits() == top.pfx.Bits() && cur.pfx.Bits() > minBits && areSiblings(top.pfx, cur.pfx) {
|
|
// Merge siblings into the parent (parent keeps the lower sibling's attributes),
|
|
// then re-check the parent against the new stack top.
|
|
stack = stack[:len(stack)-1]
|
|
parentBits := cur.pfx.Bits() - 1
|
|
parent := netip.PrefixFrom(maskAddr(cur.pfx.Addr(), parentBits, is4), parentBits).Masked()
|
|
cur = collapseItem{row: top.row, pfx: parent}
|
|
cur.row.Prefix = parent.String()
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
if !dropped {
|
|
stack = append(stack, cur)
|
|
}
|
|
}
|
|
|
|
out := make([]store.PrefixRow, 0, len(stack))
|
|
for _, it := range stack {
|
|
out = append(out, it.row)
|
|
}
|
|
sortPrefixRows(out)
|
|
return out
|
|
}
|
|
|
|
// areSiblings reports whether two same-length prefixes combine into their common parent.
|
|
func areSiblings(a, b netip.Prefix) bool {
|
|
if a.Bits() != b.Bits() || a.Bits() == 0 {
|
|
return false
|
|
}
|
|
parentBits := a.Bits() - 1
|
|
pa := maskAddr(a.Addr(), parentBits, a.Addr().Is4())
|
|
pb := maskAddr(b.Addr(), parentBits, b.Addr().Is4())
|
|
return pa == pb
|
|
}
|
|
|
|
// maskAddr clears the host bits below prefixLen (IPv4: 32-bit space; IPv6: 128-bit).
|
|
func maskAddr(a netip.Addr, prefixLen int, is4 bool) netip.Addr {
|
|
if is4 {
|
|
v := uint32FromIPv4(a)
|
|
if prefixLen <= 0 {
|
|
v = 0
|
|
} else if prefixLen < 32 {
|
|
v &= ^(uint32(1)<<(32-prefixLen) - 1)
|
|
}
|
|
return u32ToIPv4(v)
|
|
}
|
|
b := a.As16()
|
|
hostBits := 128 - prefixLen
|
|
fullBytes := hostBits / 8
|
|
for i := 15; i > 15-fullBytes; i-- {
|
|
b[i] = 0
|
|
}
|
|
if rem := hostBits % 8; rem > 0 {
|
|
idx := 15 - fullBytes
|
|
if idx >= 0 && idx < 16 {
|
|
b[idx] &= byte(0xFF << rem)
|
|
}
|
|
}
|
|
return netip.AddrFrom16(b)
|
|
}
|
|
|
|
func uint32FromIPv4(a netip.Addr) uint32 {
|
|
o := a.As4()
|
|
return uint32(o[0])<<24 | uint32(o[1])<<16 | uint32(o[2])<<8 | uint32(o[3])
|
|
}
|
|
|
|
func u32ToIPv4(v uint32) netip.Addr {
|
|
return netip.AddrFrom4([4]byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)})
|
|
}
|
|
|
|
func lessAddr(a, b netip.Addr) bool {
|
|
if a.Is4() != b.Is4() {
|
|
return a.Is4()
|
|
}
|
|
if a.Is4() {
|
|
return uint32FromIPv4(a) < uint32FromIPv4(b)
|
|
}
|
|
a16, b16 := a.As16(), b.As16()
|
|
for i := 0; i < 16; i++ {
|
|
if a16[i] != b16[i] {
|
|
return a16[i] < b16[i]
|
|
}
|
|
}
|
|
return false
|
|
}
|