2018-10-08 16:09:37 +01:00
|
|
|
// Copyright (C) 2018 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package kademlia
|
|
|
|
|
|
|
|
import (
|
2018-10-24 13:24:47 +01:00
|
|
|
"container/heap"
|
2018-10-08 16:09:37 +01:00
|
|
|
"math/big"
|
2018-11-01 17:03:46 +00:00
|
|
|
"sync"
|
2018-10-08 16:09:37 +01:00
|
|
|
|
2018-10-24 13:24:47 +01:00
|
|
|
"storj.io/storj/pkg/dht"
|
2018-10-08 16:09:37 +01:00
|
|
|
"storj.io/storj/pkg/pb"
|
|
|
|
)
|
|
|
|
|
2018-11-01 17:03:46 +00:00
|
|
|
// XorQueue is a priority queue where the priority is key XOR distance
|
2018-10-24 13:24:47 +01:00
|
|
|
type XorQueue struct {
|
|
|
|
maxLen int
|
2018-11-01 17:03:46 +00:00
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
added map[string]int
|
|
|
|
items items
|
2018-10-24 13:24:47 +01:00
|
|
|
}
|
|
|
|
|
2018-11-01 17:03:46 +00:00
|
|
|
// NewXorQueue returns a items with priority based on XOR from targetBytes
|
2018-10-24 13:24:47 +01:00
|
|
|
func NewXorQueue(size int) *XorQueue {
|
2018-11-01 17:03:46 +00:00
|
|
|
return &XorQueue{
|
|
|
|
items: make(items, 0, size),
|
|
|
|
added: make(map[string]int),
|
|
|
|
maxLen: size,
|
|
|
|
}
|
2018-10-24 13:24:47 +01:00
|
|
|
}
|
|
|
|
|
2018-11-01 17:03:46 +00:00
|
|
|
// Insert adds Nodes onto the queue
|
2018-10-24 13:24:47 +01:00
|
|
|
func (x *XorQueue) Insert(target dht.NodeID, nodes []*pb.Node) {
|
2018-11-01 17:03:46 +00:00
|
|
|
x.mu.Lock()
|
|
|
|
defer x.mu.Unlock()
|
|
|
|
|
|
|
|
unique := nodes[:0]
|
|
|
|
for _, node := range nodes {
|
|
|
|
nodeID := node.GetId()
|
|
|
|
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 dht.NodeID, node *pb.Node, limit int) bool {
|
|
|
|
x.mu.Lock()
|
|
|
|
defer x.mu.Unlock()
|
|
|
|
|
|
|
|
nodeID := node.GetId()
|
|
|
|
if x.added[nodeID] >= limit {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
x.added[nodeID]++
|
|
|
|
|
|
|
|
x.insert(target, []*pb.Node{node})
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert must hold lock while adding
|
|
|
|
func (x *XorQueue) insert(target dht.NodeID, nodes []*pb.Node) {
|
2018-10-24 13:24:47 +01:00
|
|
|
targetBytes := new(big.Int).SetBytes(target.Bytes())
|
2018-11-01 17:03:46 +00:00
|
|
|
// insert new nodes
|
2018-10-24 13:24:47 +01:00
|
|
|
for _, node := range nodes {
|
|
|
|
heap.Push(&x.items, &item{
|
|
|
|
value: node,
|
|
|
|
priority: new(big.Int).Xor(targetBytes, new(big.Int).SetBytes([]byte(node.GetId()))),
|
|
|
|
})
|
|
|
|
}
|
2018-11-01 17:03:46 +00:00
|
|
|
// resize down if we grew too big
|
2018-10-24 13:24:47 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-01 17:03:46 +00:00
|
|
|
// Closest removes the closest priority node from the queue
|
2018-10-24 13:24:47 +01:00
|
|
|
func (x *XorQueue) Closest() (*pb.Node, big.Int) {
|
2018-11-01 17:03:46 +00:00
|
|
|
x.mu.Lock()
|
|
|
|
defer x.mu.Unlock()
|
|
|
|
|
2018-10-24 13:24:47 +01:00
|
|
|
if x.Len() == 0 {
|
|
|
|
return nil, big.Int{}
|
|
|
|
}
|
|
|
|
item := *(heap.Pop(&x.items).(*item))
|
|
|
|
return item.value, *item.priority
|
|
|
|
}
|
|
|
|
|
2018-11-01 17:03:46 +00:00
|
|
|
// Len returns the number of items in the queue
|
2018-10-24 13:24:47 +01:00
|
|
|
func (x *XorQueue) Len() int {
|
|
|
|
return x.items.Len()
|
|
|
|
}
|
|
|
|
|
|
|
|
// An item is something we manage in a priority queue.
|
|
|
|
type item struct {
|
2018-10-08 16:09:37 +01:00
|
|
|
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.
|
|
|
|
}
|
|
|
|
|
2018-10-24 13:24:47 +01:00
|
|
|
// A items implements heap.Interface and holds items.
|
|
|
|
type items []*item
|
2018-10-08 16:09:37 +01:00
|
|
|
|
|
|
|
// Len returns the length of the priority queue
|
2018-10-24 13:24:47 +01:00
|
|
|
func (items items) Len() int { return len(items) }
|
2018-10-08 16:09:37 +01:00
|
|
|
|
|
|
|
// Less does what you would think
|
2018-10-24 13:24:47 +01:00
|
|
|
func (items items) Less(i, j int) bool {
|
2018-10-08 16:09:37 +01:00
|
|
|
// this sorts the nodes where the node popped has the closest location
|
2018-10-24 13:24:47 +01:00
|
|
|
return items[i].priority.Cmp(items[j].priority) < 0
|
2018-10-08 16:09:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Swap swaps two ints
|
2018-10-24 13:24:47 +01:00
|
|
|
func (items items) Swap(i, j int) {
|
|
|
|
items[i], items[j] = items[j], items[i]
|
|
|
|
items[i].index = i
|
|
|
|
items[j].index = j
|
2018-10-08 16:09:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Push adds an item to the top of the queue
|
|
|
|
// must call heap.fix to resort
|
2018-10-24 13:24:47 +01:00
|
|
|
func (items *items) Push(x interface{}) {
|
|
|
|
n := len(*items)
|
|
|
|
item := x.(*item)
|
2018-10-08 16:09:37 +01:00
|
|
|
item.index = n
|
2018-10-24 13:24:47 +01:00
|
|
|
*items = append(*items, item)
|
2018-10-08 16:09:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pop returns the item with the lowest priority
|
2018-10-24 13:24:47 +01:00
|
|
|
func (items *items) Pop() interface{} {
|
|
|
|
old := *items
|
2018-10-08 16:09:37 +01:00
|
|
|
n := len(old)
|
|
|
|
item := old[n-1]
|
|
|
|
item.index = -1 // for safety
|
2018-10-24 13:24:47 +01:00
|
|
|
*items = old[0 : n-1]
|
2018-10-08 16:09:37 +01:00
|
|
|
return item
|
|
|
|
}
|