Files
cache/lru/sized_cache.go
zeekayandHanzo Dev c08e9a7daa the corporate name is Lux Industries Inc
Lux Partners Limited is not the entity behind this work. Every place the
old name stood — copyright lines, site footers, brand config, legal and
compliance text — now names the company that actually holds it.

lux.fund is the exception and is not renamed to match: that site belongs
to Lux Assets Management LLC, a different company, and it says so.

Two kinds of line were left for a person rather than rewritten. A dated
news item recording that Lux Partners Limited was established in the Isle
of Man is a historical fact, and renaming it would falsify the record. And
a sentence asserting that the company is domiciled in the Isle of Man is a
claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle
of Man company, so swapping the name there would assert something untrue.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 12:56:42 -07:00

136 lines
2.8 KiB
Go

// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package lru
import (
"container/list"
"sync"
"github.com/luxfi/cache"
)
// SizedCache is an LRU cache bounded by total size rather than entry count.
type SizedCache[K comparable, V any] struct {
mu sync.Mutex
maxSize int
currentSize int
sizeFn func(K, V) int
items map[K]*list.Element
lru *list.List
}
type sizedEntry[K comparable, V any] struct {
key K
value V
size int
}
// NewSizedCache creates a size-bounded LRU cache.
func NewSizedCache[K comparable, V any](maxSize int, sizeFn func(K, V) int) *SizedCache[K, V] {
if maxSize <= 0 {
maxSize = 1
}
if sizeFn == nil {
sizeFn = func(K, V) int { return 1 }
}
return &SizedCache[K, V]{
maxSize: maxSize,
sizeFn: sizeFn,
items: make(map[K]*list.Element),
lru: list.New(),
}
}
// Put inserts or replaces a value.
func (c *SizedCache[K, V]) Put(key K, value V) {
c.mu.Lock()
defer c.mu.Unlock()
entrySize := c.sizeFn(key, value)
if entrySize > c.maxSize {
c.flushLocked()
return
}
if elem, ok := c.items[key]; ok {
oldEntry := elem.Value.(*sizedEntry[K, V])
c.currentSize -= oldEntry.size
c.lru.Remove(elem)
delete(c.items, key)
}
for c.currentSize > c.maxSize-entrySize {
back := c.lru.Back()
if back == nil {
break
}
oldEntry := back.Value.(*sizedEntry[K, V])
c.currentSize -= oldEntry.size
delete(c.items, oldEntry.key)
c.lru.Remove(back)
}
e := &sizedEntry[K, V]{key: key, value: value, size: entrySize}
c.items[key] = c.lru.PushFront(e)
c.currentSize += entrySize
}
// Get retrieves a value and marks it as most recently used.
func (c *SizedCache[K, V]) Get(key K) (V, bool) {
c.mu.Lock()
defer c.mu.Unlock()
if elem, ok := c.items[key]; ok {
c.lru.MoveToFront(elem)
return elem.Value.(*sizedEntry[K, V]).value, true
}
var zero V
return zero, false
}
// Evict removes a key from the cache.
func (c *SizedCache[K, V]) Evict(key K) {
c.mu.Lock()
defer c.mu.Unlock()
if elem, ok := c.items[key]; ok {
entry := elem.Value.(*sizedEntry[K, V])
c.currentSize -= entry.size
delete(c.items, key)
c.lru.Remove(elem)
}
}
// Flush removes all entries.
func (c *SizedCache[K, V]) Flush() {
c.mu.Lock()
defer c.mu.Unlock()
c.flushLocked()
}
func (c *SizedCache[K, V]) flushLocked() {
c.items = make(map[K]*list.Element)
c.lru.Init()
c.currentSize = 0
}
// Len returns number of entries.
func (c *SizedCache[K, V]) Len() int {
c.mu.Lock()
defer c.mu.Unlock()
return len(c.items)
}
// PortionFilled returns the ratio of size used to max size.
func (c *SizedCache[K, V]) PortionFilled() float64 {
c.mu.Lock()
defer c.mu.Unlock()
if c.maxSize == 0 {
return 0
}
return float64(c.currentSize) / float64(c.maxSize)
}
var _ cache.Cacher[struct{}, struct{}] = (*SizedCache[struct{}, struct{}])(nil)