From b357489d6c3e92ca3d8f640844e20a653d943a1e Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 02:47:51 +0300 Subject: [PATCH 01/20] create LFUCache type --- lfu_cache.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 lfu_cache.go diff --git a/lfu_cache.go b/lfu_cache.go new file mode 100644 index 0000000..e33c168 --- /dev/null +++ b/lfu_cache.go @@ -0,0 +1,13 @@ +package incache + +import ( + "container/list" + "sync" +) + +type LFUCache[K comparable, V any] struct { + mu sync.RWMutex + size uint + m map[K]*list.Element + evictionList *list.List +} From 202f1b9d43c7353ccd7295b653d4320cc84081f6 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 02:51:38 +0300 Subject: [PATCH 02/20] create NewLFU func --- lfu_cache.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index e33c168..745b4ec 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -11,3 +11,11 @@ type LFUCache[K comparable, V any] struct { m map[K]*list.Element evictionList *list.List } + +func NewLFU[K comparable, V any](size uint) *LFUCache[K, V] { + return &LFUCache[K, V]{ + size: size, + m: make(map[K]*list.Element), + evictionList: list.New(), + } +} From ce57faf8177c602b0bcb4395efce2a8ce3c9236c Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 02:53:29 +0300 Subject: [PATCH 03/20] create lfuItem type --- lfu_cache.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index 745b4ec..6a4a121 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -3,6 +3,7 @@ package incache import ( "container/list" "sync" + "time" ) type LFUCache[K comparable, V any] struct { @@ -19,3 +20,10 @@ func NewLFU[K comparable, V any](size uint) *LFUCache[K, V] { evictionList: list.New(), } } + +type lfuItem[K comparable, V any] struct { + key K + value V + freq uint + expireAt *time.Time +} From 29562a98201e3087cf3f42e62ac00ccf93f512db Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 03:03:18 +0300 Subject: [PATCH 04/20] implement evict func --- lfu_cache.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index 6a4a121..b7523f8 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -27,3 +27,14 @@ type lfuItem[K comparable, V any] struct { freq uint expireAt *time.Time } + +func (l *LFUCache[K, V]) evict(n int) { + for i := 0; i < n; i++ { + if b := l.evictionList.Back(); b != nil { + delete(l.m, b.Value.(*lfuItem[K, V]).key) + l.evictionList.Remove(b) + } else { + return + } + } +} From 206e7da5f28ba043fc17c599dca6972fc72f3a9d Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 03:35:40 +0300 Subject: [PATCH 05/20] implement move func --- lfu_cache.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index b7523f8..3e169d6 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -38,3 +38,29 @@ func (l *LFUCache[K, V]) evict(n int) { } } } + +func (l *LFUCache[K, V]) move(elem *list.Element) { + item := elem.Value.(*lfuItem[K, V]) + freq := item.freq + + curr := elem + for ; curr.Prev() != nil; curr = curr.Prev() { + if freq != curr.Value.(*lfuItem[K, V]).freq { + break + } + } + + if curr == elem { + item.freq++ + return + } + + if curr.Value.(*lfuItem[K, V]).freq == freq { + l.evictionList.MoveToFront(elem) + item.freq++ + return + } + + l.evictionList.MoveBefore(elem, curr) + item.freq++ +} From 799209f07a0bfe1607752cbdf3121c91df89ca71 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 03:37:43 +0300 Subject: [PATCH 06/20] implement set func --- lfu_cache.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index 3e169d6..f46f94f 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -28,6 +28,36 @@ type lfuItem[K comparable, V any] struct { expireAt *time.Time } +func (l *LFUCache[K, V]) set(key K, value V, exp time.Duration) { + item, ok := l.m[key] + var tm *time.Time + if exp > 0 { + t := time.Now().Add(exp) + tm = &t + } + if ok { + lfuItem := item.Value.(*lfuItem[K, V]) + + lfuItem.value = value + lfuItem.expireAt = tm + lfuItem.freq++ + + l.move(item) + } else { + if len(l.m) == int(l.size) { + l.evict(1) + } + + lfuItem := lfuItem[K, V]{ + key: key, + value: value, + freq: 1, + } + + l.m[key] = l.evictionList.PushBack(&lfuItem) + } +} + func (l *LFUCache[K, V]) evict(n int) { for i := 0; i < n; i++ { if b := l.evictionList.Back(); b != nil { From 9dbdb50260c75a9a676cbb40f037acd874831113 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 03:38:38 +0300 Subject: [PATCH 07/20] implement Set func --- lfu_cache.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index f46f94f..e8c2ed1 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -28,6 +28,13 @@ type lfuItem[K comparable, V any] struct { expireAt *time.Time } +func (l *LFUCache[K, V]) Set(key K, value V) { + l.mu.Lock() + defer l.mu.Unlock() + + l.set(key, value, 0) +} + func (l *LFUCache[K, V]) set(key K, value V, exp time.Duration) { item, ok := l.m[key] var tm *time.Time From 17165024908b637e83ea9d51596e5ed8a5c08160 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 03:38:50 +0300 Subject: [PATCH 08/20] implement SetWithTimeout func --- lfu_cache.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index e8c2ed1..b296d89 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -35,6 +35,13 @@ func (l *LFUCache[K, V]) Set(key K, value V) { l.set(key, value, 0) } +func (l *LFUCache[K, V]) SetWithTimeout(key K, value V, exp time.Duration) { + l.mu.Lock() + defer l.mu.Unlock() + + l.set(key, value, exp) +} + func (l *LFUCache[K, V]) set(key K, value V, exp time.Duration) { item, ok := l.m[key] var tm *time.Time From befa525ecac316a71d28bbc89dbb9c445eff28e8 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 03:43:56 +0300 Subject: [PATCH 09/20] implement delete func --- lfu_cache.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index b296d89..b3b01f5 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -72,6 +72,11 @@ func (l *LFUCache[K, V]) set(key K, value V, exp time.Duration) { } } +func (l *LFUCache[K, V]) delete(key K, elem *list.Element) { + delete(l.m, key) + l.evictionList.Remove(elem) +} + func (l *LFUCache[K, V]) evict(n int) { for i := 0; i < n; i++ { if b := l.evictionList.Back(); b != nil { From 5e034b102084884960d1734ea719d98fdef89fd6 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 03:44:19 +0300 Subject: [PATCH 10/20] implement Get func --- lfu_cache.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index b3b01f5..07b74fc 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -72,6 +72,26 @@ func (l *LFUCache[K, V]) set(key K, value V, exp time.Duration) { } } +func (l *LFUCache[K, V]) Get(key K) (v V, b bool) { + l.mu.Lock() + defer l.mu.Unlock() + + item, ok := l.m[key] + if !ok { + return + } + + lfuItem := item.Value.(*lfuItem[K, V]) + if lfuItem.expireAt != nil && lfuItem.expireAt.Before(time.Now()) { + l.delete(key, item) + return + } + + l.move(item) + + return lfuItem.value, true +} + func (l *LFUCache[K, V]) delete(key K, elem *list.Element) { delete(l.m, key) l.evictionList.Remove(elem) From 917245d5e935172713ff72bb4cd4b9f741df0e61 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 03:59:51 +0300 Subject: [PATCH 11/20] fix bugs --- lfu_cache.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lfu_cache.go b/lfu_cache.go index 07b74fc..8211d2b 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -54,7 +54,6 @@ func (l *LFUCache[K, V]) set(key K, value V, exp time.Duration) { lfuItem.value = value lfuItem.expireAt = tm - lfuItem.freq++ l.move(item) } else { @@ -130,6 +129,6 @@ func (l *LFUCache[K, V]) move(elem *list.Element) { return } - l.evictionList.MoveBefore(elem, curr) + l.evictionList.MoveAfter(elem, curr) item.freq++ } From 6788c95de83e6f0dbd005019c0e87b49ae8b6433 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:02:19 +0300 Subject: [PATCH 12/20] implement GetAll --- lfu_cache.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index 8211d2b..b4805a1 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -91,6 +91,20 @@ func (l *LFUCache[K, V]) Get(key K) (v V, b bool) { return lfuItem.value, true } +func (l *LFUCache[K, V]) GetAll() map[K]V { + l.mu.RLock() + defer l.mu.RUnlock() + + m := make(map[K]V) + for k, v := range l.m { + if v.Value.(*lfuItem[K, V]).expireAt == nil || !v.Value.(*lfuItem[K, V]).expireAt.Before(time.Now()) { + m[k] = v.Value.(*lfuItem[K, V]).value + } + } + + return m +} + func (l *LFUCache[K, V]) delete(key K, elem *list.Element) { delete(l.m, key) l.evictionList.Remove(elem) From d3be1131bafb2d644e7b41e0aa42733711a70ad5 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:03:38 +0300 Subject: [PATCH 13/20] implement NotFoundSet func --- lfu_cache.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index b4805a1..e3e255f 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -91,6 +91,19 @@ func (l *LFUCache[K, V]) Get(key K) (v V, b bool) { return lfuItem.value, true } +func (l *LFUCache[K, V]) NotFoundSet(k K, v V) bool { + l.mu.Lock() + defer l.mu.Unlock() + + _, ok := l.m[k] + if ok { + return false + } + + l.set(k, v, 0) + return true +} + func (l *LFUCache[K, V]) GetAll() map[K]V { l.mu.RLock() defer l.mu.RUnlock() From 039afd289ba0e98f552a3c03738c07da4fe4e9d0 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:19:48 +0300 Subject: [PATCH 14/20] implement NotFoundSetWithTimeout func --- lfu_cache.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index e3e255f..4362e3b 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -104,6 +104,19 @@ func (l *LFUCache[K, V]) NotFoundSet(k K, v V) bool { return true } +func (l *LFUCache[K, V]) NotFoundSetWithTimeout(k K, v V, t time.Duration) bool { + l.mu.Lock() + defer l.mu.Unlock() + + _, ok := l.m[k] + if ok { + return false + } + + l.set(k, v, t) + return true +} + func (l *LFUCache[K, V]) GetAll() map[K]V { l.mu.RLock() defer l.mu.RUnlock() From b18ef53751730aaf76f07c0d45cc7d0d67b51465 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:20:41 +0300 Subject: [PATCH 15/20] implement Delete func --- lfu_cache.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index 4362e3b..f10a9fb 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -131,6 +131,18 @@ func (l *LFUCache[K, V]) GetAll() map[K]V { return m } +func (l *LFUCache[K, V]) Delete(k K) { + l.mu.Lock() + defer l.mu.Unlock() + + item, ok := l.m[k] + if !ok { + return + } + + l.delete(k, item) +} + func (l *LFUCache[K, V]) delete(key K, elem *list.Element) { delete(l.m, key) l.evictionList.Remove(elem) From 03a328694cf693ba7e1b8a70ca00391ac50f32bc Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:25:59 +0300 Subject: [PATCH 16/20] implement TransferTo func --- lfu_cache.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index f10a9fb..9e87b06 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -131,6 +131,18 @@ func (l *LFUCache[K, V]) GetAll() map[K]V { return m } +func (src *LFUCache[K, V]) TransferTo(dst *LFUCache[K, V]) { + src.mu.Lock() + defer src.mu.Unlock() + + for k, v := range src.m { + if v.Value.(*lfuItem[K, V]).expireAt == nil || !v.Value.(*lfuItem[K, V]).expireAt.Before(time.Now()) { + src.delete(k, v) + dst.Set(k, v.Value.(*lfuItem[K, V]).value) + } + } +} + func (l *LFUCache[K, V]) Delete(k K) { l.mu.Lock() defer l.mu.Unlock() From c7516601fcf4ebb9de6a70a7bae0ef7ea53594e7 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:28:30 +0300 Subject: [PATCH 17/20] implement CopyTo func --- lfu_cache.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index 9e87b06..1d61271 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -143,6 +143,17 @@ func (src *LFUCache[K, V]) TransferTo(dst *LFUCache[K, V]) { } } +func (src *LFUCache[K, V]) CopyTo(dst *LFUCache[K, V]) { + src.mu.RLock() + defer src.mu.RUnlock() + + for k, v := range src.m { + if v.Value.(*lfuItem[K, V]).expireAt == nil || !v.Value.(*lfuItem[K, V]).expireAt.Before(time.Now()) { + dst.Set(k, v.Value.(*lfuItem[K, V]).value) + } + } +} + func (l *LFUCache[K, V]) Delete(k K) { l.mu.Lock() defer l.mu.Unlock() From aa4b3ee74c234efd4cc4fcbf2670becf68a30ae4 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:30:41 +0300 Subject: [PATCH 18/20] impl Count & Len funcs --- lfu_cache.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index 1d61271..1c8822b 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -154,6 +154,27 @@ func (src *LFUCache[K, V]) CopyTo(dst *LFUCache[K, V]) { } } +func (l *LFUCache[K, V]) Count() int { + l.mu.RLock() + defer l.mu.RUnlock() + + var count int + for _, v := range l.m { + if v.Value.(*lfuItem[K, V]).expireAt == nil || !v.Value.(*lfuItem[K, V]).expireAt.Before(time.Now()) { + count++ + } + } + + return count +} + +func (l *LFUCache[K, V]) Len() int { + l.mu.RLock() + defer l.mu.RUnlock() + + return len(l.m) +} + func (l *LFUCache[K, V]) Delete(k K) { l.mu.Lock() defer l.mu.Unlock() From 60b97a9b2f57881255dea1268a348cf5ae9f9ac3 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:32:33 +0300 Subject: [PATCH 19/20] impl Keys func --- lfu_cache.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index 1c8822b..b2935d5 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -154,6 +154,21 @@ func (src *LFUCache[K, V]) CopyTo(dst *LFUCache[K, V]) { } } +func (l *LFUCache[K, V]) Keys() []K { + l.mu.RLock() + defer l.mu.RUnlock() + + keys := make([]K, 0, l.Count()) + + for k, v := range l.m { + if v.Value.(*lfuItem[K, V]).expireAt == nil || !v.Value.(*lfuItem[K, V]).expireAt.Before(time.Now()) { + keys = append(keys, k) + } + } + + return keys +} + func (l *LFUCache[K, V]) Count() int { l.mu.RLock() defer l.mu.RUnlock() From 4c929f4d39c2b037a2e731bd72082797378fa2f9 Mon Sep 17 00:00:00 2001 From: knbr13 Date: Wed, 29 May 2024 04:32:42 +0300 Subject: [PATCH 20/20] impl Purge func --- lfu_cache.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lfu_cache.go b/lfu_cache.go index b2935d5..b124025 100644 --- a/lfu_cache.go +++ b/lfu_cache.go @@ -169,6 +169,14 @@ func (l *LFUCache[K, V]) Keys() []K { return keys } +func (l *LFUCache[K, V]) Purge() { + l.mu.Lock() + defer l.mu.Unlock() + + l.m = make(map[K]*list.Element) + l.evictionList.Init() +} + func (l *LFUCache[K, V]) Count() int { l.mu.RLock() defer l.mu.RUnlock()