@@ -12,6 +12,21 @@ type LFUCache struct {
12
12
freqList * list.List // list for freqEntry
13
13
}
14
14
15
+ var _ Cache = (* LFUCache )(nil )
16
+
17
+ type lfuItem struct {
18
+ clock Clock
19
+ key interface {}
20
+ value interface {}
21
+ freqElement * list.Element
22
+ expiration * time.Time
23
+ }
24
+
25
+ type freqEntry struct {
26
+ freq uint
27
+ items map [* lfuItem ]struct {}
28
+ }
29
+
15
30
func newLFUCache (cb * CacheBuilder ) * LFUCache {
16
31
c := & LFUCache {}
17
32
buildCache (& c .baseCache , cb )
@@ -23,7 +38,7 @@ func newLFUCache(cb *CacheBuilder) *LFUCache {
23
38
24
39
func (c * LFUCache ) init () {
25
40
c .freqList = list .New ()
26
- c .items = make (map [interface {}]* lfuItem , c .size + 1 )
41
+ c .items = make (map [interface {}]* lfuItem , c .size )
27
42
c .freqList .PushFront (& freqEntry {
28
43
freq : 0 ,
29
44
items : make (map [* lfuItem ]struct {}),
@@ -183,12 +198,28 @@ func (c *LFUCache) increment(item *lfuItem) {
183
198
nextFreq := currentFreqEntry .freq + 1
184
199
delete (currentFreqEntry .items , item )
185
200
201
+ // a boolean whether reuse the empty current entry
202
+ removable := isRemovableFreqEntry (currentFreqEntry )
203
+
204
+ // insert item into a valid entry
186
205
nextFreqElement := currentFreqElement .Next ()
187
- if nextFreqElement == nil {
188
- nextFreqElement = c .freqList .InsertAfter (& freqEntry {
189
- freq : nextFreq ,
190
- items : make (map [* lfuItem ]struct {}),
191
- }, currentFreqElement )
206
+ switch {
207
+ case nextFreqElement == nil || nextFreqElement .Value .(* freqEntry ).freq > nextFreq :
208
+ if removable {
209
+ currentFreqEntry .freq = nextFreq
210
+ nextFreqElement = currentFreqElement
211
+ } else {
212
+ nextFreqElement = c .freqList .InsertAfter (& freqEntry {
213
+ freq : nextFreq ,
214
+ items : make (map [* lfuItem ]struct {}),
215
+ }, currentFreqElement )
216
+ }
217
+ case nextFreqElement .Value .(* freqEntry ).freq == nextFreq :
218
+ if removable {
219
+ c .freqList .Remove (currentFreqElement )
220
+ }
221
+ default :
222
+ panic ("unreachable" )
192
223
}
193
224
nextFreqElement .Value .(* freqEntry ).items [item ] = struct {}{}
194
225
item .freqElement = nextFreqElement
@@ -201,7 +232,7 @@ func (c *LFUCache) evict(count int) {
201
232
if entry == nil {
202
233
return
203
234
} else {
204
- for item , _ := range entry .Value .(* freqEntry ).items {
235
+ for item := range entry .Value .(* freqEntry ).items {
205
236
if i >= count {
206
237
return
207
238
}
@@ -247,8 +278,12 @@ func (c *LFUCache) remove(key interface{}) bool {
247
278
248
279
// removeElement is used to remove a given list element from the cache
249
280
func (c * LFUCache ) removeItem (item * lfuItem ) {
281
+ entry := item .freqElement .Value .(* freqEntry )
250
282
delete (c .items , item .key )
251
- delete (item .freqElement .Value .(* freqEntry ).items , item )
283
+ delete (entry .items , item )
284
+ if isRemovableFreqEntry (entry ) {
285
+ c .freqList .Remove (item .freqElement )
286
+ }
252
287
if c .evictedFunc != nil {
253
288
c .evictedFunc (item .key , item .value )
254
289
}
@@ -325,19 +360,6 @@ func (c *LFUCache) Purge() {
325
360
c .init ()
326
361
}
327
362
328
- type freqEntry struct {
329
- freq uint
330
- items map [* lfuItem ]struct {}
331
- }
332
-
333
- type lfuItem struct {
334
- clock Clock
335
- key interface {}
336
- value interface {}
337
- freqElement * list.Element
338
- expiration * time.Time
339
- }
340
-
341
363
// IsExpired returns boolean value whether this item is expired or not.
342
364
func (it * lfuItem ) IsExpired (now * time.Time ) bool {
343
365
if it .expiration == nil {
@@ -349,3 +371,7 @@ func (it *lfuItem) IsExpired(now *time.Time) bool {
349
371
}
350
372
return it .expiration .Before (* now )
351
373
}
374
+
375
+ func isRemovableFreqEntry (entry * freqEntry ) bool {
376
+ return entry .freq != 0 && len (entry .items ) == 0
377
+ }
0 commit comments