github.com/yaling888/clash@v1.53.0/common/cache/lrucache.go (about)

     1  package cache
     2  
     3  // Modified by https://github.com/die-net/lrucache
     4  
     5  import (
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/samber/lo"
    10  
    11  	"github.com/yaling888/clash/common/generics/list"
    12  )
    13  
    14  // Option is part of Functional Options Pattern
    15  type Option[K comparable, V any] func(*LruCache[K, V])
    16  
    17  // EvictCallback is used to get a callback when a cache entry is evicted
    18  type EvictCallback[K comparable, V any] func(key K, value V)
    19  
    20  // WithEvict set to evict callback
    21  func WithEvict[K comparable, V any](cb EvictCallback[K, V]) Option[K, V] {
    22  	return func(l *LruCache[K, V]) {
    23  		l.onEvict = cb
    24  	}
    25  }
    26  
    27  // WithUpdateAgeOnGet update expires when Get element
    28  func WithUpdateAgeOnGet[K comparable, V any]() Option[K, V] {
    29  	return func(l *LruCache[K, V]) {
    30  		l.updateAgeOnGet = true
    31  	}
    32  }
    33  
    34  // WithAge defined element max age (second)
    35  func WithAge[K comparable, V any](maxAge int64) Option[K, V] {
    36  	return func(l *LruCache[K, V]) {
    37  		l.maxAge = maxAge
    38  	}
    39  }
    40  
    41  // WithSize defined max length of LruCache
    42  func WithSize[K comparable, V any](maxSize int) Option[K, V] {
    43  	return func(l *LruCache[K, V]) {
    44  		l.maxSize = maxSize
    45  	}
    46  }
    47  
    48  // WithStale decide whether Stale return is enabled.
    49  // If this feature is enabled, element will not get Evicted according to `WithAge`.
    50  func WithStale[K comparable, V any](stale bool) Option[K, V] {
    51  	return func(l *LruCache[K, V]) {
    52  		l.staleReturn = stale
    53  	}
    54  }
    55  
    56  // LruCache is a thread-safe, in-memory lru-cache that evicts the
    57  // least recently used entries from memory when (if set) the entries are
    58  // older than maxAge (in seconds).  Use the New constructor to create one.
    59  type LruCache[K comparable, V any] struct {
    60  	maxAge         int64
    61  	maxSize        int
    62  	mu             sync.Mutex
    63  	cache          map[K]*list.Element[*entry[K, V]]
    64  	lru            *list.List[*entry[K, V]] // Front is least-recent
    65  	updateAgeOnGet bool
    66  	staleReturn    bool
    67  	onEvict        EvictCallback[K, V]
    68  }
    69  
    70  // New creates an LruCache
    71  func New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {
    72  	lc := &LruCache[K, V]{
    73  		lru:   list.New[*entry[K, V]](),
    74  		cache: make(map[K]*list.Element[*entry[K, V]]),
    75  	}
    76  
    77  	for _, option := range options {
    78  		option(lc)
    79  	}
    80  
    81  	return lc
    82  }
    83  
    84  // Get returns any representation of a cached response and a bool
    85  // set to true if the key was found.
    86  func (c *LruCache[K, V]) Get(key K) (V, bool) {
    87  	el := c.get(key)
    88  	if el == nil {
    89  		return lo.Empty[V](), false
    90  	}
    91  	value := el.value
    92  
    93  	return value, true
    94  }
    95  
    96  // GetWithExpire returns any representation of a cached response,
    97  // a time.Time Give expected expires,
    98  // and a bool set to true if the key was found.
    99  // This method will NOT check the maxAge of element and will NOT update the expires.
   100  func (c *LruCache[K, V]) GetWithExpire(key K) (V, time.Time, bool) {
   101  	el := c.get(key)
   102  	if el == nil {
   103  		return lo.Empty[V](), time.Time{}, false
   104  	}
   105  
   106  	return el.value, time.Unix(el.expires, 0), true
   107  }
   108  
   109  // Exist returns if key exist in cache but not put item to the head of linked list
   110  func (c *LruCache[K, V]) Exist(key K) bool {
   111  	c.mu.Lock()
   112  	defer c.mu.Unlock()
   113  
   114  	_, ok := c.cache[key]
   115  	return ok
   116  }
   117  
   118  // Set stores any representation of a response for a given key.
   119  func (c *LruCache[K, V]) Set(key K, value V) {
   120  	expires := int64(0)
   121  	if c.maxAge > 0 {
   122  		expires = time.Now().Unix() + c.maxAge
   123  	}
   124  	c.SetWithExpire(key, value, time.Unix(expires, 0))
   125  }
   126  
   127  // SetWithExpire stores any representation of a response for a given key and given expires.
   128  // The expires time will round to second.
   129  func (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) {
   130  	c.mu.Lock()
   131  	defer c.mu.Unlock()
   132  
   133  	if le, ok := c.cache[key]; ok {
   134  		c.lru.MoveToBack(le)
   135  		e := le.Value
   136  		e.value = value
   137  		e.expires = expires.Unix()
   138  	} else {
   139  		e := &entry[K, V]{key: key, value: value, expires: expires.Unix()}
   140  		c.cache[key] = c.lru.PushBack(e)
   141  
   142  		if c.maxSize > 0 {
   143  			if elLen := c.lru.Len(); elLen > c.maxSize {
   144  				c.deleteElement(c.lru.Front())
   145  			}
   146  		}
   147  	}
   148  
   149  	c.maybeDeleteOldest()
   150  }
   151  
   152  // CloneTo clone and overwrite elements to another LruCache
   153  func (c *LruCache[K, V]) CloneTo(n *LruCache[K, V]) {
   154  	c.mu.Lock()
   155  	defer c.mu.Unlock()
   156  
   157  	n.mu.Lock()
   158  	defer n.mu.Unlock()
   159  
   160  	n.lru = list.New[*entry[K, V]]()
   161  	n.cache = make(map[K]*list.Element[*entry[K, V]])
   162  
   163  	for e := c.lru.Front(); e != nil; e = e.Next() {
   164  		elm := e.Value
   165  		n.cache[elm.key] = n.lru.PushBack(elm)
   166  	}
   167  }
   168  
   169  func (c *LruCache[K, V]) get(key K) *entry[K, V] {
   170  	c.mu.Lock()
   171  	defer c.mu.Unlock()
   172  
   173  	le, ok := c.cache[key]
   174  	if !ok {
   175  		return nil
   176  	}
   177  
   178  	if !c.staleReturn && c.maxAge > 0 && le.Value.expires <= time.Now().Unix() {
   179  		c.deleteElement(le)
   180  		c.maybeDeleteOldest()
   181  
   182  		return nil
   183  	}
   184  
   185  	c.lru.MoveToBack(le)
   186  	el := le.Value
   187  	if c.maxAge > 0 && c.updateAgeOnGet {
   188  		el.expires = time.Now().Unix() + c.maxAge
   189  	}
   190  	return el
   191  }
   192  
   193  // Delete removes the value associated with a key.
   194  func (c *LruCache[K, V]) Delete(key K) {
   195  	c.mu.Lock()
   196  
   197  	if le, ok := c.cache[key]; ok {
   198  		c.deleteElement(le)
   199  	}
   200  
   201  	c.mu.Unlock()
   202  }
   203  
   204  func (c *LruCache[K, V]) maybeDeleteOldest() {
   205  	if !c.staleReturn && c.maxAge > 0 {
   206  		now := time.Now().Unix()
   207  		for le := c.lru.Front(); le != nil && le.Value.expires <= now; le = c.lru.Front() {
   208  			c.deleteElement(le)
   209  		}
   210  	}
   211  }
   212  
   213  func (c *LruCache[K, V]) deleteElement(le *list.Element[*entry[K, V]]) {
   214  	c.lru.Remove(le)
   215  	e := le.Value
   216  	delete(c.cache, e.key)
   217  	if c.onEvict != nil {
   218  		c.onEvict(e.key, e.value)
   219  	}
   220  }
   221  
   222  func (c *LruCache[K, V]) Clear() error {
   223  	c.mu.Lock()
   224  
   225  	c.cache = make(map[K]*list.Element[*entry[K, V]])
   226  
   227  	c.mu.Unlock()
   228  	return nil
   229  }
   230  
   231  type entry[K comparable, V any] struct {
   232  	key     K
   233  	value   V
   234  	expires int64
   235  }