using System; using System.Collections.Generic; namespace Gum.Pooling { public sealed class StackPool : IPool where T : IPoolable { private readonly Stack _objectPool; private readonly IPoolableInstanceProvider _poolableInstanceProvider; private readonly Func _instanceProviderCallback; private readonly ProviderType _providerType; public int Count => _objectPool.Count; internal StackPool(IPoolableInstanceProvider poolableInstanceProvider, int initialSize = 0) { _poolableInstanceProvider = poolableInstanceProvider; _objectPool = new Stack(); _providerType = ProviderType.FromPoolableInstanceProvider; CreatePool(initialSize); } internal StackPool(Func instanceProviderCallback, int initialSize = 0) { _instanceProviderCallback = instanceProviderCallback; _objectPool = new Stack(); _providerType = ProviderType.FromMethod; CreatePool(initialSize); } private void CreatePool(int size) { for (int index = 0; index < size; index++) { T instance = Create(); Put(instance); } } private void Put(IPoolable poolable) { if (_objectPool.Contains(poolable)) { return; } _objectPool.Push(poolable); } public T Get(object[] args = null) { IPoolable poolable = _objectPool.Count > 0 ? _objectPool.Pop() : Create(args); poolable.Reset(args); return (T)poolable; } private T Create(object[] args = null) { T instance; switch (_providerType) { case ProviderType.FromPoolableInstanceProvider: instance = _poolableInstanceProvider.Create(args); break; case ProviderType.FromMethod: instance = _instanceProviderCallback.Invoke(args); break; default: throw new ArgumentOutOfRangeException(); } instance.OnReturnToPoolRequested += () => Put(instance); instance.OnDestroyed += () => RemoveFromPool(instance); return instance; } private void RemoveFromPool(IPoolable poolable) { if (poolable == null) { return; } if (_objectPool.Count == 0) { return; } Stack tempStack = new Stack(_objectPool.Count); while (_objectPool.Count > 0) { IPoolable popedPoolable = _objectPool.Pop(); if (!ReferenceEquals(popedPoolable, poolable)) { tempStack.Push(popedPoolable); } } while (tempStack.Count > 0) { _objectPool.Push(tempStack.Pop()); } } private void DestroyPool() { if (_objectPool == null) { return; } int poolSize = _objectPool.Count; for (int count = 0; count < poolSize; count++) { _objectPool.Pop()?.Erase(); } } private void ReleaseUnmanagedResources() { DestroyPool(); } public void Dispose() { ReleaseUnmanagedResources(); GC.SuppressFinalize(this); } ~StackPool() { ReleaseUnmanagedResources(); } } }