using System; using System.Collections.Generic; using MemoryPools.Collections.Specialized; namespace MemoryPools.Collections.Linq { internal sealed class GroupedResultEnumerable<TSource, TKey, TResult> : IPoolingEnumerable<TResult> { private IPoolingEnumerable<TSource> _source; private Func<TSource, TKey> _keySelector; private Func<TKey, IPoolingEnumerable<TSource>, TResult> _resultSelector; private IEqualityComparer<TKey> _comparer; private int _count; public GroupedResultEnumerable<TSource, TKey, TResult> Init( IPoolingEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IPoolingEnumerable<TSource>, TResult> resultSelector, IEqualityComparer<TKey> comparer) { _source = source ?? throw new ArgumentNullException(nameof(source)); _keySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector)); _resultSelector = resultSelector ?? throw new ArgumentNullException(nameof(resultSelector)); _comparer = comparer ?? EqualityComparer<TKey>.Default; _count = 0; return this; } public IPoolingEnumerator<TResult> GetEnumerator() { var tmpDict = Pool<PoolingDictionary<TKey, PoolingGrouping>>.Get().Init(0, _comparer); PoolingGrouping grp; foreach (var item in _source) { var key = _keySelector(item); if (!tmpDict.TryGetValue(key, out grp)) { tmpDict[key] = grp = Pool<PoolingGrouping>.Get().Init(key); } grp.InternalList.Add(item); } _count++; return Pool<GroupedResultEnumerator>.Get().Init(this, tmpDict); } private void Dispose() { if (_count == 0) return; _count--; if (_count == 0) { _comparer = default; _resultSelector = default; _keySelector = default; Pool<GroupedResultEnumerable<TSource, TKey, TResult>>.Return(this); } } IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); internal class GroupedResultEnumerator : IPoolingEnumerator<TResult> { private PoolingDictionary<TKey, PoolingGrouping> _src; private GroupedResultEnumerable<TSource, TKey, TResult> _parent; private IPoolingEnumerator<KeyValuePair<TKey, PoolingGrouping>> _enumerator; public GroupedResultEnumerator Init( GroupedResultEnumerable<TSource, TKey, TResult> parent, PoolingDictionary<TKey, PoolingGrouping> src) { _src = src; _parent = parent; _enumerator = _src.GetEnumerator(); return this; } public void Dispose() { // Cleanup contents foreach (var grouping in _src) { grouping.Value.Dispose(); Pool<PoolingGrouping>.Return(grouping.Value); } // cleanup collection _src?.Dispose(); Pool<PoolingDictionary<TKey, PoolingGrouping>>.Return(_src); _src = default; _enumerator?.Dispose(); _enumerator = default; _parent?.Dispose(); _parent = default; Pool<GroupedResultEnumerator>.Return(this); } public bool MoveNext() => _enumerator.MoveNext(); public void Reset() => _enumerator.Reset(); public TResult Current => _parent._resultSelector(_enumerator.Current.Key, _enumerator.Current.Value.InternalList); object IPoolingEnumerator.Current => Current; } internal class PoolingGrouping : IPoolingGrouping<TKey, TSource>, IDisposable { private PoolingList<TSource> _elements; public PoolingGrouping Init(TKey key) { _elements = Pool<PoolingList<TSource>>.Get().Init(); Key = key; return this; } internal PoolingList<TSource> InternalList => _elements; public IPoolingEnumerator<TSource> GetEnumerator() => _elements.GetEnumerator(); IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); public TKey Key { get; private set; } public void Dispose() { _elements?.Dispose(); Pool<PoolingList<TSource>>.Return(_elements); _elements = null; Key = default; } } } }