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 }