Created
October 6, 2015 10:52
-
-
Save hello009-commits/f39319b3b4d7bffff846 to your computer and use it in GitHub Desktop.
lru
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This package provides a simple LRU cache. It is based on the | |
// LRU implementation in groupcache: | |
// https://github.com/golang/groupcache/tree/master/lru | |
package lru | |
import ( | |
"container/list" | |
"errors" | |
"sync" | |
) | |
// Cache is a thread-safe fixed size LRU cache. | |
type Cache struct { | |
size int | |
evictList *list.List | |
items map[interface{}]*list.Element | |
lock sync.RWMutex | |
onEvicted func(key interface{}, value interface{}) | |
} | |
// entry is used to hold a value in the evictList | |
type entry struct { | |
key interface{} | |
value interface{} | |
} | |
// New creates an LRU of the given size | |
func New(size int) (*Cache, error) { | |
return NewWithEvict(size, nil) | |
} | |
func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) { | |
if size <= 0 { | |
return nil, errors.New("Must provide a positive size") | |
} | |
c := &Cache{ | |
size: size, | |
evictList: list.New(), | |
items: make(map[interface{}]*list.Element, size), | |
onEvicted: onEvicted, | |
} | |
return c, nil | |
} | |
// Purge is used to completely clear the cache | |
func (c *Cache) Purge() { | |
c.lock.Lock() | |
defer c.lock.Unlock() | |
if c.onEvicted != nil { | |
for k, v := range c.items { | |
c.onEvicted(k, v.Value) | |
} | |
} | |
c.evictList = list.New() | |
c.items = make(map[interface{}]*list.Element, c.size) | |
} | |
// Add adds a value to the cache. Returns true if an eviction occured. | |
func (c *Cache) Add(key, value interface{}) bool { | |
c.lock.Lock() | |
defer c.lock.Unlock() | |
// Check for existing item | |
if ent, ok := c.items[key]; ok { | |
c.evictList.MoveToFront(ent) | |
ent.Value.(*entry).value = value | |
return false | |
} | |
// Add new item | |
ent := &entry{key, value} | |
entry := c.evictList.PushFront(ent) | |
c.items[key] = entry | |
evict := c.evictList.Len() > c.size | |
// Verify size not exceeded | |
if evict { | |
c.removeOldest() | |
} | |
return evict | |
} | |
// Get looks up a key's value from the cache. | |
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) { | |
c.lock.Lock() | |
defer c.lock.Unlock() | |
if ent, ok := c.items[key]; ok { | |
c.evictList.MoveToFront(ent) | |
return ent.Value.(*entry).value, true | |
} | |
return | |
} | |
// Remove removes the provided key from the cache. | |
func (c *Cache) Remove(key interface{}) { | |
c.lock.Lock() | |
defer c.lock.Unlock() | |
if ent, ok := c.items[key]; ok { | |
c.removeElement(ent) | |
} | |
} | |
// RemoveOldest removes the oldest item from the cache. | |
func (c *Cache) RemoveOldest() { | |
c.lock.Lock() | |
defer c.lock.Unlock() | |
c.removeOldest() | |
} | |
// Keys returns a slice of the keys in the cache. | |
func (c *Cache) Keys() []interface{} { | |
c.lock.RLock() | |
defer c.lock.RUnlock() | |
keys := make([]interface{}, len(c.items)) | |
i := 0 | |
for k := range c.items { | |
keys[i] = k | |
i++ | |
} | |
return keys | |
} | |
// Len returns the number of items in the cache. | |
func (c *Cache) Len() int { | |
c.lock.RLock() | |
defer c.lock.RUnlock() | |
return c.evictList.Len() | |
} | |
// removeOldest removes the oldest item from the cache. | |
func (c *Cache) removeOldest() { | |
ent := c.evictList.Back() | |
if ent != nil { | |
c.removeElement(ent) | |
} | |
} | |
// removeElement is used to remove a given list element from the cache | |
func (c *Cache) removeElement(e *list.Element) { | |
c.evictList.Remove(e) | |
kv := e.Value.(*entry) | |
delete(c.items, kv.key) | |
if c.onEvicted != nil { | |
c.onEvicted(kv.key, kv.value) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment