|
|
|
@ -8,6 +8,7 @@ package discover |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"crypto/rand" |
|
|
|
|
"encoding/binary" |
|
|
|
|
"net" |
|
|
|
|
"sort" |
|
|
|
|
"sync" |
|
|
|
@ -90,10 +91,58 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Self returns the local node.
|
|
|
|
|
// The returned node should not be modified by the caller.
|
|
|
|
|
func (tab *Table) Self() *Node { |
|
|
|
|
return tab.self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ReadRandomNodes fills the given slice with random nodes from the
|
|
|
|
|
// table. It will not write the same node more than once. The nodes in
|
|
|
|
|
// the slice are copies and can be modified by the caller.
|
|
|
|
|
func (tab *Table) ReadRandomNodes(buf []*Node) (n int) { |
|
|
|
|
tab.mutex.Lock() |
|
|
|
|
defer tab.mutex.Unlock() |
|
|
|
|
// TODO: tree-based buckets would help here
|
|
|
|
|
// Find all non-empty buckets and get a fresh slice of their entries.
|
|
|
|
|
var buckets [][]*Node |
|
|
|
|
for _, b := range tab.buckets { |
|
|
|
|
if len(b.entries) > 0 { |
|
|
|
|
buckets = append(buckets, b.entries[:]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if len(buckets) == 0 { |
|
|
|
|
return 0 |
|
|
|
|
} |
|
|
|
|
// Shuffle the buckets.
|
|
|
|
|
for i := uint32(len(buckets)) - 1; i > 0; i-- { |
|
|
|
|
j := randUint(i) |
|
|
|
|
buckets[i], buckets[j] = buckets[j], buckets[i] |
|
|
|
|
} |
|
|
|
|
// Move head of each bucket into buf, removing buckets that become empty.
|
|
|
|
|
var i, j int |
|
|
|
|
for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { |
|
|
|
|
b := buckets[j] |
|
|
|
|
buf[i] = &(*b[0]) |
|
|
|
|
buckets[j] = b[1:] |
|
|
|
|
if len(b) == 1 { |
|
|
|
|
buckets = append(buckets[:j], buckets[j+1:]...) |
|
|
|
|
} |
|
|
|
|
if len(buckets) == 0 { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return i + 1 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func randUint(max uint32) uint32 { |
|
|
|
|
if max == 0 { |
|
|
|
|
return 0 |
|
|
|
|
} |
|
|
|
|
var b [4]byte |
|
|
|
|
rand.Read(b[:]) |
|
|
|
|
return binary.BigEndian.Uint32(b[:]) % max |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Close terminates the network listener and flushes the node database.
|
|
|
|
|
func (tab *Table) Close() { |
|
|
|
|
tab.net.close() |
|
|
|
|