diff --git a/gee-cache/day1-lru/geecache/go.mod b/gee-cache/day1-lru/geecache/go.mod new file mode 100644 index 0000000..f9d454e --- /dev/null +++ b/gee-cache/day1-lru/geecache/go.mod @@ -0,0 +1,3 @@ +module geecache + +go 1.13 diff --git a/gee-cache/day1-lru/geecache/lru/lru.go b/gee-cache/day1-lru/geecache/lru/lru.go new file mode 100644 index 0000000..94f7116 --- /dev/null +++ b/gee-cache/day1-lru/geecache/lru/lru.go @@ -0,0 +1,84 @@ +package lru + +import ( + "container/list" + "unsafe" +) + +// Cache is a LRU cache. It is not safe for concurrent access. +type Cache struct { + maxBytes int + nbytes int + ll *list.List + cache map[string]*list.Element + // optional and executed when an entry is purged. + OnEvicted func(key string, value interface{}) +} + +type entry struct { + key string + value interface{} +} + +// New is the Constructor of Cache +func New(maxBytes int, onEvicted func(string, interface{})) *Cache { + return &Cache{ + maxBytes: maxBytes, + ll: list.New(), + cache: make(map[string]*list.Element), + } +} + +// Add adds a value to the cache. +func (c *Cache) Add(key string, value interface{}) { + if ele, ok := c.cache[key]; ok { + c.ll.MoveToFront(ele) + kv := ele.Value.(*entry) + kv.value = value + return + } + ele := c.ll.PushFront(&entry{key, value}) + c.cache[key] = ele + c.nbytes += len(key) + sizeof(value) + + for c.maxBytes != 0 && c.maxBytes < c.nbytes { + c.RemoveOldest() + } +} + +// Get look ups a key's value +func (c *Cache) Get(key string) (value interface{}, ok bool) { + if ele, ok := c.cache[key]; ok { + c.ll.MoveToFront(ele) + kv := ele.Value.(*entry) + return kv.value, true + } + return +} + +// RemoveOldest removes the oldest item +func (c *Cache) RemoveOldest() { + ele := c.ll.Back() + if ele != nil { + c.ll.Remove(ele) + kv := ele.Value.(*entry) + delete(c.cache, kv.key) + c.nbytes -= len(kv.key) + sizeof(kv.value) + if c.OnEvicted != nil { + c.OnEvicted(kv.key, kv.value) + } + } +} + +// Value is optional interface for the value +// if it's not implemented, use unsafe.Sizeof to count +type Value interface { + Len() int // count how many bytes it takes +} + +func sizeof(value interface{}) int { + if m, ok := value.(Value); ok { + return m.Len() + } + return int(unsafe.Sizeof(value)) +} diff --git a/gee-cache/day1-lru/geecache/lru/lru_test.go b/gee-cache/day1-lru/geecache/lru/lru_test.go new file mode 100644 index 0000000..a820af9 --- /dev/null +++ b/gee-cache/day1-lru/geecache/lru/lru_test.go @@ -0,0 +1,16 @@ +package lru + +import ( + "testing" +) + +func TestGet(t *testing.T) { + lru := New(0, nil) + lru.Add("key1", 1234) + if v, ok := lru.Get("key1"); !ok || v != 1234 { + t.Fatalf("cache hit key1=1234 failed") + } + if _, ok := lru.Get("key2"); ok { + t.Fatalf("cache miss key2 failed") + } +}