commit 7d1333989e82cd9ccca4c1a02017fd67333d4f06 Author: cubixle Date: Mon Feb 19 17:33:14 2024 +0000 add LRU Cache diff --git a/lru/coverage.out b/lru/coverage.out new file mode 100644 index 0000000..935ba51 --- /dev/null +++ b/lru/coverage.out @@ -0,0 +1,24 @@ +mode: set +github.com/cubixle/lru/lru.go:11.31,17.2 1 1 +github.com/cubixle/lru/lru.go:19.40,21.9 2 1 +github.com/cubixle/lru/lru.go:21.9,23.3 1 0 +github.com/cubixle/lru/lru.go:25.2,25.14 1 1 +github.com/cubixle/lru/lru.go:25.14,27.3 1 0 +github.com/cubixle/lru/lru.go:29.2,29.15 1 1 +github.com/cubixle/lru/lru.go:32.38,34.9 2 1 +github.com/cubixle/lru/lru.go:34.9,39.23 3 1 +github.com/cubixle/lru/lru.go:39.23,46.4 4 1 +github.com/cubixle/lru/lru.go:48.3,48.9 1 1 +github.com/cubixle/lru/lru.go:51.2,52.23 2 0 +github.com/cubixle/lru/lru.go:68.22,70.2 1 1 +github.com/cubixle/lru/lru.go:72.37,73.19 1 1 +github.com/cubixle/lru/lru.go:73.19,78.3 3 1 +github.com/cubixle/lru/lru.go:80.2,85.39 5 1 +github.com/cubixle/lru/lru.go:88.50,97.2 3 1 +github.com/cubixle/lru/lru.go:99.32,100.14 1 1 +github.com/cubixle/lru/lru.go:100.14,102.3 1 0 +github.com/cubixle/lru/lru.go:104.2,108.17 4 1 +github.com/cubixle/lru/lru.go:108.17,110.3 1 0 +github.com/cubixle/lru/lru.go:113.29,116.2 2 1 +github.com/cubixle/lru/lru.go:118.24,120.18 2 1 +github.com/cubixle/lru/lru.go:120.18,123.3 2 1 diff --git a/lru/go.mod b/lru/go.mod new file mode 100644 index 0000000..45337f2 --- /dev/null +++ b/lru/go.mod @@ -0,0 +1,3 @@ +module github.com/cubixle/lru + +go 1.21.6 diff --git a/lru/lru.go b/lru/lru.go new file mode 100644 index 0000000..75c1e56 --- /dev/null +++ b/lru/lru.go @@ -0,0 +1,121 @@ +package lru + +import "log" + +type Cache struct { + cap int + m map[string]*node + l *list +} + +func NewCache(cap int) *Cache { + return &Cache{ + cap: cap, + m: make(map[string]*node, 2), + l: newList(), + } +} + +func (c *Cache) Get(key string) string { + v, ok := c.m[key] + if !ok { + return "" + } + + if v == nil { + return "" + } + + return v.data +} + +func (c *Cache) Set(key, val string) { + node, ok := c.m[key] + if !ok { + node := c.l.pushFront(key, val) + + c.m[key] = node + + if len(c.m) > c.cap { + n := c.l.back() + c.l.remove(n) + delete(c.m, n.key) + } + + return + } + + node.data = val + c.l.moveToFront(node) +} + +type node struct { + key string + data string + next *node + prev *node +} + +type list struct { + head *node +} + +func newList() *list { + return &list{} +} + +func (l *list) moveToFront(n *node) { + if l.head == nil { + n.prev = l.head + l.head = n + l.head.prev = n + } else { + n.next = l.head + n.prev = l.head.prev + l.head.prev = n + l.head = n + } +} + +func (l *list) pushFront(key, data string) *node { + n := &node{ + key: key, + data: data, + } + + l.moveToFront(n) + + return n +} + +func (l *list) remove(n *node) { + if n == nil { + return + } + + n.prev.next = n.next + if n.next != nil { + n.next.prev = n.prev + } + + n = nil +} + +func (l *list) back() *node { + return l.head.prev +} + +func (l *list) debug() { + log.Println("--------") + start := l.head.data + node := l.head + for node != nil { + log.Println(node) + + node = node.next + if node != nil && node.data == start { + break + } + } + log.Println("--------") +} diff --git a/lru/lru_test.go b/lru/lru_test.go new file mode 100644 index 0000000..42b7b46 --- /dev/null +++ b/lru/lru_test.go @@ -0,0 +1,35 @@ +package lru_test + +import ( + "testing" + + "github.com/cubixle/lru" +) + +func TestCache(t *testing.T) { + cache := lru.NewCache(2) + cache.Set("usa", "washington") + cache.Set("uk", "london") + + city := cache.Get("usa") + if city != "washington" { + t.Fatal("didn't get the correct city for usa") + } + + city = cache.Get("uk") + if city != "london" { + t.Fatal("didn't get the correct city for uk") + } + + cache.Set("france", "paris") + + city = cache.Get("france") + if city != "paris" { + t.Fatal("didn't get the correct city for uk") + } + + city = cache.Get("usa") + if city != "" { + t.Fatal("usa is still in the cache but should have been removed") + } +}