wantlist.go 4.1 KB
Newer Older
Jeromy's avatar
Jeromy committed
1 2
// package wantlist implements an object for bitswap that contains the keys
// that a given peer wants.
Jeromy's avatar
Jeromy committed
3 4 5 6
package wantlist

import (
	"sort"
7
	"sync"
8

Jeromy's avatar
Jeromy committed
9
	cid "github.com/ipfs/go-cid"
Jeromy's avatar
Jeromy committed
10 11
)

12
type ThreadSafe struct {
Jeromy's avatar
Jeromy committed
13
	lk  sync.RWMutex
Steven Allen's avatar
Steven Allen committed
14
	set map[cid.Cid]*Entry
15 16 17
}

// not threadsafe
Jeromy's avatar
Jeromy committed
18
type Wantlist struct {
Steven Allen's avatar
Steven Allen committed
19
	set map[cid.Cid]*Entry
Jeromy's avatar
Jeromy committed
20 21
}

22
type Entry struct {
23
	Cid      cid.Cid
24
	Priority int
25

Jeromy's avatar
Jeromy committed
26
	SesTrk map[uint64]struct{}
27 28
	// Trash in a book-keeping field
	Trash bool
Jeromy's avatar
Jeromy committed
29 30 31
}

// NewRefEntry creates a new reference tracked wantlist entry
32
func NewRefEntry(c cid.Cid, p int) *Entry {
Jeromy's avatar
Jeromy committed
33 34 35 36 37
	return &Entry{
		Cid:      c,
		Priority: p,
		SesTrk:   make(map[uint64]struct{}),
	}
38 39
}

40
type entrySlice []*Entry
41 42 43 44 45 46 47

func (es entrySlice) Len() int           { return len(es) }
func (es entrySlice) Swap(i, j int)      { es[i], es[j] = es[j], es[i] }
func (es entrySlice) Less(i, j int) bool { return es[i].Priority > es[j].Priority }

func NewThreadSafe() *ThreadSafe {
	return &ThreadSafe{
Steven Allen's avatar
Steven Allen committed
48
		set: make(map[cid.Cid]*Entry),
49 50 51
	}
}

52
func New() *Wantlist {
Jeromy's avatar
Jeromy committed
53
	return &Wantlist{
Steven Allen's avatar
Steven Allen committed
54
		set: make(map[cid.Cid]*Entry),
Jeromy's avatar
Jeromy committed
55 56 57
	}
}

Jeromy's avatar
Jeromy committed
58 59 60 61 62 63 64 65
// Add adds the given cid to the wantlist with the specified priority, governed
// by the session ID 'ses'.  if a cid is added under multiple session IDs, then
// it must be removed by each of those sessions before it is no longer 'in the
// wantlist'. Calls to Add are idempotent given the same arguments. Subsequent
// calls with different values for priority will not update the priority
// TODO: think through priority changes here
// Add returns true if the cid did not exist in the wantlist before this call
// (even if it was under a different session)
66
func (w *ThreadSafe) Add(c cid.Cid, priority int, ses uint64) bool {
67 68
	w.lk.Lock()
	defer w.lk.Unlock()
Steven Allen's avatar
Steven Allen committed
69
	if e, ok := w.set[c]; ok {
Jeromy's avatar
Jeromy committed
70 71 72 73
		e.SesTrk[ses] = struct{}{}
		return false
	}

Steven Allen's avatar
Steven Allen committed
74
	w.set[c] = &Entry{
Jeromy's avatar
Jeromy committed
75 76 77 78 79 80
		Cid:      c,
		Priority: priority,
		SesTrk:   map[uint64]struct{}{ses: struct{}{}},
	}

	return true
81 82
}

83
// AddEntry adds given Entry to the wantlist. For more information see Add method.
Jeromy's avatar
Jeromy committed
84
func (w *ThreadSafe) AddEntry(e *Entry, ses uint64) bool {
85 86
	w.lk.Lock()
	defer w.lk.Unlock()
Steven Allen's avatar
Steven Allen committed
87
	if ex, ok := w.set[e.Cid]; ok {
Jeromy's avatar
Jeromy committed
88 89 90
		ex.SesTrk[ses] = struct{}{}
		return false
	}
Steven Allen's avatar
Steven Allen committed
91
	w.set[e.Cid] = e
Jeromy's avatar
Jeromy committed
92 93
	e.SesTrk[ses] = struct{}{}
	return true
Jeromy's avatar
Jeromy committed
94 95
}

