diff --git a/hw04_lru_cache/.sync b/hw04_lru_cache/.sync deleted file mode 100644 index e69de29..0000000 diff --git a/hw04_lru_cache/cache.go b/hw04_lru_cache/cache.go index 086a3c9..25f9239 100644 --- a/hw04_lru_cache/cache.go +++ b/hw04_lru_cache/cache.go @@ -1,5 +1,9 @@ package hw04lrucache +import ( + "sync" +) + type Key string type Cache interface { @@ -9,8 +13,7 @@ type Cache interface { } type lruCache struct { - Cache // Remove me after realization. - + mu sync.Mutex capacity int queue List items map[Key]*ListItem @@ -28,3 +31,57 @@ func NewCache(capacity int) Cache { items: make(map[Key]*ListItem, capacity), } } + +func (cache lruCache) Set(key Key, value interface{}) bool { + cache.mu.Lock() + defer cache.mu.Unlock() + + existedListItem, ok := cache.items[key] + + if ok { + cache.queue.Remove(existedListItem) + } else { + length := cache.queue.Len() + + if length == cache.capacity { + // remove the last item from the map and queue + lastItem := cache.queue.Back() + itemKey := lastItem.Value.(cacheItem).key + delete(cache.items, itemKey) + cache.queue.Remove(lastItem) + } + } + + newListItem := cacheItem{key, value} + // add item to the front + cache.queue.PushFront(newListItem) + cache.items[key] = cache.queue.Front() + + return ok +} + +func (cache lruCache) Get(key Key) (interface{}, bool) { + cache.mu.Lock() + defer cache.mu.Unlock() + + var result interface{} + + value, ok := cache.items[key] + + if ok { + cache.queue.MoveToFront(cache.items[key]) + result = value.Value.(cacheItem).value + } else { + result = nil + } + + return result, ok +} + +func (cache *lruCache) Clear() { + cache.mu.Lock() + defer cache.mu.Unlock() + + cache.queue = NewList() + cache.items = make(map[Key]*ListItem, cache.capacity) +} \ No newline at end of file diff --git a/hw04_lru_cache/cache_test.go b/hw04_lru_cache/cache_test.go index 2b43d4c..fcc55e2 100644 --- a/hw04_lru_cache/cache_test.go +++ b/hw04_lru_cache/cache_test.go @@ -49,8 +49,87 @@ func TestCache(t *testing.T) { require.Nil(t, val) }) - t.Run("purge logic", func(t *testing.T) { - // Write me + t.Run("more elements then in cache", func(t *testing.T) { + c := NewCache(3) + + c.Set("aaa", 100) + c.Set("bbb", 200) + c.Set("ccc", 300) + c.Set("ddd", 400) + + val, ok := c.Get("aaa") + require.False(t, ok) + require.Nil(t, val) + + val, ok = c.Get("bbb") + require.True(t, ok) + require.Equal(t, 200, val) + + val, ok = c.Get("ccc") + require.True(t, ok) + require.Equal(t, 300, val) + + val, ok = c.Get("ddd") + require.True(t, ok) + require.Equal(t, 400, val) + }) + + t.Run("more elements then in cache with get", func(t *testing.T) { + c := NewCache(3) + + c.Set("aaa", 100) + c.Set("bbb", 200) + c.Set("ccc", 300) + + c.Get("ccc") + c.Get("bbb") + c.Get("aaa") + c.Set("ddd", 400) + + val, ok := c.Get("aaa") + require.True(t, ok) + require.Equal(t, 100, val) + + val, ok = c.Get("bbb") + require.True(t, ok) + require.Equal(t, 200, val) + + val, ok = c.Get("ccc") + require.False(t, ok) + require.Nil(t, val) + + val, ok = c.Get("ddd") + require.True(t, ok) + require.Equal(t, 400, val) + }) + + t.Run("more elements then in cache with set", func(t *testing.T) { + c := NewCache(3) + + c.Set("aaa", 100) + c.Set("bbb", 200) + c.Set("ccc", 300) + + c.Set("ccc", 300) + c.Set("bbb", 200) + c.Set("aaa", 100) + c.Set("ddd", 400) + + val, ok := c.Get("aaa") + require.True(t, ok) + require.Equal(t, 100, val) + + val, ok = c.Get("bbb") + require.True(t, ok) + require.Equal(t, 200, val) + + val, ok = c.Get("ccc") + require.False(t, ok) + require.Nil(t, val) + + val, ok = c.Get("ddd") + require.True(t, ok) + require.Equal(t, 400, val) }) } diff --git a/hw04_lru_cache/go.mod b/hw04_lru_cache/go.mod index 95675da..c3fb378 100644 --- a/hw04_lru_cache/go.mod +++ b/hw04_lru_cache/go.mod @@ -1,10 +1,9 @@ -module github.com/fixme_my_friend/hw04_lru_cache +module hw04_lru_cache go 1.16 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/stretchr/testify v1.7.0 - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/hw04_lru_cache/go.sum b/hw04_lru_cache/go.sum index 62f8b7f..c221f64 100644 --- a/hw04_lru_cache/go.sum +++ b/hw04_lru_cache/go.sum @@ -1,20 +1,13 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw= -github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hw04_lru_cache/list.go b/hw04_lru_cache/list.go index 928ebb5..332f3ba 100644 --- a/hw04_lru_cache/list.go +++ b/hw04_lru_cache/list.go @@ -1,5 +1,9 @@ package hw04lrucache +import ( + "fmt" +) + type List interface { Len() int Front() *ListItem @@ -8,6 +12,7 @@ type List interface { PushBack(v interface{}) *ListItem Remove(i *ListItem) MoveToFront(i *ListItem) + Print() } type ListItem struct { @@ -17,10 +22,123 @@ type ListItem struct { } type list struct { - List // Remove me after realization. - // Place your code here. + firstNode *ListItem + lastNode *ListItem + len int } func NewList() List { return new(list) } + +func (l list) Len() int { + return l.len +} + +func (l list) Front() *ListItem { + if l.len > 0 { + return l.firstNode + } + return nil +} + +func (l list) Back() *ListItem { + if l.len > 0 { + return l.lastNode + } + + return nil +} + +func (l *list) PushFront(v interface{}) *ListItem { + newItem := ListItem{v, nil, nil} + + l.len++ + + if l.len > 1 { + newItem.Next = l.Front() + l.firstNode.Prev = &newItem + } + + l.firstNode = &newItem + + if l.len == 1 { + l.lastNode = &newItem + } + + return l.Front() +} + +func (l *list) PushBack(v interface{}) *ListItem { + newItem := ListItem{v, nil, nil} + + l.len++ + + if l.len > 1 { + newItem.Prev = l.Back() + l.lastNode.Next = &newItem + } + + l.lastNode = &newItem + + if l.len == 1 { + l.firstNode = &newItem + } + + return l.Back() +} + +func (l *list) Remove(i *ListItem) { + l.len-- + + if l.Front() == i { + l.firstNode = i.Next + } + + if l.Back() == i { + l.lastNode = i.Prev + } + + if i.Prev != nil { + i.Prev.Next = i.Next + } + + if i.Next != nil { + i.Next.Prev = i.Prev + } +} + +func (l *list) MoveToFront(i *ListItem) { + if l.Front() == i { + return + } + + if i.Prev != nil { + i.Prev.Next = i.Next + } + + if i.Next != nil { + i.Next.Prev = i.Prev + } else { + l.lastNode = i.Prev + } + + i.Prev = nil + i.Next = l.Front() + l.Front().Prev = i + l.firstNode = i +} + +func (l list) Print() { + if l.firstNode == nil { + fmt.Println(nil) + return + } + + i := l.Front() + + for i != nil { + fmt.Println(i.Value) + i = i.Next + } +} \ No newline at end of file