github.com/ethereum/go-ethereum@v1.16.1/common/lru/basiclru.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package lru implements generically-typed LRU caches. 18 package lru 19 20 // BasicLRU is a simple LRU cache. 21 // 22 // This type is not safe for concurrent use. 23 // The zero value is not valid, instances must be created using NewCache. 24 type BasicLRU[K comparable, V any] struct { 25 list *list[K] 26 items map[K]cacheItem[K, V] 27 cap int 28 } 29 30 type cacheItem[K any, V any] struct { 31 elem *listElem[K] 32 value V 33 } 34 35 // NewBasicLRU creates a new LRU cache. 36 func NewBasicLRU[K comparable, V any](capacity int) BasicLRU[K, V] { 37 if capacity <= 0 { 38 capacity = 1 39 } 40 c := BasicLRU[K, V]{ 41 items: make(map[K]cacheItem[K, V]), 42 list: newList[K](), 43 cap: capacity, 44 } 45 return c 46 } 47 48 // Add adds a value to the cache. Returns true if an item was evicted to store the new item. 49 func (c *BasicLRU[K, V]) Add(key K, value V) (evicted bool) { 50 _, _, evicted = c.Add3(key, value) 51 return evicted 52 } 53 54 // Add3 adds a value to the cache. If an item was evicted to store the new one, it returns the evicted item. 55 func (c *BasicLRU[K, V]) Add3(key K, value V) (ek K, ev V, evicted bool) { 56 item, ok := c.items[key] 57 if ok { 58 item.value = value 59 c.items[key] = item 60 c.list.moveToFront(item.elem) 61 return ek, ev, false 62 } 63 64 var elem *listElem[K] 65 if c.Len() >= c.cap { 66 elem = c.list.removeLast() 67 evicted = true 68 ek = elem.v 69 ev = c.items[ek].value 70 delete(c.items, ek) 71 } else { 72 elem = new(listElem[K]) 73 } 74 75 // Store the new item. 76 // Note that if another item was evicted, we re-use its list element here. 77 elem.v = key 78 c.items[key] = cacheItem[K, V]{elem, value} 79 c.list.pushElem(elem) 80 return ek, ev, evicted 81 } 82 83 // Contains reports whether the given key exists in the cache. 84 func (c *BasicLRU[K, V]) Contains(key K) bool { 85 _, ok := c.items[key] 86 return ok 87 } 88 89 // Get retrieves a value from the cache. This marks the key as recently used. 90 func (c *BasicLRU[K, V]) Get(key K) (value V, ok bool) { 91 item, ok := c.items[key] 92 if !ok { 93 return value, false 94 } 95 c.list.moveToFront(item.elem) 96 return item.value, true 97 } 98 99 // GetOldest retrieves the least-recently-used item. 100 // Note that this does not update the item's recency. 101 func (c *BasicLRU[K, V]) GetOldest() (key K, value V, ok bool) { 102 lastElem := c.list.last() 103 if lastElem == nil { 104 return key, value, false 105 } 106 key = lastElem.v 107 item := c.items[key] 108 return key, item.value, true 109 } 110 111 // Len returns the current number of items in the cache. 112 func (c *BasicLRU[K, V]) Len() int { 113 return len(c.items) 114 } 115 116 // Peek retrieves a value from the cache, but does not mark the key as recently used. 117 func (c *BasicLRU[K, V]) Peek(key K) (value V, ok bool) { 118 item, ok := c.items[key] 119 return item.value, ok 120 } 121 122 // Purge empties the cache. 123 func (c *BasicLRU[K, V]) Purge() { 124 c.list.init() 125 clear(c.items) 126 } 127 128 // Remove drops an item from the cache. Returns true if the key was present in cache. 129 func (c *BasicLRU[K, V]) Remove(key K) bool { 130 item, ok := c.items[key] 131 if ok { 132 delete(c.items, key) 133 c.list.remove(item.elem) 134 } 135 return ok 136 } 137 138 // RemoveOldest drops the least recently used item. 139 func (c *BasicLRU[K, V]) RemoveOldest() (key K, value V, ok bool) { 140 lastElem := c.list.last() 141 if lastElem == nil { 142 return key, value, false 143 } 144 145 key = lastElem.v 146 item := c.items[key] 147 delete(c.items, key) 148 c.list.remove(lastElem) 149 return key, item.value, true 150 } 151 152 // Keys returns all keys in the cache. 153 func (c *BasicLRU[K, V]) Keys() []K { 154 keys := make([]K, 0, len(c.items)) 155 return c.list.appendTo(keys) 156 } 157 158 // list is a doubly-linked list holding items of type he. 159 // The zero value is not valid, use newList to create lists. 160 type list[T any] struct { 161 root listElem[T] 162 } 163 164 type listElem[T any] struct { 165 next *listElem[T] 166 prev *listElem[T] 167 v T 168 } 169 170 func newList[T any]() *list[T] { 171 l := new(list[T]) 172 l.init() 173 return l 174 } 175 176 // init reinitializes the list, making it empty. 177 func (l *list[T]) init() { 178 l.root.next = &l.root 179 l.root.prev = &l.root 180 } 181 182 // pushElem adds an element to the front of the list. 183 func (l *list[T]) pushElem(e *listElem[T]) { 184 e.prev = &l.root 185 e.next = l.root.next 186 l.root.next = e 187 e.next.prev = e 188 } 189 190 // moveToFront makes 'node' the head of the list. 191 func (l *list[T]) moveToFront(e *listElem[T]) { 192 e.prev.next = e.next 193 e.next.prev = e.prev 194 l.pushElem(e) 195 } 196 197 // remove removes an element from the list. 198 func (l *list[T]) remove(e *listElem[T]) { 199 e.prev.next = e.next 200 e.next.prev = e.prev 201 e.next, e.prev = nil, nil 202 } 203 204 // removeLast removes the last element of the list. 205 func (l *list[T]) removeLast() *listElem[T] { 206 last := l.last() 207 if last != nil { 208 l.remove(last) 209 } 210 return last 211 } 212 213 // last returns the last element of the list, or nil if the list is empty. 214 func (l *list[T]) last() *listElem[T] { 215 e := l.root.prev 216 if e == &l.root { 217 return nil 218 } 219 return e 220 } 221 222 // appendTo appends all list elements to a slice. 223 func (l *list[T]) appendTo(slice []T) []T { 224 for e := l.root.prev; e != &l.root; e = e.prev { 225 slice = append(slice, e.v) 226 } 227 return slice 228 }