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