github.com/searKing/golang/go@v1.2.117/exp/container/lru/lru.go (about)

     1  // Copyright 2022 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package lru
     6  
     7  import (
     8  	"container/list"
     9  )
    10  
    11  // EvictCallback is used to get a callback when a cache entry is evicted
    12  type EvictCallback[K comparable, V any] func(key K, value V)
    13  
    14  type EvictCallbackFunc[K comparable, V any] interface {
    15  	Evict(key K, value V)
    16  }
    17  
    18  // LRU is like a Go map[K]V but implements a non-thread safe fixed size LRU cache.
    19  // Loads, stores, and deletes run in amortized constant time.
    20  type LRU[K comparable, V any] struct {
    21  	size int // LRU size limit
    22  
    23  	evictList *list.List          // sequence order for lru: Latest, Old, Older, ..., Oldest
    24  	items     map[K]*list.Element // index to element access accelerate
    25  	onEvict   EvictCallback[K, V]
    26  }
    27  
    28  // entry is used to hold a value in the evictList
    29  type entry[K comparable, V any] struct {
    30  	key   K
    31  	value V
    32  }
    33  
    34  // New constructs an LRU of the given size
    35  func New[K comparable, V any](size int) *LRU[K, V] {
    36  	c := &LRU[K, V]{
    37  		size: size,
    38  	}
    39  	return c.Init()
    40  }
    41  
    42  // SetEvictCallback sets a callback when a cache entry is evicted
    43  func (c *LRU[K, V]) SetEvictCallback(onEvict EvictCallback[K, V]) *LRU[K, V] {
    44  	c.onEvict = onEvict
    45  	return c
    46  }
    47  
    48  // SetEvictCallbackFunc sets a callback func when a cache entry is evicted
    49  //
    50  // Deprecated, use SetEvictCallback instead.
    51  func (c *LRU[K, V]) SetEvictCallbackFunc(onEvict func(key K, value V)) *LRU[K, V] {
    52  	c.onEvict = onEvict
    53  	return c
    54  }
    55  
    56  // Init initializes or clears LRU l.
    57  func (c *LRU[K, V]) Init() *LRU[K, V] {
    58  	c.evictList = list.New()
    59  	c.items = make(map[K]*list.Element)
    60  	return c
    61  }
    62  
    63  // Len returns the number of items in the cache.
    64  func (c *LRU[K, V]) Len() int {
    65  	return c.evictList.Len()
    66  }
    67  
    68  // Cap returns the capacity of the cache.
    69  func (c *LRU[K, V]) Cap() int {
    70  	return c.size
    71  }
    72  
    73  // Resize changes the cache size.
    74  func (c *LRU[K, V]) Resize(size int) (evicted int) {
    75  	diff := c.Len() - size
    76  	if diff < 0 {
    77  		diff = 0
    78  	}
    79  	for i := 0; i < diff; i++ {
    80  		c.removeOldest()
    81  	}
    82  	c.size = size
    83  	return diff
    84  }
    85  
    86  // Purge is used to completely clear the cache.
    87  func (c *LRU[K, V]) Purge() {
    88  	for k, v := range c.items {
    89  		delete(c.items, k)
    90  		if c.onEvict != nil {
    91  			c.onEvict(k, v.Value.(*entry[K, V]).value)
    92  		}
    93  	}
    94  	c.evictList.Init()
    95  }
    96  
    97  // Load returns the value stored in the cache for a key, or zero if no
    98  // value is present.
    99  // The ok result indicates whether value was found in the cache.
   100  func (c *LRU[K, V]) Load(key K) (value V, ok bool) {
   101  	return c.load(key, true)
   102  }
   103  
   104  // Get looks up a key's value from the cache,
   105  // with updating the "recently used"-ness of the key.
   106  func (c *LRU[K, V]) Get(key K) (value V, ok bool) {
   107  	return c.Load(key)
   108  }
   109  
   110  // Peek returns the value stored in the cache for a key, or zero if no
   111  // value is present.
   112  // The ok result indicates whether value was found in the cache.
   113  // Without updating the "recently used"-ness of the key.
   114  func (c *LRU[K, V]) Peek(key K) (value V, ok bool) {
   115  	return c.load(key, false)
   116  }
   117  
   118  // Contains reports whether key is within the cache.
   119  // The ok result indicates whether value was found in the cache.
   120  // Without updating the "recently used"-ness of the key.
   121  func (c *LRU[K, V]) Contains(key K) (ok bool) {
   122  	_, ok = c.Peek(key)
   123  	return ok
   124  }
   125  
   126  func (c *LRU[K, V]) load(key K, update bool) (value V, ok bool) {
   127  	if e, ok := c.items[key]; ok {
   128  		if update {
   129  			// update the "recently used"-ness.
   130  			c.evictList.MoveToFront(e)
   131  		}
   132  		return e.Value.(*entry[K, V]).value, true
   133  	}
   134  	return
   135  }
   136  
   137  // Store sets the value for a key.
   138  func (c *LRU[K, V]) Store(key K, value V) {
   139  	_, _ = c.Swap(key, value)
   140  }
   141  
   142  // Add adds a value to the cache,
   143  // with updating the "recently used"-ness of the key.
   144  // Returns true if an eviction occurred.
   145  func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
   146  	full := c.evictList.Len() >= c.size
   147  	_, loaded := c.Swap(key, value)
   148  
   149  	return !loaded && full
   150  }
   151  
   152  // LoadOrStore returns the existing value for the key if present.
   153  // Otherwise, it stores and returns the given value.
   154  // The loaded result is true if the value was loaded, false if stored.
   155  func (c *LRU[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
   156  	if e, ok := c.items[key]; ok {
   157  		// update the "recently used"-ness.
   158  		c.evictList.MoveToFront(e)
   159  		return e.Value.(*entry[K, V]).value, true
   160  	}
   161  
   162  	// Add new item and update the "recently used"-ness of the key.
   163  	e := &entry[K, V]{key, value}
   164  	c.items[key] = c.evictList.PushFront(e)
   165  
   166  	evict := c.evictList.Len() > c.size
   167  	// Verify size not exceeded
   168  	if evict {
   169  		c.removeOldest()
   170  	}
   171  	return value, false
   172  }
   173  
   174  // LoadAndDelete deletes the value for a key, returning the previous value if any.
   175  // The loaded result reports whether the key was present.
   176  func (c *LRU[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
   177  	if e, ok := c.items[key]; ok {
   178  		c.removeElement(e)
   179  		return e.Value.(*entry[K, V]).value, true
   180  	}
   181  	return
   182  }
   183  
   184  // Delete deletes the value for a key.
   185  func (c *LRU[K, V]) Delete(key K) {
   186  	c.LoadAndDelete(key)
   187  }
   188  
   189  // Remove removes the provided key from the cache, returning true if the
   190  // key was contained.
   191  func (c *LRU[K, V]) Remove(key K) (present bool) {
   192  	_, present = c.LoadAndDelete(key)
   193  	return present
   194  }
   195  
   196  // Swap swaps the value for a key and returns the previous value if any.
   197  // The loaded result reports whether the key was present.
   198  func (c *LRU[K, V]) Swap(key K, value V) (previous V, loaded bool) {
   199  	// Check for existing item
   200  	if e, ok := c.items[key]; ok {
   201  		// update the "recently used"-ness.
   202  		c.evictList.MoveToFront(e)
   203  		previous, e.Value.(*entry[K, V]).value = e.Value.(*entry[K, V]).value, value
   204  		loaded = true
   205  		return previous, loaded
   206  	}
   207  
   208  	// Add new item and update the "recently used"-ness of the key.
   209  	e := &entry[K, V]{key, value}
   210  	// update the "recently used"-ness.
   211  	entry := c.evictList.PushFront(e)
   212  	c.items[key] = entry
   213  
   214  	evict := c.evictList.Len() > c.size
   215  	// Verify size not exceeded
   216  	if evict {
   217  		c.removeOldest()
   218  	}
   219  	return previous, loaded
   220  }
   221  
   222  // CompareAndSwap swaps the old and new values for key
   223  // if the value stored in the map is equal to old.
   224  // The old value must be of a comparable type.
   225  func (c *LRU[K, V]) CompareAndSwap(key K, old, new V) (swapped bool) {
   226  	// Check for existing item
   227  	if e, ok := c.items[key]; ok && any(e.Value.(*entry[K, V]).value) == any(old) {
   228  		// update the "recently used"-ness.
   229  		c.evictList.MoveToFront(e)
   230  		e.Value.(*entry[K, V]).value = new
   231  		return true
   232  	}
   233  	return false
   234  }
   235  
   236  // CompareAndDelete deletes the entry for key if its value is equal to old.
   237  // The old value must be of a comparable type.
   238  //
   239  // If there is no current value for key in the map, CompareAndDelete
   240  // returns false (even if the old value is the nil interface value).
   241  func (c *LRU[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
   242  	// Check for existing item
   243  	if e, ok := c.items[key]; ok && any(e.Value.(*entry[K, V]).value) == any(old) {
   244  		c.removeElement(e)
   245  		return true
   246  	}
   247  	return false
   248  }
   249  
   250  // Keys returns a slice of the keys in the cache, from oldest to newest.
   251  // Without updating the "recently used"-ness of the key.
   252  func (c *LRU[K, V]) Keys() []K {
   253  	keys := make([]K, len(c.items))
   254  	i := 0
   255  	for e := c.evictList.Back(); e != nil; e = e.Prev() {
   256  		keys[i] = e.Value.(*entry[K, V]).key
   257  		i++
   258  	}
   259  	return keys
   260  }
   261  
   262  // Range calls f sequentially for each key and value present in the lru from oldest to newest.
   263  // If f returns false, range stops the iteration.
   264  // Without updating the "recently used"-ness of the key.
   265  func (c *LRU[K, V]) Range(f func(key K, value V) bool) {
   266  	// Iterate through list and print its contents.
   267  	for e := c.evictList.Back(); e != nil; e = e.Prev() {
   268  		if !f(e.Value.(*entry[K, V]).key, e.Value.(*entry[K, V]).value) {
   269  			break
   270  		}
   271  	}
   272  }
   273  
   274  // PeekOldest returns the value stored in the cache for the oldest entry, or zero if no
   275  // value is present.
   276  // The ok result indicates whether value was found in the cache.
   277  // Without updating the "recently used"-ness of the key.
   278  func (c *LRU[K, V]) PeekOldest() (key K, value V, ok bool) {
   279  	e := c.evictList.Back()
   280  	if e != nil {
   281  		kv := e.Value.(*entry[K, V])
   282  		return kv.key, kv.value, true
   283  	}
   284  	return
   285  }
   286  
   287  // PeekAndDeleteOldest deletes the value for a key, returning the previous value if any.
   288  // The loaded result reports whether the key was present.
   289  func (c *LRU[K, V]) PeekAndDeleteOldest() (key K, value V, loaded bool) {
   290  	e := c.evictList.Back()
   291  	if e != nil {
   292  		c.removeElement(e)
   293  		kv := e.Value.(*entry[K, V])
   294  		return kv.key, kv.value, true
   295  	}
   296  	return
   297  }
   298  
   299  // RemoveOldest removes the oldest item from the cache.
   300  func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
   301  	return c.PeekAndDeleteOldest()
   302  }
   303  
   304  // GetOldest returns the oldest entry, without updating the "recently used"-ness
   305  // or deleting it for being stale.
   306  // Without updating the "recently used"-ness of the key.
   307  func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) {
   308  	return c.PeekOldest()
   309  }
   310  
   311  // removeOldest removes the oldest item from the cache.
   312  func (c *LRU[K, V]) removeOldest() {
   313  	e := c.evictList.Back()
   314  	if e != nil {
   315  		c.removeElement(e)
   316  	}
   317  }
   318  
   319  // removeElement is used to remove a given list element from the cache
   320  func (c *LRU[K, V]) removeElement(e *list.Element) {
   321  	c.evictList.Remove(e)
   322  	kv := e.Value.(*entry[K, V])
   323  	delete(c.items, kv.key)
   324  	if c.onEvict != nil {
   325  		c.onEvict(kv.key, kv.value)
   326  	}
   327  }