Files
zeekayandHanzo Dev b1cc108c9f gofmt
Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 01:51:51 -07:00

158 lines
3.8 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package peer
import (
"github.com/luxfi/container/sampler"
"github.com/luxfi/ids"
)
var _ Set = (*peerSet)(nil)
func NoPrecondition(Peer) bool {
return true
}
// Set contains a group of peers.
type Set interface {
// Add this peer to the set.
//
// If a peer with the same [peer.ID] is already in the set, then the new
// peer instance will replace the old peer instance.
//
// Add does not change the [peer.ID] returned from calls to [GetByIndex].
Add(peer Peer)
// GetByID attempts to fetch a [peer] whose [peer.ID] is equal to [nodeID].
// If no such peer exists in the set, then [false] will be returned.
GetByID(nodeID ids.NodeID) (Peer, bool)
// GetByIndex attempts to fetch a peer who has been allocated [index]. If
// [index] < 0 or [index] >= [Len], then false will be returned.
GetByIndex(index int) (Peer, bool)
// Remove any [peer] whose [peer.ID] is equal to [nodeID] from the set.
Remove(nodeID ids.NodeID)
// Len returns the number of peers currently in this set.
Len() int
// Sample attempts to return a random slice of peers with length [n]. The
// slice will not include any duplicates. Only peers that cause the
// [precondition] to return true will be returned in the slice.
Sample(n int, precondition func(Peer) bool) []Peer
// Returns information about all the peers.
AllInfo() []Info
// Info returns information about the requested peers if they are in the
// set.
Info(nodeIDs []ids.NodeID) []Info
}
type peerSet struct {
peersMap map[ids.NodeID]int // nodeID -> peer's index in peersSlice
peersSlice []Peer // invariant: len(peersSlice) == len(peersMap)
}
// NewSet returns a set that does not internally manage synchronization.
//
// Only [Add] and [Remove] require exclusion on the data structure. The
// remaining methods are safe for concurrent use.
func NewSet() Set {
return &peerSet{
peersMap: make(map[ids.NodeID]int),
}
}
func (s *peerSet) Add(peer Peer) {
nodeID := peer.ID()
index, ok := s.peersMap[nodeID]
if !ok {
s.peersMap[nodeID] = len(s.peersSlice)
s.peersSlice = append(s.peersSlice, peer)
} else {
s.peersSlice[index] = peer
}
}
func (s *peerSet) GetByID(nodeID ids.NodeID) (Peer, bool) {
index, ok := s.peersMap[nodeID]
if !ok {
return nil, false
}
return s.peersSlice[index], true
}
func (s *peerSet) GetByIndex(index int) (Peer, bool) {
if index < 0 || index >= len(s.peersSlice) {
return nil, false
}
return s.peersSlice[index], true
}
func (s *peerSet) Remove(nodeID ids.NodeID) {
index, ok := s.peersMap[nodeID]
if !ok {
return
}
lastIndex := len(s.peersSlice) - 1
lastPeer := s.peersSlice[lastIndex]
lastPeerID := lastPeer.ID()
s.peersMap[lastPeerID] = index
s.peersSlice[index] = lastPeer
delete(s.peersMap, nodeID)
s.peersSlice[lastIndex] = nil
s.peersSlice = s.peersSlice[:lastIndex]
}
func (s *peerSet) Len() int {
return len(s.peersSlice)
}
func (s *peerSet) Sample(n int, precondition func(Peer) bool) []Peer {
if n <= 0 {
return nil
}
sampler := sampler.NewUniform()
sampler.Initialize(uint64(len(s.peersSlice)))
peers := make([]Peer, 0, n)
for len(peers) < n {
index, hasNext := sampler.Next()
if !hasNext {
// We have run out of peers to attempt to sample.
break
}
peer := s.peersSlice[index]
if !precondition(peer) {
continue
}
peers = append(peers, peer)
}
return peers
}
func (s *peerSet) AllInfo() []Info {
peerInfo := make([]Info, len(s.peersSlice))
for i, peer := range s.peersSlice {
peerInfo[i] = peer.Info()
}
return peerInfo
}
func (s *peerSet) Info(nodeIDs []ids.NodeID) []Info {
peerInfo := make([]Info, 0, len(nodeIDs))
for _, nodeID := range nodeIDs {
if peer, ok := s.GetByID(nodeID); ok {
peerInfo = append(peerInfo, peer.Info())
}
}
return peerInfo
}