github.com/auttaja/go-tlru@v0.0.0-20200823214418-2a1d18ce6d93/cache.go (about) 1 package tlru 2 3 import ( 4 "container/list" 5 "runtime" 6 "sync" 7 "time" 8 ) 9 10 // Defines an item in the cache. 11 type cacheItem struct { 12 item interface{} 13 size int 14 destroyer *time.Timer 15 element *list.Element 16 } 17 18 // Defines the cache and all required items. 19 type Cache struct { 20 // Defines the attributes for the base cache. 21 m sync.Mutex 22 keyList *list.List 23 valueMap map[interface{}]*cacheItem 24 maxLen int 25 maxBytes int 26 27 // The total size of items in the cache. 28 totalBytes int 29 30 // Define the duration of an item in the cache. 31 duration time.Duration 32 } 33 34 // Purge the first added item possible from the cache. 35 // THIS FUNCTION IS NOT THREAD SAFE FOR PERFORMANCE REASONS (DOUBLE LOCKING)! BE CAREFUL! 36 func (c *Cache) purgeFirst() { 37 f := c.keyList.Front() 38 if f == nil { 39 return 40 } 41 c.keyList.Remove(f) 42 item := c.valueMap[f.Value] 43 c.totalBytes -= item.size 44 item.destroyer.Stop() 45 delete(c.valueMap, f.Value) 46 } 47 48 // Get is used to try and get a interface from the cache. 49 // The second boolean is meant to represent ok. If it's false, it was not in the cache. 50 func (c *Cache) Get(Key interface{}) (item interface{}, ok bool) { 51 // Lock the mutex. 52 c.m.Lock() 53 54 // Try to get from the cache. 55 x, ok := c.valueMap[Key] 56 57 // If this isn't ok, we return here. 58 if !ok { 59 c.m.Unlock() 60 return nil, false 61 } 62 63 // Revive the key. 64 c.keyList.Remove(x.element) 65 x.element = c.keyList.PushBack(Key) 66 x.destroyer.Reset(c.duration) 67 68 // Unlock the mutex. 69 c.m.Unlock() 70 71 // Return the item. 72 return x.item, true 73 } 74 75 // Used to generate a destruction function for a item. 76 func (c *Cache) destroyItem(Key interface{}, timer bool) func() { 77 return func() { 78 // Lock the mutex. 79 c.m.Lock() 80 81 // Delete the item from the cache. 82 item := c.valueMap[Key] 83 c.keyList.Remove(item.element) 84 delete(c.valueMap, Key) 85 if !timer { 86 item.destroyer.Stop() 87 } 88 c.totalBytes -= item.size 89 90 // Unlock the mutex. 91 c.m.Unlock() 92 } 93 } 94 95 // Delete is used to delete an option from the cache. 96 func (c *Cache) Delete(Key interface{}) { 97 c.destroyItem(Key, false)() 98 } 99 100 // Erase is used to erase the cache. 101 func (c *Cache) Erase() { 102 c.m.Lock() 103 c.keyList = list.New() 104 for _, v := range c.valueMap { 105 v.destroyer.Stop() 106 } 107 c.valueMap = map[interface{}]*cacheItem{} 108 c.m.Unlock() 109 c.totalBytes = 0 110 runtime.GC() 111 } 112 113 // Set is used to set a key/value interface in the cache. 114 func (c *Cache) Set(Key, Value interface{}) { 115 // Lock the mutex. 116 c.m.Lock() 117 118 // Check if the key already exists and the length. 119 item, exists := c.valueMap[Key] 120 121 // Get the total size. 122 var total int 123 if c.maxBytes != 0 { 124 total = int(sizeof(Value)) 125 if total > c.maxBytes { 126 // Don't cache this. 127 c.m.Unlock() 128 return 129 } 130 } 131 132 // If the key already exists, we should revive the key. If not, we should push it and set a timer in the map. 133 if exists { 134 c.keyList.Remove(item.element) 135 item.element = c.keyList.PushBack(Key) 136 item.destroyer.Reset(c.duration) 137 item.item = Value 138 } else { 139 // If the length is the max length, remove one. 140 l := len(c.valueMap) 141 if l == c.maxLen && c.maxLen != 0 { 142 c.purgeFirst() 143 } 144 145 // Set the cache item. 146 el := c.keyList.PushBack(Key) 147 item = &cacheItem{ 148 item: Value, 149 destroyer: time.AfterFunc(c.duration, c.destroyItem(Key, true)), 150 size: total, 151 element: el, 152 } 153 c.valueMap[Key] = item 154 c.totalBytes += total 155 } 156 157 // Ensure max bytes is greater than or equal to total bytes. 158 for c.totalBytes > c.maxBytes { 159 // Purge the first item. 160 c.purgeFirst() 161 } 162 163 // Unlock the mutex. 164 c.m.Unlock() 165 } 166 167 // NewCache is used to create the cache. 168 // Setting MaxLength of MaxBytes to 0 will mean unlimited. 169 func NewCache(MaxLength, MaxBytes int, Duration time.Duration) *Cache { 170 return &Cache{ 171 keyList: list.New(), 172 valueMap: map[interface{}]*cacheItem{}, 173 maxLen: MaxLength, 174 maxBytes: MaxBytes, 175 duration: Duration, 176 } 177 }