-
Notifications
You must be signed in to change notification settings - Fork 210
/
doublecache.go
128 lines (115 loc) · 3.1 KB
/
doublecache.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
package types
import (
"fmt"
"sync"
)
// Thread safe
type DoubleCache struct {
size uint
cacheA map[Hash12]struct{}
cacheB map[Hash12]struct{}
cacheMutex sync.RWMutex
}
func NewDoubleCache(size uint) *DoubleCache {
return &DoubleCache{size, make(map[Hash12]struct{}, size), make(map[Hash12]struct{}, size), sync.RWMutex{}}
}
// Checks if a value is already in the cache, otherwise add it. Returns bool whether or not the value was found in the cache
// (true - already in cache, false - not in cache)
func (a *DoubleCache) GetOrInsert(key Hash12) bool {
a.cacheMutex.Lock()
defer a.cacheMutex.Unlock()
if a.get(key) {
return true
}
a.insert(key)
return false
}
func (a *DoubleCache) Get(key Hash12) bool {
a.cacheMutex.RLock()
defer a.cacheMutex.RUnlock()
return a.get(key)
}
func (a *DoubleCache) get(key Hash12) bool {
_, ok := a.cacheA[key]
if ok {
return true
}
_, ok = a.cacheB[key]
if ok {
return true
}
return false
}
func (a *DoubleCache) insert(key Hash12) {
if uint(len(a.cacheA)) < a.size {
a.cacheA[key] = struct{}{}
return
}
if uint(len(a.cacheB)) < a.size {
a.cacheB[key] = struct{}{}
return
}
a.cacheA = a.cacheB
a.cacheB = make(map[Hash12]struct{}, a.size)
a.cacheB[key] = struct{}{}
}
// Thread safe
type DoubleResultCache struct {
size uint
cacheA map[Hash12]bool
cacheB map[Hash12]bool
mutex sync.RWMutex
}
func NewDoubleResultCache(size uint) *DoubleResultCache {
return &DoubleResultCache{size, make(map[Hash12]bool, size), make(map[Hash12]bool, size), sync.RWMutex{}}
}
// Checks if a value is already in the cache, otherwise add it. Returns 2 bools -
// result - (only) in case the key was found (exist == true), returns the value
// exist - saying whether or not the value was found in the cache (true - already in cache, false - not in cache)
// err - inconsistent state, other outputs should be ignored
func (a *DoubleResultCache) GetOrInsert(key Hash12, val bool) (result bool, exist bool, err error) {
a.mutex.Lock()
defer a.mutex.Unlock()
res, ok := a.get(key)
if ok {
if res != val {
// inconsistent state
return false, false, fmt.Errorf("current val %v is different than new val %v", res, val)
}
return res, true, nil
}
a.insert(key, val)
return false, false, nil
}
// Checks if a value is already in the cache. Returns 2 bools -
// result - (only) in case the key was found (exist == true), returns the value
// exist - saying whether or not the value was found in the cache (true - already in cache, false - not in cache)
func (a *DoubleResultCache) Get(key Hash12) (result bool, exist bool) {
a.mutex.Lock()
defer a.mutex.Unlock()
return a.get(key)
}
func (a *DoubleResultCache) get(key Hash12) (result bool, exist bool) {
res, ok := a.cacheA[key]
if ok {
return true, res
}
_, ok = a.cacheB[key]
if ok {
return true, res
}
return false, false
}
func (a *DoubleResultCache) insert(key Hash12, val bool) {
if uint(len(a.cacheA)) < a.size {
a.cacheA[key] = val
return
}
if uint(len(a.cacheB)) < a.size {
a.cacheB[key] = val
return
}
a.cacheA = a.cacheB
a.cacheB = make(map[Hash12]bool, a.size)
a.cacheB[key] = val
}