2015-10-25 18:21:51 -07:00
|
|
|
package p2p
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
// IPeerSet has a (immutable) subset of the methods of PeerSet.
|
|
|
|
type IPeerSet interface {
|
2018-01-01 18:27:38 -08:00
|
|
|
Has(key ID) bool
|
2018-04-28 13:45:08 -07:00
|
|
|
HasIP(ip string) bool
|
2018-01-01 18:27:38 -08:00
|
|
|
Get(key ID) Peer
|
2017-09-12 17:49:22 -07:00
|
|
|
List() []Peer
|
2015-10-25 18:21:51 -07:00
|
|
|
Size() int
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// PeerSet is a special structure for keeping a table of peers.
|
|
|
|
// Iteration over the peers is super fast and thread-safe.
|
|
|
|
type PeerSet struct {
|
2018-04-28 13:45:08 -07:00
|
|
|
mtx sync.Mutex
|
|
|
|
lookup map[ID]*peerSetItem
|
|
|
|
lookupIP map[string]struct{}
|
|
|
|
list []Peer
|
2015-10-25 18:21:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type peerSetItem struct {
|
2017-09-12 17:49:22 -07:00
|
|
|
peer Peer
|
2015-10-25 18:21:51 -07:00
|
|
|
index int
|
|
|
|
}
|
|
|
|
|
2017-08-25 18:35:00 -07:00
|
|
|
// NewPeerSet creates a new peerSet with a list of initial capacity of 256 items.
|
2015-10-25 18:21:51 -07:00
|
|
|
func NewPeerSet() *PeerSet {
|
|
|
|
return &PeerSet{
|
2018-04-28 13:45:08 -07:00
|
|
|
lookup: make(map[ID]*peerSetItem),
|
|
|
|
lookupIP: make(map[string]struct{}),
|
|
|
|
list: make([]Peer, 0, 256),
|
2015-10-25 18:21:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-06 13:40:21 -07:00
|
|
|
// Add adds the peer to the PeerSet.
|
|
|
|
// It returns ErrSwitchDuplicatePeer if the peer is already present.
|
2017-09-12 17:49:22 -07:00
|
|
|
func (ps *PeerSet) Add(peer Peer) error {
|
2015-10-25 18:21:51 -07:00
|
|
|
ps.mtx.Lock()
|
|
|
|
defer ps.mtx.Unlock()
|
2018-01-01 18:27:38 -08:00
|
|
|
if ps.lookup[peer.ID()] != nil {
|
2018-04-28 13:45:08 -07:00
|
|
|
return ErrSwitchDuplicatePeerID{peer.ID()}
|
|
|
|
}
|
|
|
|
if _, ok := ps.lookupIP[peer.RemoteIP()]; ok {
|
|
|
|
return ErrSwitchDuplicatePeerIP{peer.RemoteIP()}
|
2015-10-25 18:21:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
index := len(ps.list)
|
|
|
|
// Appending is safe even with other goroutines
|
|
|
|
// iterating over the ps.list slice.
|
|
|
|
ps.list = append(ps.list, peer)
|
2018-01-01 18:27:38 -08:00
|
|
|
ps.lookup[peer.ID()] = &peerSetItem{peer, index}
|
2018-04-28 13:45:08 -07:00
|
|
|
ps.lookupIP[peer.RemoteIP()] = struct{}{}
|
2015-10-25 18:21:51 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-06 13:40:21 -07:00
|
|
|
// Has returns true iff the PeerSet contains
|
2017-08-25 18:35:00 -07:00
|
|
|
// the peer referred to by this peerKey.
|
2018-01-01 18:27:38 -08:00
|
|
|
func (ps *PeerSet) Has(peerKey ID) bool {
|
2015-10-25 18:21:51 -07:00
|
|
|
ps.mtx.Lock()
|
|
|
|
_, ok := ps.lookup[peerKey]
|
2017-08-25 18:35:00 -07:00
|
|
|
ps.mtx.Unlock()
|
2015-10-25 18:21:51 -07:00
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2018-04-28 13:45:08 -07:00
|
|
|
// HasIP returns true iff the PeerSet contains
|
|
|
|
// the peer referred to by this IP address.
|
|
|
|
func (ps *PeerSet) HasIP(peerIP string) bool {
|
|
|
|
ps.mtx.Lock()
|
|
|
|
_, ok := ps.lookupIP[peerIP]
|
|
|
|
ps.mtx.Unlock()
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2017-08-25 18:35:00 -07:00
|
|
|
// Get looks up a peer by the provided peerKey.
|
2018-01-01 18:27:38 -08:00
|
|
|
func (ps *PeerSet) Get(peerKey ID) Peer {
|
2015-10-25 18:21:51 -07:00
|
|
|
ps.mtx.Lock()
|
|
|
|
defer ps.mtx.Unlock()
|
|
|
|
item, ok := ps.lookup[peerKey]
|
|
|
|
if ok {
|
|
|
|
return item.peer
|
|
|
|
}
|
2018-04-02 01:21:17 -07:00
|
|
|
return nil
|
2015-10-25 18:21:51 -07:00
|
|
|
}
|
|
|
|
|
2017-08-25 18:35:00 -07:00
|
|
|
// Remove discards peer by its Key, if the peer was previously memoized.
|
2017-09-12 17:49:22 -07:00
|
|
|
func (ps *PeerSet) Remove(peer Peer) {
|
2015-10-25 18:21:51 -07:00
|
|
|
ps.mtx.Lock()
|
|
|
|
defer ps.mtx.Unlock()
|
2018-04-28 13:45:08 -07:00
|
|
|
delete(ps.lookupIP[peer.RemoteIP()])
|
2018-01-01 18:27:38 -08:00
|
|
|
item := ps.lookup[peer.ID()]
|
2015-10-25 18:21:51 -07:00
|
|
|
if item == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
index := item.index
|
2017-08-25 18:35:00 -07:00
|
|
|
// Create a new copy of the list but with one less item.
|
|
|
|
// (we must copy because we'll be mutating the list).
|
2017-09-12 17:49:22 -07:00
|
|
|
newList := make([]Peer, len(ps.list)-1)
|
2015-10-25 18:21:51 -07:00
|
|
|
copy(newList, ps.list)
|
|
|
|
// If it's the last peer, that's an easy special case.
|
|
|
|
if index == len(ps.list)-1 {
|
|
|
|
ps.list = newList
|
2018-01-01 18:27:38 -08:00
|
|
|
delete(ps.lookup, peer.ID())
|
2015-10-25 18:21:51 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-08-25 18:35:00 -07:00
|
|
|
// Replace the popped item with the last item in the old list.
|
2015-10-25 18:21:51 -07:00
|
|
|
lastPeer := ps.list[len(ps.list)-1]
|
2018-01-01 18:27:38 -08:00
|
|
|
lastPeerKey := lastPeer.ID()
|
2015-10-25 18:21:51 -07:00
|
|
|
lastPeerItem := ps.lookup[lastPeerKey]
|
|
|
|
newList[index] = lastPeer
|
|
|
|
lastPeerItem.index = index
|
|
|
|
ps.list = newList
|
2018-01-01 18:27:38 -08:00
|
|
|
delete(ps.lookup, peer.ID())
|
2015-10-25 18:21:51 -07:00
|
|
|
}
|
|
|
|
|
2017-08-25 18:35:00 -07:00
|
|
|
// Size returns the number of unique items in the peerSet.
|
2015-10-25 18:21:51 -07:00
|
|
|
func (ps *PeerSet) Size() int {
|
|
|
|
ps.mtx.Lock()
|
|
|
|
defer ps.mtx.Unlock()
|
|
|
|
return len(ps.list)
|
|
|
|
}
|
|
|
|
|
2017-08-25 18:35:00 -07:00
|
|
|
// List returns the threadsafe list of peers.
|
2017-09-12 17:49:22 -07:00
|
|
|
func (ps *PeerSet) List() []Peer {
|
2015-10-25 18:21:51 -07:00
|
|
|
ps.mtx.Lock()
|
|
|
|
defer ps.mtx.Unlock()
|
|
|
|
return ps.list
|
|
|
|
}
|