eth: replace topN helper with stdlib slices.SortedFunc + DeleteFunc

Remove the custom topN generic function. Use slices.SortedFunc
(creates a sorted copy from an iterator) + slices.DeleteFunc (filters
score <= 0) from the standard library. No custom generics needed.
This commit is contained in:
Csaba Kiraly 2026-04-10 12:07:19 +02:00
parent aa5fa692e2
commit ecfdaa69c6

View file

@ -17,9 +17,9 @@
package eth package eth
import ( import (
"cmp"
mrand "math/rand" mrand "math/rand"
"slices" "slices"
"sort"
"sync" "sync"
"time" "time"
@ -189,25 +189,6 @@ func (cm *dropper) dropRandomPeer() bool {
return true return true
} }
// topN returns the top n elements from items by score (descending).
// Only elements with score > 0 are included. The input slice is not modified.
func topN[T any](items []T, n int, score func(T) float64) []T {
if n == 0 || len(items) == 0 {
return nil
}
cp := make([]T, len(items))
copy(cp, items)
sort.Slice(cp, func(i, j int) bool { return score(cp[i]) > score(cp[j]) })
var result []T
for i := 0; i < n && i < len(cp); i++ {
if score(cp[i]) > 0 {
result = append(result, cp[i])
}
}
return result
}
// protectedPeers computes the set of peers that should not be dropped based // protectedPeers computes the set of peers that should not be dropped based
// on inclusion stats. Each protection category independently selects its // on inclusion stats. Each protection category independently selects its
// top-N peers per inbound/dialed pool; the union is returned. // top-N peers per inbound/dialed pool; the union is returned.
@ -228,18 +209,29 @@ func (cm *dropper) protectedPeers(peers []*p2p.Peer) map[*p2p.Peer]bool {
dialed = append(dialed, p) dialed = append(dialed, p)
} }
} }
// protectPool selects the top-frac peers from pool by score and adds them to result.
result := make(map[*p2p.Peer]bool) result := make(map[*p2p.Peer]bool)
protectPool := func(pool []*p2p.Peer, score func(*p2p.Peer) float64, frac float64) {
n := int(float64(len(pool)) * frac)
if n == 0 {
return
}
sorted := slices.SortedFunc(slices.Values(pool), func(a, b *p2p.Peer) int {
return cmp.Compare(score(b), score(a)) // descending
})
top := slices.DeleteFunc(sorted[:min(n, len(sorted))], func(p *p2p.Peer) bool {
return score(p) <= 0
})
for _, p := range top {
result[p] = true
}
}
for _, cat := range protectionCategories { for _, cat := range protectionCategories {
// Build a score function that looks up the peer's stats.
score := func(p *p2p.Peer) float64 { score := func(p *p2p.Peer) float64 {
return cat.score(stats[p.ID().String()]) return cat.score(stats[p.ID().String()])
} }
for _, p := range topN(inbound, int(float64(len(inbound))*cat.frac), score) { protectPool(inbound, score, cat.frac)
result[p] = true protectPool(dialed, score, cat.frac)
}
for _, p := range topN(dialed, int(float64(len(dialed))*cat.frac), score) {
result[p] = true
}
} }
if len(result) > 0 { if len(result) > 0 {
log.Debug("Protecting high-value peers from drop", "protected", len(result)) log.Debug("Protecting high-value peers from drop", "protected", len(result))