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  }