From 5b57e78c0b0cbb5b9dbdc8cbba1f6fea5afdaf9c Mon Sep 17 00:00:00 2001 From: DimVlas Date: Sat, 11 May 2024 22:36:52 +0300 Subject: [PATCH] HW04 is complited --- hw04_lru_cache/cache.go | 69 +++++++++++++++++++++++++++++++++--- hw04_lru_cache/cache_test.go | 38 +++++++++++++++++--- hw04_lru_cache/list.go | 59 ++++++++++-------------------- hw04_lru_cache/list_test.go | 2 -- 4 files changed, 117 insertions(+), 51 deletions(-) diff --git a/hw04_lru_cache/cache.go b/hw04_lru_cache/cache.go index a43d727..0a7b366 100644 --- a/hw04_lru_cache/cache.go +++ b/hw04_lru_cache/cache.go @@ -1,25 +1,84 @@ package hw04lrucache +import "sync" + type Key string type Cache interface { - Set(key Key, value interface{}) bool - Get(key Key) (interface{}, bool) - Clear() + Set(key Key, value interface{}) bool // Добавить значение в кэш. + Get(key Key) (interface{}, bool) // Получить значение из кэша. + Clear() // Очистить кэш. } -type lruCache struct { - Cache // Remove me after realization. +type cacheItem struct { + Value interface{} + Key Key +} +type lruCache struct { capacity int + mtx sync.Mutex queue List items map[Key]*ListItem } +// Создать новый LRU-кэш. func NewCache(capacity int) Cache { return &lruCache{ capacity: capacity, + mtx: sync.Mutex{}, queue: NewList(), items: make(map[Key]*ListItem, capacity), } } + +// Добавить значение в кэш. +func (c *lruCache) Set(key Key, value interface{}) bool { + c.mtx.Lock() + defer c.mtx.Unlock() + + itm, ok := c.items[key] + if ok { // ключ есть в кэше + cacheVal := itm.Value.(*cacheItem) + cacheVal.Value = value + itm.Value = cacheVal + c.queue.MoveToFront(itm) + } else { // ключа нет в кэше + if c.capacity == c.queue.Len() { // кэш полностью заполнен + excessVal := c.queue.Back().Value.(*cacheItem) + delete(c.items, excessVal.Key) + + c.queue.Remove(c.queue.Back()) + } + + cacheVal := &cacheItem{Key: key, Value: value} + itm = c.queue.PushFront(cacheVal) + c.items[key] = itm + } + + return ok +} + +// Получить значение из кэша. +func (c *lruCache) Get(key Key) (interface{}, bool) { + defer c.mtx.Unlock() + c.mtx.Lock() + + itm, ok := c.items[key] + + if ok { + c.queue.MoveToFront(itm) + + return itm.Value.(*cacheItem).Value, true + } + + return nil, false +} + +// Очистить кэш. +func (c *lruCache) Clear() { + c.mtx.Lock() + c.queue = NewList() + c.items = make(map[Key]*ListItem, c.capacity) + c.mtx.Unlock() +} diff --git a/hw04_lru_cache/cache_test.go b/hw04_lru_cache/cache_test.go index 2b43d4c..8336ad5 100644 --- a/hw04_lru_cache/cache_test.go +++ b/hw04_lru_cache/cache_test.go @@ -50,13 +50,43 @@ func TestCache(t *testing.T) { }) t.Run("purge logic", func(t *testing.T) { - // Write me + c := NewCache(5) + c.Set("100", 100) + c.Set("200", 200) + c.Clear() + + val, ok := c.Get("100") + require.False(t, ok) + require.Nil(t, val) }) -} -func TestCacheMultithreading(t *testing.T) { - t.Skip() // Remove me if task with asterisk completed. + t.Run("extrude", func(t *testing.T) { + c := NewCache(5) + c.Set("100", 100) + c.Set("200", 200) + c.Set("300", 300) + c.Set("400", 400) + c.Set("500", 500) + // 500, 400, 300, 200, 100 + + val, ok := c.Get("100") // 100, 500, 400, 300, 200 + require.True(t, ok) + require.Equal(t, 100, val) + + ok = c.Set("1000", 1000) // 1000, 100, 500, 400, 300 + require.False(t, ok) + + val, ok = c.Get("200") + require.False(t, ok) + require.Nil(t, val) + + val, ok = c.Get("300") // 100, 500, 400, 300, 200 + require.True(t, ok) + require.Equal(t, 300, val) + }) +} +func TestCacheMultithreading(_ *testing.T) { c := NewCache(10) wg := &sync.WaitGroup{} wg.Add(2) diff --git a/hw04_lru_cache/list.go b/hw04_lru_cache/list.go index a83dcd5..aa6ebcf 100644 --- a/hw04_lru_cache/list.go +++ b/hw04_lru_cache/list.go @@ -1,15 +1,13 @@ package hw04lrucache -import "sync" - type List interface { - Len() int - Front() *ListItem - Back() *ListItem - PushFront(v interface{}) *ListItem - PushBack(v interface{}) *ListItem - Remove(i *ListItem) - MoveToFront(i *ListItem) + Len() int // Кол-во элементов в списке. + Front() *ListItem // Первый элемент списка. + Back() *ListItem // Последний элемент списка. + PushFront(v interface{}) *ListItem // Добавление элемента в начало списка. + PushBack(v interface{}) *ListItem // Добавление элемента в конец списка. + Remove(i *ListItem) // Удаление элемента из списка. + MoveToFront(i *ListItem) // Переместить элемент вперед. } type ListItem struct { @@ -24,27 +22,28 @@ type list struct { back *ListItem } -// Кол-во элементов в списке +// Создать новый двусвязный список. +func NewList() List { + return new(list) +} + +// Кол-во элементов в списке. func (lst *list) Len() int { return lst.len } -// Первый элемент списка +// Первый элемент списка. func (lst *list) Front() *ListItem { return lst.front } -// Последний элемент списка +// Последний элемент списка. func (lst *list) Back() *ListItem { return lst.back } -// Добавление элемента в начало списка +// Добавление элемента в начало списка. func (lst *list) PushFront(v interface{}) *ListItem { - var mtx sync.Mutex - defer mtx.Unlock() - - mtx.Lock() itm := &ListItem{ Value: v, Next: lst.front, @@ -61,12 +60,8 @@ func (lst *list) PushFront(v interface{}) *ListItem { return itm } -// Добавление элемента в конец списка +// Добавление элемента в конец списка. func (lst *list) PushBack(v interface{}) *ListItem { - var mtx sync.Mutex - defer mtx.Unlock() - - mtx.Lock() itm := &ListItem{ Value: v, Prev: lst.back, @@ -83,17 +78,12 @@ func (lst *list) PushBack(v interface{}) *ListItem { return itm } -// Удаление элемента из списка +// Удаление элемента из списка. func (lst *list) Remove(i *ListItem) { - var mtx sync.Mutex - if i == nil { return } - mtx.Lock() - defer mtx.Unlock() - if i.Prev == nil && i.Next == nil { // единственный элемент lst.front = nil lst.back = nil @@ -120,17 +110,12 @@ func (lst *list) Remove(i *ListItem) { lst.len-- } -// переместить элемент вперед +// Переместить элемент вперед. func (lst *list) MoveToFront(i *ListItem) { if i.Prev == nil { // первый элемент return } - var mtx sync.Mutex - defer mtx.Unlock() - - mtx.Lock() - if i.Next != nil { // элемент из середины i.Prev.Next = i.Next // предыдущий ссылается на следующий i.Next.Prev = i.Prev // следующий ссылается на предыдущий @@ -143,10 +128,4 @@ func (lst *list) MoveToFront(i *ListItem) { i.Next = lst.front lst.front = i i.Next.Prev = i - -} - -// Создать новый двусвязный список -func NewList() List { - return new(list) } diff --git a/hw04_lru_cache/list_test.go b/hw04_lru_cache/list_test.go index 4abfea6..8ba3d87 100644 --- a/hw04_lru_cache/list_test.go +++ b/hw04_lru_cache/list_test.go @@ -31,7 +31,6 @@ func TestList(t *testing.T) { require.Equal(t, 0, l.Len()) require.Nil(t, l.Front()) require.Nil(t, l.Back()) - }) t.Run("single back", func(t *testing.T) { @@ -70,7 +69,6 @@ func TestList(t *testing.T) { require.Equal(t, 33, l.Front().Value) require.Equal(t, 22, l.Front().Next.Value) require.Equal(t, 11, l.Back().Value) - }) t.Run("complex", func(t *testing.T) {