package engine import ( "fmt" "strconv" "sync/atomic" lru "github.com/hashicorp/golang-lru/v2" "gitea.dockr.co/kev/go-jdenticon/internal/constants" ) // 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 }