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.

147 lines
3.3 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. if ps.hasIP(peer.RemoteIP()) {
  42. return ErrSwitchDuplicatePeerIP{peer.RemoteIP()}
  43. }
  44. index := len(ps.list)
  45. // Appending is safe even with other goroutines
  46. // iterating over the ps.list slice.
  47. ps.list = append(ps.list, peer)
  48. ps.lookup[peer.ID()] = &peerSetItem{peer, index}
  49. return nil
  50. }
  51. // Has returns true iff the PeerSet contains
  52. // the peer referred to by this peerKey.
  53. func (ps *PeerSet) Has(peerKey ID) bool {
  54. ps.mtx.Lock()
  55. _, ok := ps.lookup[peerKey]
  56. ps.mtx.Unlock()
  57. return ok
  58. }
  59. // HasIP returns true if the PeerSet contains the peer referred to by this IP
  60. // address.
  61. func (ps *PeerSet) HasIP(peerIP net.IP) bool {
  62. ps.mtx.Lock()
  63. defer ps.mtx.Unlock()
  64. return ps.hasIP(peerIP)
  65. }
  66. // hasIP does not acquire a lock so it can be used in public methods which
  67. // already lock.
  68. func (ps *PeerSet) hasIP(peerIP net.IP) bool {
  69. for _, item := range ps.lookup {
  70. if item.peer.RemoteIP().Equal(peerIP) {
  71. return true
  72. }
  73. }
  74. return false
  75. }
  76. // Get looks up a peer by the provided peerKey.
  77. func (ps *PeerSet) Get(peerKey ID) Peer {
  78. ps.mtx.Lock()
  79. defer ps.mtx.Unlock()
  80. item, ok := ps.lookup[peerKey]
  81. if ok {
  82. return item.peer
  83. }
  84. return nil
  85. }
  86. // Remove discards peer by its Key, if the peer was previously memoized.
  87. func (ps *PeerSet) Remove(peer Peer) {
  88. ps.mtx.Lock()
  89. defer ps.mtx.Unlock()
  90. item := ps.lookup[peer.ID()]
  91. if item == nil {
  92. return
  93. }
  94. index := item.index
  95. // Create a new copy of the list but with one less item.
  96. // (we must copy because we'll be mutating the list).
  97. newList := make([]Peer, len(ps.list)-1)
  98. copy(newList, ps.list)
  99. // If it's the last peer, that's an easy special case.
  100. if index == len(ps.list)-1 {
  101. ps.list = newList
  102. delete(ps.lookup, peer.ID())
  103. return
  104. }
  105. // Replace the popped item with the last item in the old list.
  106. lastPeer := ps.list[len(ps.list)-1]
  107. lastPeerKey := lastPeer.ID()
  108. lastPeerItem := ps.lookup[lastPeerKey]
  109. newList[index] = lastPeer
  110. lastPeerItem.index = index
  111. ps.list = newList
  112. delete(ps.lookup, peer.ID())
  113. }
  114. // Size returns the number of unique items in the peerSet.
  115. func (ps *PeerSet) Size() int {
  116. ps.mtx.Lock()
  117. defer ps.mtx.Unlock()
  118. return len(ps.list)
  119. }
  120. // List returns the threadsafe list of peers.
  121. func (ps *PeerSet) List() []Peer {
  122. ps.mtx.Lock()
  123. defer ps.mtx.Unlock()
  124. return ps.list
  125. }