1// Package cache provides cache implementations for the application layer.
2package cache
3
4import (
5 "container/list"
6 "sync"
7)
8
9// LRU is a thread-safe LRU (Least Recently Used) cache with a fixed capacity.
10// It implements port.Cache[K, V].
11//
12// When the cache reaches capacity, the least recently accessed entry is evicted
13// to make room for new entries. Both Get and Set operations mark an entry as
14// recently used.
15type LRU[K comparable, V any] struct {
16 capacity int
17 mu sync.RWMutex
18 items map[K]*list.Element
19 order *list.List // Front = most recent, Back = least recent
20}
21
22// entry holds a key-value pair in the LRU cache.
23type entry[K comparable, V any] struct {
24 key K
25 value V
26}
27
28// NewLRU creates a new LRU cache with the given capacity.
29// Capacity must be positive; if zero or negative, a capacity of 1 is used.
30func NewLRU[K comparable, V any](capacity int) *LRU[K, V] {
31 if capacity <= 0 {
32 capacity = 1
33 }
34 return &LRU[K, V]{
35 capacity: capacity,
36 items: make(map[K]*list.Element),
37 order: list.New(),
38 }
39}
40
41// Get retrieves a value by key and marks it as recently used.
42// Returns the value and true if found, or the zero value and false if not found.
43func (c *LRU[K, V]) Get(key K) (V, bool) {
44 c.mu.Lock()
45 defer c.mu.Unlock()
46
47 if elem, ok := c.items[key]; ok {
48 c.order.MoveToFront(elem)
49 return elem.Value.(*entry[K, V]).value, true
50 }
51 var zero V
52 return zero, false
53}
54
55// Set adds or updates a value in the cache.
56// If the key already exists, its value is updated and it's marked as recently used.
57// If the cache is at capacity, the least recently used entry is evicted.
58func (c *LRU[K, V]) Set(key K, value V) {
59 c.mu.Lock()
60 defer c.mu.Unlock()
61
62 // Update existing entry
63 if elem, ok := c.items[key]; ok {
64 c.order.MoveToFront(elem)
65 elem.Value.(*entry[K, V]).value = value
66 return
67 }
68
69 // Evict LRU entry if at capacity
70 if c.order.Len() >= c.capacity {
71 oldest := c.order.Back()
72 if oldest != nil {
73 c.order.Remove(oldest)
74 delete(c.items, oldest.Value.(*entry[K, V]).key)
75 }
76 }
77
78 // Add new entry at front (most recent)
79 elem := c.order.PushFront(&entry[K, V]{key: key, value: value})
80 c.items[key] = elem
81}
82
83// Remove deletes a key from the cache.
84// If the key doesn't exist, this is a no-op.
85func (c *LRU[K, V]) Remove(key K) {
86 c.mu.Lock()
87 defer c.mu.Unlock()
88
89 if elem, ok := c.items[key]; ok {
90 c.order.Remove(elem)
91 delete(c.items, key)
92 }
93}
94
95// Len returns the number of items currently in the cache.
96func (c *LRU[K, V]) Len() int {
97 c.mu.RLock()
98 defer c.mu.RUnlock()
99 return c.order.Len()
100}
101
102// Clear removes all items from the cache.
103func (c *LRU[K, V]) Clear() {
104 c.mu.Lock()
105 defer c.mu.Unlock()
106
107 c.items = make(map[K]*list.Element)
108 c.order.Init()
109}