Files
go-jdenticon/internal/engine/cache.go
Kevin McIntyre 70b2d55613
All checks were successful
CI / Test (push) Successful in 57s
CI / Lint (push) Successful in 14s
CI / Benchmark (push) Successful in 3m27s
style: fix gofmt formatting in cmd and internal packages
2026-02-10 14:00:27 -05:00

132 lines
3.5 KiB
Go

package engine
import (
"fmt"
"strconv"
"sync/atomic"
"gitea.dockr.co/kev/go-jdenticon/internal/constants"
lru "github.com/hashicorp/golang-lru/v2"
)
// CacheMetrics holds cache performance metrics using atomic operations for efficiency
type CacheMetrics struct {
hits int64 // Use atomic operations, no mutex needed
misses int64 // Use atomic operations, no mutex needed
}
// GetHits returns the number of cache hits
func (m *CacheMetrics) GetHits() int64 {
return atomic.LoadInt64(&m.hits)
}
// GetMisses returns the number of cache misses
func (m *CacheMetrics) GetMisses() int64 {
return atomic.LoadInt64(&m.misses)
}
// recordHit records a cache hit atomically
func (m *CacheMetrics) recordHit() {
atomic.AddInt64(&m.hits, 1)
}
// recordMiss records a cache miss atomically
func (m *CacheMetrics) recordMiss() {
atomic.AddInt64(&m.misses, 1)
}
// cacheKey generates a cache key from hash and size
func (g *Generator) cacheKey(hash string, size float64) string {
// Use a simple concatenation approach for better performance
// Convert float64 size to string with appropriate precision
return hash + ":" + strconv.FormatFloat(size, 'f', 1, 64)
}
// ClearCache removes all entries from the cache and resets metrics
func (g *Generator) ClearCache() {
g.mu.Lock()
defer g.mu.Unlock()
g.cache.Purge()
// Reset metrics
atomic.StoreInt64(&g.metrics.hits, 0)
atomic.StoreInt64(&g.metrics.misses, 0)
}
// GetCacheSize returns the number of items currently in the cache
func (g *Generator) GetCacheSize() int {
g.mu.RLock()
defer g.mu.RUnlock()
return g.cache.Len()
}
// GetCacheCapacity returns the maximum number of items the cache can hold
func (g *Generator) GetCacheCapacity() int {
g.mu.RLock()
defer g.mu.RUnlock()
// LRU cache doesn't expose capacity, return the configured capacity from config
return g.config.CacheSize
}
// GetCacheMetrics returns the cache hit and miss counts
func (g *Generator) GetCacheMetrics() (hits int64, misses int64) {
return g.metrics.GetHits(), g.metrics.GetMisses()
}
// SetConfig updates the generator's color configuration and clears the cache
func (g *Generator) SetConfig(colorConfig ColorConfig) {
g.mu.Lock()
defer g.mu.Unlock()
g.config.ColorConfig = colorConfig
g.cache.Purge() // Clear cache since config changed
}
// SetGeneratorConfig updates the generator's configuration, including cache size
func (g *Generator) SetGeneratorConfig(config GeneratorConfig) error {
g.mu.Lock()
defer g.mu.Unlock()
// Validate cache size
if config.CacheSize <= 0 {
return fmt.Errorf("jdenticon: engine: invalid cache size: %d", config.CacheSize)
}
// Create new cache with updated size if necessary
if config.CacheSize != g.config.CacheSize {
newCache, err := lru.New[string, *Icon](config.CacheSize)
if err != nil {
return fmt.Errorf("jdenticon: engine: failed to create new cache: %w", err)
}
g.cache = newCache
} else {
// Same cache size, just clear existing cache
g.cache.Purge()
}
g.config = config
// Update resolved max icon size
if config.MaxIconSize > 0 {
g.maxIconSize = config.MaxIconSize
} else {
g.maxIconSize = constants.DefaultMaxIconSize
}
return nil
}
// GetConfig returns the current color configuration
func (g *Generator) GetConfig() ColorConfig {
g.mu.RLock()
defer g.mu.RUnlock()
return g.config.ColorConfig
}
// GetGeneratorConfig returns the current generator configuration
func (g *Generator) GetGeneratorConfig() GeneratorConfig {
g.mu.RLock()
defer g.mu.RUnlock()
return g.config
}