-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cache New Get RemoveOldest Add Len
- Loading branch information
0 parents
commit 5e8c73c
Showing
5 changed files
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.jpg | ||
*.png |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"go.inferGopath": false | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module geecache | ||
|
||
go 1.18 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package lru | ||
|
||
import ( | ||
"container/list" | ||
) | ||
|
||
// 核心数据结构 | ||
// map + double linked list | ||
|
||
// Cache is a LRU cache. Not safe for ocncurrent access. | ||
type Cache struct { | ||
maxBytes int64 // 允许的最大内存 | ||
nbytes int64 // 已使用内存 | ||
ll *list.List // 双向链表 | ||
cache map[string]*list.Element | ||
OnEvicted func(key string, value Value) // 某条记录被移除时的回调,可以为 nil | ||
} | ||
|
||
type entry struct { | ||
key string | ||
value Value | ||
} | ||
|
||
type Value interface { | ||
Len() int | ||
} | ||
|
||
// New 实例化 | ||
func New(maxBytes int64, onEvicted func(string, Value)) *Cache { | ||
return &Cache{ | ||
maxBytes: maxBytes, | ||
ll: list.New(), | ||
cache: make(map[string]*list.Element), | ||
OnEvicted: onEvicted, | ||
} | ||
} | ||
|
||
// Get 查找 | ||
// | ||
// 如果键对应的链表结点存在,将对应节点移动到队首,并返回找到的值 | ||
func (c *Cache) Get(key string) (value Value, ok bool) { | ||
if ele, ok := c.cache[key]; ok { | ||
c.ll.MoveToFront(ele) | ||
kv := ele.Value.(*entry) | ||
return kv.value, true | ||
} | ||
return | ||
} | ||
|
||
// RemoveOldest 缓存淘汰 | ||
// | ||
// 移除最近最少访问的节点(尾节点) | ||
func (c *Cache) RemoveOldest() { | ||
ele := c.ll.Back() | ||
if ele != nil { | ||
c.ll.Remove(ele) | ||
kv := ele.Value.(*entry) | ||
//TODO | ||
delete(c.cache, kv.key) | ||
c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len()) | ||
if c.OnEvicted != nil { | ||
c.OnEvicted(kv.key, kv.value) | ||
} | ||
} | ||
} | ||
|
||
// Add 添加一个值到缓存中 | ||
func (c *Cache) Add(key string, value Value) { | ||
// 已存在,修改值,并将该节点移动到队首 | ||
if ele, ok := c.cache[key]; ok { | ||
c.ll.MoveToFront(ele) | ||
kv := ele.Value.(*entry) | ||
c.nbytes += int64(value.Len()) - int64(kv.value.Len()) | ||
kv.value = value | ||
} else { | ||
ele := c.ll.PushFront(&entry{key, value}) | ||
c.cache[key] = ele | ||
c.nbytes += int64(len(key)) + int64(value.Len()) | ||
} | ||
for c.maxBytes != 0 && c.maxBytes < c.nbytes { | ||
c.RemoveOldest() | ||
} | ||
} | ||
|
||
// Len 获取已添加数据条数 | ||
func (c *Cache) Len() int { | ||
return c.ll.Len() | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package lru | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
type String string | ||
|
||
func (s String) Len() int { | ||
return len(s) | ||
} | ||
|
||
func TestGet(t *testing.T) { | ||
lru := New(int64(0), nil) | ||
lru.Add("key1", String("1234")) | ||
if v, ok := lru.Get("key1"); !ok || string(v.(String)) != "1234" { | ||
t.Fatalf("cache hit key1=1234 failed") | ||
} | ||
if _, ok := lru.Get("key2"); ok { | ||
t.Fatalf("cache miss key2 failed") | ||
} | ||
} | ||
|
||
func TestRemoveOldest(t *testing.T) { | ||
k1, k2, k3 := "key1", "key2", "k3" | ||
v1, v2, v3 := "value1", "value2", "v3" | ||
cap := len(k1 + k2 + v1 + v2) | ||
lru := New(int64(cap), nil) | ||
lru.Add(k1, String(v1)) | ||
lru.Add(k2, String(v2)) | ||
lru.Add(k3, String(v3)) | ||
|
||
if _, ok := lru.Get("key1"); ok || lru.Len() != 2 { | ||
t.Fatalf("RemoveOldest key1 failed") | ||
} | ||
} | ||
|
||
func TestOnEvicted(t *testing.T) { | ||
keys := make([]string, 0) | ||
callback := func(key string, value Value) { | ||
keys = append(keys, key) | ||
} | ||
lru := New(int64(10), callback) | ||
lru.Add("key1", String("123456")) | ||
lru.Add("k2", String("k2")) | ||
lru.Add("k3", String("k3")) | ||
lru.Add("k4", String("k4")) | ||
|
||
expect := []string{"key1", "k2"} | ||
|
||
if !reflect.DeepEqual(expect, keys) { | ||
t.Fatalf("Call OnEvicted failed, expect keys equals to %s", expect) | ||
} | ||
} |