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 }