-
Notifications
You must be signed in to change notification settings - Fork 1
/
cache.go
121 lines (107 loc) · 4.06 KB
/
cache.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
// Package directcache is a high performance GC-free cache library.
package directcache
import "github.com/cespare/xxhash/v2"
const (
// BucketCount is the count of buckets in a Cache instance.
BucketCount = 256
// MinCapacity is the minimum capacity in bytes of the Cache.
MinCapacity = BucketCount * 256
)
// Cache caches key-value entries of type []byte.
type Cache struct {
buckets [BucketCount]bucket
cap int
}
// New creates a new Cache instance with the given capacity in bytes.
// The instance capacity will be set to MinCapacity at minimum.
func New(capacity int) *Cache {
c := &Cache{}
c.Reset(capacity)
return c
}
// Capacity returns the cache capacity.
func (c *Cache) Capacity() int { return c.cap }
// Reset resets the cache with new capacity and drops all cached entries.
func (c *Cache) Reset(capacity int) {
if capacity < MinCapacity {
capacity = MinCapacity
}
bktCap := capacity / BucketCount
for i := 0; i < BucketCount; i++ {
c.buckets[i].Reset(bktCap)
}
c.cap = capacity
}
// SetEvictionPolicy customizes the cache eviction policy.
// shouldEvict is called when no space to insert the new entry and have to evict an old entry.
// If shouldEvict returns true the old entry will evict immediately, and if false the old entry
// will likely be kept. The provided entry is read-only and never modify its key or value.
func (c *Cache) SetEvictionPolicy(shouldEvict func(entry Entry) bool) {
for i := 0; i < BucketCount; i++ {
c.buckets[i].SetEvictionPolicy(shouldEvict)
}
}
// Set stores the (key, val) entry in the cache, and returns false on failure.
// It always succeeds unless the size of the entry exceeds 1/BucketCount of the cache capacity.
//
// It's safe to modify contents of key and val after Set returns.
func (c *Cache) Set(key, val []byte) bool {
keyHash := xxhash.Sum64(key)
return c.buckets[keyHash%BucketCount].Set(key, keyHash, len(val), func(_val []byte) {
copy(_val, val)
})
}
// Del deletes the entry matching the given key from the cache.
// false is returned if no entry matched.
//
// It's safe to modify contents of key after Del returns.
func (c *Cache) Del(key []byte) bool {
keyHash := xxhash.Sum64(key)
return c.buckets[keyHash%BucketCount].Del(key, keyHash)
}
// Get returns the value of the entry matching the given key.
// It returns false if no matched entry.
//
// It's safe to modify contents of key after Get returns.
func (c *Cache) Get(key []byte) (val []byte, ok bool) {
keyHash := xxhash.Sum64(key)
ok = c.buckets[keyHash%BucketCount].Get(key, keyHash, func(_val []byte) {
val = append(val, _val...)
}, false)
return
}
// Has returns false if no entry matching the given key.
//
// It's safe to modify contents of key after Has returns.
func (c *Cache) Has(key []byte) bool {
keyHash := xxhash.Sum64(key)
return c.buckets[keyHash%BucketCount].Get(key, keyHash, nil, false)
}
// AdvGet is the advanced version of Get. val is (zero-copy) accessed via fn callback.
// It returns false if no entry matching the given key, and fn will not be called then.
// If peek is true, the entry's recently-used flag is not updated.
//
// val is only valid inside fn and should never be modified.
// It's safe to modify contents of key after AdvGet returns.
func (c *Cache) AdvGet(key []byte, fn func(val []byte), peek bool) bool {
keyHash := xxhash.Sum64(key)
return c.buckets[keyHash%BucketCount].Get(key, keyHash, fn, peek)
}
// AdvSet is the advanced version of Set. fn callback is for value assignment.
// It always succeeds unless the size of the entry exceeds 1/BucketCount of the cache capacity.
//
// It's safe to modify contents of key after AdvSet returns.
func (c *Cache) AdvSet(key []byte, valLen int, fn func(val []byte)) bool {
keyHash := xxhash.Sum64(key)
return c.buckets[keyHash%BucketCount].Set(key, keyHash, valLen, fn)
}
// Dump dumps all saved entires bucket by bucket in the order of insertion.
// It's interrupted if f returns false。
// The provided entry is read-only and never modify its key or value.
func (c *Cache) Dump(f func(Entry) bool) {
for i := 0; i < BucketCount; i++ {
if !c.buckets[i].Dump(f) {
break
}
}
}