diff --git a/hw04_lru_cache/cache.go b/hw04_lru_cache/cache.go index 738399e..89ec238 100644 --- a/hw04_lru_cache/cache.go +++ b/hw04_lru_cache/cache.go @@ -1,22 +1,79 @@ package hw04_lru_cache //nolint:golint,stylecheck +import ( + "log" + "sync" +) type Key string type Cache interface { - // Place your code here + Set(key Key, value interface{}) bool // Добавить значение в кэш по ключу + Get(key Key) (interface{}, bool) // Получить значение из кэша по ключу + Clear() // Очистить кэш } type lruCache struct { - // Place your code here: - // - capacity - // - queue - // - items + capacity int + queue *List + items map[Key]*ListItem + mx sync.RWMutex } -type cacheItem struct { - // Place your code here +type Item struct { + Key Key + Value interface{} } func NewCache(capacity int) Cache { - return &lruCache{} + return &lruCache{ + capacity: capacity, + queue: NewList(), + items: make(map[Key]*ListItem), + } +} + +func (l *lruCache) Set(key Key, value interface{}) bool { + if _, exists := l.items[key]; exists { + l.mx.RLock() + l.items[key].Value = Item{Value: value, Key: key} + l.queue.MoveToFront(l.items[key]) + l.mx.RUnlock() + return exists + } + if l.queue.Len() == l.capacity { + l.mx.RLock() + k, ok := l.queue.Back().Value.(Item) + if !ok { + log.Fatal("Ошибка приведения типов") + } + delete(l.items, k.Key) + l.queue.Remove(l.queue.Back()) + l.mx.RUnlock() + } + l.mx.RLock() + l.items[key] = l.queue.PushFront(Item{Value: value, Key: key}) + l.mx.RUnlock() + return false +} + +func (l *lruCache) Get(key Key) (interface{}, bool) { + l.mx.Lock() + defer l.mx.Unlock() + if l.items[key] == nil { + return nil, false + } + l.queue.MoveToFront(l.items[key]) + s, ok := l.items[key].Value.(Item) + if !ok { + log.Fatal("Ошибка приведения типов") + } + return s.Value, true +} + +func (l *lruCache) Clear() { + l.mx.Lock() + l.items = nil + l.queue.len = 0 + l.queue.Info = ListItem{} + l.mx.Unlock() } diff --git a/hw04_lru_cache/cache_test.go b/hw04_lru_cache/cache_test.go index e99447d..09ab0bd 100644 --- a/hw04_lru_cache/cache_test.go +++ b/hw04_lru_cache/cache_test.go @@ -50,12 +50,35 @@ func TestCache(t *testing.T) { }) t.Run("purge logic", func(t *testing.T) { - // Write me + c := NewCache(3) + + wasInCache := c.Set("aaa", 100) + require.False(t, wasInCache) + + wasInCache = c.Set("bbb", 200) + require.False(t, wasInCache) + + wasInCache = c.Set("ccc", 300) + require.False(t, wasInCache) + + _, ok := c.Get("bbb") + require.True(t, ok) + + _, ok = c.Get("aaa") + require.True(t, ok) + + wasInCache = c.Set("ddd", 400) + require.False(t, wasInCache) + + _, ok = c.Get("ddd") + require.True(t, ok) + + _, ok = c.Get("ccc") + require.False(t, ok) }) } func TestCacheMultithreading(t *testing.T) { - t.Skip() // Remove if task with asterisk completed c := NewCache(10) wg := &sync.WaitGroup{} diff --git a/hw04_lru_cache/go.mod b/hw04_lru_cache/go.mod index b4abc63..ba531d5 100644 --- a/hw04_lru_cache/go.mod +++ b/hw04_lru_cache/go.mod @@ -1,4 +1,4 @@ -module github.com/fixme_my_friend/hw04_lru_cache +module github.com/tiburon-777/HW_OTUS/hw04_lru_cache go 1.14 diff --git a/hw04_lru_cache/list.go b/hw04_lru_cache/list.go index eab309f..8e7db93 100644 --- a/hw04_lru_cache/list.go +++ b/hw04_lru_cache/list.go @@ -1,17 +1,101 @@ package hw04_lru_cache //nolint:golint,stylecheck -type List interface { - // Place your code here +type ListInterface interface { + Len() int // длина списка + Front() *ListItem // первый Item + Back() *ListItem // последний Item + PushFront(v interface{}) *ListItem // добавить значение в начало + PushBack(v interface{}) *ListItem // добавить значение в конец + Remove(i *ListItem) // удалить элемент + MoveToFront(i *ListItem) // переместить элемент в начало } -type listItem struct { - // Place your code here +type ListItem struct { + Value interface{} // значение + Next *ListItem // следующий элемент + Prev *ListItem // предыдущий элемент } -type list struct { - // Place your code here +type List struct { + Info ListItem + len int } -func NewList() List { - return &list{} +func NewList() *List { + return &List{len: 0} +} + +func (l *List) Len() int { + return l.len +} + +func (l *List) Front() *ListItem { + return l.Info.Next +} + +func (l *List) Back() *ListItem { + return l.Info.Prev +} + +func (l *List) PushFront(v interface{}) *ListItem { + e := &ListItem{Value: v} + if l.len != 0 { + e.Prev = l.Info.Next + l.Info.Next.Next = e + } else { + l.Info.Prev = e + } + l.Info.Next = e + l.len++ + return e +} + +func (l *List) PushBack(v interface{}) *ListItem { + e := &ListItem{Value: v} + if l.len != 0 { + e.Next = l.Info.Prev + l.Info.Prev.Prev = e + } else { + l.Info.Next = e + } + l.Info.Prev = e + l.len++ + return e +} + +func (l *List) Remove(i *ListItem) { + if i.Prev == nil { + i.Prev = &ListItem{} + l.Info.Prev = i.Next + } + if i.Next == nil { + i.Next = &ListItem{} + l.Info.Next = i.Prev + } + i.Prev.Next = i.Next + i.Next.Prev = i.Prev + if l.Len() > 1 { + l.Info.Next.Next = nil + l.Info.Prev.Prev = nil + } else { + l.Info = ListItem{} + } + l.len-- +} + +func (l *List) MoveToFront(i *ListItem) { + if l.Info.Next == i { + return + } + if i.Prev != nil { + i.Prev.Next = i.Next + i.Next.Prev = i.Prev + } else { + i.Next.Prev = i.Prev + l.Info.Prev = i.Next + } + i.Prev = l.Front() + l.Front().Next = i + i.Next = nil + l.Info.Next = i } diff --git a/hw04_lru_cache/list_test.go b/hw04_lru_cache/list_test.go index 02109fd..a219aab 100644 --- a/hw04_lru_cache/list_test.go +++ b/hw04_lru_cache/list_test.go @@ -18,14 +18,50 @@ func TestList(t *testing.T) { t.Run("complex", func(t *testing.T) { l := NewList() - l.PushFront(10) // [10] - l.PushBack(20) // [10, 20] - l.PushBack(30) // [10, 20, 30] - require.Equal(t, l.Len(), 3) + l.PushFront(10) + require.Equal(t, []int{10}, transpond(l)) + require.Equal(t, 1, l.Len()) + + l.Remove(l.Front()) + require.Equal(t, []int{}, transpond(l)) + require.Equal(t, 0, l.Len()) + + l.PushFront(10) + require.Equal(t, []int{10}, transpond(l)) + require.Equal(t, 1, l.Len()) + + l.PushBack(20) + require.Equal(t, []int{20, 10}, transpond(l)) + require.Equal(t, 2, l.Len()) + + l.PushBack(30) + require.Equal(t, []int{30, 20, 10}, transpond(l)) + require.Equal(t, 3, l.Len()) middle := l.Back().Next // 20 - l.Remove(middle) // [10, 30] - require.Equal(t, l.Len(), 2) + l.Remove(middle) + require.Equal(t, []int{30, 10}, transpond(l)) + require.Equal(t, 2, l.Len()) + + l.PushFront(20) + require.Equal(t, []int{30, 10, 20}, transpond(l)) + require.Equal(t, 3, l.Len()) + + l.Remove(l.Back()) + require.Equal(t, []int{10, 20}, transpond(l)) + require.Equal(t, 2, l.Len()) + + l.PushBack(30) + require.Equal(t, []int{30, 10, 20}, transpond(l)) + require.Equal(t, 3, l.Len()) + + l.Remove(l.Front()) + require.Equal(t, []int{30, 10}, transpond(l)) + require.Equal(t, 2, l.Len()) + + l.PushFront(20) + require.Equal(t, []int{30, 10, 20}, transpond(l)) + require.Equal(t, 3, l.Len()) for i, v := range [...]int{40, 50, 60, 70, 80} { if i%2 == 0 { @@ -33,19 +69,28 @@ func TestList(t *testing.T) { } else { l.PushBack(v) } - } // [80, 60, 40, 10, 30, 50, 70] - - require.Equal(t, l.Len(), 7) - require.Equal(t, 80, l.Front().Value) - require.Equal(t, 70, l.Back().Value) - - l.MoveToFront(l.Front()) // [80, 60, 40, 10, 30, 50, 70] - l.MoveToFront(l.Back()) // [70, 80, 60, 40, 10, 30, 50] - - elems := make([]int, 0, l.Len()) - for i := l.Back(); i != nil; i = i.Next { - elems = append(elems, i.Value.(int)) } - require.Equal(t, []int{50, 30, 10, 40, 60, 80, 70}, elems) + require.Equal(t, []int{70, 50, 30, 10, 20, 40, 60, 80}, transpond(l)) + require.Equal(t, 8, l.Len()) + + l.MoveToFront(l.Front()) + require.Equal(t, []int{70, 50, 30, 10, 20, 40, 60, 80}, transpond(l)) + require.Equal(t, 8, l.Len()) + + l.MoveToFront(l.Back().Next.Next) // 30 + require.Equal(t, []int{70, 50, 10, 20, 40, 60, 80, 30}, transpond(l)) + require.Equal(t, 8, l.Len()) + + l.MoveToFront(l.Back()) + require.Equal(t, []int{50, 10, 20, 40, 60, 80, 30, 70}, transpond(l)) + require.Equal(t, 8, l.Len()) }) } + +func transpond(l *List) []int { + elems := make([]int, 0, l.Len()) + for i := l.Back(); i != nil; i = i.Next { + elems = append(elems, i.Value.(int)) + } + return elems +}