github.com/searKing/golang/go@v1.2.74/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 implements a non-thread safe fixed size LRU cache
    19  type LRU[K comparable, V any] struct {
    20  	size int // LRU size limit
    21  
    22  	evictList *list.List          // sequence order for lru: Latest, Old, Older, ..., Oldest
    23  	items     map[K]*list.Element // index to element access accelerate
    24  	onEvict   EvictCallback[K, V]
    25  }
    26  
    27  // entry is used to hold a value in the evictList
    28  type entry[K comparable, V any] struct {
    29  	key   K
    30  	value V
    31  }
    32  
    33  // New constructs an LRU of the given size
    34  func New[K comparable, V any](size int) *LRU[K, V] {
    35  	c := &LRU[K, V]{
    36  		size: size,
    37  	}
    38  	return c.Init()
    39  }
    40  
    41  // SetEvictCallback sets a callback when a cache entry is evicted
    42  func (c *LRU[K, V]) SetEvictCallback(onEvict EvictCallback[K, V]) *LRU[K, V] {
    43  	c.onEvict = onEvict
    44  	return c
    45  }
    46  
    47  // SetEvictCallbackFunc sets a callback func when a cache entry is evicted
    48  func (c *LRU[K, V]) SetEvictCallbackFunc(onEvict func(key K, value V)) *LRU[K, V] {
    49  	c.onEvict = onEvict
    50  	return c
    51  }
    52  
    53  // Init initializes or clears LRU l.
    54  func (c *LRU[K, V]) Init() *LRU[K, V] {
    55  	c.evictList = list.New()
    56  	c.items = make(map[K]*list.Element)
    57  	return c
    58  }
    59  
    60  // Purge is used to completely clear the cache.
    61  func (c *LRU[K, V]) Purge() {
    62  	for k, v := range c.items {
    63  		if c.onEvict != nil {
    64  			c.onEvict(k, v.Value.(*entry[K, V]).value)
    65  		}
    66  		delete(c.items, k)
    67  	}
    68  	c.evictList.Init()
    69  }
    70  
    71  // Add adds a value to the cache.  Returns true if an eviction occurred.
    72  func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
    73  	// Check for existing item
    74  	if ent, ok := c.items[key]; ok {
    75  		c.evictList.MoveToFront(ent)
    76  		ent.Value.(*entry[K, V]).value = value
    77  		return false
    78  	}
    79  
    80  	// Add new item
    81  	ent := &entry[K, V]{key, value}
    82  	entry := c.evictList.PushFront(ent)
    83  	c.items[key] = entry
    84  
    85  	evict := c.evictList.Len() > c.size
    86  	// Verify size not exceeded
    87  	if evict {
    88  		c.removeOldest()
    89  	}
    90  	return evict
    91  }
    92  
    93  // Get looks up a key's value from the cache.
    94  func (c *LRU[K, V]) Get(key K) (value V, ok bool) {
    95  	if ent, ok := c.items[key]; ok {
    96  		c.evictList.MoveToFront(ent)
    97  		if ent.Value.(*entry[K, V]) == nil {
    98  			var zero V
    99  			return zero, false
   100  		}
   101  		return ent.Value.(*entry[K, V]).value, true
   102  	}
   103  	return
   104  }
   105  
   106  // Contains checks if a key is in the cache, without updating the recent-ness
   107  // or deleting it for being stale.
   108  func (c *LRU[K, V]) Contains(key K) (ok bool) {
   109  	_, ok = c.items[key]
   110  	return ok
   111  }
   112  
   113  // Peek returns the key value (or undefined if not found) without updating
   114  // the "recently used"-ness of the key.
   115  func (c *LRU[K, V]) Peek(key K) (value V, ok bool) {
   116  	var ent *list.Element
   117  	if ent, ok = c.items[key]; ok {
   118  		return ent.Value.(*entry[K, V]).value, true
   119  	}
   120  	var zero V
   121  	return zero, ok
   122  }
   123  
   124  // Remove removes the provided key from the cache, returning if the
   125  // key was contained.
   126  func (c *LRU[K, V]) Remove(key K) (present bool) {
   127  	if ent, ok := c.items[key]; ok {
   128  		c.removeElement(ent)
   129  		return true
   130  	}
   131  	return false
   132  }
   133  
   134  // RemoveOldest removes the oldest item from the cache.
   135  func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
   136  	ent := c.evictList.Back()
   137  	if ent != nil {
   138  		c.removeElement(ent)
   139  		kv := ent.Value.(*entry[K, V])
   140  		return kv.key, kv.value, true
   141  	}
   142  	var zeroK K
   143  	var zeroV V
   144  	return zeroK, zeroV, false
   145  }
   146  
   147  // GetOldest returns the oldest entry
   148  func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) {
   149  	ent := c.evictList.Back()
   150  	if ent != nil {
   151  		kv := ent.Value.(*entry[K, V])
   152  		return kv.key, kv.value, true
   153  	}
   154  	var zeroK K
   155  	var zeroV V
   156  	return zeroK, zeroV, false
   157  }
   158  
   159  // Keys returns a slice of the keys in the cache, from oldest to newest.
   160  func (c *LRU[K, V]) Keys() []K {
   161  	keys := make([]K, len(c.items))
   162  	i := 0
   163  	for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
   164  		keys[i] = ent.Value.(*entry[K, V]).key
   165  		i++
   166  	}
   167  	return keys
   168  }
   169  
   170  // Len returns the number of items in the cache.
   171  func (c *LRU[K, V]) Len() int {
   172  	return c.evictList.Len()
   173  }
   174  
   175  // Cap returns the capacity of the cache.
   176  func (c *LRU[K, V]) Cap() int {
   177  	return c.size
   178  }
   179  
   180  // Resize changes the cache size.
   181  func (c *LRU[K, V]) Resize(size int) (evicted int) {
   182  	diff := c.Len() - size
   183  	if diff < 0 {
   184  		diff = 0
   185  	}
   186  	for i := 0; i < diff; i++ {
   187  		c.removeOldest()
   188  	}
   189  	c.size = size
   190  	return diff
   191  }
   192  
   193  // removeOldest removes the oldest item from the cache.
   194  func (c *LRU[K, V]) removeOldest() {
   195  	ent := c.evictList.Back()
   196  	if ent != nil {
   197  		c.removeElement(ent)
   198  	}
   199  }
   200  
   201  // removeElement is used to remove a given list element from the cache
   202  func (c *LRU[K, V]) removeElement(e *list.Element) {
   203  	c.evictList.Remove(e)
   204  	kv := e.Value.(*entry[K, V])
   205  	delete(c.items, kv.key)
   206  	if c.onEvict != nil {
   207  		c.onEvict(kv.key, kv.value)
   208  	}
   209  }