/
striped_map.go
103 lines (79 loc) · 1.75 KB
/
striped_map.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
package concurrency
import (
"hash/crc32"
"sync"
"sync/atomic"
)
const (
defaultStripeCount int = 16
)
type StripedMap[T any] struct {
stripeCount int
maps []map[string]T
counts []atomic.Int32
locks []sync.RWMutex
total atomic.Int32
}
func NewStripedMap[T any](numStripes int) *StripedMap[T] {
count := numStripes
if count <= 0 {
count = defaultStripeCount
}
s := &StripedMap[T]{
stripeCount: count,
}
for i := 0; i < count; i++ {
s.maps = append(s.maps, make(map[string]T))
s.locks = append(s.locks, sync.RWMutex{})
s.counts = append(s.counts, atomic.Int32{})
}
return s
}
func (s *StripedMap[T]) Put(key string, value T) {
idx := s.hash(key)
_, found := s.Get(key)
s.locks[idx].Lock()
s.maps[idx][key] = value
// Only increment counters if we are not updating an existing key.
if !found {
s.counts[idx].Add(1)
s.total.Add(1)
}
s.locks[idx].Unlock()
}
func (s *StripedMap[T]) Get(key string) (T, bool) {
idx := s.hash(key)
s.locks[idx].RLock()
defer s.locks[idx].RUnlock()
v, ok := s.maps[idx][key]
return v, ok
}
func (s *StripedMap[T]) Delete(key string) {
idx := s.hash(key)
_, found := s.Get(key)
if !found {
// Return early if the key does not exist.
return
}
s.locks[idx].Lock()
defer s.locks[idx].Unlock()
s.counts[idx].Add(-1)
s.total.Add(-1)
delete(s.maps[idx], key)
}
func (s *StripedMap[T]) Len() int {
return int(s.total.Load())
}
func (s *StripedMap[T]) LengthsPerStripe() map[int]int {
m := make(map[int]int)
for i := 0; i < s.stripeCount; i++ {
s.locks[i].RLock()
defer s.locks[i].RUnlock()
m[i] = int(s.counts[i].Load())
}
return m
}
func (s *StripedMap[T]) hash(key string) int {
hashSum := crc32.ChecksumIEEE([]byte(key))
return int(hashSum) % s.stripeCount
}