-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlru_cache.go
136 lines (112 loc) · 2.72 KB
/
lru_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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package collection
import (
"context"
"sync"
)
// LRUCache hosts up to a given number of items. When more are presented, the least recently used item
// is evicted from the cache.
type LRUCache[K comparable, V any] struct {
capacity uint
entries map[K]*lruEntry[K, V]
touched *LinkedList[*lruEntry[K, V]]
key sync.RWMutex
}
type lruEntry[K any, V any] struct {
Node *llNode[*lruEntry[K, V]]
Key K
Value V
}
// NewLRUCache creates an empty cache, which will accommodate the given number of items.
func NewLRUCache[K comparable, V any](capacity uint) *LRUCache[K, V] {
return &LRUCache[K, V]{
capacity: capacity,
entries: make(map[K]*lruEntry[K, V], capacity+1),
touched: NewLinkedList[*lruEntry[K, V]](),
}
}
// Put adds a value to the cache. The added value may be expelled without warning.
func (lru *LRUCache[K, V]) Put(key K, value V) {
lru.key.Lock()
defer lru.key.Unlock()
entry, ok := lru.entries[key]
if ok {
lru.touched.removeNode(entry.Node)
} else {
entry = &lruEntry[K, V]{
Node: &llNode[*lruEntry[K, V]]{},
Key: key,
}
}
entry.Node.payload = entry
entry.Value = value
lru.touched.addNodeFront(entry.Node)
lru.entries[key] = entry
if lru.touched.Length() > lru.capacity {
removed, ok := lru.touched.RemoveBack()
if ok {
delete(lru.entries, removed.Key)
}
}
}
// Get retrieves a cached value, if it is still present.
func (lru *LRUCache[K, V]) Get(key K) (V, bool) {
lru.key.RLock()
defer lru.key.RUnlock()
entry, ok := lru.entries[key]
if !ok {
return *new(V), false
}
lru.touched.removeNode(entry.Node)
lru.touched.addNodeFront(entry.Node)
return entry.Node.payload.Value, true
}
// Remove explicitly takes an item out of the cache.
func (lru *LRUCache[K, V]) Remove(key K) bool {
lru.key.RLock()
defer lru.key.RUnlock()
entry, ok := lru.entries[key]
if !ok {
return false
}
lru.touched.removeNode(entry.Node)
delete(lru.entries, key)
return true
}
// Enumerate lists each value in the cache.
func (lru *LRUCache[K, V]) Enumerate(ctx context.Context) Enumerator[V] {
retval := make(chan V)
nested := lru.touched.Enumerate(ctx)
go func() {
lru.key.RLock()
defer lru.key.RUnlock()
defer close(retval)
for entry := range nested {
select {
case retval <- entry.Value:
break
case <-ctx.Done():
return
}
}
}()
return retval
}
// EnumerateKeys lists each key in the cache.
func (lru *LRUCache[K, V]) EnumerateKeys(ctx context.Context) Enumerator[K] {
retval := make(chan K)
nested := lru.touched.Enumerate(ctx)
go func() {
lru.key.RLock()
defer lru.key.RUnlock()
defer close(retval)
for entry := range nested {
select {
case retval <- entry.Key:
break
case <-ctx.Done():
return
}
}
}()
return retval
}