github.com/MetalBlockchain/metalgo@v1.11.9/cache/lru_cache.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package cache
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/MetalBlockchain/metalgo/utils"
    10  	"github.com/MetalBlockchain/metalgo/utils/linked"
    11  )
    12  
    13  var _ Cacher[struct{}, struct{}] = (*LRU[struct{}, struct{}])(nil)
    14  
    15  // LRU is a key value store with bounded size. If the size is attempted to be
    16  // exceeded, then an element is removed from the cache before the insertion is
    17  // done, based on evicting the least recently used value.
    18  type LRU[K comparable, V any] struct {
    19  	lock     sync.Mutex
    20  	elements *linked.Hashmap[K, V]
    21  	// If set to <= 0, will be set internally to 1.
    22  	Size int
    23  }
    24  
    25  func (c *LRU[K, V]) Put(key K, value V) {
    26  	c.lock.Lock()
    27  	defer c.lock.Unlock()
    28  
    29  	c.put(key, value)
    30  }
    31  
    32  func (c *LRU[K, V]) Get(key K) (V, bool) {
    33  	c.lock.Lock()
    34  	defer c.lock.Unlock()
    35  
    36  	return c.get(key)
    37  }
    38  
    39  func (c *LRU[K, _]) Evict(key K) {
    40  	c.lock.Lock()
    41  	defer c.lock.Unlock()
    42  
    43  	c.evict(key)
    44  }
    45  
    46  func (c *LRU[_, _]) Flush() {
    47  	c.lock.Lock()
    48  	defer c.lock.Unlock()
    49  
    50  	c.flush()
    51  }
    52  
    53  func (c *LRU[_, _]) Len() int {
    54  	c.lock.Lock()
    55  	defer c.lock.Unlock()
    56  
    57  	return c.len()
    58  }
    59  
    60  func (c *LRU[_, _]) PortionFilled() float64 {
    61  	c.lock.Lock()
    62  	defer c.lock.Unlock()
    63  
    64  	return c.portionFilled()
    65  }
    66  
    67  func (c *LRU[K, V]) put(key K, value V) {
    68  	c.resize()
    69  
    70  	if c.elements.Len() == c.Size {
    71  		oldestKey, _, _ := c.elements.Oldest()
    72  		c.elements.Delete(oldestKey)
    73  	}
    74  	c.elements.Put(key, value)
    75  }
    76  
    77  func (c *LRU[K, V]) get(key K) (V, bool) {
    78  	c.resize()
    79  
    80  	val, ok := c.elements.Get(key)
    81  	if !ok {
    82  		return utils.Zero[V](), false
    83  	}
    84  	c.elements.Put(key, val) // Mark [k] as MRU.
    85  	return val, true
    86  }
    87  
    88  func (c *LRU[K, _]) evict(key K) {
    89  	c.resize()
    90  
    91  	c.elements.Delete(key)
    92  }
    93  
    94  func (c *LRU[K, V]) flush() {
    95  	if c.elements != nil {
    96  		c.elements.Clear()
    97  	}
    98  }
    99  
   100  func (c *LRU[_, _]) len() int {
   101  	if c.elements == nil {
   102  		return 0
   103  	}
   104  	return c.elements.Len()
   105  }
   106  
   107  func (c *LRU[_, _]) portionFilled() float64 {
   108  	return float64(c.len()) / float64(c.Size)
   109  }
   110  
   111  // Initializes [c.elements] if it's nil.
   112  // Sets [c.size] to 1 if it's <= 0.
   113  // Removes oldest elements to make number of elements
   114  // in the cache == [c.size] if necessary.
   115  func (c *LRU[K, V]) resize() {
   116  	if c.elements == nil {
   117  		c.elements = linked.NewHashmap[K, V]()
   118  	}
   119  	if c.Size <= 0 {
   120  		c.Size = 1
   121  	}
   122  	for c.elements.Len() > c.Size {
   123  		oldestKey, _, _ := c.elements.Oldest()
   124  		c.elements.Delete(oldestKey)
   125  	}
   126  }