github.com/sagernet/sing@v0.4.0-beta.19.0.20240518125136-f67a0988a636/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/sagernet/sing/common"
    10  	"github.com/sagernet/sing/common/x/list"
    11  )
    12  
    13  type Option[K comparable, V any] func(*LruCache[K, V])
    14  
    15  type EvictCallback[K comparable, V any] func(key K, value V)
    16  
    17  func WithEvict[K comparable, V any](cb EvictCallback[K, V]) Option[K, V] {
    18  	return func(l *LruCache[K, V]) {
    19  		l.onEvict = cb
    20  	}
    21  }
    22  
    23  func WithUpdateAgeOnGet[K comparable, V any]() Option[K, V] {
    24  	return func(l *LruCache[K, V]) {
    25  		l.updateAgeOnGet = true
    26  	}
    27  }
    28  
    29  func WithAge[K comparable, V any](maxAge int64) Option[K, V] {
    30  	return func(l *LruCache[K, V]) {
    31  		l.maxAge = maxAge
    32  	}
    33  }
    34  
    35  func WithSize[K comparable, V any](maxSize int) Option[K, V] {
    36  	return func(l *LruCache[K, V]) {
    37  		l.maxSize = maxSize
    38  	}
    39  }
    40  
    41  func WithStale[K comparable, V any](stale bool) Option[K, V] {
    42  	return func(l *LruCache[K, V]) {
    43  		l.staleReturn = stale
    44  	}
    45  }
    46  
    47  type LruCache[K comparable, V any] struct {
    48  	maxAge         int64
    49  	maxSize        int
    50  	mu             sync.Mutex
    51  	cache          map[K]*list.Element[*entry[K, V]]
    52  	lru            list.List[*entry[K, V]] // Front is least-recent
    53  	updateAgeOnGet bool
    54  	staleReturn    bool
    55  	onEvict        EvictCallback[K, V]
    56  }
    57  
    58  func New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {
    59  	lc := &LruCache[K, V]{
    60  		cache: make(map[K]*list.Element[*entry[K, V]]),
    61  	}
    62  
    63  	for _, option := range options {
    64  		option(lc)
    65  	}
    66  
    67  	return lc
    68  }
    69  
    70  func (c *LruCache[K, V]) Load(key K) (V, bool) {
    71  	entry := c.get(key)
    72  	if entry == nil {
    73  		return common.DefaultValue[V](), false
    74  	}
    75  	value := entry.value
    76  
    77  	return value, true
    78  }
    79  
    80  func (c *LruCache[K, V]) LoadOrStore(key K, constructor func() V) (V, bool) {
    81  	c.mu.Lock()
    82  	defer c.mu.Unlock()
    83  
    84  	le, ok := c.cache[key]
    85  	if ok {
    86  		if c.maxAge > 0 && le.Value.expires <= time.Now().Unix() {
    87  			c.deleteElement(le)
    88  			goto create
    89  		}
    90  
    91  		c.lru.MoveToBack(le)
    92  		entry := le.Value
    93  		if c.maxAge > 0 && c.updateAgeOnGet {
    94  			entry.expires = time.Now().Unix() + c.maxAge
    95  		}
    96  		return entry.value, true
    97  	}
    98  
    99  create:
   100  	value := constructor()
   101  	if le, ok := c.cache[key]; ok {
   102  		c.lru.MoveToBack(le)
   103  		e := le.Value
   104  		e.value = value
   105  		e.expires = time.Now().Unix() + c.maxAge
   106  	} else {
   107  		e := &entry[K, V]{key: key, value: value, expires: time.Now().Unix() + c.maxAge}
   108  		c.cache[key] = c.lru.PushBack(e)
   109  	}
   110  
   111  	c.maybeDeleteOldest()
   112  	return value, false
   113  }
   114  
   115  func (c *LruCache[K, V]) LoadOrStoreWithAge(key K, maxAge int64, constructor func() V) (V, bool) {
   116  	c.mu.Lock()
   117  	defer c.mu.Unlock()
   118  
   119  	if maxAge == 0 {
   120  		maxAge = c.maxAge
   121  	}
   122  
   123  	le, ok := c.cache[key]
   124  	if ok {
   125  		if c.maxAge > 0 && le.Value.expires <= time.Now().Unix() {
   126  			c.deleteElement(le)
   127  			goto create
   128  		}
   129  
   130  		c.lru.MoveToBack(le)
   131  		entry := le.Value
   132  		if c.maxAge > 0 && c.updateAgeOnGet {
   133  			entry.expires = time.Now().Unix() + maxAge
   134  		}
   135  		return entry.value, true
   136  	}
   137  
   138  create:
   139  	value := constructor()
   140  	if le, ok := c.cache[key]; ok {
   141  		c.lru.MoveToBack(le)
   142  		e := le.Value
   143  		e.value = value
   144  		e.expires = time.Now().Unix() + maxAge
   145  	} else {
   146  		e := &entry[K, V]{key: key, value: value, expires: time.Now().Unix() + c.maxAge}
   147  		c.cache[key] = c.lru.PushBack(e)
   148  	}
   149  
   150  	c.maybeDeleteOldest()
   151  	return value, false
   152  }
   153  
   154  func (c *LruCache[K, V]) LoadWithExpire(key K) (V, time.Time, bool) {
   155  	entry := c.get(key)
   156  	if entry == nil {
   157  		return common.DefaultValue[V](), time.Time{}, false
   158  	}
   159  
   160  	return entry.value, time.Unix(entry.expires, 0), true
   161  }
   162  
   163  func (c *LruCache[K, V]) Exist(key K) bool {
   164  	c.mu.Lock()
   165  	defer c.mu.Unlock()
   166  
   167  	_, ok := c.cache[key]
   168  	return ok
   169  }
   170  
   171  func (c *LruCache[K, V]) Store(key K, value V) {
   172  	expires := int64(0)
   173  	if c.maxAge > 0 {
   174  		expires = time.Now().Unix() + c.maxAge
   175  	}
   176  	c.StoreWithExpire(key, value, time.Unix(expires, 0))
   177  }
   178  
   179  func (c *LruCache[K, V]) StoreWithExpire(key K, value V, expires time.Time) {
   180  	c.mu.Lock()
   181  	defer c.mu.Unlock()
   182  
   183  	if le, ok := c.cache[key]; ok {
   184  		c.lru.MoveToBack(le)
   185  		e := le.Value
   186  		e.value = value
   187  		e.expires = expires.Unix()
   188  	} else {
   189  		e := &entry[K, V]{key: key, value: value, expires: expires.Unix()}
   190  		c.cache[key] = c.lru.PushBack(e)
   191  
   192  		if c.maxSize > 0 {
   193  			if n := c.lru.Len(); n > c.maxSize {
   194  				c.deleteElement(c.lru.Front())
   195  			}
   196  		}
   197  	}
   198  
   199  	c.maybeDeleteOldest()
   200  }
   201  
   202  func (c *LruCache[K, V]) CloneTo(n *LruCache[K, V]) {
   203  	c.mu.Lock()
   204  	defer c.mu.Unlock()
   205  
   206  	n.mu.Lock()
   207  	defer n.mu.Unlock()
   208  
   209  	n.lru = list.List[*entry[K, V]]{}
   210  	n.cache = make(map[K]*list.Element[*entry[K, V]])
   211  
   212  	for e := c.lru.Front(); e != nil; e = e.Next() {
   213  		elm := e.Value
   214  		n.cache[elm.key] = n.lru.PushBack(elm)
   215  	}
   216  }
   217  
   218  func (c *LruCache[K, V]) Range(block func(key K, value V)) {
   219  	c.mu.Lock()
   220  	defer c.mu.Unlock()
   221  	for le := c.lru.Front(); le != nil; le = le.Next() {
   222  		block(le.Value.key, le.Value.value)
   223  	}
   224  }
   225  
   226  func (c *LruCache[K, V]) get(key K) *entry[K, V] {
   227  	c.mu.Lock()
   228  	defer c.mu.Unlock()
   229  
   230  	le, ok := c.cache[key]
   231  	if !ok {
   232  		return nil
   233  	}
   234  
   235  	if !c.staleReturn && c.maxAge > 0 && le.Value.expires <= time.Now().Unix() {
   236  		c.deleteElement(le)
   237  		c.maybeDeleteOldest()
   238  
   239  		return nil
   240  	}
   241  
   242  	c.lru.MoveToBack(le)
   243  	entry := le.Value
   244  	if c.maxAge > 0 && c.updateAgeOnGet {
   245  		entry.expires = time.Now().Unix() + c.maxAge
   246  	}
   247  	return entry
   248  }
   249  
   250  // Delete removes the value associated with a key.
   251  func (c *LruCache[K, V]) Delete(key K) {
   252  	c.mu.Lock()
   253  
   254  	if le, ok := c.cache[key]; ok {
   255  		c.deleteElement(le)
   256  	}
   257  
   258  	c.mu.Unlock()
   259  }
   260  
   261  func (c *LruCache[K, V]) Clear() {
   262  	c.mu.Lock()
   263  	defer c.mu.Unlock()
   264  	for element := c.lru.Front(); element != nil; element = element.Next() {
   265  		c.deleteElement(element)
   266  	}
   267  }
   268  
   269  func (c *LruCache[K, V]) maybeDeleteOldest() {
   270  	if !c.staleReturn && c.maxAge > 0 {
   271  		now := time.Now().Unix()
   272  		for le := c.lru.Front(); le != nil && le.Value.expires <= now; le = c.lru.Front() {
   273  			c.deleteElement(le)
   274  		}
   275  	}
   276  }
   277  
   278  func (c *LruCache[K, V]) deleteElement(le *list.Element[*entry[K, V]]) {
   279  	c.lru.Remove(le)
   280  	e := le.Value
   281  	delete(c.cache, e.key)
   282  	if c.onEvict != nil {
   283  		c.onEvict(e.key, e.value)
   284  	}
   285  }
   286  
   287  type entry[K comparable, V any] struct {
   288  	key     K
   289  	value   V
   290  	expires int64
   291  }