-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmaglev.go
115 lines (94 loc) · 2.28 KB
/
maglev.go
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
// Package maglev implements maglev consistent hashing
/*
http://research.google.com/pubs/pub44824.html
*/
package maglev
import (
"github.com/dchest/siphash"
)
const (
SmallM = 65537
BigM = 655373
)
type Table struct {
n int
lookup []int
m uint64
// currentOffsets saves the current offset of i index
currentOffsets []uint64
// skips saves the skips of i index
skips []uint64
// originOffsets saves the first currentOffsets
originOffsets []uint64
}
func New(names []string, m uint64) *Table {
offsets, skips := generateOffsetAndSkips(names, m)
t := &Table{
n: len(names),
skips: skips,
currentOffsets: offsets,
originOffsets: make([]uint64, len(names)),
m: m,
}
// save first currentOffsets to originOffsets, for reset
copy(t.originOffsets, t.currentOffsets)
t.lookup = t.populate(m, nil)
return t
}
func (t *Table) Lookup(key uint64) int {
return t.lookup[key%uint64(len(t.lookup))]
}
func (t *Table) Rebuild(dead []int) {
t.lookup = t.populate(t.m, dead)
}
// generateOffsetAndSkips generates the first offset and skip, which is used to generate hash sequence
func generateOffsetAndSkips(names []string, M uint64) ([]uint64, []uint64) {
offsets := make([]uint64, len(names))
skips := make([]uint64, len(names))
for i, name := range names {
b := []byte(name)
h := siphash.Hash(0xdeadbeefcafebabe, 0, b)
offsets[i], skips[i] = (h>>32)%M, ((h&0xffffffff)%(M-1) + 1)
}
return offsets, skips
}
func (t *Table) populate(M uint64, dead []int) []int {
t.resetOffsets()
N := len(t.currentOffsets)
entry := make([]int, M)
for j := range entry {
entry[j] = -1
}
var n uint64
for {
d := dead
for i := 0; i < N; i++ {
if len(d) > 0 && d[0] == i {
d = d[1:]
continue
}
var c uint64
t.nextOffset(i, &c)
for entry[c] >= 0 {
t.nextOffset(i, &c)
}
entry[c] = i
n++
if n == M {
return entry
}
}
}
}
// nextOffset generate next offset of i index, by adding skips[i]
func (t *Table) nextOffset(i int, c *uint64) {
*c = t.currentOffsets[i]
t.currentOffsets[i] += t.skips[i]
if t.currentOffsets[i] >= t.m {
t.currentOffsets[i] -= t.m
}
}
// resetOffsets reset originOffsets to current offset
func (t *Table) resetOffsets() {
copy(t.currentOffsets, t.originOffsets)
}