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  }