Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method GetAndAdd to LRU Cache #82

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions lru.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,23 @@ func (c *Cache) Purge() {

// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *Cache) Add(key, value interface{}) (evicted bool) {
_, _, evicted = c.GetAndAdd(key, value)
return
}

// Get looks up a key's value from the cache.
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
c.lock.Lock()
value, ok = c.lru.Get(key)
c.lock.Unlock()
return value, ok
}

// GetAndAdd returns the previous key's value from the cache and adds a new one.
func (c *Cache) GetAndAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the changes in simplelru, I think you could do the same here -- instead of copying the function entirely, modify Add above to call GetAndAdd and simply throw away the parameters that aren't a part of the call.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes, it's my bad. Refactored, thanks!

var k, v interface{}
c.lock.Lock()
evicted = c.lru.Add(key, value)
previous, ok, evicted = c.lru.GetAndAdd(key, value)
if c.onEvictedCB != nil && evicted {
k, v = c.evictedKeys[0], c.evictedVals[0]
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
Expand All @@ -85,14 +99,6 @@ func (c *Cache) Add(key, value interface{}) (evicted bool) {
return
}

// Get looks up a key's value from the cache.
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
c.lock.Lock()
value, ok = c.lru.Get(key)
c.lock.Unlock()
return value, ok
}

// Contains checks if a key is in the cache, without updating the
// recent-ness or deleting it for being stale.
func (c *Cache) Contains(key interface{}) bool {
Expand Down
19 changes: 19 additions & 0 deletions lru_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,25 @@ func TestLRUAdd(t *testing.T) {
}
}

func TestLRU_GetAndAdd(t *testing.T) {
l, err := New(1)
if err != nil {
t.Fatalf("err: %v", err)
}
if previous, ok, evicted := l.GetAndAdd(1, 1); ok || evicted {
t.Errorf("1 should not be found, eviction should not be: %v, %v, %v", previous, ok, evicted)
}
if previous, ok, evicted := l.GetAndAdd(1, 10); !ok || previous != 1 || evicted {
t.Errorf("1 should be returned as previous value, eviction should not be: %v, %v, %v", previous, ok, evicted)
}
if v, ok := l.Get(1); !ok || v != 10 {
t.Errorf("1 should be set to 10: %v, %v", v, ok)
}
if previous, ok, evicted := l.GetAndAdd(2, 2); ok || !evicted {
t.Errorf("2 should not be found, eviction should be: %v, %v, %v", previous, ok, evicted)
}
}

// test that Contains doesn't update recent-ness
func TestLRUContains(t *testing.T) {
l, err := New(2)
Expand Down
17 changes: 12 additions & 5 deletions simplelru/lru.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,33 @@ func (c *LRU) Purge() {
c.evictList.Init()
}

// Add adds a value to the cache. Returns true if an eviction occurred.
// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *LRU) Add(key, value interface{}) (evicted bool) {
_, _, evicted = c.GetAndAdd(key, value)
return
}

// GetAndAdd returns the previous key's value from the cache and adds a new one.
func (c *LRU) GetAndAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
// Check for existing item
if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent)
previous = ent.Value.(*entry).value
ent.Value.(*entry).value = value
return false
return previous, true, false
}

// Add new item
ent := &entry{key, value}
entry := c.evictList.PushFront(ent)
c.items[key] = entry

evict := c.evictList.Len() > c.size
evicted = c.evictList.Len() > c.size
// Verify size not exceeded
if evict {
if evicted {
c.removeOldest()
}
return evict
return nil, false, evicted
}

// Get looks up a key's value from the cache.
Expand Down
3 changes: 3 additions & 0 deletions simplelru/lru_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type LRUCache interface {
// updates the "recently used"-ness of the key. #value, isFound
Get(key interface{}) (value interface{}, ok bool)

// Returns the previous key's value from the cache and adds a new one.
GetAndAdd(key, value interface{}) (previous interface{}, ok, evicted bool)

// Checks if a key exists in cache without updating the recent-ness.
Contains(key interface{}) (ok bool)

Expand Down
19 changes: 19 additions & 0 deletions simplelru/lru_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,25 @@ func TestLRU_Add(t *testing.T) {
}
}

func TestLRU_GetAndAdd(t *testing.T) {
l, err := NewLRU(1, nil)
if err != nil {
t.Fatalf("err: %v", err)
}
if previous, ok, evicted := l.GetAndAdd(1, 1); ok || evicted {
t.Errorf("1 should not be found, eviction should not be: %v, %v, %v", previous, ok, evicted)
}
if previous, ok, evicted := l.GetAndAdd(1, 10); !ok || previous != 1 || evicted {
t.Errorf("1 should be returned as previous value, eviction should not be: %v, %v, %v", previous, ok, evicted)
}
if v, ok := l.Get(1); !ok || v != 10 {
t.Errorf("1 should be set to 10: %v, %v", v, ok)
}
if previous, ok, evicted := l.GetAndAdd(2, 2); ok || !evicted {
t.Errorf("2 should not be found, eviction should be: %v, %v, %v", previous, ok, evicted)
}
}

// Test that Contains doesn't update recent-ness
func TestLRU_Contains(t *testing.T) {
l, err := NewLRU(2, nil)
Expand Down