diff --git a/lib/routing.go b/lib/routing.go index b82d2e1..995e235 100644 --- a/lib/routing.go +++ b/lib/routing.go @@ -6,7 +6,10 @@ import ( "log" "bytes" "fmt" - ) + "container/vector" + "sort" + "time" +) type RoutingTable interface { SeeHost(h *Host) @@ -14,28 +17,39 @@ type RoutingTable interface { GetString() string // may return "", subject to change GetHTML() string + GetClosest(t string, n uint) *RTHostList } type BRoutingTable struct { - id string - buckets [](*Bucket) + id string + buckets [](*Bucket) maxbucket int - seehost chan *Host - quitchan chan bool - stringchan chan chan string - htmlchan chan chan string + seehost chan *Host + quitchan chan bool + stringchan chan chan string + htmlchan chan chan string + closestchan chan *closestReq - Log bool + Log bool logger *log.Logger } +type closestReq struct { + target string + n uint + retchan chan *RTHostList +} + type RTHost struct { Host *Host Distance Distance } + +type RTHostList struct { + v *vector.Vector } type Bucket struct { @@ -43,20 +57,63 @@ type Bucket struct { } +func NewRTHostList() *RTHostList { + r := new(RTHostList) + r.v = new(vector.Vector) + return r +} + +func (l *RTHostList) Push(h *RTHost) { l.v.Push(h) } + +func (l *RTHostList) Len() int { + el := l.v.Len() + //fmt.Printf("len called: %d\n", el) + return el +} + +func (l *RTHostList) Less(i, j int) bool { + //fmt.Printf("less called %d < %d\n", i, j) + return l.v.At(i).(*RTHost).Distance.Less(l.v.At(j).(*RTHost).Distance) +} + +func (l *RTHostList) Swap(i, j int) { + //fmt.Printf("swap called %d < %d\n", i, j) + l.v.Swap(i, j) +} + +func (l *RTHostList) Slice(i, j int) *RTHostList { + r := new(RTHostList) + r.v = l.v.Slice(i, j) + return r +} + +func (l *RTHostList) Data() []*RTHost { + v := l.v + vl := v.Len() + rs := make([]*RTHost, vl) + for i := 0; i < vl; i++ { + rs[i] = v.At(i).(*RTHost) + } + return rs +} + func NewBRoutingTable(id string) (rt *BRoutingTable) { rt = new(BRoutingTable) rt.id = id - rt.buckets = make([](*Bucket), HASHLEN * 8) + rt.buckets = make([](*Bucket), HASHLEN*8) firstbucket := new(Bucket) - firstbucket.hosts = make([](*RTHost), K + 1)[0:0] + firstbucket.hosts = make([](*RTHost), K+1)[0:0] rt.buckets[0] = firstbucket rt.maxbucket = 0 rt.seehost = make(chan *Host) rt.quitchan = make(chan bool) + rt.stringchan = make(chan chan string) + rt.htmlchan = make(chan chan string) + rt.closestchan = make(chan *closestReq) rt.Log = true rt.logger = log.New(os.Stdout, nil, "BRoutingTable: ", 0) @@ -70,22 +127,22 @@ func NewBRoutingTable(id string) (rt *BRoutingTable) { func (rt *BRoutingTable) main() { for { select { - case h := <- rt.seehost: + case h := <-rt.seehost: rt.seeHost(h) - case <- rt.quitchan: + case <-rt.quitchan: return - case r := <- rt.stringchan: + case r := <-rt.stringchan: r <- rt.string() - case r := <- rt.htmlchan: + case r := <-rt.htmlchan: r <- rt.html() + case r := <-rt.closestchan: + r.retchan <- rt.getClosest(r.target, r.n) } } } -func (rt *BRoutingTable) SeeHost(h *Host) { - rt.seehost <- h -} +func (rt *BRoutingTable) SeeHost(h *Host) { rt.seehost <- h } func (rt *BRoutingTable) seeHost(h *Host) { @@ -93,7 +150,7 @@ func (rt *BRoutingTable) seeHost(h *Host) { rt.logger.Logf("see host: %v dist %v\n", h, XOR(h.Id, rt.id)[0:5]) } bucketno, pos, maxbucketno := rt.findHost(h) - /*rt.logger.Logf("is in %d/%d maxbucketno %d\n", bucketno, pos, maxbucketno) + /*rt.logger.Logf("is in %d/%d maxbucketno %d\n", bucketno, pos, maxbucketno) rt.logger.Logf("dist %v to %v\n", XOR(h.Id, rt.id), h) rt.logger.Logf("own id is %x\n", rt.id) rt.logger.Logf("h.Id is %x", h.Id)*/ @@ -112,7 +169,7 @@ func (rt *BRoutingTable) seeHost(h *Host) { if rt.Log { rt.logger.Logf("bucket %d not full yet -> inserting\n", bucketno) } - bucket.hosts = bucket.hosts[0:hl+1] + bucket.hosts = bucket.hosts[0 : hl+1] bucket.hosts[hl] = rthost } else { if maxbucketno == bucketno { @@ -121,7 +178,7 @@ func (rt *BRoutingTable) seeHost(h *Host) { } } else { rt.newBucket() - bucket.hosts = bucket.hosts[0:hl+1] + bucket.hosts = bucket.hosts[0 : hl+1] bucket.hosts[hl] = rthost rt.balanceleftright(uint(bucketno)) } @@ -138,16 +195,16 @@ func (rt *BRoutingTable) seeHost(h *Host) { func (rt *BRoutingTable) newBucket() *Bucket { - if rt.maxbucket == (HASHLEN * 8 - 1) { + if rt.maxbucket == (HASHLEN*8 - 1) { return nil } b := new(Bucket) - b.hosts = make([](*RTHost), K + 1)[0:0] + b.hosts = make([](*RTHost), K+1)[0:0] rt.maxbucket++ rt.buckets[rt.maxbucket] = b - + return b } @@ -173,7 +230,7 @@ func (rt *BRoutingTable) findHost(h *Host) (bucketno, pos, maxbucketno int) { hosts := rt.buckets[bucketno].hosts for i, rh := range hosts { pos = i - if rh.host.Id == h.Id { + if rh.Host.Id == h.Id { return } } @@ -189,14 +246,14 @@ func (rt *BRoutingTable) balanceleftright(lefti uint) { left := rt.buckets[lefti] right := rt.buckets[righti] - newleft := make([](*RTHost), K + 1) - newright := make([](*RTHost), K + 1) + newleft := make([](*RTHost), K+1) + newright := make([](*RTHost), K+1) nleft := 0 nright := 0 for _, rth := range left.hosts { - if BucketNo(rth.distance) == lefti { + if BucketNo(rth.Distance) == lefti { newleft[nleft] = rth nleft++ } else { @@ -206,7 +263,7 @@ func (rt *BRoutingTable) balanceleftright(lefti uint) { } for _, rth := range right.hosts { - if BucketNo(rth.distance) == lefti { + if BucketNo(rth.Distance) == lefti { newleft[nleft] = rth nleft++ } else { @@ -216,13 +273,13 @@ func (rt *BRoutingTable) balanceleftright(lefti uint) { } rt.logger.Logf("rebalanced to %d/%d\n", nleft, nright) - + newleft = newleft[0:nleft] newright = newright[0:nright] left.hosts = newleft right.hosts = newright - + fmt.Printf("%v", rt) } @@ -231,14 +288,14 @@ func (rt *BRoutingTable) balanceleftright(lefti uint) { func (rt *BRoutingTable) string() string { buf := bytes.NewBuffer(nil) - buf.WriteString("BRoutingTable ===>\n") + buf.WriteString("BRoutingTable
\n") for b := 0; b <= rt.maxbucket; b++ { buf.WriteString(fmt.Sprintf("bucket %d\n", b)) for _, rth := range rt.buckets[b].hosts { buf.WriteString(fmt.Sprintf("\t%x | %v @ %v\n", rth.Host.Id, XOR(rth.Host.Id, rt.id)[0:5], rth.Host.Addr)) } } - + buf.WriteString("<===\n") return buf.String() } @@ -264,7 +321,7 @@ func (rt *BRoutingTable) html() string { } buf.WriteString("") - + buf.WriteString("") return buf.String() } @@ -274,12 +331,88 @@ func (rt *BRoutingTable) html() string { func (rt *BRoutingTable) GetString() string { r := make(chan string) rt.stringchan <- r - return <- r + return <-r } // goroutine safe. not for internal use! func (rt *BRoutingTable) GetHTML() string { r := make(chan string) rt.htmlchan <- r - return <- r + return <-r +} + +// goroutine safe. not for internal use! +func (rt *BRoutingTable) GetClosest(t string, n uint) *RTHostList { + r := make(chan *RTHostList) + rt.closestchan <- &closestReq{t, n, r} + return <-r +} + +// TODO: wtf behaviour? gets the closests n nodes to us close to t???? +// not goroutine safe. for internal use! +func (rt *BRoutingTable) getClosest(t string, n uint) *RTHostList { + //rt.logger.Logf("looking for %x dist %v
\n", t, XOR(rt.id, t)) + //d := XOR(t, rt.id) + t1 := time.Nanoseconds() + h := new(Host) + h.Id = t + + bucketno, _, _ := rt.findHost(h) + + if bucketno < 0 { + return nil + } + + buckets := rt.buckets + hl := NewRTHostList() + + var rn uint = 0 + + for _, el := range buckets[bucketno].hosts { + hl.Push(el) + rn++ + } + + rt.logger.Logf("match bucket: rn %d\n", rn) + + // init for for loop + cangoup := bucketno < rt.maxbucket + cangodown := bucketno > 0 + delta := 1 + + for (rn < n) && (cangoup || cangodown) { + cangoup = (bucketno + delta) < rt.maxbucket + cangodown = (bucketno - delta) > 0 + //rt.logger.Logf("bucketno %d rt.maxbucket %d\n", bucketno, rt.maxbucket) + //rt.logger.Logf("bucketno - delta: %d. > 0: %v", bucketno-delta, (bucketno-delta) > 0) + //rt.logger.Logf("round delta %d cangoup %v cangodown %v\n", delta, cangoup, cangodown) + + if cangoup { + for _, el := range buckets[bucketno+delta].hosts { + hl.Push(el) + rn++ + } + } + + if cangodown { + for _, el := range buckets[bucketno-delta].hosts { + hl.Push(el) + rn++ + } + } + + delta++ + } + + //rt.logger.Logf("sorting %v...\n", hl) + sort.Sort(hl) + //rt.logger.Logf("done!\n") + if rn >= n { + hl = hl.Slice(0, int(n-1)) + } + + t2 := time.Nanoseconds() + rt.logger.Logf("finding closest took %d us\n", (t2-t1)/1000) + + return hl }