github.com/scottcagno/storage@v1.8.0/pkg/generic/cache/lru.go (about) 1 package cache 2 3 import ( 4 "fmt" 5 "log" 6 "sync" 7 ) 8 9 // DefaultSize is the max size of the cache before 10 // the older items automatically get evicted 11 const DefaultSize = 256 12 13 // item is an item in the cache (doubly linked) 14 type item[K comparable, V any] struct { 15 key K 16 value V 17 prev, next *item[K, V] 18 } 19 20 func (i *item[K, V]) String() string { 21 ss := fmt.Sprintf("item[key=%v, value=%v, prev=%p, next=%p]", i.key, i.value, i.prev, i.next) 22 return ss 23 } 24 25 // LRU is an LRU cache 26 type LRU[K comparable, V any] struct { 27 size int // max num of items 28 items map[K]*item[K, V] // actives items 29 head, tail *item[K, V] // head and tail of list 30 mu sync.RWMutex 31 } 32 33 func NewLRU[K comparable, V any](size int) *LRU[K, V] { 34 if size < 1 { 35 size = DefaultSize 36 } 37 lru := &LRU[K, V]{ 38 size: size, 39 items: make(map[K]*item[K, V], size), 40 head: new(item[K, V]), 41 tail: new(item[K, V]), 42 } 43 lru.head.next = lru.tail 44 lru.tail.prev = lru.head 45 return lru 46 } 47 48 func (l *LRU[K, V]) init(size int) { 49 if l.size < 1 { 50 size = DefaultSize 51 } 52 l.size = size 53 l.items = make(map[K]*item[K, V], size) 54 l.head = new(item[K, V]) 55 l.tail = new(item[K, V]) 56 l.head.next = l.tail 57 l.tail.prev = l.head 58 } 59 60 // evict pops, removes and returns (effectively evicting) an item 61 func (l *LRU[K, V]) evict() *item[K, V] { 62 i := l.tail.prev 63 l.pop(i) 64 delete(l.items, i.key) 65 return i 66 } 67 68 // pop, stack operation 69 func (l *LRU[K, V]) pop(i *item[K, V]) { 70 i.prev.next = i.next 71 i.next.prev = i.prev 72 } 73 74 // push, stack operation 75 func (l *LRU[K, V]) push(i *item[K, V]) { 76 l.head.next.prev = i 77 i.next = l.head.next 78 i.prev = l.head 79 l.head.next = i 80 } 81 82 // Resize sets the max size of the LRU cache and returns the evicted items. It will panic 83 // if the size is less than one item. I the value is less than the number of items in the 84 // cache, then items will be evicted. 85 func (l *LRU[K, V]) Resize(size int) (ekeys, evals []interface{}) { 86 l.mu.Lock() 87 defer l.mu.Unlock() 88 if size < 1 { 89 log.Panicln("invalid size") 90 } 91 for size < len(l.items) { 92 i := l.evict() 93 ekeys, evals = append(ekeys, i.key), append(evals, i.value) 94 } 95 l.size = size 96 return ekeys, evals 97 } 98 99 // Len returns the current length of the cache 100 func (l *LRU[K, V]) Len() int { 101 l.mu.Lock() 102 defer l.mu.Unlock() 103 return len(l.items) 104 } 105 106 // SetEvicted inserts or replaces a value for a given key. 107 // The item is returned if this operation causes an eviction. 108 func (l *LRU[K, V]) SetEvicted(key K, value V) (prev V, replaced bool, ekey K, eval V, evicted bool) { 109 l.mu.Lock() 110 defer l.mu.Unlock() 111 if l.items == nil { 112 l.init(l.size) 113 } 114 i := l.items[key] 115 if i == nil { 116 if len(l.items) == l.size { 117 i = l.evict() 118 ekey, eval, evicted = i.key, i.value, true 119 } else { 120 i = new(item[K, V]) 121 } 122 i.key, i.value = key, value 123 l.push(i) 124 l.items[key] = i 125 } else { 126 prev, replaced = i.value, true 127 i.value = value 128 if l.head.next != i { 129 l.pop(i) 130 l.push(i) 131 } 132 } 133 return prev, replaced, ekey, eval, evicted 134 } 135 136 // Set inserts or replaces a value for the given key 137 func (l *LRU[K, V]) Set(key K, value V) (V, bool) { 138 prev, replaced, _, _, _ := l.SetEvicted(key, value) 139 return prev, replaced 140 } 141 142 // Get returns a value for the given key (if it exists) 143 func (l *LRU[K, V]) Get(key K) (V, bool) { 144 l.mu.Lock() 145 defer l.mu.Unlock() 146 i := l.items[key] 147 if i == nil { 148 return *new(V), false 149 } 150 if l.head.next != i { 151 l.pop(i) 152 l.push(i) 153 } 154 return i.value, true 155 } 156 157 // Del removes and value for the given key (if it exists) 158 func (l *LRU[K, V]) Del(key K) (V, bool) { 159 l.mu.Lock() 160 defer l.mu.Unlock() 161 i := l.items[key] 162 if i == nil { 163 return *new(V), false 164 } 165 delete(l.items, key) 166 l.pop(i) 167 return i.value, true 168 } 169 170 // Range iterates over all keys and values in the order of most 171 // recently used to least recently used items. 172 func (l *LRU[K, V]) Range(iter func(key K, value V) bool) { 173 l.mu.Lock() 174 defer l.mu.Unlock() 175 if head := l.head; head != nil { 176 i := head.next 177 for i != l.tail { 178 if !iter(i.key, i.value) { 179 return 180 } 181 i = i.next 182 } 183 } 184 } 185 186 // Reverse iterates over all keys and values in the order of least 187 // recently used to most recently used items. 188 func (l *LRU[K, V]) Reverse(iter func(key K, value V) bool) { 189 l.mu.Lock() 190 defer l.mu.Unlock() 191 if tail := l.tail; tail != nil { 192 i := tail.prev 193 for i != l.head { 194 if !iter(i.key, i.value) { 195 return 196 } 197 i = i.prev 198 } 199 } 200 } 201 202 func (l *LRU[K, V]) String() string { 203 ss := fmt.Sprintf("lur:\n") 204 ss += fmt.Sprintf("\tsize=%d\n", l.size) 205 ss += fmt.Sprintf("\thead=%s\n", l.head) 206 ss += fmt.Sprintf("\ttail=%s\n", l.tail) 207 return ss 208 //size int 209 //items map[K]*item[K, V] 210 //head *item[K, V] 211 //tail *item[K, V] 212 }