cache - cache.go
1 // Package cache implements a simple LRU or MRU cache store.
2 package cache
3
4 import "sync"
5
6 type item[T comparable, U any] struct {
7 key T
8 data U
9 prev, next *item[T, U]
10 }
11
12 type mru[T comparable, U any] struct{}
13
14 func (mru[T, U]) Remove(first, _ **item[T, U]) *item[T, U] {
15 ret := *first
16
17 (*first).next.prev = nil
18 *first = (*first).next
19 (*first).prev = nil
20
21 return ret
22 }
23
24 type lru[T comparable, U any] struct {
25 first, last *item[T, U]
26 }
27
28 func (lru[T, U]) Remove(_, last **item[T, U]) *item[T, U] {
29 ret := *last
30
31 (*last).prev.next = nil
32 *last = (*last).prev
33 (*last).next = nil
34
35 return ret
36 }
37
38 type ru[T comparable, U any] interface {
39 Remove(first, last **item[T, U]) *item[T, U]
40 }
41
42 type cache[T comparable, U any, V ru[T, U]] struct {
43 mu sync.Mutex
44 maxItems uint64
45 data map[T]*item[T, U]
46 first, last *item[T, U]
47 remover V
48 }
49
50 // Cache is an interface containing the methods used by the LRU and MRU types.
51 type Cache[T comparable, U any] interface {
52 Get(T) (U, bool)
53 Set(T, U) bool
54 Remove(T) bool
55 MaxItems() uint64
56 Count() uint64
57 Clear()
58 }
59
60 // LRU implements a Least-Recently-Used cache, in which a maximum number of
61 // items are store, with new items evicting the item that was accessed longest
62 // ago.
63 type LRU[T comparable, U any] struct {
64 cache[T, U, lru[T, U]]
65 }
66
67 // NewLRU create a new LRU cache with the specified manximum number of items.
68 func NewLRU[T comparable, U any](maxItems uint64) *LRU[T, U] {
69 return &LRU[T, U]{
70 cache: cache[T, U, lru[T, U]]{
71 maxItems: maxItems,
72 data: make(map[T]*item[T, U], maxItems+1),
73 },
74 }
75 }
76
77 // MRU implements a Most-Recently-Used cache, in which a maximum number of items
78 // are store, with new items evicting the item that was accessed longest ago.
79 type MRU[T comparable, U any] struct {
80 cache[T, U, mru[T, U]]
81 }
82
83 // NewMRU create a new MRU cache with the specified manximum number of items.
84 func NewMRU[T comparable, U any](maxItems uint64) *MRU[T, U] {
85 return &MRU[T, U]{
86 cache: cache[T, U, mru[T, U]]{
87 maxItems: maxItems,
88 data: make(map[T]*item[T, U]),
89 },
90 }
91 }
92
93 // Get retrieves an item from the cache by its ID.
94 //
95 // If the item is currently in the store, it returns the stored data and true.
96 //
97 // If the item does not exist, it returns the zero value for that type and
98 // false.
99 func (c *cache[T, U, V]) Get(id T) (U, bool) {
100 c.mu.Lock()
101 defer c.mu.Unlock()
102
103 item, ok := c.data[id]
104 if !ok {
105 var data U
106
107 return data, false
108 }
109
110 c.set(item, false)
111
112 return item.data, true
113 }
114
115 // Set sets the given ID to the given data.
116 //
117 // Returns false if the ID already exists, true otherwise.
118 func (c *cache[T, U, V]) Set(id T, data U) bool {
119 c.mu.Lock()
120 defer c.mu.Unlock()
121
122 it, ok := c.data[id]
123 if ok {
124 it.data = data
125
126 c.set(it, false)
127
128 return false
129 }
130
131 it = &item[T, U]{key: id, data: data}
132 c.data[id] = it
133
134 c.set(it, uint64(len(c.data)) > c.maxItems)
135
136 return true
137 }
138
139 func (c *cache[T, U, V]) set(it *item[T, U], remove bool) {
140 if c.first == it {
141 return
142 }
143
144 if remove {
145 if toRemove := c.remover.Remove(&c.first, &c.last); toRemove != nil {
146 delete(c.data, toRemove.key)
147 }
148 }
149
150 if c.last == it {
151 c.last = it.prev
152 }
153
154 if it.prev != nil {
155 it.prev.next = it.next
156 }
157
158 if it.next != nil {
159 it.next.prev = it.prev
160 }
161
162 if c.first != nil {
163 c.first.prev = it
164 it.next = c.first
165 } else {
166 c.last = it
167 }
168
169 it.prev = nil
170 c.first = it
171 }
172
173 // Remove removes the given ID, and its associated data, from the store.
174 //
175 // Returns false if the ID did not exist, true otherwise.
176 func (c *cache[T, U, V]) Remove(id T) bool {
177 c.mu.Lock()
178 defer c.mu.Unlock()
179
180 it, ok := c.data[id]
181 if !ok {
182 return false
183 }
184
185 if it.prev != nil {
186 it.prev.next = it.next
187 }
188
189 if it.next != nil {
190 it.next.prev = it.prev
191 }
192
193 if c.first == it {
194 c.first = it.next
195 }
196
197 if c.last == it {
198 c.last = it.prev
199 }
200
201 delete(c.data, id)
202
203 return true
204 }
205
206 // MaxItems returns the maximum number of items this cache can store, as
207 // specified during creation.
208 func (c *cache[T, U, V]) MaxItems() uint64 {
209 return c.maxItems
210 }
211
212 // Count returns the number of items currently stored in the cache.
213 func (c *cache[T, U, V]) Count() uint64 {
214 c.mu.Lock()
215 defer c.mu.Unlock()
216
217 return uint64(len(c.data))
218 }
219
220 // Clear empties the cache.
221 func (c *cache[T, U, V]) Clear() {
222 c.mu.Lock()
223 defer c.mu.Unlock()
224
225 clear(c.data)
226
227 c.first = nil
228 c.last = nil
229 }
230