2a0c4e60d2
* preparing for use of `customtype` gogo extension with `NodeID` type * review changes * preparing for use of `customtype` gogo extension with `NodeID` type * review changes * wip * tests passing * wip fixing tests * more wip test fixing * remove NodeIDList from proto files * linter fixes * linter fixes * linter/review fixes * more freaking linter fixes * omg just kill me - linterrrrrrrr * travis linter, i will muder you and your family in your sleep * goimports everything - burn in hell travis * goimports update * go mod tidy
156 lines
3.6 KiB
Go
156 lines
3.6 KiB
Go
// Copyright (C) 2018 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package kademlia
|
|
|
|
import (
|
|
"container/heap"
|
|
"math/big"
|
|
"sync"
|
|
|
|
"storj.io/storj/pkg/pb"
|
|
"storj.io/storj/pkg/storj"
|
|
)
|
|
|
|
// XorQueue is a priority queue where the priority is key XOR distance
|
|
type XorQueue struct {
|
|
maxLen int
|
|
|
|
mu sync.Mutex
|
|
added map[storj.NodeID]int
|
|
items items
|
|
}
|
|
|
|
// NewXorQueue returns a items with priority based on XOR from targetBytes
|
|
func NewXorQueue(size int) *XorQueue {
|
|
return &XorQueue{
|
|
items: make(items, 0, size),
|
|
added: make(map[storj.NodeID]int),
|
|
maxLen: size,
|
|
}
|
|
}
|
|
|
|
// Insert adds Nodes onto the queue
|
|
func (x *XorQueue) Insert(target storj.NodeID, nodes []*pb.Node) {
|
|
x.mu.Lock()
|
|
defer x.mu.Unlock()
|
|
|
|
unique := nodes[:0]
|
|
for _, node := range nodes {
|
|
nodeID := node.Id
|
|
if _, added := x.added[nodeID]; !added {
|
|
x.added[nodeID]++
|
|
unique = append(unique, node)
|
|
}
|
|
}
|
|
|
|
x.insert(target, unique)
|
|
}
|
|
|
|
// Reinsert adds a Nodes onto the queue if it's been added >= limit times previously
|
|
func (x *XorQueue) Reinsert(target storj.NodeID, node *pb.Node, limit int) bool {
|
|
x.mu.Lock()
|
|
defer x.mu.Unlock()
|
|
|
|
nodeID := node.Id
|
|
if x.added[nodeID] >= limit {
|
|
return false
|
|
}
|
|
x.added[nodeID]++
|
|
|
|
x.insert(target, []*pb.Node{node})
|
|
return true
|
|
}
|
|
|
|
func reverse(b []byte) (r []byte) {
|
|
for _, v := range b {
|
|
r = append([]byte{v}, r...)
|
|
}
|
|
return r
|
|
}
|
|
|
|
// insert must hold lock while adding
|
|
func (x *XorQueue) insert(target storj.NodeID, nodes []*pb.Node) {
|
|
targetBytes := new(big.Int).SetBytes(reverse(target.Bytes()))
|
|
// insert new nodes
|
|
for _, node := range nodes {
|
|
heap.Push(&x.items, &item{
|
|
value: node,
|
|
priority: new(big.Int).Xor(targetBytes, new(big.Int).SetBytes(reverse(node.Id.Bytes()))),
|
|
})
|
|
}
|
|
// resize down if we grew too big
|
|
if x.items.Len() > x.maxLen {
|
|
olditems := x.items
|
|
x.items = items{}
|
|
for i := 0; i < x.maxLen && len(olditems) > 0; i++ {
|
|
item := heap.Pop(&olditems)
|
|
heap.Push(&x.items, item)
|
|
}
|
|
heap.Init(&x.items)
|
|
}
|
|
}
|
|
|
|
// Closest removes the closest priority node from the queue
|
|
func (x *XorQueue) Closest() (*pb.Node, big.Int) {
|
|
x.mu.Lock()
|
|
defer x.mu.Unlock()
|
|
|
|
if x.Len() == 0 {
|
|
return nil, big.Int{}
|
|
}
|
|
item := *(heap.Pop(&x.items).(*item))
|
|
return item.value, *item.priority
|
|
}
|
|
|
|
// Len returns the number of items in the queue
|
|
func (x *XorQueue) Len() int {
|
|
return x.items.Len()
|
|
}
|
|
|
|
// An item is something we manage in a priority queue.
|
|
type item struct {
|
|
value *pb.Node // The value of the item; arbitrary.
|
|
priority *big.Int // The priority of the item in the queue.
|
|
// The index is needed by update and is maintained by the heap.Interface methods.
|
|
index int // The index of the item in the heap.
|
|
}
|
|
|
|
// A items implements heap.Interface and holds items.
|
|
type items []*item
|
|
|
|
// Len returns the length of the priority queue
|
|
func (items items) Len() int { return len(items) }
|
|
|
|
// Less does what you would think
|
|
func (items items) Less(i, j int) bool {
|
|
// this sorts the nodes where the node popped has the closest location
|
|
return items[i].priority.Cmp(items[j].priority) < 0
|
|
}
|
|
|
|
// Swap swaps two ints
|
|
func (items items) Swap(i, j int) {
|
|
items[i], items[j] = items[j], items[i]
|
|
items[i].index = i
|
|
items[j].index = j
|
|
}
|
|
|
|
// Push adds an item to the top of the queue
|
|
// must call heap.fix to resort
|
|
func (items *items) Push(x interface{}) {
|
|
n := len(*items)
|
|
item := x.(*item)
|
|
item.index = n
|
|
*items = append(*items, item)
|
|
}
|
|
|
|
// Pop returns the item with the lowest priority
|
|
func (items *items) Pop() interface{} {
|
|
old := *items
|
|
n := len(old)
|
|
item := old[n-1]
|
|
item.index = -1 // for safety
|
|
*items = old[0 : n-1]
|
|
return item
|
|
}
|