github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/common/cache/lrucache.go (about)

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