package main
|
|
|
|
import (
|
|
"math/bits"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
alpha = 3 // 'alpha', max parallalel calls
|
|
B = 20 // 'B', 160 bits, ID length
|
|
KBucketSize = 20 // 'K', bucket size
|
|
)
|
|
|
|
type ListedNode struct {
|
|
ID ID
|
|
Addr string
|
|
}
|
|
|
|
type Node struct {
|
|
ID ID
|
|
KBuckets [B * 8][]ListedNode
|
|
}
|
|
|
|
func (n Node) String() string {
|
|
r := "Node ID: " + n.ID.String() + ", KBuckets:\n"
|
|
for i, kb := range n.KBuckets {
|
|
if len(kb) > 0 {
|
|
r += " KBucket " + strconv.Itoa(i) + "\n"
|
|
for _, e := range kb {
|
|
r += " " + e.ID.String() + "\n"
|
|
}
|
|
}
|
|
}
|
|
return r
|
|
}
|
|
|
|
func NewNode() (Node, error) {
|
|
// TODO if node already has id, import it
|
|
id, err := NewID()
|
|
if err != nil {
|
|
return Node{}, err
|
|
}
|
|
|
|
var n Node
|
|
n.ID = id
|
|
return n, nil
|
|
}
|
|
|
|
func LoadNode(idStr string) (Node, error) {
|
|
id, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
|
if err != nil {
|
|
return Node{}, err
|
|
}
|
|
var n Node
|
|
n.ID = id
|
|
return n, nil
|
|
}
|
|
|
|
func (n Node) KBucket(o ID) int {
|
|
d := n.ID.Distance(o)
|
|
return kBucketByDistance(d[:])
|
|
|
|
}
|
|
|
|
func kBucketByDistance(b []byte) int {
|
|
for i := 0; i < B; i++ {
|
|
for a := b[i] ^ 0; a != 0; a &= a - 1 {
|
|
return (B-1-i)*8 + (7 - bits.TrailingZeros8(bits.Reverse8(uint8(a))))
|
|
}
|
|
}
|
|
return (B*8 - 1) - (B*8 - 1)
|
|
}
|
|
|
|
func (n *Node) Update(o ListedNode) {
|
|
k := n.KBucket(o.ID)
|
|
kb := n.KBuckets[k]
|
|
if len(kb) >= KBucketSize {
|
|
// TODO ping the kb[0], and if no response, delete it, and add the current o (ID)
|
|
return
|
|
}
|
|
// check that is not already in the list
|
|
exist, _ := existsInListedNodes(n.KBuckets[k], o)
|
|
if exist {
|
|
// update position to the bottom
|
|
return
|
|
}
|
|
// not exists, add it to the kBucket
|
|
n.KBuckets[k] = append(n.KBuckets[k], o)
|
|
return
|
|
}
|
|
|
|
func existsInListedNodes(lns []ListedNode, ln ListedNode) (bool, int) {
|
|
for i, v := range lns {
|
|
if v.ID.Equal(ln.ID) {
|
|
return true, i
|
|
}
|
|
}
|
|
return false, 0
|
|
}
|