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.

144 lines
3.2 KiB

9 years ago
6 years ago
9 years ago
6 years ago
7 years ago
9 years ago
6 years ago
9 years ago
7 years ago
9 years ago
9 years ago
6 years ago
9 years ago
6 years ago
7 years ago
9 years ago
6 years ago
9 years ago
9 years ago
9 years ago
9 years ago
6 years ago
6 years ago
6 years ago
9 years ago
9 years ago
7 years ago
9 years ago
6 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
  1. package p2p
  2. import (
  3. "net"
  4. "sync"
  5. )
  6. // IPeerSet has a (immutable) subset of the methods of PeerSet.
  7. type IPeerSet interface {
  8. Has(key ID) bool
  9. HasIP(ip net.IP) bool
  10. Get(key ID) Peer
  11. List() []Peer
  12. Size() int
  13. }
  14. //-----------------------------------------------------------------------------
  15. // PeerSet is a special structure for keeping a table of peers.
  16. // Iteration over the peers is super fast and thread-safe.
  17. type PeerSet struct {
  18. mtx sync.Mutex
  19. lookup map[ID]*peerSetItem
  20. list []Peer
  21. }
  22. type peerSetItem struct {
  23. peer Peer
  24. index int
  25. }
  26. // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items.
  27. func NewPeerSet() *PeerSet {
  28. return &PeerSet{
  29. lookup: make(map[ID]*peerSetItem),
  30. list: make([]Peer, 0, 256),
  31. }
  32. }
  33. // Add adds the peer to the PeerSet.
  34. // It returns an error carrying the reason, if the peer is already present.
  35. func (ps *PeerSet) Add(peer Peer) error {
  36. ps.mtx.Lock()
  37. defer ps.mtx.Unlock()
  38. if ps.lookup[peer.ID()] != nil {
  39. return ErrSwitchDuplicatePeerID{peer.ID()}
  40. }
  41. index := len(ps.list)
  42. // Appending is safe even with other goroutines
  43. // iterating over the ps.list slice.
  44. ps.list = append(ps.list, peer)
  45. ps.lookup[peer.ID()] = &peerSetItem{peer, index}
  46. return nil
  47. }
  48. // Has returns true if the set contains the peer referred to by this
  49. // peerKey, otherwise false.
  50. func (ps *PeerSet) Has(peerKey ID) bool {
  51. ps.mtx.Lock()
  52. _, ok := ps.lookup[peerKey]
  53. ps.mtx.Unlock()
  54. return ok
  55. }
  56. // HasIP returns true if the set contains the peer referred to by this IP
  57. // address, otherwise false.
  58. func (ps *PeerSet) HasIP(peerIP net.IP) bool {
  59. ps.mtx.Lock()
  60. defer ps.mtx.Unlock()
  61. return ps.hasIP(peerIP)
  62. }
  63. // hasIP does not acquire a lock so it can be used in public methods which
  64. // already lock.
  65. func (ps *PeerSet) hasIP(peerIP net.IP) bool {
  66. for _, item := range ps.lookup {
  67. if item.peer.RemoteIP().Equal(peerIP) {
  68. return true
  69. }
  70. }
  71. return false
  72. }
  73. // Get looks up a peer by the provided peerKey. Returns nil if peer is not
  74. // found.
  75. func (ps *PeerSet) Get(peerKey ID) Peer {
  76. ps.mtx.Lock()
  77. defer ps.mtx.Unlock()
  78. item, ok := ps.lookup[peerKey]
  79. if ok {
  80. return item.peer
  81. }
  82. return nil
  83. }
  84. // Remove discards peer by its Key, if the peer was previously memoized.
  85. func (ps *PeerSet) Remove(peer Peer) {
  86. ps.mtx.Lock()
  87. defer ps.mtx.Unlock()
  88. item := ps.lookup[peer.ID()]
  89. if item == nil {
  90. return
  91. }
  92. index := item.index
  93. // Create a new copy of the list but with one less item.
  94. // (we must copy because we'll be mutating the list).
  95. newList := make([]Peer, len(ps.list)-1)
  96. copy(newList, ps.list)
  97. // If it's the last peer, that's an easy special case.
  98. if index == len(ps.list)-1 {
  99. ps.list = newList
  100. delete(ps.lookup, peer.ID())
  101. return
  102. }
  103. // Replace the popped item with the last item in the old list.
  104. lastPeer := ps.list[len(ps.list)-1]
  105. lastPeerKey := lastPeer.ID()
  106. lastPeerItem := ps.lookup[lastPeerKey]
  107. newList[index] = lastPeer
  108. lastPeerItem.index = index
  109. ps.list = newList
  110. delete(ps.lookup, peer.ID())
  111. }
  112. // Size returns the number of unique items in the peerSet.
  113. func (ps *PeerSet) Size() int {
  114. ps.mtx.Lock()
  115. defer ps.mtx.Unlock()
  116. return len(ps.list)
  117. }
  118. // List returns the threadsafe list of peers.
  119. func (ps *PeerSet) List() []Peer {
  120. ps.mtx.Lock()
  121. defer ps.mtx.Unlock()
  122. return ps.list
  123. }