mirror of
https://github.com/luxfi/cache.git
synced 2026-09-24 00:24:18 +00:00
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]>
136 lines
2.8 KiB
Go
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)
|