Jeromy's avatar
Jeromy committed
96 97 98 99
// Remove removes the given cid from being tracked by the given session.
// 'true' is returned if this call to Remove removed the final session ID
// tracking the cid. (meaning true will be returned iff this call caused the
// value of 'Contains(c)' to change from true to false)
100
func (w *ThreadSafe) Remove(c cid.Cid, ses uint64) bool {
101 102
	w.lk.Lock()
	defer w.lk.Unlock()
Steven Allen's avatar
Steven Allen committed
103
	e, ok := w.set[c]
Jeromy's avatar
Jeromy committed
104 105 106 107 108 109
	if !ok {
		return false
	}

	delete(e.SesTrk, ses)
	if len(e.SesTrk) == 0 {
Steven Allen's avatar
Steven Allen committed
110
		delete(w.set, c)
Jeromy's avatar
Jeromy committed
111 112 113
		return true
	}
	return false
114 115
}

Jeromy's avatar
Jeromy committed
116 117
// Contains returns true if the given cid is in the wantlist tracked by one or
// more sessions
118
func (w *ThreadSafe) Contains(k cid.Cid) (*Entry, bool) {
119 120
	w.lk.RLock()
	defer w.lk.RUnlock()
Steven Allen's avatar
Steven Allen committed
121
	e, ok := w.set[k]
Jeromy's avatar
Jeromy committed
122
	return e, ok
123 124
}

125
func (w *ThreadSafe) Entries() []*Entry {
126 127
	w.lk.RLock()
	defer w.lk.RUnlock()
128
	es := make([]*Entry, 0, len(w.set))
Jeromy's avatar
Jeromy committed
129 130 131 132
	for _, e := range w.set {
		es = append(es, e)
	}
	return es
133 134
}

135
func (w *ThreadSafe) SortedEntries() []*Entry {
136 137
	es := w.Entries()
	sort.Sort(entrySlice(es))
Jeromy's avatar
Jeromy committed
138
	return es
139 140
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
141 142 143
func (w *ThreadSafe) Len() int {
	w.lk.RLock()
	defer w.lk.RUnlock()
Jeromy's avatar
Jeromy committed
144
	return len(w.set)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
145 146 147 148 149 150
}

func (w *Wantlist) Len() int {
	return len(w.set)
}

151
func (w *Wantlist) Add(c cid.Cid, priority int) bool {
Steven Allen's avatar
Steven Allen committed
152
	if _, ok := w.set[c]; ok {
153
		return false
Jeromy's avatar
Jeromy committed
154
	}
155

Steven Allen's avatar
Steven Allen committed
156
	w.set[c] = &Entry{
157
		Cid:      c,
Jeromy's avatar
Jeromy committed
158
		Priority: priority,
159
	}
160 161

	return true
162 163
}

164
func (w *Wantlist) AddEntry(e *Entry) bool {
Steven Allen's avatar
Steven Allen committed
165
	if _, ok := w.set[e.Cid]; ok {
166
		return false
Jeromy's avatar
Jeromy committed
167
	}
Steven Allen's avatar
Steven Allen committed
168
	w.set[e.Cid] = e
169
	return true
Jeromy's avatar
Jeromy committed
170 171
}

172
func (w *Wantlist) Remove(c cid.Cid) bool {
Steven Allen's avatar
Steven Allen committed
173
	_, ok := w.set[c]
174
	if !ok {
175
		return false
176 177
	}

Steven Allen's avatar
Steven Allen committed
178
	delete(w.set, c)
179
	return true
Jeromy's avatar
Jeromy committed
180 181
}

Steven Allen's avatar
Steven Allen committed
182 183
func (w *Wantlist) Contains(c cid.Cid) (*Entry, bool) {
	e, ok := w.set[c]
184
	return e, ok
Jeromy's avatar
Jeromy committed
185 186
}

187
func (w *Wantlist) Entries() []*Entry {
188
	es := make([]*Entry, 0, len(w.set))
Jeromy's avatar
Jeromy committed
189 190 191 192 193 194
	for _, e := range w.set {
		es = append(es, e)
	}
	return es
}

195
func (w *Wantlist) SortedEntries() []*Entry {
196 197
	es := w.Entries()
	sort.Sort(entrySlice(es))
Jeromy's avatar
Jeromy committed
198 199
	return es
}