// MIT License - Copyright (c) 2026 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
namespace WallstopStudios.UnityHelpers.Core.DataStructure
{
using System;
using System.Runtime.CompilerServices;
using WallstopStudios.UnityHelpers.Core.Helper;
///
/// Immutable snapshot of cache performance statistics.
///
///
/// Statistics are only recorded when is enabled.
/// Use to retrieve the current snapshot.
///
public readonly struct CacheStatistics : IEquatable
{
///
/// The number of times a requested key was found in the cache.
///
public long HitCount { get; }
///
/// The number of times a requested key was not found in the cache.
///
public long MissCount { get; }
///
/// The total number of entries evicted from the cache (for any reason).
///
public long EvictionCount { get; }
///
/// The number of times the cache loaded a value using the factory function.
///
public long LoadCount { get; }
///
/// The number of entries that were evicted due to TTL expiration.
///
public long ExpiredCount { get; }
///
/// The current number of entries in the cache.
///
public int CurrentSize { get; }
///
/// The maximum number of entries the cache has held at any point.
///
public int PeakSize { get; }
///
/// The number of times the cache has grown due to thrash detection.
///
public int GrowthEvents { get; }
private readonly int _hash;
///
/// The cache hit rate as a value between 0.0 and 1.0.
/// Returns 0.0 if no requests have been made.
///
public double HitRate
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
long total = HitCount + MissCount;
return total > 0 ? HitCount / (double)total : 0.0;
}
}
///
/// The cache miss rate as a value between 0.0 and 1.0.
/// Returns 0.0 if no requests have been made.
///
public double MissRate
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => 1.0 - HitRate;
}
///
/// Creates a new statistics snapshot.
///
public CacheStatistics(
long hitCount,
long missCount,
long evictionCount,
long loadCount,
long expiredCount,
int currentSize,
int peakSize,
int growthEvents
)
{
HitCount = hitCount;
MissCount = missCount;
EvictionCount = evictionCount;
LoadCount = loadCount;
ExpiredCount = expiredCount;
CurrentSize = currentSize;
PeakSize = peakSize;
GrowthEvents = growthEvents;
_hash = Objects.HashCode(
hitCount,
missCount,
evictionCount,
loadCount,
expiredCount,
currentSize,
peakSize,
growthEvents
);
}
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(CacheStatistics other)
{
return _hash == other._hash
&& HitCount == other.HitCount
&& MissCount == other.MissCount
&& EvictionCount == other.EvictionCount
&& LoadCount == other.LoadCount
&& ExpiredCount == other.ExpiredCount
&& CurrentSize == other.CurrentSize
&& PeakSize == other.PeakSize
&& GrowthEvents == other.GrowthEvents;
}
///
public override bool Equals(object obj)
{
return obj is CacheStatistics other && Equals(other);
}
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return _hash;
}
///
/// Equality operator.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(CacheStatistics left, CacheStatistics right)
{
return left.Equals(right);
}
///
/// Inequality operator.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(CacheStatistics left, CacheStatistics right)
{
return !left.Equals(right);
}
///
public override string ToString()
{
return $"CacheStatistics(Hits={HitCount}, Misses={MissCount}, Evictions={EvictionCount}, "
+ $"Size={CurrentSize}, Peak={PeakSize}, HitRate={HitRate:P2})";
}
}
}