1 //go:build !(js && wasm)
2 3 package database
4 5 import (
6 "container/list"
7 "sync"
8 )
9 10 // LRUCache provides a thread-safe LRU cache with configurable max size.
11 // It starts empty and grows on demand up to maxSize. When at capacity,
12 // the least recently used entry is evicted to make room for new entries.
13 type LRUCache[K comparable, V any] struct {
14 mu sync.Mutex
15 items map[K]*list.Element
16 order *list.List // Front = most recent, Back = least recent
17 maxSize int
18 }
19 20 // lruEntry holds a key-value pair for the LRU list.
21 type lruEntry[K comparable, V any] struct {
22 key K
23 value V
24 }
25 26 // NewLRUCache creates a new LRU cache with the given maximum size.
27 // The cache starts empty and grows on demand.
28 func NewLRUCache[K comparable, V any](maxSize int) *LRUCache[K, V] {
29 if maxSize <= 0 {
30 maxSize = 1000 // Default minimum
31 }
32 return &LRUCache[K, V]{
33 items: make(map[K]*list.Element),
34 order: list.New(),
35 maxSize: maxSize,
36 }
37 }
38 39 // Get retrieves a value by key and marks it as recently used.
40 // Returns the value and true if found, zero value and false otherwise.
41 func (c *LRUCache[K, V]) Get(key K) (value V, found bool) {
42 c.mu.Lock()
43 defer c.mu.Unlock()
44 45 if elem, ok := c.items[key]; ok {
46 c.order.MoveToFront(elem)
47 entry := elem.Value.(*lruEntry[K, V])
48 return entry.value, true
49 }
50 var zero V
51 return zero, false
52 }
53 54 // Put adds or updates a value, evicting the LRU entry if at capacity.
55 func (c *LRUCache[K, V]) Put(key K, value V) {
56 c.mu.Lock()
57 defer c.mu.Unlock()
58 59 // Update existing entry
60 if elem, ok := c.items[key]; ok {
61 c.order.MoveToFront(elem)
62 elem.Value.(*lruEntry[K, V]).value = value
63 return
64 }
65 66 // Evict LRU if at capacity
67 if len(c.items) >= c.maxSize {
68 oldest := c.order.Back()
69 if oldest != nil {
70 entry := oldest.Value.(*lruEntry[K, V])
71 delete(c.items, entry.key)
72 c.order.Remove(oldest)
73 }
74 }
75 76 // Add new entry
77 entry := &lruEntry[K, V]{key: key, value: value}
78 elem := c.order.PushFront(entry)
79 c.items[key] = elem
80 }
81 82 // Delete removes an entry from the cache.
83 func (c *LRUCache[K, V]) Delete(key K) {
84 c.mu.Lock()
85 defer c.mu.Unlock()
86 87 if elem, ok := c.items[key]; ok {
88 delete(c.items, key)
89 c.order.Remove(elem)
90 }
91 }
92 93 // Len returns the current number of entries in the cache.
94 func (c *LRUCache[K, V]) Len() int {
95 c.mu.Lock()
96 defer c.mu.Unlock()
97 return len(c.items)
98 }
99 100 // MaxSize returns the maximum capacity of the cache.
101 func (c *LRUCache[K, V]) MaxSize() int {
102 return c.maxSize
103 }
104 105 // Clear removes all entries from the cache.
106 func (c *LRUCache[K, V]) Clear() {
107 c.mu.Lock()
108 defer c.mu.Unlock()
109 c.items = make(map[K]*list.Element)
110 c.order.Init()
111 }
112 113 // Contains returns true if the key exists in the cache without updating LRU order.
114 func (c *LRUCache[K, V]) Contains(key K) bool {
115 c.mu.Lock()
116 defer c.mu.Unlock()
117 _, ok := c.items[key]
118 return ok
119 }
120