github.com/searKing/golang/go@v1.2.117/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 is like a Go map[K]V but implements a non-thread safe fixed size LRU cache. 19 // Loads, stores, and deletes run in amortized constant time. 20 type LRU[K comparable, V any] struct { 21 size int // LRU size limit 22 23 evictList *list.List // sequence order for lru: Latest, Old, Older, ..., Oldest 24 items map[K]*list.Element // index to element access accelerate 25 onEvict EvictCallback[K, V] 26 } 27 28 // entry is used to hold a value in the evictList 29 type entry[K comparable, V any] struct { 30 key K 31 value V 32 } 33 34 // New constructs an LRU of the given size 35 func New[K comparable, V any](size int) *LRU[K, V] { 36 c := &LRU[K, V]{ 37 size: size, 38 } 39 return c.Init() 40 } 41 42 // SetEvictCallback sets a callback when a cache entry is evicted 43 func (c *LRU[K, V]) SetEvictCallback(onEvict EvictCallback[K, V]) *LRU[K, V] { 44 c.onEvict = onEvict 45 return c 46 } 47 48 // SetEvictCallbackFunc sets a callback func when a cache entry is evicted 49 // 50 // Deprecated, use SetEvictCallback instead. 51 func (c *LRU[K, V]) SetEvictCallbackFunc(onEvict func(key K, value V)) *LRU[K, V] { 52 c.onEvict = onEvict 53 return c 54 } 55 56 // Init initializes or clears LRU l. 57 func (c *LRU[K, V]) Init() *LRU[K, V] { 58 c.evictList = list.New() 59 c.items = make(map[K]*list.Element) 60 return c 61 } 62 63 // Len returns the number of items in the cache. 64 func (c *LRU[K, V]) Len() int { 65 return c.evictList.Len() 66 } 67 68 // Cap returns the capacity of the cache. 69 func (c *LRU[K, V]) Cap() int { 70 return c.size 71 } 72 73 // Resize changes the cache size. 74 func (c *LRU[K, V]) Resize(size int) (evicted int) { 75 diff := c.Len() - size 76 if diff < 0 { 77 diff = 0 78 } 79 for i := 0; i < diff; i++ { 80 c.removeOldest() 81 } 82 c.size = size 83 return diff 84 } 85 86 // Purge is used to completely clear the cache. 87 func (c *LRU[K, V]) Purge() { 88 for k, v := range c.items { 89 delete(c.items, k) 90 if c.onEvict != nil { 91 c.onEvict(k, v.Value.(*entry[K, V]).value) 92 } 93 } 94 c.evictList.Init() 95 } 96 97 // Load returns the value stored in the cache for a key, or zero if no 98 // value is present. 99 // The ok result indicates whether value was found in the cache. 100 func (c *LRU[K, V]) Load(key K) (value V, ok bool) { 101 return c.load(key, true) 102 } 103 104 // Get looks up a key's value from the cache, 105 // with updating the "recently used"-ness of the key. 106 func (c *LRU[K, V]) Get(key K) (value V, ok bool) { 107 return c.Load(key) 108 } 109 110 // Peek returns the value stored in the cache for a key, or zero if no 111 // value is present. 112 // The ok result indicates whether value was found in the cache. 113 // Without updating the "recently used"-ness of the key. 114 func (c *LRU[K, V]) Peek(key K) (value V, ok bool) { 115 return c.load(key, false) 116 } 117 118 // Contains reports whether key is within the cache. 119 // The ok result indicates whether value was found in the cache. 120 // Without updating the "recently used"-ness of the key. 121 func (c *LRU[K, V]) Contains(key K) (ok bool) { 122 _, ok = c.Peek(key) 123 return ok 124 } 125 126 func (c *LRU[K, V]) load(key K, update bool) (value V, ok bool) { 127 if e, ok := c.items[key]; ok { 128 if update { 129 // update the "recently used"-ness. 130 c.evictList.MoveToFront(e) 131 } 132 return e.Value.(*entry[K, V]).value, true 133 } 134 return 135 } 136 137 // Store sets the value for a key. 138 func (c *LRU[K, V]) Store(key K, value V) { 139 _, _ = c.Swap(key, value) 140 } 141 142 // Add adds a value to the cache, 143 // with updating the "recently used"-ness of the key. 144 // Returns true if an eviction occurred. 145 func (c *LRU[K, V]) Add(key K, value V) (evicted bool) { 146 full := c.evictList.Len() >= c.size 147 _, loaded := c.Swap(key, value) 148 149 return !loaded && full 150 } 151 152 // LoadOrStore returns the existing value for the key if present. 153 // Otherwise, it stores and returns the given value. 154 // The loaded result is true if the value was loaded, false if stored. 155 func (c *LRU[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { 156 if e, ok := c.items[key]; ok { 157 // update the "recently used"-ness. 158 c.evictList.MoveToFront(e) 159 return e.Value.(*entry[K, V]).value, true 160 } 161 162 // Add new item and update the "recently used"-ness of the key. 163 e := &entry[K, V]{key, value} 164 c.items[key] = c.evictList.PushFront(e) 165 166 evict := c.evictList.Len() > c.size 167 // Verify size not exceeded 168 if evict { 169 c.removeOldest() 170 } 171 return value, false 172 } 173 174 // LoadAndDelete deletes the value for a key, returning the previous value if any. 175 // The loaded result reports whether the key was present. 176 func (c *LRU[K, V]) LoadAndDelete(key K) (value V, loaded bool) { 177 if e, ok := c.items[key]; ok { 178 c.removeElement(e) 179 return e.Value.(*entry[K, V]).value, true 180 } 181 return 182 } 183 184 // Delete deletes the value for a key. 185 func (c *LRU[K, V]) Delete(key K) { 186 c.LoadAndDelete(key) 187 } 188 189 // Remove removes the provided key from the cache, returning true if the 190 // key was contained. 191 func (c *LRU[K, V]) Remove(key K) (present bool) { 192 _, present = c.LoadAndDelete(key) 193 return present 194 } 195 196 // Swap swaps the value for a key and returns the previous value if any. 197 // The loaded result reports whether the key was present. 198 func (c *LRU[K, V]) Swap(key K, value V) (previous V, loaded bool) { 199 // Check for existing item 200 if e, ok := c.items[key]; ok { 201 // update the "recently used"-ness. 202 c.evictList.MoveToFront(e) 203 previous, e.Value.(*entry[K, V]).value = e.Value.(*entry[K, V]).value, value 204 loaded = true 205 return previous, loaded 206 } 207 208 // Add new item and update the "recently used"-ness of the key. 209 e := &entry[K, V]{key, value} 210 // update the "recently used"-ness. 211 entry := c.evictList.PushFront(e) 212 c.items[key] = entry 213 214 evict := c.evictList.Len() > c.size 215 // Verify size not exceeded 216 if evict { 217 c.removeOldest() 218 } 219 return previous, loaded 220 } 221 222 // CompareAndSwap swaps the old and new values for key 223 // if the value stored in the map is equal to old. 224 // The old value must be of a comparable type. 225 func (c *LRU[K, V]) CompareAndSwap(key K, old, new V) (swapped bool) { 226 // Check for existing item 227 if e, ok := c.items[key]; ok && any(e.Value.(*entry[K, V]).value) == any(old) { 228 // update the "recently used"-ness. 229 c.evictList.MoveToFront(e) 230 e.Value.(*entry[K, V]).value = new 231 return true 232 } 233 return false 234 } 235 236 // CompareAndDelete deletes the entry for key if its value is equal to old. 237 // The old value must be of a comparable type. 238 // 239 // If there is no current value for key in the map, CompareAndDelete 240 // returns false (even if the old value is the nil interface value). 241 func (c *LRU[K, V]) CompareAndDelete(key K, old V) (deleted bool) { 242 // Check for existing item 243 if e, ok := c.items[key]; ok && any(e.Value.(*entry[K, V]).value) == any(old) { 244 c.removeElement(e) 245 return true 246 } 247 return false 248 } 249 250 // Keys returns a slice of the keys in the cache, from oldest to newest. 251 // Without updating the "recently used"-ness of the key. 252 func (c *LRU[K, V]) Keys() []K { 253 keys := make([]K, len(c.items)) 254 i := 0 255 for e := c.evictList.Back(); e != nil; e = e.Prev() { 256 keys[i] = e.Value.(*entry[K, V]).key 257 i++ 258 } 259 return keys 260 } 261 262 // Range calls f sequentially for each key and value present in the lru from oldest to newest. 263 // If f returns false, range stops the iteration. 264 // Without updating the "recently used"-ness of the key. 265 func (c *LRU[K, V]) Range(f func(key K, value V) bool) { 266 // Iterate through list and print its contents. 267 for e := c.evictList.Back(); e != nil; e = e.Prev() { 268 if !f(e.Value.(*entry[K, V]).key, e.Value.(*entry[K, V]).value) { 269 break 270 } 271 } 272 } 273 274 // PeekOldest returns the value stored in the cache for the oldest entry, or zero if no 275 // value is present. 276 // The ok result indicates whether value was found in the cache. 277 // Without updating the "recently used"-ness of the key. 278 func (c *LRU[K, V]) PeekOldest() (key K, value V, ok bool) { 279 e := c.evictList.Back() 280 if e != nil { 281 kv := e.Value.(*entry[K, V]) 282 return kv.key, kv.value, true 283 } 284 return 285 } 286 287 // PeekAndDeleteOldest deletes the value for a key, returning the previous value if any. 288 // The loaded result reports whether the key was present. 289 func (c *LRU[K, V]) PeekAndDeleteOldest() (key K, value V, loaded bool) { 290 e := c.evictList.Back() 291 if e != nil { 292 c.removeElement(e) 293 kv := e.Value.(*entry[K, V]) 294 return kv.key, kv.value, true 295 } 296 return 297 } 298 299 // RemoveOldest removes the oldest item from the cache. 300 func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) { 301 return c.PeekAndDeleteOldest() 302 } 303 304 // GetOldest returns the oldest entry, without updating the "recently used"-ness 305 // or deleting it for being stale. 306 // Without updating the "recently used"-ness of the key. 307 func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) { 308 return c.PeekOldest() 309 } 310 311 // removeOldest removes the oldest item from the cache. 312 func (c *LRU[K, V]) removeOldest() { 313 e := c.evictList.Back() 314 if e != nil { 315 c.removeElement(e) 316 } 317 } 318 319 // removeElement is used to remove a given list element from the cache 320 func (c *LRU[K, V]) removeElement(e *list.Element) { 321 c.evictList.Remove(e) 322 kv := e.Value.(*entry[K, V]) 323 delete(c.items, kv.key) 324 if c.onEvict != nil { 325 c.onEvict(kv.key, kv.value) 326 } 327 }