-
Notifications
You must be signed in to change notification settings - Fork 1
/
lru.go
155 lines (123 loc) · 3.18 KB
/
lru.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package lru
import (
"container/list"
"errors"
"fmt"
"reflect"
)
// LRU is a basic implmentation of a least
// recently used cache using a map
type LRU struct {
capaticty int
convenienceCap uintptr
size uintptr
used *list.List
items map[interface{}]*list.Element
}
// NewLRU returns a new LRU capable of holding items
// up to the given capacity in bytes
// TODO: Consider setting a max
func NewLRU(capacity int) *LRU {
return &LRU{
capaticty: capacity,
convenienceCap: uintptr(capacity),
size: uintptr(0),
used: list.New(),
items: map[interface{}]*list.Element{},
}
}
type entry struct {
key interface{}
value interface{}
size uintptr
}
// Set stores this value in the interface with the given key.
// If storing this entry increases the size of the cache greater
// than the capacity, then the last used element is removed
func (l *LRU) Set(key interface{}, value interface{}) error {
entrySize := reflect.TypeOf(value).Size()
if entrySize > l.convenienceCap {
return errors.New("Cannot store item that is larger than the cache capacity")
}
newSize := l.setSize(entrySize)
_, ok := l.items[key]
if !ok {
entry := entry{
key: key,
value: value,
size: entrySize,
}
listEntry := l.used.PushFront(entry)
l.size = newSize
l.items[key] = listEntry
}
return nil
}
func (l *LRU) setSize(entrySize uintptr) uintptr {
newSize := l.size + entrySize
if newSize <= l.convenienceCap {
return newSize
}
l.removeLastElement()
return l.setSize(entrySize)
}
func (l *LRU) removeLastElement() {
lastElement := l.used.Back()
l.used.Remove(lastElement)
entry := lastElement.Value.(entry)
delete(l.items, entry.key)
l.size -= entry.size
}
// Get returns the element with this key, if it exists.
// The returned element becomes the most recently used
// element in the cache and is less likely to be removed
func (l *LRU) Get(key interface{}) (interface{}, bool) {
entry, ok := l.get(key)
if ok {
l.used.MoveToFront(entry)
}
return entry, ok
}
// Peek returns the element with this key, if it exists.
// Unlike Get, the returned element does not become the
// most recently used element
func (l *LRU) Peek(key interface{}) (interface{}, bool) {
return l.get(key)
}
func (l *LRU) get(key interface{}) (*list.Element, bool) {
entry, ok := l.items[key]
if !ok {
return nil, ok
}
return entry, ok
}
// Del removes the entry with the given key. If they entry
// does not exist, an error is returned
func (l *LRU) Del(key interface{}) error {
entry, ok := l.get(key)
if !ok {
return fmt.Errorf("cannot delete non-existent entry: %v", key)
}
l.removeEntry(entry)
return nil
}
func (l *LRU) removeEntry(e *list.Element) {
l.used.Remove(e)
l.size -= e.Value.(entry).size
}
// Size returns the current size of the cache as an integer
func (l *LRU) Size() int {
return int(l.size)
}
// Has returns true if the give key contains an entry in
// the cache
func (l *LRU) Has(key interface{}) bool {
_, ok := l.items[key]
return ok
}
// Reset clears the cache, throwing away all elements
func (l *LRU) Reset() {
l.used.Init()
l.items = map[interface{}]*list.Element{}
l.size = uintptr(0)
}