github.com/ethersphere/bee/v2@v2.2.0/pkg/topology/pslice/pslice.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pslice 6 7 import ( 8 "sync" 9 10 "github.com/ethersphere/bee/v2/pkg/swarm" 11 "github.com/ethersphere/bee/v2/pkg/topology" 12 ) 13 14 // PSlice maintains a list of addresses, indexing them by their different proximity orders. 15 type PSlice struct { 16 peers [][]swarm.Address // the slice of peers 17 baseBytes []byte 18 mu sync.RWMutex 19 maxBins int 20 } 21 22 // New creates a new PSlice. 23 func New(maxBins int, base swarm.Address) *PSlice { 24 return &PSlice{ 25 peers: make([][]swarm.Address, maxBins), 26 baseBytes: base.Bytes(), 27 maxBins: maxBins, 28 } 29 } 30 31 // Add a peer at a certain PO. 32 func (s *PSlice) Add(addrs ...swarm.Address) { 33 s.mu.Lock() 34 defer s.mu.Unlock() 35 36 // bypass unnecessary allocations below if address count is one 37 if len(addrs) == 1 { 38 addr := addrs[0] 39 po := s.po(addr.Bytes()) 40 if e, _ := s.index(addr, po); e { 41 return 42 } 43 s.peers[po] = append(s.peers[po], addr) 44 return 45 } 46 47 addrPo := make([]uint8, 0, len(addrs)) 48 binChange := make([]int, s.maxBins) 49 exists := make([]bool, len(addrs)) 50 51 for i, addr := range addrs { 52 po := s.po(addr.Bytes()) 53 addrPo = append(addrPo, po) 54 if e, _ := s.index(addr, po); e { 55 exists[i] = true 56 } else { 57 binChange[po]++ 58 } 59 } 60 61 for i, count := range binChange { 62 peers := s.peers[i] 63 if count > 0 && cap(peers) < len(peers)+count { 64 newPeers := make([]swarm.Address, len(peers), len(peers)+count) 65 copy(newPeers, peers) 66 s.peers[i] = newPeers 67 } 68 } 69 70 for i, addr := range addrs { 71 if exists[i] { 72 continue 73 } 74 75 po := addrPo[i] 76 s.peers[po] = append(s.peers[po], addr) 77 } 78 } 79 80 // iterates over all peers from deepest bin to shallowest. 81 func (s *PSlice) EachBin(pf topology.EachPeerFunc) error { 82 83 for i := s.maxBins - 1; i >= 0; i-- { 84 85 s.mu.RLock() 86 peers := s.peers[i] 87 s.mu.RUnlock() 88 89 for _, peer := range peers { 90 stop, next, err := pf(peer, uint8(i)) 91 if err != nil { 92 return err 93 } 94 if stop { 95 return nil 96 } 97 if next { 98 break 99 } 100 } 101 } 102 103 return nil 104 } 105 106 // EachBinRev iterates over all peers from shallowest bin to deepest. 107 func (s *PSlice) EachBinRev(pf topology.EachPeerFunc) error { 108 109 for i := 0; i < s.maxBins; i++ { 110 111 s.mu.RLock() 112 peers := s.peers[i] 113 s.mu.RUnlock() 114 115 for _, peer := range peers { 116 117 stop, next, err := pf(peer, uint8(i)) 118 if err != nil { 119 return err 120 } 121 if stop { 122 return nil 123 } 124 if next { 125 break 126 } 127 } 128 } 129 return nil 130 } 131 132 func (s *PSlice) BinSize(bin uint8) int { 133 134 if int(bin) >= s.maxBins { 135 return 0 136 } 137 138 s.mu.RLock() 139 defer s.mu.RUnlock() 140 141 return len(s.peers[bin]) 142 } 143 144 func (s *PSlice) BinPeers(bin uint8) []swarm.Address { 145 146 if int(bin) >= s.maxBins { 147 return nil 148 } 149 150 s.mu.RLock() 151 defer s.mu.RUnlock() 152 153 ret := make([]swarm.Address, len(s.peers[bin])) 154 copy(ret, s.peers[bin]) 155 156 return ret 157 } 158 159 // Length returns the number of peers in the Pslice. 160 func (s *PSlice) Length() int { 161 s.mu.RLock() 162 defer s.mu.RUnlock() 163 164 var ret int 165 166 for _, peers := range s.peers { 167 ret += len(peers) 168 } 169 170 return ret 171 } 172 173 // ShallowestEmpty returns the shallowest empty bin if one exists. 174 // If such bin does not exists, returns true as bool value. 175 func (s *PSlice) ShallowestEmpty() (uint8, bool) { 176 s.mu.RLock() 177 defer s.mu.RUnlock() 178 179 for i, peers := range s.peers { 180 181 if len(peers) == 0 { 182 return uint8(i), false 183 } 184 185 } 186 187 return 0, true 188 } 189 190 // Exists checks if a peer exists. 191 func (s *PSlice) Exists(addr swarm.Address) bool { 192 s.mu.RLock() 193 defer s.mu.RUnlock() 194 195 e, _ := s.index(addr, s.po(addr.Bytes())) 196 return e 197 } 198 199 // Remove a peer at a certain PO. 200 func (s *PSlice) Remove(addr swarm.Address) { 201 s.mu.Lock() 202 defer s.mu.Unlock() 203 204 po := s.po(addr.Bytes()) 205 206 e, i := s.index(addr, po) 207 if !e { 208 return 209 } 210 211 // Since order of elements does not matter, the optimized removing process 212 // below replaces the index to be removed with the last element of the array, 213 // and shortens the array by one. 214 215 // make copy of the bin slice with one fewer element 216 newLength := len(s.peers[po]) - 1 217 cpy := make([]swarm.Address, newLength) 218 copy(cpy, s.peers[po][:newLength]) 219 220 // if the index is the last element, then assign slice and return early 221 if i == newLength { 222 s.peers[po] = cpy 223 return 224 } 225 226 // replace index being removed with last element 227 lastItem := s.peers[po][newLength] 228 cpy[i] = lastItem 229 230 // assign the copy with the index removed back to the original array 231 s.peers[po] = cpy 232 } 233 234 func (s *PSlice) po(peer []byte) uint8 { 235 po := swarm.Proximity(s.baseBytes, peer) 236 if int(po) >= s.maxBins { 237 return uint8(s.maxBins) - 1 238 } 239 return po 240 } 241 242 // index returns if a peer exists and the index in the slice. 243 func (s *PSlice) index(addr swarm.Address, po uint8) (bool, int) { 244 idx := swarm.IndexOfAddress(s.peers[po], addr) 245 if idx != -1 { 246 return true, idx 247 } 248 return false, 0 249 }