You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
2.4 KiB

9 years ago
9 years ago
9 years ago
  1. package p2p
  2. import (
  3. "sync"
  4. )
  5. // IPeerSet has a (immutable) subset of the methods of PeerSet.
  6. type IPeerSet interface {
  7. Has(key string) bool
  8. Get(key string) *Peer
  9. List() []*Peer
  10. Size() int
  11. }
  12. //-----------------------------------------------------------------------------
  13. // PeerSet is a special structure for keeping a table of peers.
  14. // Iteration over the peers is super fast and thread-safe.
  15. // We also track how many peers per IP range and avoid too many
  16. type PeerSet struct {
  17. mtx sync.Mutex
  18. lookup map[string]*peerSetItem
  19. list []*Peer
  20. }
  21. type peerSetItem struct {
  22. peer *Peer
  23. index int
  24. }
  25. func NewPeerSet() *PeerSet {
  26. return &PeerSet{
  27. lookup: make(map[string]*peerSetItem),
  28. list: make([]*Peer, 0, 256),
  29. }
  30. }
  31. // Returns false if peer with key (PubKeyEd25519) is already in set
  32. // or if we have too many peers from the peer's IP range
  33. func (ps *PeerSet) Add(peer *Peer) error {
  34. ps.mtx.Lock()
  35. defer ps.mtx.Unlock()
  36. if ps.lookup[peer.Key] != nil {
  37. return ErrSwitchDuplicatePeer
  38. }
  39. index := len(ps.list)
  40. // Appending is safe even with other goroutines
  41. // iterating over the ps.list slice.
  42. ps.list = append(ps.list, peer)
  43. ps.lookup[peer.Key] = &peerSetItem{peer, index}
  44. return nil
  45. }
  46. func (ps *PeerSet) Has(peerKey string) bool {
  47. ps.mtx.Lock()
  48. defer ps.mtx.Unlock()
  49. _, ok := ps.lookup[peerKey]
  50. return ok
  51. }
  52. func (ps *PeerSet) Get(peerKey string) *Peer {
  53. ps.mtx.Lock()
  54. defer ps.mtx.Unlock()
  55. item, ok := ps.lookup[peerKey]
  56. if ok {
  57. return item.peer
  58. } else {
  59. return nil
  60. }
  61. }
  62. func (ps *PeerSet) Remove(peer *Peer) {
  63. ps.mtx.Lock()
  64. defer ps.mtx.Unlock()
  65. item := ps.lookup[peer.Key]
  66. if item == nil {
  67. return
  68. }
  69. index := item.index
  70. // Copy the list but without the last element.
  71. // (we must copy because we're mutating the list)
  72. newList := make([]*Peer, len(ps.list)-1)
  73. copy(newList, ps.list)
  74. // If it's the last peer, that's an easy special case.
  75. if index == len(ps.list)-1 {
  76. ps.list = newList
  77. delete(ps.lookup, peer.Key)
  78. return
  79. }
  80. // Move the last item from ps.list to "index" in list.
  81. lastPeer := ps.list[len(ps.list)-1]
  82. lastPeerKey := lastPeer.Key
  83. lastPeerItem := ps.lookup[lastPeerKey]
  84. newList[index] = lastPeer
  85. lastPeerItem.index = index
  86. ps.list = newList
  87. delete(ps.lookup, peer.Key)
  88. }
  89. func (ps *PeerSet) Size() int {
  90. ps.mtx.Lock()
  91. defer ps.mtx.Unlock()
  92. return len(ps.list)
  93. }
  94. // threadsafe list of peers.
  95. func (ps *PeerSet) List() []*Peer {
  96. ps.mtx.Lock()
  97. defer ps.mtx.Unlock()
  98. return ps.list
  99. }