-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy paths2lru.go
126 lines (96 loc) · 2.59 KB
/
s2lru.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
package tinylfu
import "github.com/dgryski/go-tinylfu/internal/list"
type slruItem[K comparable, V any] struct {
listid int
key K
value V
keyh uint64
}
// Cache is an LRU cache. It is not safe for concurrent access.
type slruCache[K comparable, V any] struct {
data map[K]*list.Element[*slruItem[K, V]]
onecap, twocap int
one, two *list.List[*slruItem[K, V]]
}
func newSLRU[K comparable, V any](onecap, twocap int, data map[K]*list.Element[*slruItem[K, V]]) *slruCache[K, V] {
return &slruCache[K, V]{
data: data,
onecap: onecap,
one: list.New[*slruItem[K, V]](),
twocap: twocap,
two: list.New[*slruItem[K, V]](),
}
}
// get updates the cache data structures for a get
func (slru *slruCache[K, V]) get(v *list.Element[*slruItem[K, V]]) {
item := v.Value
// already on list two?
if item.listid == 2 {
slru.two.MoveToFront(v)
return
}
// must be list one
// is there space on the next list?
if slru.two.Len() < slru.twocap {
// just do the remove/add
slru.one.Remove(v)
item.listid = 2
slru.data[item.key] = slru.two.PushFront(item)
return
}
back := slru.two.Back()
bitem := back.Value
// swap the key/values
*bitem, *item = *item, *bitem
bitem.listid = 2
item.listid = 1
// update pointers in the map
slru.data[item.key] = v
slru.data[bitem.key] = back
// move the elements to the front of their lists
slru.one.MoveToFront(v)
slru.two.MoveToFront(back)
}
// add adds a value to the cache
func (slru *slruCache[K, V]) add(newitem slruItem[K, V]) (oitem slruItem[K, V], evicted bool) {
newitem.listid = 1
if slru.one.Len() < slru.onecap || (slru.Len() < slru.onecap+slru.twocap) {
slru.data[newitem.key] = slru.one.PushFront(&newitem)
return
}
// reuse the tail item
e := slru.one.Back()
item := e.Value
delete(slru.data, item.key) // delete old key
oitem = *item
*item = newitem
slru.data[item.key] = e // insert new key
slru.one.MoveToFront(e)
return oitem, true
}
func (slru *slruCache[K, V]) victim() *slruItem[K, V] {
if slru.Len() < slru.onecap+slru.twocap {
return nil
}
v := slru.one.Back()
return v.Value
}
// Len returns the total number of items in the cache
func (slru *slruCache[K, V]) Len() int {
return slru.one.Len() + slru.two.Len()
}
// Remove removes an item from the cache, returning the item and a boolean indicating if it was found
func (slru *slruCache[K, V]) Remove(key K) (V, bool) {
v, ok := slru.data[key]
if !ok {
return *new(V), false
}
item := v.Value
if item.listid == 2 {
slru.two.Remove(v)
} else {
slru.one.Remove(v)
}
delete(slru.data, key)
return item.value, true
}