github.com/MetalBlockchain/metalgo@v1.11.9/cache/lru_cache.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package cache 5 6 import ( 7 "sync" 8 9 "github.com/MetalBlockchain/metalgo/utils" 10 "github.com/MetalBlockchain/metalgo/utils/linked" 11 ) 12 13 var _ Cacher[struct{}, struct{}] = (*LRU[struct{}, struct{}])(nil) 14 15 // LRU is a key value store with bounded size. If the size is attempted to be 16 // exceeded, then an element is removed from the cache before the insertion is 17 // done, based on evicting the least recently used value. 18 type LRU[K comparable, V any] struct { 19 lock sync.Mutex 20 elements *linked.Hashmap[K, V] 21 // If set to <= 0, will be set internally to 1. 22 Size int 23 } 24 25 func (c *LRU[K, V]) Put(key K, value V) { 26 c.lock.Lock() 27 defer c.lock.Unlock() 28 29 c.put(key, value) 30 } 31 32 func (c *LRU[K, V]) Get(key K) (V, bool) { 33 c.lock.Lock() 34 defer c.lock.Unlock() 35 36 return c.get(key) 37 } 38 39 func (c *LRU[K, _]) Evict(key K) { 40 c.lock.Lock() 41 defer c.lock.Unlock() 42 43 c.evict(key) 44 } 45 46 func (c *LRU[_, _]) Flush() { 47 c.lock.Lock() 48 defer c.lock.Unlock() 49 50 c.flush() 51 } 52 53 func (c *LRU[_, _]) Len() int { 54 c.lock.Lock() 55 defer c.lock.Unlock() 56 57 return c.len() 58 } 59 60 func (c *LRU[_, _]) PortionFilled() float64 { 61 c.lock.Lock() 62 defer c.lock.Unlock() 63 64 return c.portionFilled() 65 } 66 67 func (c *LRU[K, V]) put(key K, value V) { 68 c.resize() 69 70 if c.elements.Len() == c.Size { 71 oldestKey, _, _ := c.elements.Oldest() 72 c.elements.Delete(oldestKey) 73 } 74 c.elements.Put(key, value) 75 } 76 77 func (c *LRU[K, V]) get(key K) (V, bool) { 78 c.resize() 79 80 val, ok := c.elements.Get(key) 81 if !ok { 82 return utils.Zero[V](), false 83 } 84 c.elements.Put(key, val) // Mark [k] as MRU. 85 return val, true 86 } 87 88 func (c *LRU[K, _]) evict(key K) { 89 c.resize() 90 91 c.elements.Delete(key) 92 } 93 94 func (c *LRU[K, V]) flush() { 95 if c.elements != nil { 96 c.elements.Clear() 97 } 98 } 99 100 func (c *LRU[_, _]) len() int { 101 if c.elements == nil { 102 return 0 103 } 104 return c.elements.Len() 105 } 106 107 func (c *LRU[_, _]) portionFilled() float64 { 108 return float64(c.len()) / float64(c.Size) 109 } 110 111 // Initializes [c.elements] if it's nil. 112 // Sets [c.size] to 1 if it's <= 0. 113 // Removes oldest elements to make number of elements 114 // in the cache == [c.size] if necessary. 115 func (c *LRU[K, V]) resize() { 116 if c.elements == nil { 117 c.elements = linked.NewHashmap[K, V]() 118 } 119 if c.Size <= 0 { 120 c.Size = 1 121 } 122 for c.elements.Len() > c.Size { 123 oldestKey, _, _ := c.elements.Oldest() 124 c.elements.Delete(oldestKey) 125 } 126 }