github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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 item, ok := c.items[key] 51 if ok { 52 // Already exists in cache. 53 item.value = value 54 c.items[key] = item 55 c.list.moveToFront(item.elem) 56 return false 57 } 58 59 var elem *listElem[K] 60 if c.Len() >= c.cap { 61 elem = c.list.removeLast() 62 delete(c.items, elem.v) 63 evicted = true 64 } else { 65 elem = new(listElem[K]) 66 } 67 68 // Store the new item. 69 // Note that, if another item was evicted, we re-use its list element here. 70 elem.v = key 71 c.items[key] = cacheItem[K, V]{elem, value} 72 c.list.pushElem(elem) 73 return evicted 74 } 75 76 // Contains reports whether the given key exists in the cache. 77 func (c *BasicLRU[K, V]) Contains(key K) bool { 78 _, ok := c.items[key] 79 return ok 80 } 81 82 // Get retrieves a value from the cache. This marks the key as recently used. 83 func (c *BasicLRU[K, V]) Get(key K) (value V, ok bool) { 84 item, ok := c.items[key] 85 if !ok { 86 return value, false 87 } 88 c.list.moveToFront(item.elem) 89 return item.value, true 90 } 91 92 // GetOldest retrieves the least-recently-used item. 93 // Note that this does not update the item's recency. 94 func (c *BasicLRU[K, V]) GetOldest() (key K, value V, ok bool) { 95 lastElem := c.list.last() 96 if lastElem == nil { 97 return key, value, false 98 } 99 key = lastElem.v 100 item := c.items[key] 101 return key, item.value, true 102 } 103 104 // Len returns the current number of items in the cache. 105 func (c *BasicLRU[K, V]) Len() int { 106 return len(c.items) 107 } 108 109 // Peek retrieves a value from the cache, but does not mark the key as recently used. 110 func (c *BasicLRU[K, V]) Peek(key K) (value V, ok bool) { 111 item, ok := c.items[key] 112 return item.value, ok 113 } 114 115 // Purge empties the cache. 116 func (c *BasicLRU[K, V]) Purge() { 117 c.list.init() 118 clear(c.items) 119 } 120 121 // Remove drops an item from the cache. Returns true if the key was present in cache. 122 func (c *BasicLRU[K, V]) Remove(key K) bool { 123 item, ok := c.items[key] 124 if ok { 125 delete(c.items, key) 126 c.list.remove(item.elem) 127 } 128 return ok 129 } 130 131 // RemoveOldest drops the least recently used item. 132 func (c *BasicLRU[K, V]) RemoveOldest() (key K, value V, ok bool) { 133 lastElem := c.list.last() 134 if lastElem == nil { 135 return key, value, false 136 } 137 138 key = lastElem.v 139 item := c.items[key] 140 delete(c.items, key) 141 c.list.remove(lastElem) 142 return key, item.value, true 143 } 144 145 // Keys returns all keys in the cache. 146 func (c *BasicLRU[K, V]) Keys() []K { 147 keys := make([]K, 0, len(c.items)) 148 return c.list.appendTo(keys) 149 } 150 151 // list is a doubly-linked list holding items of type he. 152 // The zero value is not valid, use newList to create lists. 153 type list[T any] struct { 154 root listElem[T] 155 } 156 157 type listElem[T any] struct { 158 next *listElem[T] 159 prev *listElem[T] 160 v T 161 } 162 163 func newList[T any]() *list[T] { 164 l := new(list[T]) 165 l.init() 166 return l 167 } 168 169 // init reinitializes the list, making it empty. 170 func (l *list[T]) init() { 171 l.root.next = &l.root 172 l.root.prev = &l.root 173 } 174 175 // pushElem adds an element to the front of the list. 176 func (l *list[T]) pushElem(e *listElem[T]) { 177 e.prev = &l.root 178 e.next = l.root.next 179 l.root.next = e 180 e.next.prev = e 181 } 182 183 // moveToFront makes 'node' the head of the list. 184 func (l *list[T]) moveToFront(e *listElem[T]) { 185 e.prev.next = e.next 186 e.next.prev = e.prev 187 l.pushElem(e) 188 } 189 190 // remove removes an element from the list. 191 func (l *list[T]) remove(e *listElem[T]) { 192 e.prev.next = e.next 193 e.next.prev = e.prev 194 e.next, e.prev = nil, nil 195 } 196 197 // removeLast removes the last element of the list. 198 func (l *list[T]) removeLast() *listElem[T] { 199 last := l.last() 200 if last != nil { 201 l.remove(last) 202 } 203 return last 204 } 205 206 // last returns the last element of the list, or nil if the list is empty. 207 func (l *list[T]) last() *listElem[T] { 208 e := l.root.prev 209 if e == &l.root { 210 return nil 211 } 212 return e 213 } 214 215 // appendTo appends all list elements to a slice. 216 func (l *list[T]) appendTo(slice []T) []T { 217 for e := l.root.prev; e != &l.root; e = e.prev { 218 slice = append(slice, e.v) 219 } 220 return slice 221 }