blob: a86c8539e0663155bd55997953f2d9dba2425e2f [file] [log] [blame]
divyadesai81bb7ba2020-03-11 11:45:23 +00001package simplelru
2
3import (
4 "container/list"
5 "errors"
6)
7
8// EvictCallback is used to get a callback when a cache entry is evicted
9type EvictCallback func(key interface{}, value interface{})
10
11// LRU implements a non-thread safe fixed size LRU cache
12type LRU struct {
13 size int
14 evictList *list.List
15 items map[interface{}]*list.Element
16 onEvict EvictCallback
17}
18
19// entry is used to hold a value in the evictList
20type entry struct {
21 key interface{}
22 value interface{}
23}
24
25// NewLRU constructs an LRU of the given size
26func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
27 if size <= 0 {
28 return nil, errors.New("Must provide a positive size")
29 }
30 c := &LRU{
31 size: size,
32 evictList: list.New(),
33 items: make(map[interface{}]*list.Element),
34 onEvict: onEvict,
35 }
36 return c, nil
37}
38
39// Purge is used to completely clear the cache.
40func (c *LRU) Purge() {
41 for k, v := range c.items {
42 if c.onEvict != nil {
43 c.onEvict(k, v.Value.(*entry).value)
44 }
45 delete(c.items, k)
46 }
47 c.evictList.Init()
48}
49
50// Add adds a value to the cache. Returns true if an eviction occurred.
51func (c *LRU) Add(key, value interface{}) (evicted bool) {
52 // Check for existing item
53 if ent, ok := c.items[key]; ok {
54 c.evictList.MoveToFront(ent)
55 ent.Value.(*entry).value = value
56 return false
57 }
58
59 // Add new item
60 ent := &entry{key, value}
61 entry := c.evictList.PushFront(ent)
62 c.items[key] = entry
63
64 evict := c.evictList.Len() > c.size
65 // Verify size not exceeded
66 if evict {
67 c.removeOldest()
68 }
69 return evict
70}
71
72// Get looks up a key's value from the cache.
73func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
74 if ent, ok := c.items[key]; ok {
75 c.evictList.MoveToFront(ent)
76 if ent.Value.(*entry) == nil {
77 return nil, false
78 }
79 return ent.Value.(*entry).value, true
80 }
81 return
82}
83
84// Contains checks if a key is in the cache, without updating the recent-ness
85// or deleting it for being stale.
86func (c *LRU) Contains(key interface{}) (ok bool) {
87 _, ok = c.items[key]
88 return ok
89}
90
91// Peek returns the key value (or undefined if not found) without updating
92// the "recently used"-ness of the key.
93func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
94 var ent *list.Element
95 if ent, ok = c.items[key]; ok {
96 return ent.Value.(*entry).value, true
97 }
98 return nil, ok
99}
100
101// Remove removes the provided key from the cache, returning if the
102// key was contained.
103func (c *LRU) Remove(key interface{}) (present bool) {
104 if ent, ok := c.items[key]; ok {
105 c.removeElement(ent)
106 return true
107 }
108 return false
109}
110
111// RemoveOldest removes the oldest item from the cache.
112func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) {
113 ent := c.evictList.Back()
114 if ent != nil {
115 c.removeElement(ent)
116 kv := ent.Value.(*entry)
117 return kv.key, kv.value, true
118 }
119 return nil, nil, false
120}
121
122// GetOldest returns the oldest entry
123func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) {
124 ent := c.evictList.Back()
125 if ent != nil {
126 kv := ent.Value.(*entry)
127 return kv.key, kv.value, true
128 }
129 return nil, nil, false
130}
131
132// Keys returns a slice of the keys in the cache, from oldest to newest.
133func (c *LRU) Keys() []interface{} {
134 keys := make([]interface{}, len(c.items))
135 i := 0
136 for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
137 keys[i] = ent.Value.(*entry).key
138 i++
139 }
140 return keys
141}
142
143// Len returns the number of items in the cache.
144func (c *LRU) Len() int {
145 return c.evictList.Len()
146}
147
148// Resize changes the cache size.
149func (c *LRU) Resize(size int) (evicted int) {
150 diff := c.Len() - size
151 if diff < 0 {
152 diff = 0
153 }
154 for i := 0; i < diff; i++ {
155 c.removeOldest()
156 }
157 c.size = size
158 return diff
159}
160
161// removeOldest removes the oldest item from the cache.
162func (c *LRU) removeOldest() {
163 ent := c.evictList.Back()
164 if ent != nil {
165 c.removeElement(ent)
166 }
167}
168
169// removeElement is used to remove a given list element from the cache
170func (c *LRU) removeElement(e *list.Element) {
171 c.evictList.Remove(e)
172 kv := e.Value.(*entry)
173 delete(c.items, kv.key)
174 if c.onEvict != nil {
175 c.onEvict(kv.key, kv.value)
176 }
177}