all repos

rss-tools @ master

get rss feed from sources that(i need and) dont provide one

rss-tools/vendor/go.etcd.io/bbolt/internal/freelist/hashmap.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
we're vendoring now, 7 days ago
1
package freelist
2
3
import (
4
	"fmt"
5
	"reflect"
6
	"sort"
7
8
	"go.etcd.io/bbolt/internal/common"
9
)
10
11
// pidSet holds the set of starting pgids which have the same span size
12
type pidSet map[common.Pgid]struct{}
13
14
type hashMap struct {
15
	*shared
16
17
	freePagesCount uint64                 // count of free pages(hashmap version)
18
	freemaps       map[uint64]pidSet      // key is the size of continuous pages(span), value is a set which contains the starting pgids of same size
19
	forwardMap     map[common.Pgid]uint64 // key is start pgid, value is its span size
20
	backwardMap    map[common.Pgid]uint64 // key is end pgid, value is its span size
21
}
22
23
func (f *hashMap) Init(pgids common.Pgids) {
24
	// reset the counter when freelist init
25
	f.freePagesCount = 0
26
	f.freemaps = make(map[uint64]pidSet)
27
	f.forwardMap = make(map[common.Pgid]uint64)
28
	f.backwardMap = make(map[common.Pgid]uint64)
29
30
	if len(pgids) == 0 {
31
		return
32
	}
33
34
	if !sort.SliceIsSorted([]common.Pgid(pgids), func(i, j int) bool { return pgids[i] < pgids[j] }) {
35
		panic("pgids not sorted")
36
	}
37
38
	size := uint64(1)
39
	start := pgids[0]
40
41
	for i := 1; i < len(pgids); i++ {
42
		// continuous page
43
		if pgids[i] == pgids[i-1]+1 {
44
			size++
45
		} else {
46
			f.addSpan(start, size)
47
48
			size = 1
49
			start = pgids[i]
50
		}
51
	}
52
53
	// init the tail
54
	if size != 0 && start != 0 {
55
		f.addSpan(start, size)
56
	}
57
58
	f.reindex()
59
}
60
61
func (f *hashMap) Allocate(txid common.Txid, n int) common.Pgid {
62
	if n == 0 {
63
		return 0
64
	}
65
66
	// if we have a exact size match just return short path
67
	if bm, ok := f.freemaps[uint64(n)]; ok {
68
		for pid := range bm {
69
			// remove the span
70
			f.delSpan(pid, uint64(n))
71
72
			f.allocs[pid] = txid
73
74
			for i := common.Pgid(0); i < common.Pgid(n); i++ {
75
				delete(f.cache, pid+i)
76
			}
77
			return pid
78
		}
79
	}
80
81
	// lookup the map to find larger span
82
	for size, bm := range f.freemaps {
83
		if size < uint64(n) {
84
			continue
85
		}
86
87
		for pid := range bm {
88
			// remove the initial
89
			f.delSpan(pid, size)
90
91
			f.allocs[pid] = txid
92
93
			remain := size - uint64(n)
94
95
			// add remain span
96
			f.addSpan(pid+common.Pgid(n), remain)
97
98
			for i := common.Pgid(0); i < common.Pgid(n); i++ {
99
				delete(f.cache, pid+i)
100
			}
101
			return pid
102
		}
103
	}
104
105
	return 0
106
}
107
108
func (f *hashMap) FreeCount() int {
109
	common.Verify(func() {
110
		expectedFreePageCount := f.hashmapFreeCountSlow()
111
		common.Assert(int(f.freePagesCount) == expectedFreePageCount,
112
			"freePagesCount (%d) is out of sync with free pages map (%d)", f.freePagesCount, expectedFreePageCount)
113
	})
114
	return int(f.freePagesCount)
115
}
116
117
func (f *hashMap) freePageIds() common.Pgids {
118
	count := f.FreeCount()
119
	if count == 0 {
120
		return common.Pgids{}
121
	}
122
123
	m := make([]common.Pgid, 0, count)
124
125
	startPageIds := make([]common.Pgid, 0, len(f.forwardMap))
126
	for k := range f.forwardMap {
127
		startPageIds = append(startPageIds, k)
128
	}
129
	sort.Sort(common.Pgids(startPageIds))
130
131
	for _, start := range startPageIds {
132
		if size, ok := f.forwardMap[start]; ok {
133
			for i := 0; i < int(size); i++ {
134
				m = append(m, start+common.Pgid(i))
135
			}
136
		}
137
	}
138
139
	return m
140
}
141
142
func (f *hashMap) hashmapFreeCountSlow() int {
143
	count := 0
144
	for _, size := range f.forwardMap {
145
		count += int(size)
146
	}
147
	return count
148
}
149
150
func (f *hashMap) addSpan(start common.Pgid, size uint64) {
151
	f.backwardMap[start-1+common.Pgid(size)] = size
152
	f.forwardMap[start] = size
153
	if _, ok := f.freemaps[size]; !ok {
154
		f.freemaps[size] = make(map[common.Pgid]struct{})
155
	}
156
157
	f.freemaps[size][start] = struct{}{}
158
	f.freePagesCount += size
159
}
160
161
func (f *hashMap) delSpan(start common.Pgid, size uint64) {
162
	delete(f.forwardMap, start)
163
	delete(f.backwardMap, start+common.Pgid(size-1))
164
	delete(f.freemaps[size], start)
165
	if len(f.freemaps[size]) == 0 {
166
		delete(f.freemaps, size)
167
	}
168
	f.freePagesCount -= size
169
}
170
171
func (f *hashMap) mergeSpans(ids common.Pgids) {
172
	common.Verify(func() {
173
		ids1Freemap := f.idsFromFreemaps()
174
		ids2Forward := f.idsFromForwardMap()
175
		ids3Backward := f.idsFromBackwardMap()
176
177
		if !reflect.DeepEqual(ids1Freemap, ids2Forward) {
178
			panic(fmt.Sprintf("Detected mismatch, f.freemaps: %v, f.forwardMap: %v", f.freemaps, f.forwardMap))
179
		}
180
		if !reflect.DeepEqual(ids1Freemap, ids3Backward) {
181
			panic(fmt.Sprintf("Detected mismatch, f.freemaps: %v, f.backwardMap: %v", f.freemaps, f.backwardMap))
182
		}
183
184
		sort.Sort(ids)
185
		prev := common.Pgid(0)
186
		for _, id := range ids {
187
			// The ids shouldn't have duplicated free ID.
188
			if prev == id {
189
				panic(fmt.Sprintf("detected duplicated free ID: %d in ids: %v", id, ids))
190
			}
191
			prev = id
192
193
			// The ids shouldn't have any overlap with the existing f.freemaps.
194
			if _, ok := ids1Freemap[id]; ok {
195
				panic(fmt.Sprintf("detected overlapped free page ID: %d between ids: %v and existing f.freemaps: %v", id, ids, f.freemaps))
196
			}
197
		}
198
	})
199
	for _, id := range ids {
200
		// try to see if we can merge and update
201
		f.mergeWithExistingSpan(id)
202
	}
203
}
204
205
// mergeWithExistingSpan merges pid to the existing free spans, try to merge it backward and forward
206
func (f *hashMap) mergeWithExistingSpan(pid common.Pgid) {
207
	prev := pid - 1
208
	next := pid + 1
209
210
	preSize, mergeWithPrev := f.backwardMap[prev]
211
	nextSize, mergeWithNext := f.forwardMap[next]
212
	newStart := pid
213
	newSize := uint64(1)
214
215
	if mergeWithPrev {
216
		//merge with previous span
217
		start := prev + 1 - common.Pgid(preSize)
218
		f.delSpan(start, preSize)
219
220
		newStart -= common.Pgid(preSize)
221
		newSize += preSize
222
	}
223
224
	if mergeWithNext {
225
		// merge with next span
226
		f.delSpan(next, nextSize)
227
		newSize += nextSize
228
	}
229
230
	f.addSpan(newStart, newSize)
231
}
232
233
// idsFromFreemaps get all free page IDs from f.freemaps.
234
// used by test only.
235
func (f *hashMap) idsFromFreemaps() map[common.Pgid]struct{} {
236
	ids := make(map[common.Pgid]struct{})
237
	for size, idSet := range f.freemaps {
238
		for start := range idSet {
239
			for i := 0; i < int(size); i++ {
240
				id := start + common.Pgid(i)
241
				if _, ok := ids[id]; ok {
242
					panic(fmt.Sprintf("detected duplicated free page ID: %d in f.freemaps: %v", id, f.freemaps))
243
				}
244
				ids[id] = struct{}{}
245
			}
246
		}
247
	}
248
	return ids
249
}
250
251
// idsFromForwardMap get all free page IDs from f.forwardMap.
252
// used by test only.
253
func (f *hashMap) idsFromForwardMap() map[common.Pgid]struct{} {
254
	ids := make(map[common.Pgid]struct{})
255
	for start, size := range f.forwardMap {
256
		for i := 0; i < int(size); i++ {
257
			id := start + common.Pgid(i)
258
			if _, ok := ids[id]; ok {
259
				panic(fmt.Sprintf("detected duplicated free page ID: %d in f.forwardMap: %v", id, f.forwardMap))
260
			}
261
			ids[id] = struct{}{}
262
		}
263
	}
264
	return ids
265
}
266
267
// idsFromBackwardMap get all free page IDs from f.backwardMap.
268
// used by test only.
269
func (f *hashMap) idsFromBackwardMap() map[common.Pgid]struct{} {
270
	ids := make(map[common.Pgid]struct{})
271
	for end, size := range f.backwardMap {
272
		for i := 0; i < int(size); i++ {
273
			id := end - common.Pgid(i)
274
			if _, ok := ids[id]; ok {
275
				panic(fmt.Sprintf("detected duplicated free page ID: %d in f.backwardMap: %v", id, f.backwardMap))
276
			}
277
			ids[id] = struct{}{}
278
		}
279
	}
280
	return ids
281
}
282
283
func NewHashMapFreelist() Interface {
284
	hm := &hashMap{
285
		shared:      newShared(),
286
		freemaps:    make(map[uint64]pidSet),
287
		forwardMap:  make(map[common.Pgid]uint64),
288
		backwardMap: make(map[common.Pgid]uint64),
289
	}
290
	hm.Interface = hm
291
	return hm
292
}