github.com/searKing/golang/go@v1.2.74/exp/container/lru/lru.go (about) 1 // Copyright 2022 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package lru 6 7 import ( 8 "container/list" 9 ) 10 11 // EvictCallback is used to get a callback when a cache entry is evicted 12 type EvictCallback[K comparable, V any] func(key K, value V) 13 14 type EvictCallbackFunc[K comparable, V any] interface { 15 Evict(key K, value V) 16 } 17 18 // LRU implements a non-thread safe fixed size LRU cache 19 type LRU[K comparable, V any] struct { 20 size int // LRU size limit 21 22 evictList *list.List // sequence order for lru: Latest, Old, Older, ..., Oldest 23 items map[K]*list.Element // index to element access accelerate 24 onEvict EvictCallback[K, V] 25 } 26 27 // entry is used to hold a value in the evictList 28 type entry[K comparable, V any] struct { 29 key K 30 value V 31 } 32 33 // New constructs an LRU of the given size 34 func New[K comparable, V any](size int) *LRU[K, V] { 35 c := &LRU[K, V]{ 36 size: size, 37 } 38 return c.Init() 39 } 40 41 // SetEvictCallback sets a callback when a cache entry is evicted 42 func (c *LRU[K, V]) SetEvictCallback(onEvict EvictCallback[K, V]) *LRU[K, V] { 43 c.onEvict = onEvict 44 return c 45 } 46 47 // SetEvictCallbackFunc sets a callback func when a cache entry is evicted 48 func (c *LRU[K, V]) SetEvictCallbackFunc(onEvict func(key K, value V)) *LRU[K, V] { 49 c.onEvict = onEvict 50 return c 51 } 52 53 // Init initializes or clears LRU l. 54 func (c *LRU[K, V]) Init() *LRU[K, V] { 55 c.evictList = list.New() 56 c.items = make(map[K]*list.Element) 57 return c 58 } 59 60 // Purge is used to completely clear the cache. 61 func (c *LRU[K, V]) Purge() { 62 for k, v := range c.items { 63 if c.onEvict != nil { 64 c.onEvict(k, v.Value.(*entry[K, V]).value) 65 } 66 delete(c.items, k) 67 } 68 c.evictList.Init() 69 } 70 71 // Add adds a value to the cache. Returns true if an eviction occurred. 72 func (c *LRU[K, V]) Add(key K, value V) (evicted bool) { 73 // Check for existing item 74 if ent, ok := c.items[key]; ok { 75 c.evictList.MoveToFront(ent) 76 ent.Value.(*entry[K, V]).value = value 77 return false 78 } 79 80 // Add new item 81 ent := &entry[K, V]{key, value} 82 entry := c.evictList.PushFront(ent) 83 c.items[key] = entry 84 85 evict := c.evictList.Len() > c.size 86 // Verify size not exceeded 87 if evict { 88 c.removeOldest() 89 } 90 return evict 91 } 92 93 // Get looks up a key's value from the cache. 94 func (c *LRU[K, V]) Get(key K) (value V, ok bool) { 95 if ent, ok := c.items[key]; ok { 96 c.evictList.MoveToFront(ent) 97 if ent.Value.(*entry[K, V]) == nil { 98 var zero V 99 return zero, false 100 } 101 return ent.Value.(*entry[K, V]).value, true 102 } 103 return 104 } 105 106 // Contains checks if a key is in the cache, without updating the recent-ness 107 // or deleting it for being stale. 108 func (c *LRU[K, V]) Contains(key K) (ok bool) { 109 _, ok = c.items[key] 110 return ok 111 } 112 113 // Peek returns the key value (or undefined if not found) without updating 114 // the "recently used"-ness of the key. 115 func (c *LRU[K, V]) Peek(key K) (value V, ok bool) { 116 var ent *list.Element 117 if ent, ok = c.items[key]; ok { 118 return ent.Value.(*entry[K, V]).value, true 119 } 120 var zero V 121 return zero, ok 122 } 123 124 // Remove removes the provided key from the cache, returning if the 125 // key was contained. 126 func (c *LRU[K, V]) Remove(key K) (present bool) { 127 if ent, ok := c.items[key]; ok { 128 c.removeElement(ent) 129 return true 130 } 131 return false 132 } 133 134 // RemoveOldest removes the oldest item from the cache. 135 func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) { 136 ent := c.evictList.Back() 137 if ent != nil { 138 c.removeElement(ent) 139 kv := ent.Value.(*entry[K, V]) 140 return kv.key, kv.value, true 141 } 142 var zeroK K 143 var zeroV V 144 return zeroK, zeroV, false 145 } 146 147 // GetOldest returns the oldest entry 148 func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) { 149 ent := c.evictList.Back() 150 if ent != nil { 151 kv := ent.Value.(*entry[K, V]) 152 return kv.key, kv.value, true 153 } 154 var zeroK K 155 var zeroV V 156 return zeroK, zeroV, false 157 } 158 159 // Keys returns a slice of the keys in the cache, from oldest to newest. 160 func (c *LRU[K, V]) Keys() []K { 161 keys := make([]K, len(c.items)) 162 i := 0 163 for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { 164 keys[i] = ent.Value.(*entry[K, V]).key 165 i++ 166 } 167 return keys 168 } 169 170 // Len returns the number of items in the cache. 171 func (c *LRU[K, V]) Len() int { 172 return c.evictList.Len() 173 } 174 175 // Cap returns the capacity of the cache. 176 func (c *LRU[K, V]) Cap() int { 177 return c.size 178 } 179 180 // Resize changes the cache size. 181 func (c *LRU[K, V]) Resize(size int) (evicted int) { 182 diff := c.Len() - size 183 if diff < 0 { 184 diff = 0 185 } 186 for i := 0; i < diff; i++ { 187 c.removeOldest() 188 } 189 c.size = size 190 return diff 191 } 192 193 // removeOldest removes the oldest item from the cache. 194 func (c *LRU[K, V]) removeOldest() { 195 ent := c.evictList.Back() 196 if ent != nil { 197 c.removeElement(ent) 198 } 199 } 200 201 // removeElement is used to remove a given list element from the cache 202 func (c *LRU[K, V]) removeElement(e *list.Element) { 203 c.evictList.Remove(e) 204 kv := e.Value.(*entry[K, V]) 205 delete(c.items, kv.key) 206 if c.onEvict != nil { 207 c.onEvict(kv.key, kv.value) 208 } 209 }