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.

119 lines
2.7 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
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. type PeerSet struct {
  16. mtx sync.Mutex
  17. lookup map[string]*peerSetItem
  18. list []*Peer
  19. }
  20. type peerSetItem struct {
  21. peer *Peer
  22. index int
  23. }
  24. // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items.
  25. func NewPeerSet() *PeerSet {
  26. return &PeerSet{
  27. lookup: make(map[string]*peerSetItem),
  28. list: make([]*Peer, 0, 256),
  29. }
  30. }
  31. // Add adds the peer to the PeerSet.
  32. // It returns ErrSwitchDuplicatePeer if the peer is already present.
  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. // Has returns true iff the PeerSet contains
  47. // the peer referred to by this peerKey.
  48. func (ps *PeerSet) Has(peerKey string) bool {
  49. ps.mtx.Lock()
  50. _, ok := ps.lookup[peerKey]
  51. ps.mtx.Unlock()
  52. return ok
  53. }
  54. // Get looks up a peer by the provided peerKey.
  55. func (ps *PeerSet) Get(peerKey string) *Peer {
  56. ps.mtx.Lock()
  57. defer ps.mtx.Unlock()
  58. item, ok := ps.lookup[peerKey]
  59. if ok {
  60. return item.peer
  61. } else {
  62. return nil
  63. }
  64. }
  65. // Remove discards peer by its Key, if the peer was previously memoized.
  66. func (ps *PeerSet) Remove(peer *Peer) {
  67. ps.mtx.Lock()
  68. defer ps.mtx.Unlock()
  69. item := ps.lookup[peer.Key]
  70. if item == nil {
  71. return
  72. }
  73. index := item.index
  74. // Create a new copy of the list but with one less item.
  75. // (we must copy because we'll be mutating the list).
  76. newList := make([]*Peer, len(ps.list)-1)
  77. copy(newList, ps.list)
  78. // If it's the last peer, that's an easy special case.
  79. if index == len(ps.list)-1 {
  80. ps.list = newList
  81. delete(ps.lookup, peer.Key)
  82. return
  83. }
  84. // Replace the popped item with the last item in the old list.
  85. lastPeer := ps.list[len(ps.list)-1]
  86. lastPeerKey := lastPeer.Key
  87. lastPeerItem := ps.lookup[lastPeerKey]
  88. newList[index] = lastPeer
  89. lastPeerItem.index = index
  90. ps.list = newList
  91. delete(ps.lookup, peer.Key)
  92. }
  93. // Size returns the number of unique items in the peerSet.
  94. func (ps *PeerSet) Size() int {
  95. ps.mtx.Lock()
  96. defer ps.mtx.Unlock()
  97. return len(ps.list)
  98. }
  99. // List returns the threadsafe list of peers.
  100. func (ps *PeerSet) List() []*Peer {
  101. ps.mtx.Lock()
  102. defer ps.mtx.Unlock()
  103. return ps.list
  104. }