-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbucket.go
159 lines (143 loc) · 3 KB
/
bucket.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
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package s_cache
import (
"crypto/rand"
"math"
"math/big"
insecurerand "math/rand"
"os"
"sync"
"sync/atomic"
)
type RecallFunc func(key string, val *Node) bool
type shardBuckets struct {
r *Cache
table []*bucket
count int32
seed uint32
hashAlgo func(seed uint32, k string) uint32
}
type bucket struct {
m map[string]*Node
mutex sync.RWMutex
}
func computeCapacity(param int) (size int) {
if param <= 16 {
return 16
}
n := param - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
if n < 0 {
return math.MaxInt32
} else {
return int(n + 1)
}
}
func makeShardBuckets(r *Cache, shardCount int) *shardBuckets {
shardCount = computeCapacity(shardCount)
table := make([]*bucket, shardCount)
for i := 0; i < shardCount; i++ {
table[i] = &bucket{
m: make(map[string]*Node),
}
}
max := big.NewInt(0).SetUint64(uint64(math.MaxUint32))
rnd, err := rand.Int(rand.Reader, max)
var seed uint32
if err != nil {
os.Stderr.Write([]byte("WARNING: s-cache's makeShardBuckets failed to read from the system CSPRNG (/dev/urandom or equivalent.) Your system's security may be compromised. Continuing with an insecure seed.\n"))
seed = insecurerand.Uint32()
} else {
seed = uint32(rnd.Uint64())
}
d := &shardBuckets{
r: r,
count: 0,
table: table,
seed: seed,
hashAlgo: djb33,
}
return d
}
func (s *shardBuckets) spread(hashcode uint32) uint32 {
if s == nil {
panic("dict is nil")
}
dictSize := uint32(len(s.table))
return (dictSize - 1) & hashcode
}
func (s *shardBuckets) getShared(index uint32) *bucket {
return s.table[index]
}
func (s *shardBuckets) addCount() {
atomic.AddInt32(&s.count, 1)
}
func (s *shardBuckets) decreaseCount() {
atomic.AddInt32(&s.count, -1)
}
func (s *shardBuckets) get(r *Cache, key string, noset bool) (n *Node, added bool) {
if s == nil {
panic("dict is nil")
}
hashcode := s.hashAlgo(s.seed, key)
index := s.spread(hashcode)
shared := s.getShared(index)
shared.mutex.Lock()
defer shared.mutex.Unlock()
n, exists := shared.m[key]
if exists {
// node has existed, add the ref
atomic.AddInt32(&n.ref, 1)
return n, false
} else if !exists && !noset {
n = &Node{
r: r,
key: key,
ref: 1,
}
shared.m[key] = n
s.addCount()
return n, true
} else {
return nil, false
}
}
func (s *shardBuckets) len() (length int) {
return int(atomic.LoadInt32(&s.count))
}
func (s *shardBuckets) remove(key string) (val *Node, existed bool) {
if s == nil {
panic("dict is nil")
}
hashcode := s.hashAlgo(s.seed, key)
index := s.spread(hashcode)
shared := s.getShared(index)
shared.mutex.Lock()
defer shared.mutex.Unlock()
if v, ok := shared.m[key]; ok {
delete(shared.m, key)
s.decreaseCount()
return v, true
} else {
return nil, false
}
}
func (s *shardBuckets) forEach(recall RecallFunc) {
if s == nil {
return
}
for _, t := range s.table {
t.mutex.RLock()
func() {
defer t.mutex.RUnlock()
for k, v := range t.m {
if recall(k, v) {
return
}
}
}()
}
}