Files
container/cache.go
T
zeekay 9502dbf6e9 fix: invoke the LRU eviction callback
NewLRUCacheWithOnEvict accepted an onEvict func and discarded it: the struct
held no such field and Put never called it, so every caller passing a callback
got silence. Store it and call it on capacity eviction. The root package had no
tests; these cover the callback, LRU recency, and the nil-callback path.
2026-08-21 00:11:43 -07:00

86 lines
1.9 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Package container provides LRU cache implementation
package container
import "container/list"
// LRUCache implements least-recently-used cache
type LRUCache[K comparable, V any] struct {
capacity int
elements map[K]*list.Element
lru *list.List
onEvict func(K, V)
}
// NewLRUCache creates a new LRU cache
type cacheEntry[K comparable, V any] struct {
key K
value V
}
func NewLRUCache[K comparable, V any](size int) *LRUCache[K, V] {
return &LRUCache[K, V]{
capacity: size,
elements: make(map[K]*list.Element),
lru: list.New(),
}
}
func NewLRUCacheWithOnEvict[K comparable, V any](size int, onEvict func(K, V)) *LRUCache[K, V] {
return &LRUCache[K, V]{
capacity: size,
elements: make(map[K]*list.Element),
lru: list.New(),
onEvict: onEvict,
}
}
func (c *LRUCache[K, V]) Get(key K) (V, bool) {
if elem, exists := c.elements[key]; exists {
c.lru.MoveToFront(elem)
return elem.Value.(*cacheEntry[K, V]).value, true
}
var zero V
return zero, false
}
func (c *LRUCache[K, V]) Put(key K, value V) {
if elem, exists := c.elements[key]; exists {
c.lru.MoveToFront(elem)
elem.Value.(*cacheEntry[K, V]).value = value
return
}
if len(c.elements) >= c.capacity {
back := c.lru.Back()
if back != nil {
c.lru.Remove(back)
evicted := back.Value.(*cacheEntry[K, V])
delete(c.elements, evicted.key)
if c.onEvict != nil {
c.onEvict(evicted.key, evicted.value)
}
}
}
elem := c.lru.PushFront(&cacheEntry[K, V]{key, value})
c.elements[key] = elem
}
func (c *LRUCache[K, V]) Delete(key K) {
if elem, exists := c.elements[key]; exists {
c.lru.Remove(elem)
delete(c.elements, key)
}
}
func (c *LRUCache[K, V]) Len() int {
return len(c.elements)
}
func (c *LRUCache[K, V]) Evict(key K) {
c.Delete(key)
}