forked from nictuku/dht
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpeer_store.go
More file actions
140 lines (125 loc) · 3.57 KB
/
peer_store.go
File metadata and controls
140 lines (125 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package dht
// TODO: Cleanup stale peer contacts.
import (
"container/ring"
log "github.com/golang/glog"
"github.com/golang/groupcache/lru"
)
// For the inner map, the key address in binary form. value=ignored.
type peerContactsSet struct {
set map[string]bool
// Needed to ensure different peers are returned each time.
ring *ring.Ring
}
// next returns up to 8 peer contacts, if available. Further calls will return a
// different set of contacts, if possible.
func (p *peerContactsSet) next() []string {
count := kNodes
if count > len(p.set) {
count = len(p.set)
}
x := make([]string, 0, count)
var next *ring.Ring
for i := 0; i < count; i++ {
next = p.ring.Next()
x = append(x, next.Value.(string))
p.ring = next
}
return x
}
// put adds a peerContact to an infohash contacts set. peerContact must be a binary encoded contact
// address where the first four bytes form the IP and the last byte is the port. IPv6 addresses are
// not currently supported. peerContact with less than 6 bytes will not be stored.
func (p *peerContactsSet) put(peerContact string) bool {
if len(peerContact) < 6 {
return false
}
if ok := p.set[peerContact]; ok {
return false
}
p.set[peerContact] = true
r := &ring.Ring{Value: peerContact}
if p.ring == nil {
p.ring = r
} else {
p.ring.Link(r)
}
return true
}
// Size is the number of contacts known for an infohash.
func (p *peerContactsSet) Size() int {
return len(p.set)
}
func newPeerStore(maxInfoHashes, maxInfoHashPeers int) *peerStore {
return &peerStore{
infoHashPeers: lru.New(maxInfoHashes),
localActiveDownloads: make(map[InfoHash]bool),
maxInfoHashes: maxInfoHashes,
maxInfoHashPeers: maxInfoHashPeers,
}
}
type peerStore struct {
// cache of peers for infohashes. Each key is an infohash and the
// values are peerContactsSet.
infoHashPeers *lru.Cache
// infoHashes for which we are peers.
localActiveDownloads map[InfoHash]bool
maxInfoHashes int
maxInfoHashPeers int
}
func (h *peerStore) get(ih InfoHash) *peerContactsSet {
c, ok := h.infoHashPeers.Get(string(ih))
if !ok {
return nil
}
contacts := c.(*peerContactsSet)
return contacts
}
// count shows the number of known peers for the given infohash.
func (h *peerStore) count(ih InfoHash) int {
peers := h.get(ih)
if peers == nil {
return 0
}
return len(peers.set)
}
// peerContacts returns a random set of 8 peers for the ih InfoHash.
func (h *peerStore) peerContacts(ih InfoHash) []string {
peers := h.get(ih)
if peers == nil {
return nil
}
return peers.next()
}
// addContact as a peer for the provided ih. Returns true if the contact was
// added, false otherwise (e.g: already present, or invalid).
func (h *peerStore) addContact(ih InfoHash, peerContact string) bool {
var peers *peerContactsSet
p, ok := h.infoHashPeers.Get(string(ih))
if ok {
var okType bool
peers, okType = p.(*peerContactsSet)
if okType && peers != nil {
if len(peers.set) >= h.maxInfoHashPeers {
// Already tracking too many peers for this infohash.
// TODO: Use a circular buffer and discard
// other contacts.
return false
}
h.infoHashPeers.Add(string(ih), peers)
return peers.put(peerContact)
}
// Bogus peer contacts, reset them.
}
peers = &peerContactsSet{set: make(map[string]bool)}
h.infoHashPeers.Add(string(ih), peers)
return peers.put(peerContact)
}
func (h *peerStore) addLocalDownload(ih InfoHash) {
h.localActiveDownloads[ih] = true
}
func (h *peerStore) hasLocalDownload(ih InfoHash) bool {
_, ok := h.localActiveDownloads[ih]
log.V(3).Infof("hasLocalDownload for %x: %v", ih, ok)
return ok
}