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  }