2025-12-26 14:22:46 +08:00
|
|
|
namespace AlicizaX.UI
|
2025-05-28 19:37:38 +08:00
|
|
|
{
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
public class MixedObjectPool<T> : IMixedObjectPool<T> where T : class
|
2025-05-28 19:37:38 +08:00
|
|
|
{
|
|
|
|
|
private const int DEFAULT_MAX_SIZE_PER_TYPE = 10;
|
|
|
|
|
|
2026-03-31 15:18:50 +08:00
|
|
|
private readonly Dictionary<string, Stack<T>> entries;
|
|
|
|
|
private readonly Dictionary<string, int> typeSize;
|
|
|
|
|
private readonly Dictionary<string, int> activeCountByType;
|
|
|
|
|
private readonly Dictionary<string, int> peakActiveByType;
|
2025-05-28 19:37:38 +08:00
|
|
|
private readonly IMixedObjectFactory<T> factory;
|
|
|
|
|
|
2026-03-31 15:18:50 +08:00
|
|
|
private readonly int defaultMaxSizePerType;
|
|
|
|
|
private int hitCount;
|
|
|
|
|
private int missCount;
|
|
|
|
|
private int destroyCount;
|
2025-05-28 19:37:38 +08:00
|
|
|
|
|
|
|
|
public MixedObjectPool(IMixedObjectFactory<T> factory) : this(factory, DEFAULT_MAX_SIZE_PER_TYPE)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MixedObjectPool(IMixedObjectFactory<T> factory, int defaultMaxSizePerType)
|
|
|
|
|
{
|
|
|
|
|
this.factory = factory;
|
|
|
|
|
this.defaultMaxSizePerType = defaultMaxSizePerType;
|
|
|
|
|
|
|
|
|
|
if (defaultMaxSizePerType <= 0)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("The maxSize must be greater than 0.");
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 15:18:50 +08:00
|
|
|
entries = new Dictionary<string, Stack<T>>(StringComparer.Ordinal);
|
|
|
|
|
typeSize = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
|
|
|
activeCountByType = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
|
|
|
peakActiveByType = new Dictionary<string, int>(StringComparer.Ordinal);
|
2025-05-28 19:37:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public T Allocate(string typeName)
|
|
|
|
|
{
|
2026-03-31 15:18:50 +08:00
|
|
|
Stack<T> stack = GetOrCreateStack(typeName);
|
|
|
|
|
if (stack.Count > 0)
|
2025-05-28 19:37:38 +08:00
|
|
|
{
|
2026-03-31 15:18:50 +08:00
|
|
|
T obj = stack.Pop();
|
|
|
|
|
hitCount++;
|
|
|
|
|
TrackAllocate(typeName);
|
2025-05-28 19:37:38 +08:00
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 15:18:50 +08:00
|
|
|
missCount++;
|
|
|
|
|
TrackAllocate(typeName);
|
2025-05-28 19:37:38 +08:00
|
|
|
return factory.Create(typeName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Free(string typeName, T obj)
|
|
|
|
|
{
|
|
|
|
|
if (obj == null) return;
|
|
|
|
|
|
|
|
|
|
if (!factory.Validate(typeName, obj))
|
|
|
|
|
{
|
|
|
|
|
factory.Destroy(typeName, obj);
|
2026-03-31 15:18:50 +08:00
|
|
|
destroyCount++;
|
|
|
|
|
TrackFree(typeName);
|
2025-05-28 19:37:38 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int maxSize = GetMaxSize(typeName);
|
2026-03-31 15:18:50 +08:00
|
|
|
Stack<T> stack = GetOrCreateStack(typeName);
|
|
|
|
|
|
|
|
|
|
factory.Reset(typeName, obj);
|
|
|
|
|
TrackFree(typeName);
|
|
|
|
|
|
|
|
|
|
if (stack.Count >= maxSize)
|
2025-05-28 19:37:38 +08:00
|
|
|
{
|
|
|
|
|
factory.Destroy(typeName, obj);
|
2026-03-31 15:18:50 +08:00
|
|
|
destroyCount++;
|
2025-05-28 19:37:38 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 15:18:50 +08:00
|
|
|
stack.Push(obj);
|
2025-05-28 19:37:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int GetMaxSize(string typeName)
|
|
|
|
|
{
|
|
|
|
|
if (typeSize.TryGetValue(typeName, out int size))
|
|
|
|
|
{
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return defaultMaxSizePerType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetMaxSize(string typeName, int value)
|
|
|
|
|
{
|
2026-03-31 15:18:50 +08:00
|
|
|
typeSize[typeName] = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void EnsureCapacity(string typeName, int value)
|
|
|
|
|
{
|
|
|
|
|
if (value <= 0)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int current = GetMaxSize(typeName);
|
|
|
|
|
if (value > current)
|
|
|
|
|
{
|
|
|
|
|
typeSize[typeName] = value;
|
|
|
|
|
}
|
2025-05-28 19:37:38 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 15:18:50 +08:00
|
|
|
public void Warm(string typeName, int count)
|
|
|
|
|
{
|
|
|
|
|
if (count <= 0)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int maxSize = GetMaxSize(typeName);
|
|
|
|
|
if (count > maxSize)
|
|
|
|
|
{
|
|
|
|
|
count = maxSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stack<T> stack = GetOrCreateStack(typeName);
|
|
|
|
|
while (stack.Count < count)
|
|
|
|
|
{
|
|
|
|
|
stack.Push(factory.Create(typeName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int GetActiveCount(string typeName)
|
|
|
|
|
{
|
|
|
|
|
return activeCountByType.TryGetValue(typeName, out int count) ? count : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int GetPeakActiveCount(string typeName)
|
|
|
|
|
{
|
|
|
|
|
return peakActiveByType.TryGetValue(typeName, out int count) ? count : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int HitCount => hitCount;
|
|
|
|
|
|
|
|
|
|
public int MissCount => missCount;
|
|
|
|
|
|
|
|
|
|
public int DestroyCount => destroyCount;
|
|
|
|
|
|
2025-05-28 19:37:38 +08:00
|
|
|
protected virtual void Clear()
|
|
|
|
|
{
|
|
|
|
|
foreach (var kv in entries)
|
|
|
|
|
{
|
|
|
|
|
string typeName = kv.Key;
|
2026-03-31 15:18:50 +08:00
|
|
|
Stack<T> stack = kv.Value;
|
2025-05-28 19:37:38 +08:00
|
|
|
|
2026-03-31 15:18:50 +08:00
|
|
|
if (stack == null || stack.Count <= 0) continue;
|
2025-05-28 19:37:38 +08:00
|
|
|
|
2026-03-31 15:18:50 +08:00
|
|
|
while (stack.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
factory.Destroy(typeName, stack.Pop());
|
|
|
|
|
destroyCount++;
|
|
|
|
|
}
|
2025-05-28 19:37:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entries.Clear();
|
|
|
|
|
typeSize.Clear();
|
2026-03-31 15:18:50 +08:00
|
|
|
activeCountByType.Clear();
|
|
|
|
|
peakActiveByType.Clear();
|
2025-05-28 19:37:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
Clear();
|
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
|
}
|
2026-03-31 15:18:50 +08:00
|
|
|
|
|
|
|
|
private Stack<T> GetOrCreateStack(string typeName)
|
|
|
|
|
{
|
|
|
|
|
if (!entries.TryGetValue(typeName, out Stack<T> stack))
|
|
|
|
|
{
|
|
|
|
|
stack = new Stack<T>(GetMaxSize(typeName));
|
|
|
|
|
entries[typeName] = stack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return stack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void TrackAllocate(string typeName)
|
|
|
|
|
{
|
2026-04-15 14:51:57 +08:00
|
|
|
activeCountByType.TryGetValue(typeName, out int active);
|
|
|
|
|
active++;
|
2026-03-31 15:18:50 +08:00
|
|
|
activeCountByType[typeName] = active;
|
2026-04-15 14:51:57 +08:00
|
|
|
|
|
|
|
|
peakActiveByType.TryGetValue(typeName, out int peak);
|
|
|
|
|
if (active > peak)
|
2026-03-31 15:18:50 +08:00
|
|
|
{
|
|
|
|
|
peakActiveByType[typeName] = active;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void TrackFree(string typeName)
|
|
|
|
|
{
|
2026-04-15 14:51:57 +08:00
|
|
|
activeCountByType.TryGetValue(typeName, out int active);
|
2026-03-31 15:18:50 +08:00
|
|
|
if (active > 0)
|
|
|
|
|
{
|
|
|
|
|
activeCountByType[typeName] = active - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 14:51:57 +08:00
|
|
|
peakActiveByType.TryGetValue(typeName, out int peak);
|
|
|
|
|
int recommendedMax = peak + 1;
|
|
|
|
|
typeSize.TryGetValue(typeName, out int currentMax);
|
|
|
|
|
if (currentMax <= 0)
|
|
|
|
|
{
|
|
|
|
|
currentMax = defaultMaxSizePerType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (recommendedMax > currentMax)
|
2026-03-31 15:18:50 +08:00
|
|
|
{
|
|
|
|
|
typeSize[typeName] = recommendedMax;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-28 19:37:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|