|
@@ -0,0 +1,84 @@
|
|
|
+package lru_memory_cache
|
|
|
+
|
|
|
+import (
|
|
|
+ "container/list"
|
|
|
+ "errors"
|
|
|
+ "sync"
|
|
|
+)
|
|
|
+
|
|
|
+type Cache struct {
|
|
|
+ capacity int
|
|
|
+
|
|
|
+ mu sync.RWMutex
|
|
|
+ items map[string]*list.Element
|
|
|
+ queue *list.List
|
|
|
+}
|
|
|
+
|
|
|
+type Item struct {
|
|
|
+ Key string
|
|
|
+ Value interface{}
|
|
|
+}
|
|
|
+
|
|
|
+func NewCache(c Config) (*Cache, error) {
|
|
|
+ if c.capacity == 0 {
|
|
|
+ return nil, errors.New("empty capacity")
|
|
|
+ }
|
|
|
+
|
|
|
+ return &Cache{
|
|
|
+ capacity: int(c.capacity),
|
|
|
+ items: make(map[string]*list.Element),
|
|
|
+ queue: list.New(),
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (c *Cache) Set(key string, value interface{}) {
|
|
|
+ c.mu.Lock()
|
|
|
+ defer c.mu.Unlock()
|
|
|
+
|
|
|
+ if element, found := c.items[key]; found {
|
|
|
+ c.queue.MoveToFront(element)
|
|
|
+ element.Value.(*Item).Value = value
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if c.queue.Len() == c.capacity {
|
|
|
+ c.clean()
|
|
|
+ }
|
|
|
+
|
|
|
+ item := &Item{
|
|
|
+ Key: key,
|
|
|
+ Value: value,
|
|
|
+ }
|
|
|
+
|
|
|
+ element := c.queue.PushFront(item)
|
|
|
+ c.items[item.Key] = element
|
|
|
+}
|
|
|
+
|
|
|
+func (c *Cache) Get(key string) (interface{}, bool) {
|
|
|
+ c.mu.RLock()
|
|
|
+ defer c.mu.RUnlock()
|
|
|
+
|
|
|
+ if element, found := c.items[key]; found {
|
|
|
+ c.queue.MoveToFront(element)
|
|
|
+ return element.Value.(*Item).Value, true
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil, false
|
|
|
+}
|
|
|
+
|
|
|
+func (c *Cache) Delete(key string) {
|
|
|
+ c.mu.Lock()
|
|
|
+ defer c.mu.Unlock()
|
|
|
+
|
|
|
+ if element, found := c.items[key]; found {
|
|
|
+ c.queue.Remove(element)
|
|
|
+ delete(c.items, key)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (c *Cache) clean() {
|
|
|
+ if element := c.queue.Back(); element != nil {
|
|
|
+ item := c.queue.Remove(element).(*Item)
|
|
|
+ delete(c.items, item.Key)
|
|
|
+ }
|
|
|
+}
|