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.

113 lines
2.3 KiB

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. func NewPeerSet() *PeerSet {
  25. return &PeerSet{
  26. lookup: make(map[string]*peerSetItem),
  27. list: make([]*Peer, 0, 256),
  28. }
  29. }
  30. // Returns false if peer with key (PubKeyEd25519) is already set
  31. func (ps *PeerSet) Add(peer *Peer) error {
  32. ps.mtx.Lock()
  33. defer ps.mtx.Unlock()
  34. if ps.lookup[peer.Key] != nil {
  35. return ErrSwitchDuplicatePeer
  36. }
  37. index := len(ps.list)
  38. // Appending is safe even with other goroutines
  39. // iterating over the ps.list slice.
  40. ps.list = append(ps.list, peer)
  41. ps.lookup[peer.Key] = &peerSetItem{peer, index}
  42. return nil
  43. }
  44. func (ps *PeerSet) Has(peerKey string) bool {
  45. ps.mtx.Lock()
  46. defer ps.mtx.Unlock()
  47. _, ok := ps.lookup[peerKey]
  48. return ok
  49. }
  50. func (ps *PeerSet) Get(peerKey string) *Peer {
  51. ps.mtx.Lock()
  52. defer ps.mtx.Unlock()
  53. item, ok := ps.lookup[peerKey]
  54. if ok {
  55. return item.peer
  56. } else {
  57. return nil
  58. }
  59. }
  60. func (ps *PeerSet) Remove(peer *Peer) {
  61. ps.mtx.Lock()
  62. defer ps.mtx.Unlock()
  63. item := ps.lookup[peer.Key]
  64. if item == nil {
  65. return
  66. }
  67. index := item.index
  68. // Copy the list but without the last element.
  69. // (we must copy because we're mutating the list)
  70. newList := make([]*Peer, len(ps.list)-1)
  71. copy(newList, ps.list)
  72. // If it's the last peer, that's an easy special case.
  73. if index == len(ps.list)-1 {
  74. ps.list = newList
  75. delete(ps.lookup, peer.Key)
  76. return
  77. }
  78. // Move the last item from ps.list to "index" in list.
  79. lastPeer := ps.list[len(ps.list)-1]
  80. lastPeerKey := lastPeer.Key
  81. lastPeerItem := ps.lookup[lastPeerKey]
  82. newList[index] = lastPeer
  83. lastPeerItem.index = index
  84. ps.list = newList
  85. delete(ps.lookup, peer.Key)
  86. }
  87. func (ps *PeerSet) Size() int {
  88. ps.mtx.Lock()
  89. defer ps.mtx.Unlock()
  90. return len(ps.list)
  91. }
  92. // threadsafe list of peers.
  93. func (ps *PeerSet) List() []*Peer {
  94. ps.mtx.Lock()
  95. defer ps.mtx.Unlock()
  96. return ps.list
  97. }