github.com/theQRL/go-zond@v0.1.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 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 for k := range c.items { 119 delete(c.items, k) 120 } 121 } 122 123 // Remove drops an item from the cache. Returns true if the key was present in cache. 124 func (c *BasicLRU[K, V]) Remove(key K) bool { 125 item, ok := c.items[key] 126 if ok { 127 delete(c.items, key) 128 c.list.remove(item.elem) 129 } 130 return ok 131 } 132 133 // RemoveOldest drops the least recently used item. 134 func (c *BasicLRU[K, V]) RemoveOldest() (key K, value V, ok bool) { 135 lastElem := c.list.last() 136 if lastElem == nil { 137 return key, value, false 138 } 139 140 key = lastElem.v 141 item := c.items[key] 142 delete(c.items, key) 143 c.list.remove(lastElem) 144 return key, item.value, true 145 } 146 147 // Keys returns all keys in the cache. 148 func (c *BasicLRU[K, V]) Keys() []K { 149 keys := make([]K, 0, len(c.items)) 150 return c.list.appendTo(keys) 151 } 152 153 // list is a doubly-linked list holding items of type he. 154 // The zero value is not valid, use newList to create lists. 155 type list[T any] struct { 156 root listElem[T] 157 } 158 159 type listElem[T any] struct { 160 next *listElem[T] 161 prev *listElem[T] 162 v T 163 } 164 165 func newList[T any]() *list[T] { 166 l := new(list[T]) 167 l.init() 168 return l 169 } 170 171 // init reinitializes the list, making it empty. 172 func (l *list[T]) init() { 173 l.root.next = &l.root 174 l.root.prev = &l.root 175 } 176 177 // push adds an element to the front of the list. 178 func (l *list[T]) pushElem(e *listElem[T]) { 179 e.prev = &l.root 180 e.next = l.root.next 181 l.root.next = e 182 e.next.prev = e 183 } 184 185 // moveToFront makes 'node' the head of the list. 186 func (l *list[T]) moveToFront(e *listElem[T]) { 187 e.prev.next = e.next 188 e.next.prev = e.prev 189 l.pushElem(e) 190 } 191 192 // remove removes an element from the list. 193 func (l *list[T]) remove(e *listElem[T]) { 194 e.prev.next = e.next 195 e.next.prev = e.prev 196 e.next, e.prev = nil, nil 197 } 198 199 // removeLast removes the last element of the list. 200 func (l *list[T]) removeLast() *listElem[T] { 201 last := l.last() 202 if last != nil { 203 l.remove(last) 204 } 205 return last 206 } 207 208 // last returns the last element of the list, or nil if the list is empty. 209 func (l *list[T]) last() *listElem[T] { 210 e := l.root.prev 211 if e == &l.root { 212 return nil 213 } 214 return e 215 } 216 217 // appendTo appends all list elements to a slice. 218 func (l *list[T]) appendTo(slice []T) []T { 219 for e := l.root.prev; e != &l.root; e = e.prev { 220 slice = append(slice, e.v) 221 } 222 return slice 223 }