table.go 4.45 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3
package dht

import (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
4
	"container/list"
5
	"sort"
Jeromy's avatar
Jeromy committed
6
	"sync"
7

8
	peer "github.com/jbenet/go-ipfs/peer"
Jeromy's avatar
Jeromy committed
9
	u "github.com/jbenet/go-ipfs/util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11 12 13 14
)

// RoutingTable defines the routing table.
type RoutingTable struct {

15 16 17
	// ID of the local peer
	local ID

Jeromy's avatar
Jeromy committed
18 19 20
	// Blanket lock, refine later for better performance
	tabLock sync.RWMutex

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21
	// kBuckets define all the fingers to other nodes.
22 23 24 25
	Buckets []*Bucket
	bucketsize int
}

26 27 28 29 30 31
func NewRoutingTable(bucketsize int, local_id ID) *RoutingTable {
	rt := new(RoutingTable)
	rt.Buckets = []*Bucket{new(Bucket)}
	rt.bucketsize = bucketsize
	rt.local = local_id
	return rt
32 33 34 35 36
}

// Update adds or moves the given peer to the front of its respective bucket
// If a peer gets removed from a bucket, it is returned
func (rt *RoutingTable) Update(p *peer.Peer) *peer.Peer {
Jeromy's avatar
Jeromy committed
37 38
	rt.tabLock.Lock()
	defer rt.tabLock.Unlock()
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
	peer_id := convertPeerID(p.ID)
	cpl := xor(peer_id, rt.local).commonPrefixLen()

	b_id := cpl
	if b_id >= len(rt.Buckets) {
		b_id = len(rt.Buckets) - 1
	}

	bucket := rt.Buckets[b_id]
	e := bucket.Find(p.ID)
	if e == nil {
		// New peer, add to bucket
		bucket.PushFront(p)

		// Are we past the max bucket size?
		if bucket.Len() > rt.bucketsize {
			if b_id == len(rt.Buckets) - 1 {
				new_bucket := bucket.Split(b_id, rt.local)
				rt.Buckets = append(rt.Buckets, new_bucket)
58
				if new_bucket.Len() > rt.bucketsize {
59
					// TODO: This is a very rare and annoying case
60 61
					panic("Case not handled.")
				}
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

				// If all elements were on left side of split...
				if bucket.Len() > rt.bucketsize {
					return bucket.PopBack()
				}
			} else {
				// If the bucket cant split kick out least active node
				return bucket.PopBack()
			}
		}
		return nil
	} else {
		// If the peer is already in the table, move it to the front.
		// This signifies that it it "more active" and the less active nodes
		// Will as a result tend towards the back of the list
		bucket.MoveToFront(e)
		return nil
	}
}

// A helper struct to sort peers by their distance to the local node
type peerDistance struct {
	p *peer.Peer
	distance ID
}
87 88

// peerSorterArr implements sort.Interface to sort peers by xor distance
89 90 91 92
type peerSorterArr []*peerDistance
func (p peerSorterArr) Len() int {return len(p)}
func (p peerSorterArr) Swap(a, b int) {p[a],p[b] = p[b],p[a]}
func (p peerSorterArr) Less(a, b int) bool {
93
	return p[a].distance.Less(p[b].distance)
94 95 96
}
//

Jeromy's avatar
Jeromy committed
97
func copyPeersFromList(target ID, peerArr peerSorterArr, peerList *list.List) peerSorterArr {
98
		for e := peerList.Front(); e != nil; e = e.Next() {
99 100 101 102
		p := e.Value.(*peer.Peer)
		p_id := convertPeerID(p.ID)
		pd := peerDistance{
			p: p,
Jeromy's avatar
Jeromy committed
103
			distance: xor(target, p_id),
104 105
		}
		peerArr = append(peerArr, &pd)
106
		if e == nil {
Jeromy's avatar
Jeromy committed
107 108 109
			u.POut("list element was nil.")
			return peerArr
		}
110 111 112 113
	}
	return peerArr
}

114
// Returns a single peer that is nearest to the given ID
115 116
func (rt *RoutingTable) NearestPeer(id ID) *peer.Peer {
	peers := rt.NearestPeers(id, 1)
117 118 119 120 121
	if len(peers) > 0 {
		return peers[0]
	} else {
		return nil
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
122 123
}

124
// Returns a list of the 'count' closest peers to the given ID
125
func (rt *RoutingTable) NearestPeers(id ID, count int) []*peer.Peer {
Jeromy's avatar
Jeromy committed
126 127
	rt.tabLock.RLock()
	defer rt.tabLock.RUnlock()
128 129 130 131 132
	cpl := xor(id, rt.local).commonPrefixLen()

	// Get bucket at cpl index or last bucket
	var bucket *Bucket
	if cpl >= len(rt.Buckets) {
133
		cpl = len(rt.Buckets) - 1
134
	}
135
	bucket = rt.Buckets[cpl]
136

137
	var peerArr peerSorterArr
138
	if bucket.Len() == 0 {
139 140 141 142
		// In the case of an unusual split, one bucket may be empty.
		// if this happens, search both surrounding buckets for nearest peer
		if cpl > 0 {
			plist := (*list.List)(rt.Buckets[cpl - 1])
Jeromy's avatar
Jeromy committed
143
			peerArr = copyPeersFromList(id, peerArr, plist)
144
		}
145

146 147
		if cpl < len(rt.Buckets) - 1 {
			plist := (*list.List)(rt.Buckets[cpl + 1])
Jeromy's avatar
Jeromy committed
148
			peerArr = copyPeersFromList(id, peerArr, plist)
149
		}
150 151
	} else {
		plist := (*list.List)(bucket)
Jeromy's avatar
Jeromy committed
152
		peerArr = copyPeersFromList(id, peerArr, plist)
153 154
	}

155
	// Sort by distance to local peer
156 157 158 159 160 161 162 163
	sort.Sort(peerArr)

	var out []*peer.Peer
	for i := 0; i < count && i < peerArr.Len(); i++ {
		out = append(out, peerArr[i].p)
	}

	return out
164
}
Jeromy's avatar
Jeromy committed
165 166 167 168 169 170 171 172 173

// Returns the total number of peers in the routing table
func (rt *RoutingTable) Size() int {
	var tot int
	for _,buck := range rt.Buckets {
		tot += buck.Len()
	}
	return tot
}
174 175 176 177 178 179 180 181 182 183 184

// NOTE: This is potentially unsafe... use at your own risk
func (rt *RoutingTable) listpeers() []*peer.Peer {
	var peers []*peer.Peer
	for _,buck := range rt.Buckets {
		for e := buck.getIter(); e != nil; e = e.Next() {
			peers = append(peers, e.Value.(*peer.Peer))
		}
	}
	return peers
}