forked from TrueCloudLab/neoneo-go
156 lines
3.6 KiB
Go
156 lines
3.6 KiB
Go
|
package peermgr
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"sort"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/CityOfZion/neo-go/pkg/wire/util"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
//ErrCacheLimit is returned when the cache limit is reached
|
||
|
ErrCacheLimit = errors.New("nomore items can be added to the cache")
|
||
|
|
||
|
//ErrNoItems is returned when pickItem is called and there are no items in the cache
|
||
|
ErrNoItems = errors.New("there are no items in the cache")
|
||
|
|
||
|
//ErrDuplicateItem is returned when you try to add the same item, more than once to the cache
|
||
|
ErrDuplicateItem = errors.New("this item is already in the cache")
|
||
|
)
|
||
|
|
||
|
//BlockInfo holds the necessary information that the cache needs
|
||
|
// to sort and store block requests
|
||
|
type BlockInfo struct {
|
||
|
BlockHash util.Uint256
|
||
|
BlockIndex uint32
|
||
|
}
|
||
|
|
||
|
// Equals returns true if two blockInfo objects
|
||
|
// have the same hash and the same index
|
||
|
func (bi *BlockInfo) Equals(other BlockInfo) bool {
|
||
|
return bi.BlockHash.Equals(other.BlockHash) && bi.BlockIndex == other.BlockIndex
|
||
|
}
|
||
|
|
||
|
// indexSorter sorts the blockInfos by blockIndex.
|
||
|
type indexSorter []BlockInfo
|
||
|
|
||
|
func (is indexSorter) Len() int { return len(is) }
|
||
|
func (is indexSorter) Swap(i, j int) { is[i], is[j] = is[j], is[i] }
|
||
|
func (is indexSorter) Less(i, j int) bool { return is[i].BlockIndex < is[j].BlockIndex }
|
||
|
|
||
|
//blockCache will cache any pending block requests
|
||
|
// for the node when there are no available nodes
|
||
|
type blockCache struct {
|
||
|
cacheLimit int
|
||
|
cacheLock sync.Mutex
|
||
|
cache []BlockInfo
|
||
|
}
|
||
|
|
||
|
func newBlockCache(cacheLimit int) *blockCache {
|
||
|
return &blockCache{
|
||
|
cache: make([]BlockInfo, 0, cacheLimit),
|
||
|
cacheLimit: cacheLimit,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (bc *blockCache) addBlockInfo(bi BlockInfo) error {
|
||
|
if bc.cacheLen() == bc.cacheLimit {
|
||
|
return ErrCacheLimit
|
||
|
}
|
||
|
|
||
|
bc.cacheLock.Lock()
|
||
|
defer bc.cacheLock.Unlock()
|
||
|
|
||
|
// Check for duplicates. slice will always be small so a simple for loop will work
|
||
|
for _, bInfo := range bc.cache {
|
||
|
if bInfo.Equals(bi) {
|
||
|
return ErrDuplicateItem
|
||
|
}
|
||
|
}
|
||
|
bc.cache = append(bc.cache, bi)
|
||
|
|
||
|
sort.Sort(indexSorter(bc.cache))
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (bc *blockCache) addBlockInfos(bis []BlockInfo) error {
|
||
|
|
||
|
if len(bis)+bc.cacheLen() > bc.cacheLimit {
|
||
|
return errors.New("too many items to add, this will exceed the cache limit")
|
||
|
}
|
||
|
|
||
|
for _, bi := range bis {
|
||
|
err := bc.addBlockInfo(bi)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (bc *blockCache) cacheLen() int {
|
||
|
bc.cacheLock.Lock()
|
||
|
defer bc.cacheLock.Unlock()
|
||
|
return len(bc.cache)
|
||
|
}
|
||
|
|
||
|
func (bc *blockCache) pickFirstItem() (BlockInfo, error) {
|
||
|
return bc.pickItem(0)
|
||
|
}
|
||
|
|
||
|
func (bc *blockCache) pickAllItems() ([]BlockInfo, error) {
|
||
|
|
||
|
numOfItems := bc.cacheLen()
|
||
|
|
||
|
items := make([]BlockInfo, 0, numOfItems)
|
||
|
|
||
|
for i := 0; i < numOfItems; i++ {
|
||
|
bi, err := bc.pickFirstItem()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
items = append(items, bi)
|
||
|
}
|
||
|
return items, nil
|
||
|
}
|
||
|
|
||
|
func (bc *blockCache) pickItem(i uint) (BlockInfo, error) {
|
||
|
if bc.cacheLen() < 1 {
|
||
|
return BlockInfo{}, ErrNoItems
|
||
|
}
|
||
|
|
||
|
if i >= uint(bc.cacheLen()) {
|
||
|
return BlockInfo{}, errors.New("index out of range")
|
||
|
}
|
||
|
|
||
|
bc.cacheLock.Lock()
|
||
|
defer bc.cacheLock.Unlock()
|
||
|
|
||
|
item := bc.cache[i]
|
||
|
bc.cache = append(bc.cache[:i], bc.cache[i+1:]...)
|
||
|
return item, nil
|
||
|
}
|
||
|
|
||
|
func (bc *blockCache) removeHash(hashToRemove util.Uint256) error {
|
||
|
index, err := bc.findHash(hashToRemove)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
_, err = bc.pickItem(uint(index))
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func (bc *blockCache) findHash(hashToFind util.Uint256) (int, error) {
|
||
|
bc.cacheLock.Lock()
|
||
|
defer bc.cacheLock.Unlock()
|
||
|
for i, bInfo := range bc.cache {
|
||
|
if bInfo.BlockHash.Equals(hashToFind) {
|
||
|
return i, nil
|
||
|
}
|
||
|
}
|
||
|
return -1, errors.New("hash cannot be found in the cache")
|
||
|
}
|