github.com/XiaoMi/Gaea@v1.2.5/util/cache/lru_cache.go (about) 1 /* 2 Copyright 2017 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package cache implements a LRU cache. 18 // 19 // The implementation borrows heavily from SmallLRUCache 20 // (originally by Nathan Schrenk). The object maintains a doubly-linked list of 21 // elements. When an element is accessed, it is promoted to the head of the 22 // list. When space is needed, the element at the tail of the list 23 // (the least recently used element) is evicted. 24 package cache 25 26 import ( 27 "container/list" 28 "fmt" 29 "sync" 30 "time" 31 ) 32 33 // LRUCache is a typical LRU cache implementation. If the cache 34 // reaches the capacity, the least recently used item is deleted from 35 // the cache. Note the capacity is not the number of items, but the 36 // total sum of the Size() of each item. 37 type LRUCache struct { 38 mu sync.Mutex 39 40 // list & table contain *entry objects. 41 list *list.List 42 table map[string]*list.Element 43 44 size int64 45 capacity int64 46 evictions int64 47 } 48 49 // Value is the interface values that go into LRUCache need to satisfy 50 type Value interface { 51 // Size returns how big this value is. If you want to just track 52 // the cache by number of objects, you may return the size as 1. 53 Size() int 54 } 55 56 // CachedString 每个字符串记1个, 与StringValue不同 57 type CachedString string 58 59 // Size return cached string size 60 func (c CachedString) Size() int { 61 return 1 62 } 63 64 // StringValue alias name of string 65 type StringValue string 66 67 // Size return cached string value lenght 68 func (s StringValue) Size() int { 69 return len(s) 70 } 71 72 // Item is what is stored in the cache 73 type Item struct { 74 Key string 75 Value Value 76 } 77 78 type entry struct { 79 key string 80 value Value 81 size int64 82 timeAccessed time.Time 83 } 84 85 // NewLRUCache creates a new empty cache with the given capacity. 86 func NewLRUCache(capacity int64) *LRUCache { 87 return &LRUCache{ 88 list: list.New(), 89 table: make(map[string]*list.Element), 90 capacity: capacity, 91 } 92 } 93 94 // Get returns a value from the cache, and marks the entry as most 95 // recently used. 96 func (lru *LRUCache) Get(key string) (v Value, ok bool) { 97 lru.mu.Lock() 98 defer lru.mu.Unlock() 99 100 element := lru.table[key] 101 if element == nil { 102 return nil, false 103 } 104 lru.moveToFront(element) 105 return element.Value.(*entry).value, true 106 } 107 108 // Peek returns a value from the cache without changing the LRU order. 109 func (lru *LRUCache) Peek(key string) (v Value, ok bool) { 110 lru.mu.Lock() 111 defer lru.mu.Unlock() 112 113 element := lru.table[key] 114 if element == nil { 115 return nil, false 116 } 117 return element.Value.(*entry).value, true 118 } 119 120 // Set sets a value in the cache. 121 func (lru *LRUCache) Set(key string, value Value) { 122 lru.mu.Lock() 123 defer lru.mu.Unlock() 124 125 if element := lru.table[key]; element != nil { 126 lru.updateInplace(element, value) 127 } else { 128 lru.addNew(key, value) 129 } 130 } 131 132 // SetIfAbsent will set the value in the cache if not present. If the 133 // value exists in the cache, we don't set it. 134 func (lru *LRUCache) SetIfAbsent(key string, value Value) { 135 lru.mu.Lock() 136 defer lru.mu.Unlock() 137 138 if element := lru.table[key]; element != nil { 139 lru.moveToFront(element) 140 } else { 141 lru.addNew(key, value) 142 } 143 } 144 145 // Delete removes an entry from the cache, and returns if the entry existed. 146 func (lru *LRUCache) Delete(key string) bool { 147 lru.mu.Lock() 148 defer lru.mu.Unlock() 149 150 element := lru.table[key] 151 if element == nil { 152 return false 153 } 154 155 lru.list.Remove(element) 156 delete(lru.table, key) 157 lru.size -= element.Value.(*entry).size 158 return true 159 } 160 161 // Clear will clear the entire cache. 162 func (lru *LRUCache) Clear() { 163 lru.mu.Lock() 164 defer lru.mu.Unlock() 165 166 lru.list.Init() 167 lru.table = make(map[string]*list.Element) 168 lru.size = 0 169 } 170 171 // SetCapacity will set the capacity of the cache. If the capacity is 172 // smaller, and the current cache size exceed that capacity, the cache 173 // will be shrank. 174 func (lru *LRUCache) SetCapacity(capacity int64) { 175 lru.mu.Lock() 176 defer lru.mu.Unlock() 177 178 lru.capacity = capacity 179 lru.checkCapacity() 180 } 181 182 // Stats returns a few stats on the cache. 183 func (lru *LRUCache) Stats() (length, size, capacity, evictions int64, oldest time.Time) { 184 lru.mu.Lock() 185 defer lru.mu.Unlock() 186 if lastElem := lru.list.Back(); lastElem != nil { 187 oldest = lastElem.Value.(*entry).timeAccessed 188 } 189 return int64(lru.list.Len()), lru.size, lru.capacity, lru.evictions, oldest 190 } 191 192 // StatsJSON returns stats as a JSON object in a string. 193 func (lru *LRUCache) StatsJSON() string { 194 if lru == nil { 195 return "{}" 196 } 197 l, s, c, e, o := lru.Stats() 198 return fmt.Sprintf("{\"Length\": %v, \"Size\": %v, \"Capacity\": %v, \"Evictions\": %v, \"OldestAccess\": \"%v\"}", l, s, c, e, o) 199 } 200 201 // Length returns how many elements are in the cache 202 func (lru *LRUCache) Length() int64 { 203 lru.mu.Lock() 204 defer lru.mu.Unlock() 205 return int64(lru.list.Len()) 206 } 207 208 // Size returns the sum of the objects' Size() method. 209 func (lru *LRUCache) Size() int64 { 210 lru.mu.Lock() 211 defer lru.mu.Unlock() 212 return lru.size 213 } 214 215 // Capacity returns the cache maximum capacity. 216 func (lru *LRUCache) Capacity() int64 { 217 lru.mu.Lock() 218 defer lru.mu.Unlock() 219 return lru.capacity 220 } 221 222 // Evictions returns the eviction count. 223 func (lru *LRUCache) Evictions() int64 { 224 lru.mu.Lock() 225 defer lru.mu.Unlock() 226 return lru.evictions 227 } 228 229 // Oldest returns the insertion time of the oldest element in the cache, 230 // or a IsZero() time if cache is empty. 231 func (lru *LRUCache) Oldest() (oldest time.Time) { 232 lru.mu.Lock() 233 defer lru.mu.Unlock() 234 if lastElem := lru.list.Back(); lastElem != nil { 235 oldest = lastElem.Value.(*entry).timeAccessed 236 } 237 return 238 } 239 240 // Keys returns all the keys for the cache, ordered from most recently 241 // used to last recently used. 242 func (lru *LRUCache) Keys() []string { 243 lru.mu.Lock() 244 defer lru.mu.Unlock() 245 246 keys := make([]string, 0, lru.list.Len()) 247 for e := lru.list.Front(); e != nil; e = e.Next() { 248 keys = append(keys, e.Value.(*entry).key) 249 } 250 return keys 251 } 252 253 // Items returns all the values for the cache, ordered from most recently 254 // used to last recently used. 255 func (lru *LRUCache) Items() []Item { 256 lru.mu.Lock() 257 defer lru.mu.Unlock() 258 259 items := make([]Item, 0, lru.list.Len()) 260 for e := lru.list.Front(); e != nil; e = e.Next() { 261 v := e.Value.(*entry) 262 items = append(items, Item{Key: v.key, Value: v.value}) 263 } 264 return items 265 } 266 267 func (lru *LRUCache) updateInplace(element *list.Element, value Value) { 268 valueSize := int64(value.Size()) 269 sizeDiff := valueSize - element.Value.(*entry).size 270 element.Value.(*entry).value = value 271 element.Value.(*entry).size = valueSize 272 lru.size += sizeDiff 273 lru.moveToFront(element) 274 lru.checkCapacity() 275 } 276 277 func (lru *LRUCache) moveToFront(element *list.Element) { 278 lru.list.MoveToFront(element) 279 element.Value.(*entry).timeAccessed = time.Now() 280 } 281 282 func (lru *LRUCache) addNew(key string, value Value) { 283 newEntry := &entry{key, value, int64(value.Size()), time.Now()} 284 element := lru.list.PushFront(newEntry) 285 lru.table[key] = element 286 lru.size += newEntry.size 287 lru.checkCapacity() 288 } 289 290 func (lru *LRUCache) checkCapacity() { 291 // Partially duplicated from Delete 292 for lru.size > lru.capacity { 293 delElem := lru.list.Back() 294 delValue := delElem.Value.(*entry) 295 lru.list.Remove(delElem) 296 delete(lru.table, delValue.key) 297 lru.size -= delValue.size 298 lru.evictions++ 299 } 300 }