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 }