github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/cache/lru_cache.go (about) 1 // Copyright 2012, Google Inc. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cache 6 7 import ( 8 "container/list" 9 "fmt" 10 "sync" 11 "time" 12 ) 13 14 type LRUCache struct { 15 mu sync.Mutex 16 17 // list & table of *entry objects 18 list *list.List 19 table map[string]*list.Element 20 21 // Our current size, in bytes. Obviously a gross simplification and low-grade 22 // approximation. 23 size uint64 24 25 // How many bytes we are limiting the cache to. 26 capacity uint64 27 } 28 29 func NewLRUCache(capacity uint64) *LRUCache { 30 return &LRUCache{ 31 list: list.New(), 32 table: make(map[string]*list.Element), 33 capacity: capacity, 34 } 35 } 36 37 func (lru *LRUCache) Get(key string) (v interface{}, ok bool) { 38 lru.mu.Lock() 39 defer lru.mu.Unlock() 40 41 element := lru.table[key] 42 if element == nil { 43 return nil, false 44 } 45 lru.moveToFront(element) 46 return element.Value.(*entry).value, true 47 } 48 49 func (lru *LRUCache) Set(key string, value interface{}, size int) { 50 lru.mu.Lock() 51 defer lru.mu.Unlock() 52 53 size = adjustValueSize(value, size) 54 if element := lru.table[key]; element != nil { 55 lru.updateInplace(element, value, size) 56 } else { 57 lru.addNew(key, value, size) 58 } 59 } 60 61 func (lru *LRUCache) SetIfAbsent(key string, value interface{}, size int) { 62 lru.mu.Lock() 63 defer lru.mu.Unlock() 64 65 size = adjustValueSize(value, size) 66 if element := lru.table[key]; element != nil { 67 lru.moveToFront(element) 68 } else { 69 lru.addNew(key, value, size) 70 } 71 } 72 73 func (lru *LRUCache) Take(key string) (v interface{}, ok bool) { 74 lru.mu.Lock() 75 defer lru.mu.Unlock() 76 77 element := lru.table[key] 78 if element == nil { 79 return nil, false 80 } 81 82 lru.list.Remove(element) 83 delete(lru.table, key) 84 lru.size -= uint64(element.Value.(*entry).size) 85 return element.Value.(*entry).value, true 86 } 87 88 func (lru *LRUCache) Delete(key string) bool { 89 lru.mu.Lock() 90 defer lru.mu.Unlock() 91 92 element := lru.table[key] 93 if element == nil { 94 return false 95 } 96 97 lru.list.Remove(element) 98 delete(lru.table, key) 99 lru.size -= uint64(element.Value.(*entry).size) 100 return true 101 } 102 103 func (lru *LRUCache) Clear() { 104 lru.mu.Lock() 105 defer lru.mu.Unlock() 106 107 lru.list.Init() 108 lru.table = make(map[string]*list.Element) 109 lru.size = 0 110 } 111 112 func (lru *LRUCache) SetCapacity(capacity uint64) { 113 lru.mu.Lock() 114 defer lru.mu.Unlock() 115 116 lru.capacity = capacity 117 lru.checkCapacity() 118 } 119 120 func (lru *LRUCache) Stats() (length, size, capacity uint64, oldest time.Time) { 121 lru.mu.Lock() 122 defer lru.mu.Unlock() 123 if lastElem := lru.list.Back(); lastElem != nil { 124 oldest = lastElem.Value.(*entry).timeAccessed 125 } 126 return uint64(lru.list.Len()), lru.size, lru.capacity, oldest 127 } 128 129 func (lru *LRUCache) StatsJSON() string { 130 if lru == nil { 131 return "{}" 132 } 133 l, s, c, o := lru.Stats() 134 return fmt.Sprintf("{\"Length\": %v, \"Size\": %v, \"Capacity\": %v, \"OldestAccess\": \"%v\"}", l, s, c, o) 135 } 136 137 func (lru *LRUCache) Keys() []string { 138 lru.mu.Lock() 139 defer lru.mu.Unlock() 140 141 keys := make([]string, 0, lru.list.Len()) 142 for e := lru.list.Front(); e != nil; e = e.Next() { 143 keys = append(keys, e.Value.(*entry).key) 144 } 145 return keys 146 } 147 148 func (lru *LRUCache) Items() []Item { 149 lru.mu.Lock() 150 defer lru.mu.Unlock() 151 152 items := make([]Item, 0, lru.list.Len()) 153 for e := lru.list.Front(); e != nil; e = e.Next() { 154 v := e.Value.(*entry) 155 items = append(items, Item{Key: v.key, Value: v.value, Size: v.size}) 156 } 157 return items 158 } 159 160 func (lru *LRUCache) updateInplace(element *list.Element, value interface{}, size int) { 161 sizeDiff := size - element.Value.(*entry).size 162 element.Value.(*entry).value = value 163 element.Value.(*entry).size = size 164 lru.size += uint64(sizeDiff) 165 lru.moveToFront(element) 166 lru.checkCapacity() 167 } 168 169 func (lru *LRUCache) moveToFront(element *list.Element) { 170 lru.list.MoveToFront(element) 171 element.Value.(*entry).timeAccessed = time.Now() 172 } 173 174 func (lru *LRUCache) addNew(key string, value interface{}, size int) { 175 newEntry := &entry{ 176 key: key, 177 value: value, 178 size: size, 179 timeAccessed: time.Now(), 180 } 181 element := lru.list.PushFront(newEntry) 182 lru.table[key] = element 183 lru.size += uint64(newEntry.size) 184 lru.checkCapacity() 185 } 186 187 func (lru *LRUCache) checkCapacity() { 188 // Partially duplicated from Delete 189 for lru.size > lru.capacity { 190 delElem := lru.list.Back() 191 delValue := delElem.Value.(*entry) 192 lru.list.Remove(delElem) 193 delete(lru.table, delValue.key) 194 lru.size -= uint64(delValue.size) 195 } 196 }