1package lru
2
3import (
4	"sync"
5
6	"github.com/hashicorp/golang-lru/simplelru"
7)
8
9// Cache is a thread-safe fixed size LRU cache.
10type Cache struct {
11	lru  simplelru.LRUCache
12	lock sync.RWMutex
13}
14
15// New creates an LRU of the given size.
16func New(size int) (*Cache, error) {
17	return NewWithEvict(size, nil)
18}
19
20// NewWithEvict constructs a fixed size cache with the given eviction
21// callback.
22func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
23	lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted))
24	if err != nil {
25		return nil, err
26	}
27	c := &Cache{
28		lru: lru,
29	}
30	return c, nil
31}
32
33// Purge is used to completely clear the cache.
34func (c *Cache) Purge() {
35	c.lock.Lock()
36	c.lru.Purge()
37	c.lock.Unlock()
38}
39
40// Add adds a value to the cache. Returns true if an eviction occurred.
41func (c *Cache) Add(key, value interface{}) (evicted bool) {
42	c.lock.Lock()
43	evicted = c.lru.Add(key, value)
44	c.lock.Unlock()
45	return evicted
46}
47
48// Get looks up a key's value from the cache.
49func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
50	c.lock.Lock()
51	value, ok = c.lru.Get(key)
52	c.lock.Unlock()
53	return value, ok
54}
55
56// Contains checks if a key is in the cache, without updating the
57// recent-ness or deleting it for being stale.
58func (c *Cache) Contains(key interface{}) bool {
59	c.lock.RLock()
60	containKey := c.lru.Contains(key)
61	c.lock.RUnlock()
62	return containKey
63}
64
65// Peek returns the key value (or undefined if not found) without updating
66// the "recently used"-ness of the key.
67func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
68	c.lock.RLock()
69	value, ok = c.lru.Peek(key)
70	c.lock.RUnlock()
71	return value, ok
72}
73
74// ContainsOrAdd checks if a key is in the cache without updating the
75// recent-ness or deleting it for being stale, and if not, adds the value.
76// Returns whether found and whether an eviction occurred.
77func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
78	c.lock.Lock()
79	defer c.lock.Unlock()
80
81	if c.lru.Contains(key) {
82		return true, false
83	}
84	evicted = c.lru.Add(key, value)
85	return false, evicted
86}
87
88// PeekOrAdd checks if a key is in the cache without updating the
89// recent-ness or deleting it for being stale, and if not, adds the value.
90// Returns whether found and whether an eviction occurred.
91func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
92	c.lock.Lock()
93	defer c.lock.Unlock()
94
95	previous, ok = c.lru.Peek(key)
96	if ok {
97		return previous, true, false
98	}
99
100	evicted = c.lru.Add(key, value)
101	return nil, false, evicted
102}
103
104// Remove removes the provided key from the cache.
105func (c *Cache) Remove(key interface{}) (present bool) {
106	c.lock.Lock()
107	present = c.lru.Remove(key)
108	c.lock.Unlock()
109	return
110}
111
112// Resize changes the cache size.
113func (c *Cache) Resize(size int) (evicted int) {
114	c.lock.Lock()
115	evicted = c.lru.Resize(size)
116	c.lock.Unlock()
117	return evicted
118}
119
120// RemoveOldest removes the oldest item from the cache.
121func (c *Cache) RemoveOldest() (key interface{}, value interface{}, ok bool) {
122	c.lock.Lock()
123	key, value, ok = c.lru.RemoveOldest()
124	c.lock.Unlock()
125	return
126}
127
128// GetOldest returns the oldest entry
129func (c *Cache) GetOldest() (key interface{}, value interface{}, ok bool) {
130	c.lock.Lock()
131	key, value, ok = c.lru.GetOldest()
132	c.lock.Unlock()
133	return
134}
135
136// Keys returns a slice of the keys in the cache, from oldest to newest.
137func (c *Cache) Keys() []interface{} {
138	c.lock.RLock()
139	keys := c.lru.Keys()
140	c.lock.RUnlock()
141	return keys
142}
143
144// Len returns the number of items in the cache.
145func (c *Cache) Len() int {
146	c.lock.RLock()
147	length := c.lru.Len()
148	c.lock.RUnlock()
149	return length
150}
151