Skip to content

Commit 0c42780

Browse files
committed
Add remaining methods and tests
1 parent b9a7f97 commit 0c42780

File tree

2 files changed

+179
-7
lines changed

2 files changed

+179
-7
lines changed

Diff for: lru.go

+64-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package lru
33
import (
44
"container/list"
55
"errors"
6+
"fmt"
67
"reflect"
78
)
89

@@ -32,6 +33,7 @@ func NewLRU(capacity int) *LRU {
3233
type entry struct {
3334
key interface{}
3435
value interface{}
36+
size uintptr
3537
}
3638

3739
// Set stores this value in the interface with the given key.
@@ -46,12 +48,16 @@ func (l *LRU) Set(key interface{}, value interface{}) error {
4648

4749
newSize := l.size + entrySize
4850
if newSize > l.convenienceCap {
49-
l.removeElement()
51+
l.removeLastElement()
5052
}
5153

5254
_, ok := l.items[key]
5355
if !ok {
54-
entry := entry{key: key, value: value}
56+
entry := entry{
57+
key: key,
58+
value: value,
59+
size: entrySize,
60+
}
5561
listEntry := l.used.PushFront(entry)
5662
l.size += newSize
5763
l.items[key] = listEntry
@@ -61,16 +67,14 @@ func (l *LRU) Set(key interface{}, value interface{}) error {
6167

6268
}
6369

64-
func (l *LRU) removeElement() {
70+
func (l *LRU) removeLastElement() {
6571

6672
lastElement := l.used.Back()
6773
l.used.Remove(lastElement)
6874

6975
entry := lastElement.Value.(entry)
7076
delete(l.items, entry.key)
71-
72-
lastElementSize := reflect.TypeOf(lastElement).Size()
73-
l.size -= lastElementSize
77+
l.size -= entry.size
7478

7579
}
7680

@@ -79,11 +83,64 @@ func (l *LRU) removeElement() {
7983
// element in the cache and is less likely to be removed
8084
func (l *LRU) Get(key interface{}) (interface{}, bool) {
8185

86+
entry, ok := l.get(key)
87+
if ok {
88+
l.used.MoveToFront(entry)
89+
}
90+
return entry, ok
91+
92+
}
93+
94+
// Peek returns the element with this key, if it exists.
95+
// Unlike Get, the returned element does not become the
96+
// most recently used element
97+
func (l *LRU) Peek(key interface{}) (interface{}, bool) {
98+
return l.get(key)
99+
}
100+
101+
func (l *LRU) get(key interface{}) (*list.Element, bool) {
102+
82103
entry, ok := l.items[key]
83104
if !ok {
84105
return nil, ok
85106
}
86-
l.used.MoveToFront(entry)
87107
return entry, ok
88108

89109
}
110+
111+
// Del removes the entry with the given key. If they entry
112+
// does not exist, an error is returned
113+
func (l *LRU) Del(key interface{}) error {
114+
115+
entry, ok := l.get(key)
116+
if !ok {
117+
return fmt.Errorf("cannot delete non-existent entry: %v", key)
118+
}
119+
l.removeEntry(entry)
120+
return nil
121+
122+
}
123+
124+
func (l *LRU) removeEntry(e *list.Element) {
125+
l.used.Remove(e)
126+
l.size -= e.Value.(entry).size
127+
}
128+
129+
// Size returns the current size of the cache as an integer
130+
func (l *LRU) Size() int {
131+
return int(l.size)
132+
}
133+
134+
// Has returns true if the give key contains an entry in
135+
// the cache
136+
func (l *LRU) Has(key interface{}) bool {
137+
_, ok := l.items[key]
138+
return ok
139+
}
140+
141+
// Reset clears the cache, throwing away all elements
142+
func (l *LRU) Reset() {
143+
l.used.Init()
144+
l.items = map[interface{}]*list.Element{}
145+
l.size = uintptr(0)
146+
}

Diff for: lru_test.go

+115
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package lru
33
import (
44
"container/list"
55
"testing"
6+
7+
"github.com/go-test/deep"
68
)
79

810
func TestLRU_Set(t *testing.T) {
@@ -70,3 +72,116 @@ func TestLRU_Set_TooLarge(t *testing.T) {
7072
}
7173

7274
}
75+
76+
func TestLRU_Peek(t *testing.T) {
77+
78+
l := NewLRU(42)
79+
80+
err := l.Set("first", "first value")
81+
if err != nil {
82+
t.Fatal(err)
83+
}
84+
85+
err = l.Set("second", "second value")
86+
if err != nil {
87+
t.Fatal(err)
88+
}
89+
90+
secondEntry, ok := l.Peek("second")
91+
if !ok {
92+
t.Error("Expected to peek second element")
93+
}
94+
listElement, ok := secondEntry.(*list.Element)
95+
if !ok {
96+
t.Fatal("expected list entry type")
97+
}
98+
listEntry, ok := listElement.Value.(entry)
99+
if !ok {
100+
t.Fatal("expected list element to be an entry")
101+
}
102+
if listEntry.value != "second value" {
103+
t.Errorf("expected 'second value' got: %s", listEntry.value)
104+
}
105+
if diff := deep.Equal(l.used.Front(), secondEntry); diff != nil {
106+
t.Errorf("expected cache's front entry to be second entry, \ngot: %s", diff)
107+
}
108+
109+
}
110+
111+
func TestLRU_Del(t *testing.T) {
112+
113+
l := NewLRU(16)
114+
115+
err := l.Set("first", "first value")
116+
if err != nil {
117+
t.Fatal(err)
118+
}
119+
120+
err = l.Del("first")
121+
if err != nil {
122+
t.Error("expected no error when deleting existing entry")
123+
}
124+
if l.Size() != 0 {
125+
t.Errorf("expected cache size to be zero, got: %d", l.Size())
126+
}
127+
128+
}
129+
130+
func TestLRU_Del_NonExistent(t *testing.T) {
131+
132+
l := NewLRU(16)
133+
134+
err := l.Set("first", "first value")
135+
if err != nil {
136+
t.Fatal(err)
137+
}
138+
139+
err = l.Del("second")
140+
if err == nil {
141+
t.Error("expected an error when attempting to delete a non-existent entry")
142+
}
143+
144+
}
145+
146+
func TestLRU_Has(t *testing.T) {
147+
148+
l := NewLRU(8)
149+
150+
err := l.Set("first", 1)
151+
if err != nil {
152+
t.Fatal(err)
153+
}
154+
155+
ok := l.Has("first")
156+
if !ok {
157+
t.Error("expected Has to return true for existing entry")
158+
}
159+
160+
}
161+
162+
func TestLRU_Reset(t *testing.T) {
163+
164+
l := NewLRU(16)
165+
166+
err := l.Set("first", 1)
167+
if err != nil {
168+
t.Fatal(err)
169+
}
170+
171+
err = l.Set("second", 2)
172+
if err != nil {
173+
t.Fatal(err)
174+
}
175+
176+
l.Reset()
177+
if l.used.Len() != 0 {
178+
t.Errorf("exepcted Reset to clear list, got len: %d", l.used.Len())
179+
}
180+
if len(l.items) != 0 {
181+
t.Errorf("expected Reset to clear map, got len: %d", len(l.items))
182+
}
183+
if l.Size() != 0 {
184+
t.Errorf("expected Reset to set cache size to 0, got: %d", l.Size())
185+
}
186+
187+
}

0 commit comments

Comments
 (0)