From 4c55c887f3b9e7adfdf59fec0c12a37c1229b7f9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 9 Mar 2022 20:58:34 +0300 Subject: [PATCH] New pools and pool collections implementations https://github.com/sidristij/memory-pools --- .../Services/Collections/ConcurrentHashSet.cs | 100 +++ .../Services/DOM/DSL/Services/TContainer.cs | 8 +- .../DOM/DSL/Services/TContainerFactory.cs | 19 +- .../Collections/IPoolingEnumerable.cs | 27 + .../Collections/IPoolingEnumerable_T.cs | 11 + .../Collections/IPoolingEnumerator.cs | 22 + .../Collections/IPoolingEnumerator_T.cs | 11 + .../Collections/IPoolingGrouping.cs | 9 + .../MemoryPools/Collections/Linq/Aggregate.cs | 82 ++ .../MemoryPools/Collections/Linq/AnyAll.cs | 76 ++ .../Collections/Linq/Append.Enumerable.cs | 91 +++ .../Collections/Linq/AppendPrepend.cs | 11 + .../Collections/Linq/AsPoolingCollections.cs | 98 +++ .../MemoryPools/Collections/Linq/Average.cs | 759 ++++++++++++++++++ .../Collections/Linq/Cast.Enumerable.cs | 71 ++ .../MemoryPools/Collections/Linq/Cast.cs | 14 + .../Collections/Linq/Concat.Enumerable.cs | 93 +++ .../MemoryPools/Collections/Linq/Concat.cs | 11 + .../Collections/Linq/Distinct.Enumerable.cs | 96 +++ .../MemoryPools/Collections/Linq/Distinct.cs | 39 + .../Collections/Linq/Except.Enumerable.cs | 83 ++ .../MemoryPools/Collections/Linq/Except.cs | 24 + .../Collections/Linq/FirstFirstOrDefault.cs | 110 +++ .../Collections/Linq/GenericEnumerable.cs | 52 ++ .../Linq/GenericPoolingEnumerator.cs | 57 ++ .../Collections/Linq/GroupBy.Enumerable.cs | 142 ++++ .../Linq/GroupBy.ResultEnumerable.cs | 142 ++++ .../MemoryPools/Collections/Linq/GroupBy.cs | 32 + .../Collections/Linq/Intersect.Enumerable.cs | 98 +++ .../MemoryPools/Collections/Linq/Intersect.cs | 24 + .../MemoryPools/Collections/Linq/Join.cs | 112 +++ .../Collections/Linq/LastLastOrDefault.cs | 99 +++ .../MemoryPools/Collections/Linq/MinMax.cs | 410 ++++++++++ .../Collections/Linq/OfType.Enumerable.cs | 77 ++ .../MemoryPools/Collections/Linq/OfType.cs | 11 + .../Collections/Linq/Prepend.Enumerable.cs | 88 ++ .../Collections/Linq/Reverse.Enumerable.cs | 76 ++ .../MemoryPools/Collections/Linq/Reverse.cs | 20 + .../Collections/Linq/Select.Enumerable.cs | 75 ++ .../Linq/Select.WithContextEnumerable.cs | 85 ++ .../MemoryPools/Collections/Linq/Select.cs | 17 + .../Linq/SelectMany.ExprEnumerable.cs | 117 +++ .../SelectMany.ExprWithContextEnumerable.cs | 126 +++ .../Collections/Linq/SelectMany.cs | 22 + .../Collections/Linq/SimpleExpressions.cs | 167 ++++ .../Collections/Linq/SingleSingleOrDefault.cs | 139 ++++ .../Linq/SkipTake.ExprPoolingEnumerable.cs | 99 +++ .../MemoryPools/Collections/Linq/SkipTake.cs | 15 + .../MemoryPools/Collections/Linq/Sum.cs | 126 +++ .../Collections/Linq/Union.Enumerable.cs | 70 ++ .../MemoryPools/Collections/Linq/Union.cs | 26 + .../Collections/Linq/Where.Enumerable.cs | 88 ++ .../Linq/Where.WithContextEnumerable.cs | 94 +++ .../MemoryPools/Collections/Linq/Where.cs | 13 + .../Collections/Linq/Zip.Enumerable.cs | 83 ++ .../MemoryPools/Collections/Linq/Zip.cs | 8 + .../AsSingleQueryList.EnumerableRef.cs | 70 ++ .../AsSingleQueryList.EnumerableVal.cs | 60 ++ .../Specialized/Helpers/HashHelpers.cs | 91 +++ .../Collections/Specialized/IPoolingNode.cs | 15 + .../Specialized/IdealHashDictionary.cs | 60 ++ .../Specialized/IdealHashObjectBase.cs | 9 + .../Collections/Specialized/LocalList.cs | 280 +++++++ .../Collections/Specialized/LongLocalList.cs | 139 ++++ .../PoolingDictionary.KeysCollection.cs | 85 ++ .../PoolingDictionary.ValuesCollection.cs | 94 +++ .../Specialized/PoolingDictionary.cs | 365 +++++++++ .../Specialized/PoolingList.SingleQuery.cs | 46 ++ .../Collections/Specialized/PoolingList.cs | 19 + .../Specialized/PoolingListBase.cs | 314 ++++++++ .../Specialized/PoolingListCanon.cs | 24 + .../Collections/Specialized/PoolingNode.cs | 18 + .../Specialized/PoolingNodeBase.cs | 33 + .../Specialized/PoolingNodeCanon.cs | 36 + .../Collections/Specialized/PoolingQueue.cs | 128 +++ .../Specialized/PoolingQueueRef.cs | 10 + .../Specialized/PoolingQueueVal.cs | 17 + .../Collections/Specialized/PoolingStack.cs | 10 + .../Specialized/PoolingStackBase.cs | 108 +++ .../Specialized/PoolingStackCanon.cs | 13 + .../Collections/Specialized/PoolsDefaults.cs | 9 + .../Services/MemoryPools/DefaultObjectPool.cs | 101 +++ .../MemoryPools/DefaultObjectPoolProvider.cs | 34 + .../MemoryPools/DefaultPooledObjectPolicy.cs | 26 + .../MemoryPools/DisposableObjectPool.cs | 92 +++ .../MemoryPools/IPooledObjectPolicy.cs | 24 + ZeroLevel/Services/MemoryPools/JetPool.cs | 18 + .../Memory/CountdownMemoryOwner.cs | 81 ++ .../MemoryPools/Memory/InternalArraysPool.cs | 36 + .../Services/MemoryPools/Memory/JetStack.cs | 90 +++ .../Services/MemoryPools/Memory/MemoryEx.cs | 35 + .../MemoryPools/Memory/MemoryOwner.cs | 25 + .../BucketsBasedCrossThreadsArrayPool.cs | 43 + .../BucketsBasedCrossThreadsMemoryPool.cs | 51 ++ .../MemoryPools/Memory/Pooling/Utilities.cs | 26 + ZeroLevel/Services/MemoryPools/ObjectPool.cs | 32 + .../MemoryPools/ObjectPoolProvider.cs | 25 + ZeroLevel/Services/MemoryPools/Pool.cs | 52 ++ .../MemoryPools/PooledObjectPolicy.cs | 17 + .../Network/Extensions/ExchangeExtension.cs | 66 +- .../Network/FileTransfer/FileSender.cs | 10 +- .../Services/Network/Model/RequestInfo.cs | 2 + .../Services/Network/Utils/RequestBuffer.cs | 14 +- ZeroLevel/Services/Pools/ObjectPool.cs | 2 + ZeroLevel/Services/Semantic/WordTokenizer.cs | 9 +- 105 files changed, 7586 insertions(+), 53 deletions(-) create mode 100644 ZeroLevel/Services/Collections/ConcurrentHashSet.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerable_T.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerator.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerator_T.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/IPoolingGrouping.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Aggregate.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/AnyAll.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Append.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/AppendPrepend.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/AsPoolingCollections.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Average.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Cast.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Cast.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Concat.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Concat.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Distinct.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Distinct.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Except.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Except.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/FirstFirstOrDefault.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/GenericEnumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/GenericPoolingEnumerator.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.ResultEnumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Intersect.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Intersect.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Join.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/LastLastOrDefault.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/MinMax.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/OfType.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/OfType.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Prepend.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Reverse.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Reverse.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Select.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Select.WithContextEnumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Select.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.ExprEnumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.ExprWithContextEnumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/SimpleExpressions.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/SingleSingleOrDefault.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/SkipTake.ExprPoolingEnumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/SkipTake.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Sum.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Union.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Union.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Where.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Where.WithContextEnumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Where.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Zip.Enumerable.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Linq/Zip.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/AsSingleQueryList.EnumerableRef.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/AsSingleQueryList.EnumerableVal.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/Helpers/HashHelpers.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/IPoolingNode.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/IdealHashDictionary.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/IdealHashObjectBase.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/LocalList.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/LongLocalList.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.KeysCollection.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.ValuesCollection.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingList.SingleQuery.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingList.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingListBase.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingListCanon.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNode.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNodeBase.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNodeCanon.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueue.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueueRef.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueueVal.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStack.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStackBase.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStackCanon.cs create mode 100644 ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolsDefaults.cs create mode 100644 ZeroLevel/Services/MemoryPools/DefaultObjectPool.cs create mode 100644 ZeroLevel/Services/MemoryPools/DefaultObjectPoolProvider.cs create mode 100644 ZeroLevel/Services/MemoryPools/DefaultPooledObjectPolicy.cs create mode 100644 ZeroLevel/Services/MemoryPools/DisposableObjectPool.cs create mode 100644 ZeroLevel/Services/MemoryPools/IPooledObjectPolicy.cs create mode 100644 ZeroLevel/Services/MemoryPools/JetPool.cs create mode 100644 ZeroLevel/Services/MemoryPools/Memory/CountdownMemoryOwner.cs create mode 100644 ZeroLevel/Services/MemoryPools/Memory/InternalArraysPool.cs create mode 100644 ZeroLevel/Services/MemoryPools/Memory/JetStack.cs create mode 100644 ZeroLevel/Services/MemoryPools/Memory/MemoryEx.cs create mode 100644 ZeroLevel/Services/MemoryPools/Memory/MemoryOwner.cs create mode 100644 ZeroLevel/Services/MemoryPools/Memory/Pooling/BucketsBasedCrossThreadsArrayPool.cs create mode 100644 ZeroLevel/Services/MemoryPools/Memory/Pooling/BucketsBasedCrossThreadsMemoryPool.cs create mode 100644 ZeroLevel/Services/MemoryPools/Memory/Pooling/Utilities.cs create mode 100644 ZeroLevel/Services/MemoryPools/ObjectPool.cs create mode 100644 ZeroLevel/Services/MemoryPools/ObjectPoolProvider.cs create mode 100644 ZeroLevel/Services/MemoryPools/Pool.cs create mode 100644 ZeroLevel/Services/MemoryPools/PooledObjectPolicy.cs diff --git a/ZeroLevel/Services/Collections/ConcurrentHashSet.cs b/ZeroLevel/Services/Collections/ConcurrentHashSet.cs new file mode 100644 index 0000000..3e10552 --- /dev/null +++ b/ZeroLevel/Services/Collections/ConcurrentHashSet.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace ZeroLevel.Services.Collections +{ + public class ConcurrentHashSet : IDisposable + { + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + private readonly HashSet _hashSet = new HashSet(); + + #region Implementation of ICollection ...ish + public bool Add(T item) + { + _lock.EnterWriteLock(); + try + { + return _hashSet.Add(item); + } + finally + { + if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); + } + } + + public void Clear() + { + _lock.EnterWriteLock(); + try + { + _hashSet.Clear(); + } + finally + { + if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); + } + } + + public bool Contains(T item) + { + _lock.EnterReadLock(); + try + { + return _hashSet.Contains(item); + } + finally + { + if (_lock.IsReadLockHeld) _lock.ExitReadLock(); + } + } + + public bool Remove(T item) + { + _lock.EnterWriteLock(); + try + { + return _hashSet.Remove(item); + } + finally + { + if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); + } + } + + public int Count + { + get + { + _lock.EnterReadLock(); + try + { + return _hashSet.Count; + } + finally + { + if (_lock.IsReadLockHeld) _lock.ExitReadLock(); + } + } + } + #endregion + + #region Dispose + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + protected virtual void Dispose(bool disposing) + { + if (disposing) + if (_lock != null) + _lock.Dispose(); + } + ~ConcurrentHashSet() + { + Dispose(false); + } + #endregion + } +} diff --git a/ZeroLevel/Services/DOM/DSL/Services/TContainer.cs b/ZeroLevel/Services/DOM/DSL/Services/TContainer.cs index ae6adf5..b3594c3 100644 --- a/ZeroLevel/Services/DOM/DSL/Services/TContainer.cs +++ b/ZeroLevel/Services/DOM/DSL/Services/TContainer.cs @@ -83,8 +83,8 @@ namespace DOM.DSL.Services #endregion Private classes - private readonly TContainerFactory _factory; - private readonly TRender _render; + private TContainerFactory _factory; + private TRender _render; private object _current; public int Index { get; set; } @@ -136,7 +136,9 @@ namespace DOM.DSL.Services } } - public TContainer(TContainerFactory factory, TRender render) + public TContainer() { } + + public void Init(TContainerFactory factory, TRender render) { this._factory = factory; this._render = render; diff --git a/ZeroLevel/Services/DOM/DSL/Services/TContainerFactory.cs b/ZeroLevel/Services/DOM/DSL/Services/TContainerFactory.cs index 1d292e1..193468a 100644 --- a/ZeroLevel/Services/DOM/DSL/Services/TContainerFactory.cs +++ b/ZeroLevel/Services/DOM/DSL/Services/TContainerFactory.cs @@ -1,24 +1,26 @@ -using System.Threading; -using ZeroLevel.Services.Pools; +using MemoryPools; +using System.Threading; namespace DOM.DSL.Services { public class TContainerFactory { - private readonly Pool _pool; - + private readonly DefaultObjectPool _pool; + private readonly TRender _render; private static int _get_count = 0; private static int _release_count = 0; internal TContainerFactory(TRender render) { - _pool = new Pool(64, p => new TContainer(this, render)); + _render = render; + _pool = new DefaultObjectPool(new DefaultPooledObjectPolicy()); } internal TContainer Get(object value) { Interlocked.Increment(ref _get_count); - var c = _pool.Acquire(); + var c = _pool.Get(); + c.Init(this, _render); c.Reset(value); return c; } @@ -26,7 +28,8 @@ namespace DOM.DSL.Services internal TContainer Get(object value, int index) { Interlocked.Increment(ref _get_count); - var c = _pool.Acquire(); + var c = _pool.Get(); + c.Init(this, _render); c.Reset(value); c.Index = index; return c; @@ -37,7 +40,7 @@ namespace DOM.DSL.Services if (container != null) { Interlocked.Increment(ref _release_count); - _pool.Release(container); + _pool.Return(container); } } diff --git a/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerable.cs new file mode 100644 index 0000000..18912b1 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerable.cs @@ -0,0 +1,27 @@ +using MemoryPools.Collections.Linq; +using System.Collections.Generic; + +/*https://github.com/sidristij/memory-pools/blob/master/MemoryPools.Collections*/ + +namespace MemoryPools.Collections +{ + public interface IPoolingEnumerable + { + // Returns an enumerator that iterates through the collection. + /// An enumerator that can be used to iterate through the collection. + IPoolingEnumerator GetEnumerator(); + } + + public static partial class EnumerableEx + { + public static IPoolingEnumerable AsPooling(this IEnumerable source) + { + return Pool>.Get().Init(source); + } + + public static IEnumerable AsEnumerable(this IPoolingEnumerable source) + { + return Pool>.Get().Init(source); + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerable_T.cs b/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerable_T.cs new file mode 100644 index 0000000..01841ac --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerable_T.cs @@ -0,0 +1,11 @@ +/*https://github.com/sidristij/memory-pools/blob/master/MemoryPools.Collections*/ + +namespace MemoryPools.Collections +{ + public interface IPoolingEnumerable : IPoolingEnumerable + { + // Returns an enumerator that iterates through the collection. + /// An enumerator that can be used to iterate through the collection. + new IPoolingEnumerator GetEnumerator(); + } +} diff --git a/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerator.cs b/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerator.cs new file mode 100644 index 0000000..033808d --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerator.cs @@ -0,0 +1,22 @@ +using System; + +/*https://github.com/sidristij/memory-pools/blob/master/MemoryPools.Collections*/ + +namespace MemoryPools.Collections +{ + public interface IPoolingEnumerator : IDisposable + { + /// Advances the enumerator to the next element of the collection. + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// The collection was modified after the enumerator was created. + bool MoveNext(); + + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// The collection was modified after the enumerator was created. + void Reset(); + + // Gets the element in the collection at the current position of the enumerator. + /// The element in the collection at the current position of the enumerator. + object Current { get; } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerator_T.cs b/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerator_T.cs new file mode 100644 index 0000000..818209f --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/IPoolingEnumerator_T.cs @@ -0,0 +1,11 @@ +/*https://github.com/sidristij/memory-pools/blob/master/MemoryPools.Collections*/ + +namespace MemoryPools.Collections +{ + public interface IPoolingEnumerator : IPoolingEnumerator + { + // Gets the element in the collection at the current position of the enumerator. + /// The element in the collection at the current position of the enumerator. + new T Current { get; } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Collections/IPoolingGrouping.cs b/ZeroLevel/Services/MemoryPools/Collections/IPoolingGrouping.cs new file mode 100644 index 0000000..23277e2 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/IPoolingGrouping.cs @@ -0,0 +1,9 @@ +/*https://github.com/sidristij/memory-pools/blob/master/MemoryPools.Collections*/ + +namespace MemoryPools.Collections +{ + public interface IPoolingGrouping : IPoolingEnumerable + { + TKey Key { get; } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Aggregate.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Aggregate.cs new file mode 100644 index 0000000..e3d680c --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Aggregate.cs @@ -0,0 +1,82 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static TSource Aggregate(this IPoolingEnumerable source, Func func) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (func == null) + { + throw new ArgumentNullException(nameof(func)); + } + + using (var enumerator = source.GetEnumerator()) + { + if (!enumerator.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + var result = enumerator.Current; + while (enumerator.MoveNext()) + { + result = func(result, enumerator.Current); + } + return result; + } + } + + public static TAccumulate Aggregate(this IPoolingEnumerable source, TAccumulate seed, Func func) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (func == null) + { + throw new ArgumentNullException(nameof(func)); + } + + var result = seed; + foreach (var element in source) + { + result = func(result, element); + } + + return result; + } + + public static TResult Aggregate(this IPoolingEnumerable source, TAccumulate seed, Func func, Func resultSelector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (func == null) + { + throw new ArgumentNullException(nameof(func)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + + var result = seed; + foreach (var element in source) + { + result = func(result, element); + } + + return resultSelector(result); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/AnyAll.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/AnyAll.cs new file mode 100644 index 0000000..bdf3bba --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/AnyAll.cs @@ -0,0 +1,76 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static bool Any(this IPoolingEnumerable source) + { + var enumerator = source.GetEnumerator(); + var hasItems = enumerator.MoveNext(); + enumerator.Dispose(); + return hasItems; + } + + public static bool Any(this IPoolingEnumerable source, Func condition) + { + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (condition(enumerator.Current)) + { + enumerator.Dispose(); + return true; + } + } + enumerator.Dispose(); + return false; + } + + public static bool Any(this IPoolingEnumerable source, TContext context, Func condition) + { + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (condition(context, enumerator.Current)) + { + enumerator.Dispose(); + return true; + } + } + enumerator.Dispose(); + return false; + } + + public static bool All(this IPoolingEnumerable source, Func condition) + { + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (!condition(enumerator.Current)) + { + enumerator.Dispose(); + return false; + } + } + enumerator.Dispose(); + return true; + + } + + public static bool All(this IPoolingEnumerable source, TContext context, Func condition) + { + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (!condition(context, enumerator.Current)) + { + enumerator.Dispose(); + return false; + } + } + enumerator.Dispose(); + return true; + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Append.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Append.Enumerable.cs new file mode 100644 index 0000000..dc74331 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Append.Enumerable.cs @@ -0,0 +1,91 @@ +namespace MemoryPools.Collections.Linq +{ + internal class AppendExprEnumerable : IPoolingEnumerable + { + private int _count; + + private IPoolingEnumerable _src; + private T _element; + + public AppendExprEnumerable Init(IPoolingEnumerable src, T element) + { + _src = src; + _count = 0; + _element = element; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(_src.GetEnumerator(), this, _element); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _element = default; + Pool>.Return(this); + } + } + + internal class AppendExprEnumerator : IPoolingEnumerator + { + private IPoolingEnumerator _src; + private AppendExprEnumerable _parent; + private T _element; + private int _overcount; + + public AppendExprEnumerator Init(IPoolingEnumerator src, AppendExprEnumerable parent, T element) + { + _src = src; + _parent = parent; + _element = element; + _overcount = 0; + return this; + } + + public bool MoveNext() + { + if (!_src.MoveNext()) + { + if (_overcount == 0) + { + _overcount++; + return true; + } + + _overcount++; + return false; + } + + return true; + } + + public void Reset() + { + _overcount = 0; + _src.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public T Current => _overcount == 1 ? _element : (T) _src.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = null; + _src?.Dispose(); + _src = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/AppendPrepend.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/AppendPrepend.cs new file mode 100644 index 0000000..153a69b --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/AppendPrepend.cs @@ -0,0 +1,11 @@ +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable Prepend(this IPoolingEnumerable source, T element) => + Pool>.Get().Init(source, element); + + public static IPoolingEnumerable Append(this IPoolingEnumerable source, T element) => + Pool>.Get().Init(source, element); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/AsPoolingCollections.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/AsPoolingCollections.cs new file mode 100644 index 0000000..80ef550 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/AsPoolingCollections.cs @@ -0,0 +1,98 @@ +using MemoryPools.Collections.Specialized; +using System; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static PoolingList AsPoolingList(this IEnumerable source) + { + var collection = Pool>.Get().Init(); + collection.AddRange(source); + return collection; + } + + public static PoolingList AsPoolingList(this IPoolingEnumerable source) + { + var collection = Pool>.Get().Init(); + collection.AddRange(source); + return collection; + } + + public static PoolingDictionary AsPoolingDictionary(this IEnumerable> source) + { + return AsPoolingDictionary(source.AsPooling()); + } + + public static PoolingDictionary AsPoolingDictionary(this IPoolingEnumerable> source) + { + var collection = Pool>.Get().Init(); + collection.AddRange(source); + return collection; + } + + public static PoolingDictionary AsPoolingDictionary(this IEnumerable source, Func keySelector, Func valueSelector) + { + return AsPoolingDictionary(source.AsPooling(), keySelector, valueSelector); + } + + public static PoolingDictionary AsPoolingDictionary(this IPoolingEnumerable source, Func keySelector, Func valueSelector) + { + var collection = Pool>.Get().Init(); + collection.AddRange( + source + .Select((keySelector, valueSelector), (ctx, x) => new KeyValuePair(ctx.keySelector(x), ctx.valueSelector(x))) + ); + return collection; + } + + public static void AddRange(this PoolingList target, IEnumerable src) + { + foreach (var item in src) + { + target.Add(item); + } + } + + public static void AddRange(this PoolingList target, IPoolingEnumerable src) + { + foreach (var item in src) + { + target.Add(item); + } + } + + public static void AddRange(this PoolingDictionary target, IEnumerable> src) + { + foreach (var item in src) + { + target.Add(item.Key, item.Value); + } + } + + public static void AddRange(this PoolingDictionary target, IPoolingEnumerable> src) + { + foreach (var item in src) + { + target.Add(item.Key, item.Value); + } + } + + public static void AddRangeSafe(this PoolingDictionary target, IEnumerable> src) + { + foreach (var item in src) + { + target[item.Key] = item.Value; + } + } + + public static void AddRangeSafe(this PoolingDictionary target, IPoolingEnumerable> src) + { + foreach (var item in src) + { + target[item.Key] = item.Value; + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Average.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Average.cs new file mode 100644 index 0000000..6228c5d --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Average.cs @@ -0,0 +1,759 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (var enumerator = source.GetEnumerator()) + { + if (!enumerator.MoveNext()) + { + // throw new InvalidOperationException("Sequence contains no elements"); + } + + long sum = enumerator.Current; + long count = 1; + checked + { + while (enumerator.MoveNext()) + { + sum += enumerator.Current; + ++count; + } + } + + return (double)sum / count; + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double? Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (var e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + int? v = e.Current; + if (v.HasValue) + { + long sum = v.GetValueOrDefault(); + long count = 1; + checked + { + while (e.MoveNext()) + { + v = e.Current; + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + } + + return (double)sum / count; + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + long sum = e.Current; + long count = 1; + checked + { + while (e.MoveNext()) + { + sum += e.Current; + ++count; + } + } + + return (double)sum / count; + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double? Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + long? v = e.Current; + if (v.HasValue) + { + long sum = v.GetValueOrDefault(); + long count = 1; + checked + { + while (e.MoveNext()) + { + v = e.Current; + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + } + + return (double)sum / count; + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static float Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + double sum = e.Current; + long count = 1; + while (e.MoveNext()) + { + sum += e.Current; + ++count; + } + + return (float)(sum / count); + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static float? Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + float? v = e.Current; + if (v.HasValue) + { + double sum = v.GetValueOrDefault(); + long count = 1; + checked + { + while (e.MoveNext()) + { + v = e.Current; + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + } + + return (float)(sum / count); + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + double sum = e.Current; + long count = 1; + while (e.MoveNext()) + { + // There is an opportunity to short-circuit here, in that if e.Current is + // ever NaN then the result will always be NaN. Assuming that this case is + // rare enough that not checking is the better approach generally. + sum += e.Current; + ++count; + } + + return sum / count; + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double? Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + double? v = e.Current; + if (v.HasValue) + { + double sum = v.GetValueOrDefault(); + long count = 1; + checked + { + while (e.MoveNext()) + { + v = e.Current; + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + } + + return sum / count; + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static decimal Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + decimal sum = e.Current; + long count = 1; + while (e.MoveNext()) + { + sum += e.Current; + ++count; + } + + return sum / count; + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static decimal? Average(this IPoolingEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + decimal? v = e.Current; + if (v.HasValue) + { + decimal sum = v.GetValueOrDefault(); + long count = 1; + while (e.MoveNext()) + { + v = e.Current; + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + + return sum / count; + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + long sum = selector(e.Current); + long count = 1; + checked + { + while (e.MoveNext()) + { + sum += selector(e.Current); + ++count; + } + } + + return (double)sum / count; + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double? Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (var e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + int? v = selector(e.Current); + if (v.HasValue) + { + long sum = v.GetValueOrDefault(); + long count = 1; + checked + { + while (e.MoveNext()) + { + v = selector(e.Current); + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + } + + return (double)sum / count; + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + long sum = selector(e.Current); + long count = 1; + checked + { + while (e.MoveNext()) + { + sum += selector(e.Current); + ++count; + } + } + + return (double)sum / count; + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double? Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (var e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + long? v = selector(e.Current); + if (v.HasValue) + { + long sum = v.GetValueOrDefault(); + long count = 1; + checked + { + while (e.MoveNext()) + { + v = selector(e.Current); + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + } + + return (double)sum / count; + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static float Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + double sum = selector(e.Current); + long count = 1; + while (e.MoveNext()) + { + sum += selector(e.Current); + ++count; + } + + return (float)(sum / count); + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static float? Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + float? v = selector(e.Current); + if (v.HasValue) + { + double sum = v.GetValueOrDefault(); + long count = 1; + checked + { + while (e.MoveNext()) + { + v = selector(e.Current); + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + } + + return (float)(sum / count); + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + double sum = selector(e.Current); + long count = 1; + while (e.MoveNext()) + { + // There is an opportunity to short-circuit here, in that if e.Current is + // ever NaN then the result will always be NaN. Assuming that this case is + // rare enough that not checking is the better approach generally. + sum += selector(e.Current); + ++count; + } + + return sum / count; + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static double? Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + double? v = selector(e.Current); + if (v.HasValue) + { + double sum = v.GetValueOrDefault(); + long count = 1; + checked + { + while (e.MoveNext()) + { + v = selector(e.Current); + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + } + + return sum / count; + } + } + } + + return null; + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static decimal Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + + decimal sum = selector(e.Current); + long count = 1; + while (e.MoveNext()) + { + sum += selector(e.Current); + ++count; + } + + return sum / count; + } + } + + /// + /// Calculates avg of all given numbers. Complexity = O(N) + /// + public static decimal? Average(this IPoolingEnumerable source, Func selector) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } + + using (IPoolingEnumerator e = source.GetEnumerator()) + { + while (e.MoveNext()) + { + decimal? v = selector(e.Current); + if (v.HasValue) + { + decimal sum = v.GetValueOrDefault(); + long count = 1; + while (e.MoveNext()) + { + v = selector(e.Current); + if (v.HasValue) + { + sum += v.GetValueOrDefault(); + ++count; + } + } + + return sum / count; + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Cast.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Cast.Enumerable.cs new file mode 100644 index 0000000..5c60674 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Cast.Enumerable.cs @@ -0,0 +1,71 @@ +namespace MemoryPools.Collections.Linq +{ + internal class CastExprEnumerable : IPoolingEnumerable + { + private int _count; + + private IPoolingEnumerable _src; + + public CastExprEnumerable Init(IPoolingEnumerable src) + { + _src = src; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(_src.GetEnumerator(), this); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + Pool>.Return(this); + } + } + + internal class CastExprEnumerator : IPoolingEnumerator + { + private IPoolingEnumerator _src; + private CastExprEnumerable _parent; + + public CastExprEnumerator Init(IPoolingEnumerator src, CastExprEnumerable parent) + { + _src = src; + _parent = parent; + return this; + } + + public bool MoveNext() + { + return _src.MoveNext(); + } + + public void Reset() + { + _src.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public T Current => (T)_src.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = null; + _src?.Dispose(); + _src = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Cast.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Cast.cs new file mode 100644 index 0000000..94f9dd3 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Cast.cs @@ -0,0 +1,14 @@ +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + /// + /// Casts all elements to the given type. Complexity = O(N) + /// + public static IPoolingEnumerable Cast(this IPoolingEnumerable source) + { + if (source is IPoolingEnumerable res) return res; + return Pool>.Get().Init(source); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Concat.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Concat.Enumerable.cs new file mode 100644 index 0000000..d1c5358 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Concat.Enumerable.cs @@ -0,0 +1,93 @@ +namespace MemoryPools.Collections.Linq +{ + internal class ConcatExprEnumerable : IPoolingEnumerable + { + private IPoolingEnumerable _src, _second; + private int _count; + + public ConcatExprEnumerable Init(IPoolingEnumerable src, IPoolingEnumerable second) + { + _src = src; + _count = 0; + _second = second; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator(), _second.GetEnumerator()); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _second = default; + Pool>.Return(this); + } + } + + internal class ConcatExprEnumerator : IPoolingEnumerator + { + private ConcatExprEnumerable _parent; + private IPoolingEnumerator _src, _second; + private bool _first; + + public ConcatExprEnumerator Init( + ConcatExprEnumerable parent, IPoolingEnumerator src, IPoolingEnumerator second) + { + _parent = parent; + _src = src; + _second = second; + _first = true; + return this; + } + + public bool MoveNext() + { + if (_first) + { + if (_src.MoveNext()) + { + return true; + } + + _first = false; + } + + return _second.MoveNext(); + } + + public void Reset() + { + _first = true; + _src.Reset(); + _second.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public T Current => _first ? _src.Current : _second.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = default; + _src?.Dispose(); + _src = default; + _second?.Dispose(); + _second = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Concat.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Concat.cs new file mode 100644 index 0000000..e12de5a --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Concat.cs @@ -0,0 +1,11 @@ +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + /// + /// Returns all elements from and all -- from . Complexity = O(N+M) + /// + public static IPoolingEnumerable Concat(this IPoolingEnumerable source, IPoolingEnumerable second) => + Pool>.Get().Init(source, second); + } +} diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Distinct.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Distinct.Enumerable.cs new file mode 100644 index 0000000..4d50c67 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Distinct.Enumerable.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + internal class DistinctExprEnumerable : IPoolingEnumerable + { + private int _count; + private IPoolingEnumerator _parent; + private IEqualityComparer _comparer; + private Func _selector; + + public DistinctExprEnumerable Init(IPoolingEnumerator parent, Func selector, IEqualityComparer comparer = default) + { + _parent = parent; + _selector = selector; + _comparer = comparer; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _parent, _selector, _comparer); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _parent?.Dispose(); + _parent = default; + _selector = default; + Pool>.Return(this); + } + } + internal class DistinctExprEnumerator : IPoolingEnumerator + { + private IPoolingEnumerator _src; + private Func _selector; + private PoolingDictionary _hashset; + private DistinctExprEnumerable _parent; + + public DistinctExprEnumerator Init( + DistinctExprEnumerable parent, + IPoolingEnumerator src, + Func selector, + IEqualityComparer comparer) + { + _src = src; + _parent = parent; + _selector = selector; + _hashset = Pool>.Get().Init(0, comparer ?? EqualityComparer.Default); + return this; + } + + public bool MoveNext() + { + while (_src.MoveNext()) + { + var key = _selector(_src.Current); + if(_hashset.ContainsKey(key)) continue; + + _hashset[key] = 1; + return true; + } + + return false; + } + + public void Reset() => _src.Reset(); + + object IPoolingEnumerator.Current => Current; + + public T Current => _src.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = default; + + _hashset?.Dispose(); + _hashset = default; + + _src = default; + _selector = default; + Pool.Return(this); + } + } + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Distinct.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Distinct.cs new file mode 100644 index 0000000..afa8969 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Distinct.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + /// + /// Returns distinct elements from a sequence by using the default equality comparer to compare values. Complexity - O(N) + /// + public static IPoolingEnumerable Distinct(this IPoolingEnumerable source) => + Pool>.Get().Init(source.GetEnumerator(), x => x); + + /// + /// Returns distinct elements from a sequence by using the default equality comparer to compare values and selector to select key to compare by. Complexity - O(N) + /// + public static IPoolingEnumerable DistinctBy( + this IPoolingEnumerable source, + Func selector) => + Pool>.Get().Init(source.GetEnumerator(), selector); + + /// + /// Returns distinct elements from a sequence by using a specified to compare values. Complexity - O(N) + /// + public static IPoolingEnumerable Distinct( + this IPoolingEnumerable source, + IEqualityComparer comparer) => + Pool>.Get().Init(source.GetEnumerator(), x => x,comparer); + + /// + /// Returns distinct elements from a sequence by using a specified to compare values and selector to select key to compare by. Complexity - O(N) + /// + public static IPoolingEnumerable DistinctBy( + this IPoolingEnumerable source, + Func selector, + IEqualityComparer comparer) => + Pool>.Get().Init(source.GetEnumerator(), selector, comparer); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Except.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Except.Enumerable.cs new file mode 100644 index 0000000..79f841b --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Except.Enumerable.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + internal class ExceptExprEnumerable : IPoolingEnumerable + { + private int _count; + private IPoolingEnumerable _src; + private IEqualityComparer _comparer; + private PoolingDictionary _except; + + public ExceptExprEnumerable Init(IPoolingEnumerable src, PoolingDictionary except, IEqualityComparer comparer = default) + { + _src = src; + _except = except; + _comparer = comparer; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator()); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _except?.Dispose(); + Pool>.Return(_except); + _except = default; + Pool>.Return(this); + } + } + internal class ExceptExprEnumerator : IPoolingEnumerator + { + private ExceptExprEnumerable _parent; + private IPoolingEnumerator _src; + + public ExceptExprEnumerator Init(ExceptExprEnumerable parent, IPoolingEnumerator src) + { + _src = src; + _parent = parent; + return this; + } + + public bool MoveNext() + { + while (_src.MoveNext()) + { + if(_parent._except.ContainsKey(_src.Current)) continue; + return true; + } + + return false; + } + + public void Reset() => _src.Reset(); + + object IPoolingEnumerator.Current => Current; + + public T Current => _src.Current; + + public void Dispose() + { + _src?.Dispose(); + _src = null; + + _parent?.Dispose(); + _parent = default; + + Pool.Return(this); + } + } + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Except.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Except.cs new file mode 100644 index 0000000..db5710d --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Except.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable Except(this IPoolingEnumerable source, IPoolingEnumerable except) + { + var exceptDict = Pool>.Get().Init(0); + foreach (var item in except) exceptDict[item] = 1; + + return Pool>.Get().Init(source, exceptDict); + } + + public static IPoolingEnumerable Except(this IPoolingEnumerable source, IPoolingEnumerable except, IEqualityComparer comparer) + { + var exceptDict = Pool>.Get().Init(0); + foreach (var item in except) exceptDict[item] = 1; + + return Pool>.Get().Init(source, exceptDict, comparer); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/FirstFirstOrDefault.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/FirstFirstOrDefault.cs new file mode 100644 index 0000000..fe888ae --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/FirstFirstOrDefault.cs @@ -0,0 +1,110 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + /// + /// Gets first element from sequence. Complexity = O(1) + /// + public static T First(this IPoolingEnumerable source) + { + var enumerator = source.GetEnumerator(); + var hasItems = enumerator.MoveNext(); + if (!hasItems) + { + throw new InvalidOperationException("Sequence is empty"); + } + var element = enumerator.Current; + enumerator.Dispose(); + return element; + } + + /// + /// Gets first element from sequence by given . Complexity = O(1) - O(N) + /// + public static T First(this IPoolingEnumerable source, Func condition) + { + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (condition(enumerator.Current)) + { + var item = enumerator.Current; + enumerator.Dispose(); + return item; + } + } + enumerator.Dispose(); + throw new InvalidOperationException("Sequence is empty"); + } + + /// + /// Gets first element from sequence by given . Complexity = O(1) - O(N) + /// + public static T First(this IPoolingEnumerable source, TContext context, Func condition) + { + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (!condition(context, enumerator.Current)) continue; + + var item = enumerator.Current; + enumerator.Dispose(); + return item; + } + enumerator.Dispose(); + throw new InvalidOperationException("Sequence is empty"); + } + + /// + /// Gets first element from sequence. Complexity = O(1) + /// + public static T FirstOrDefault(this IPoolingEnumerable source) + { + var enumerator = source.GetEnumerator(); + var hasItem = enumerator.MoveNext(); + + var item= hasItem ? enumerator.Current : default; + enumerator.Dispose(); + + return item; + } + + /// + /// Gets first element from sequence by given . Complexity = O(1) - O(N) + /// + public static T FirstOrDefault(this IPoolingEnumerable source, Func condition) + { + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (!condition(enumerator.Current)) continue; + + var elem = enumerator.Current; + enumerator.Dispose(); + return elem; + } + enumerator.Dispose(); + return default; + } + + /// + /// Gets first element from sequence by given . Complexity = O(1) - O(N) + /// + public static T FirstOrDefault(this IPoolingEnumerable source, TContext context, Func condition) + { + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (!condition(context, enumerator.Current)) continue; + + var elem = enumerator.Current; + enumerator.Dispose(); + return elem; + } + enumerator.Dispose(); + return default; + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/GenericEnumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/GenericEnumerable.cs new file mode 100644 index 0000000..e455503 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/GenericEnumerable.cs @@ -0,0 +1,52 @@ +using System.Collections; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Linq +{ + internal class GenericPoolingEnumerable : IPoolingEnumerable + { + private IEnumerable _enumerable; + + public GenericPoolingEnumerable Init(IEnumerable enumerable) + { + _enumerable = enumerable; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + var enumerator = _enumerable.GetEnumerator(); + _enumerable = default; + Pool>.Return(this); + return Pool>.Get().Init(enumerator); + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + internal class GenericEnumerable : IEnumerable + { + private IPoolingEnumerable _enumerable; + + public GenericEnumerable Init(IPoolingEnumerable enumerable) + { + _enumerable = enumerable; + return this; + } + + public IEnumerator GetEnumerator() + { + var enumerator = _enumerable.GetEnumerator(); + _enumerable = default; + Pool>.Return(this); + return Pool>.Get().Init(enumerator); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/GenericPoolingEnumerator.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/GenericPoolingEnumerator.cs new file mode 100644 index 0000000..20f0d4b --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/GenericPoolingEnumerator.cs @@ -0,0 +1,57 @@ +using System.Collections; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Linq +{ + internal sealed class GenericPoolingEnumerator : IPoolingEnumerator + { + private IEnumerator _source; + + public GenericPoolingEnumerator Init(IEnumerator source) + { + _source = source; + return this; + } + + public bool MoveNext() => _source.MoveNext(); + + public void Reset() => _source.Reset(); + + object IPoolingEnumerator.Current => Current; + + public T Current => _source.Current; + + public void Dispose() + { + _source.Dispose(); + _source = default; + Pool>.Return(this); + } + } + + internal sealed class GenericEnumerator : IEnumerator + { + private IPoolingEnumerator _source; + + public GenericEnumerator Init(IPoolingEnumerator source) + { + _source = source; + return this; + } + + public bool MoveNext() => _source.MoveNext(); + + public void Reset() => _source.Reset(); + + object IEnumerator.Current => Current; + + public T Current => _source.Current; + + public void Dispose() + { + _source.Dispose(); + _source = default; + Pool>.Return(this); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.Enumerable.cs new file mode 100644 index 0000000..776a59c --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.Enumerable.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + internal sealed class GroupedEnumerable : IPoolingEnumerable> + { + private IPoolingEnumerable _source; + private Func _keySelector; + private Func _elementSelector; + private IEqualityComparer _comparer; + private int _count; + + public GroupedEnumerable Init( + IPoolingEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + _source = source ?? throw new ArgumentNullException(nameof(source)); + _keySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector)); + _elementSelector = elementSelector ?? throw new ArgumentNullException(nameof(elementSelector)); + _comparer = comparer ?? EqualityComparer.Default; + _count = 0; + return this; + } + + public IPoolingEnumerator> GetEnumerator() + { + var tmpDict = Pool>.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.Get().Init(key); + } + + grp.InternalList.Add(_elementSelector(item)); + } + + _count++; + return Pool.Get().Init(this, tmpDict); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + + if (_count == 0) + { + _comparer = default; + _elementSelector = default; + _keySelector = default; + Pool>.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + + internal class PoolingGroupingEnumerator : IPoolingEnumerator> + { + private PoolingDictionary _src; + private GroupedEnumerable _parent; + private IPoolingEnumerator> _enumerator; + + public PoolingGroupingEnumerator Init( + GroupedEnumerable parent, + PoolingDictionary src) + { + _src = src; + _parent = parent; + _enumerator = _src.GetEnumerator(); + return this; + } + + public void Dispose() + { + // Cleanup contents + foreach (var grouping in _src) + { + grouping.Value.Dispose(); + Pool.Return(grouping.Value); + } + + // cleanup collection + _src?.Dispose(); + Pool>.Return(_src); + _src = default; + + _enumerator?.Dispose(); + _enumerator = default; + + _parent?.Dispose(); + _parent = default; + + Pool.Return(this); + } + + public bool MoveNext() => _enumerator.MoveNext(); + + public void Reset() => _enumerator.Reset(); + + public IPoolingGrouping Current => _enumerator.Current.Value; + + object IPoolingEnumerator.Current => Current; + } + + internal class PoolingGrouping : IPoolingGrouping, IDisposable + { + private PoolingList _elements; + + public PoolingGrouping Init(TKey key) + { + _elements = Pool>.Get().Init(); + Key = key; + return this; + } + + internal PoolingList InternalList => _elements; + + public IPoolingEnumerator GetEnumerator() => _elements.GetEnumerator(); + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + + public TKey Key { get; private set; } + + public void Dispose() + { + _elements?.Dispose(); + Pool>.Return(_elements); + _elements = null; + + Key = default; + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.ResultEnumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.ResultEnumerable.cs new file mode 100644 index 0000000..300f9a6 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.ResultEnumerable.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + internal sealed class GroupedResultEnumerable : IPoolingEnumerable + { + private IPoolingEnumerable _source; + private Func _keySelector; + private Func, TResult> _resultSelector; + private IEqualityComparer _comparer; + private int _count; + + public GroupedResultEnumerable Init( + IPoolingEnumerable source, + Func keySelector, + Func, TResult> resultSelector, + IEqualityComparer 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.Default; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + var tmpDict = Pool>.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.Get().Init(key); + } + + grp.InternalList.Add(item); + } + + _count++; + return Pool.Get().Init(this, tmpDict); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + + if (_count == 0) + { + _comparer = default; + _resultSelector = default; + _keySelector = default; + Pool>.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + + internal class GroupedResultEnumerator : IPoolingEnumerator + { + private PoolingDictionary _src; + private GroupedResultEnumerable _parent; + private IPoolingEnumerator> _enumerator; + + public GroupedResultEnumerator Init( + GroupedResultEnumerable parent, + PoolingDictionary src) + { + _src = src; + _parent = parent; + _enumerator = _src.GetEnumerator(); + return this; + } + + public void Dispose() + { + // Cleanup contents + foreach (var grouping in _src) + { + grouping.Value.Dispose(); + Pool.Return(grouping.Value); + } + + // cleanup collection + _src?.Dispose(); + Pool>.Return(_src); + _src = default; + + _enumerator?.Dispose(); + _enumerator = default; + + _parent?.Dispose(); + _parent = default; + + Pool.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, IDisposable + { + private PoolingList _elements; + + public PoolingGrouping Init(TKey key) + { + _elements = Pool>.Get().Init(); + Key = key; + return this; + } + + internal PoolingList InternalList => _elements; + + public IPoolingEnumerator GetEnumerator() => _elements.GetEnumerator(); + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + + public TKey Key { get; private set; } + + public void Dispose() + { + _elements?.Dispose(); + Pool>.Return(_elements); + _elements = null; + + Key = default; + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.cs new file mode 100644 index 0000000..e340855 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/GroupBy.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable> GroupBy(this IPoolingEnumerable source, Func keySelector) => + Pool>.Get().Init(source, keySelector, x => x, null); + + public static IPoolingEnumerable> GroupBy(this IPoolingEnumerable source, Func keySelector, IEqualityComparer comparer) => + Pool>.Get().Init(source, keySelector, x => x, comparer); + + public static IPoolingEnumerable> GroupBy(this IPoolingEnumerable source, Func keySelector, Func elementSelector) => + Pool>.Get().Init(source, keySelector, elementSelector, null); + + public static IPoolingEnumerable> GroupBy(this IPoolingEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) => + Pool>.Get().Init(source, keySelector, elementSelector, comparer); + + public static IPoolingEnumerable GroupBy(this IPoolingEnumerable source, Func keySelector, Func, TResult> resultSelector) => + Pool>.Get().Init(source, keySelector, resultSelector, null); + + // public static IPoolingEnumerable GroupBy(this IEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector) => + // new GroupedResultEnumerable(source, keySelector, elementSelector, resultSelector, null); + + public static IPoolingEnumerable GroupBy(this IPoolingEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer comparer) => + Pool>.Get().Init(source, keySelector, resultSelector, comparer); + + // public static IEnumerable GroupBy(this IEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector, IEqualityComparer comparer) => + // new GroupedResultEnumerable(source, keySelector, elementSelector, resultSelector, comparer); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Intersect.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Intersect.Enumerable.cs new file mode 100644 index 0000000..0bdaad9 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Intersect.Enumerable.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + internal class IntersectExprEnumerable : IPoolingEnumerable + { + private int _count; + private IPoolingEnumerable _src; + private IEqualityComparer _comparer; + private PoolingDictionary _second; + + public IntersectExprEnumerable Init( + IPoolingEnumerable src, + PoolingDictionary second, + IEqualityComparer comparer = default) + { + _src = src; + _count = 0; + _second = second; + _comparer = comparer ?? EqualityComparer.Default; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator(), _comparer); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _second?.Dispose(); + Pool>.Return(_second); + + _second = default; + Pool>.Return(this); + } + } + + internal class IntersectExprEnumerator : IPoolingEnumerator + { + private IntersectExprEnumerable _parent; + private IPoolingEnumerator _src; + private PoolingDictionary _alreadyDoneItems; + + public IntersectExprEnumerator Init(IntersectExprEnumerable parent, IPoolingEnumerator src, IEqualityComparer comparer) + { + _src = src; + _parent = parent; + _alreadyDoneItems = Pool>.Get().Init(0, comparer); + return this; + } + + public bool MoveNext() + { + while (_src.MoveNext()) + { + if (_parent._second.ContainsKey(_src.Current) && + !_alreadyDoneItems.ContainsKey(_src.Current)) + { + _alreadyDoneItems[_src.Current] = 1; + return true; + } + } + + return false; + } + + public void Reset() => _src.Reset(); + + object IPoolingEnumerator.Current => Current; + + public T Current => _src.Current; + + public void Dispose() + { + _src?.Dispose(); + _src = null; + + _alreadyDoneItems?.Dispose(); + Pool>.Return(_alreadyDoneItems); + _alreadyDoneItems = default; + + _parent?.Dispose(); + _parent = default; + + Pool.Return(this); + } + } + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Intersect.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Intersect.cs new file mode 100644 index 0000000..60021ab --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Intersect.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable Intersect(this IPoolingEnumerable source, IPoolingEnumerable intersectWith) + { + var second = Pool>.Get().Init(0); + foreach (var item in intersectWith) second[item] = 1; + + return Pool>.Get().Init(source, second); + } + + public static IPoolingEnumerable Intersect(this IPoolingEnumerable source, IPoolingEnumerable intersectWith, IEqualityComparer comparer) + { + var second = Pool>.Get().Init(0); + foreach (var item in intersectWith) second[item] = 1; + + return Pool>.Get().Init(source, second, comparer); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Join.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Join.cs new file mode 100644 index 0000000..3cda8d1 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Join.cs @@ -0,0 +1,112 @@ +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + // public static IPoolingEnumerable Join( + // this IPoolingEnumerable outer, + // IPoolingEnumerable inner, + // Func outerKeySelector, + // Func innerKeySelector, + // Func resultSelector) + // { + // if (outer == null) + // { + // throw new ArgumentNullException(nameof(outer)); + // } + // + // if (inner == null) + // { + // throw new ArgumentNullException(nameof(inner)); + // } + // + // if (outerKeySelector == null) + // { + // throw new ArgumentNullException(nameof(outerKeySelector)); + // } + // + // if (innerKeySelector == null) + // { + // throw new ArgumentNullException(nameof(innerKeySelector)); + // } + // + // if (resultSelector == null) + // { + // throw new ArgumentNullException(nameof(resultSelector)); + // } + // + // return JoinIterator(outer, inner, outerKeySelector, innerKeySelector, resultSelector, null); + // } + // + // public static IPoolingEnumerable Join( + // this IEnumerable outer, + // IEnumerable inner, + // Func outerKeySelector, + // Func innerKeySelector, + // Func resultSelector, + // IEqualityComparer comparer) + // { + // if (outer == null) + // { + // throw new ArgumentNullException(nameof(outer)); + // } + // + // if (inner == null) + // { + // throw new ArgumentNullException(nameof(inner)); + // } + // + // if (outerKeySelector == null) + // { + // throw new ArgumentNullException(nameof(outerKeySelector)); + // } + // + // if (innerKeySelector == null) + // { + // throw new ArgumentNullException(nameof(innerKeySelector)); + // } + // + // if (resultSelector == null) + // { + // throw new ArgumentNullException(nameof(resultSelector)); + // } + // + // return JoinIterator(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer); + // } + + // private static IPoolingEnumerable JoinIterator( + // IPoolingEnumerable outer, + // IEnumerable inner, + // Func outerKeySelector, + // Func innerKeySelector, + // Func resultSelector, + // IEqualityComparer comparer) + // { + // using (var e = outer.GetEnumerator()) + // { + // var dict = InternalPool>.Get(); + // if (e.MoveNext()) + // { + // Lookup lookup = Lookup.CreateForJoin(inner, innerKeySelector, comparer); + // if (lookup.Count != 0) + // { + // do + // { + // TOuter item = e.Current; + // Grouping g = lookup.GetGrouping(outerKeySelector(item), create: false); + // if (g != null) + // { + // int count = g._count; + // TInner[] elements = g._elements; + // for (int i = 0; i != count; ++i) + // { + // yield return resultSelector(item, elements[i]); + // } + // } + // } + // while (e.MoveNext()); + // } + // } + // } + // } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/LastLastOrDefault.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/LastLastOrDefault.cs new file mode 100644 index 0000000..b145d18 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/LastLastOrDefault.cs @@ -0,0 +1,99 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static T Last(this IPoolingEnumerable source) + { + var enumerator = source.GetEnumerator(); + T element = default; + var hasItems = false; + while (enumerator.MoveNext()) + { + element = enumerator.Current; + hasItems = true; + } + enumerator.Dispose(); + return hasItems ? element : throw new InvalidOperationException("Sequence is empty"); + } + + public static T Last(this IPoolingEnumerable source, Func condition) + { + var enumerator = source.GetEnumerator(); + T element = default; + var hasItems = false; + while (enumerator.MoveNext()) + { + if (!condition(enumerator.Current)) continue; + + element = enumerator.Current; + hasItems = true; + } + enumerator.Dispose(); + return hasItems ? element : throw new InvalidOperationException("Sequence is empty"); + } + + public static T Last(this IPoolingEnumerable source, TContext context, Func condition) + { + var enumerator = source.GetEnumerator(); + T element = default; + var hasItems = false; + while (enumerator.MoveNext()) + { + if (!condition(context, enumerator.Current)) continue; + + element = enumerator.Current; + hasItems = true; + } + enumerator.Dispose(); + return hasItems ? element : throw new InvalidOperationException("Sequence is empty"); + } + + public static T LastOrDefault(this IPoolingEnumerable source) + { + var enumerator = source.GetEnumerator(); + T element = default; + var hasItems = false; + while (enumerator.MoveNext()) + { + element = enumerator.Current; + hasItems = true; + } + enumerator.Dispose(); + return hasItems ? element : default; + } + + public static T LastOrDefault(this IPoolingEnumerable source, Func condition) + { + var enumerator = source.GetEnumerator(); + T element = default; + var hasItems = false; + while (enumerator.MoveNext()) + { + if (!condition(enumerator.Current)) continue; + + element = enumerator.Current; + hasItems = true; + } + enumerator.Dispose(); + return hasItems ? element : default; + } + + public static T LastOrDefault(this IPoolingEnumerable source, TContext context, Func condition) + { + var enumerator = source.GetEnumerator(); + T element = default; + var hasItems = false; + while (enumerator.MoveNext()) + { + if (!condition(context, enumerator.Current)) continue; + + element = enumerator.Current; + hasItems = true; + } + enumerator.Dispose(); + return hasItems ? element : default; + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/MinMax.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/MinMax.cs new file mode 100644 index 0000000..9d46d3a --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/MinMax.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static int Min(this IPoolingEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + int value = 0; + bool hasValue = false; + foreach (int x in source) { + if (hasValue) { + if (x < value) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static int? Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + int? value = null; + foreach (int? x in source) { + if (value == null || x < value) + value = x; + } + return value; + } + + public static long Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + long value = 0; + bool hasValue = false; + foreach (long x in source) { + if (hasValue) { + if (x < value) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static long? Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + long? value = null; + foreach (long? x in source) { + if (value == null || x < value) value = x; + } + return value; + } + + public static float Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + float value = 0; + bool hasValue = false; + foreach (float x in source) { + if (hasValue) { + // Normally NaN < anything is false, as is anything < NaN + // However, this leads to some irksome outcomes in Min and Max. + // If we use those semantics then Min(NaN, 5.0) is NaN, but + // Min(5.0, NaN) is 5.0! To fix this, we impose a total + // ordering where NaN is smaller than every value, including + // negative infinity. + if (x < value || System.Single.IsNaN(x)) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static float? Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + float? value = null; + foreach (float? x in source) { + if (x == null) continue; + if (value == null || x < value || System.Single.IsNaN((float)x)) value = x; + } + return value; + } + + public static double Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + double value = 0; + bool hasValue = false; + foreach (double x in source) { + if (hasValue) { + if (x < value || Double.IsNaN(x)) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static double? Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + double? value = null; + foreach (double? x in source) { + if (x == null) continue; + if (value == null || x < value || Double.IsNaN((double)x)) value = x; + } + return value; + } + + public static decimal Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + decimal value = 0; + bool hasValue = false; + foreach (decimal x in source) { + if (hasValue) { + if (x < value) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static decimal? Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + decimal? value = null; + foreach (decimal? x in source) { + if (value == null || x < value) value = x; + } + return value; + } + + public static TSource Min(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + var comparer = Comparer.Default; + var value = default(TSource); + if (value == null) { + foreach (var x in source) { + if (x != null && (value == null || comparer.Compare(x, value) < 0)) + value = x; + } + return value; + } + + bool hasValue = false; + foreach (TSource x in source) { + if (hasValue) { + if (comparer.Compare(x, value) < 0) + value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static int Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static int? Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static long Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static long? Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static float Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static float? Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static double Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static double? Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static decimal Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static decimal? Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static TResult Min(this IPoolingEnumerable source, Func selector) { + return source.Select(selector).Min(); + } + + public static int Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + int value = 0; + bool hasValue = false; + foreach (int x in source) { + if (hasValue) { + if (x > value) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static int? Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + int? value = null; + foreach (int? x in source) { + if (value == null || x > value) value = x; + } + return value; + } + + public static long Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + long value = 0; + bool hasValue = false; + foreach (long x in source) { + if (hasValue) { + if (x > value) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static long? Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + long? value = null; + foreach (long? x in source) { + if (value == null || x > value) value = x; + } + return value; + } + + public static double Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + double value = 0; + bool hasValue = false; + foreach (double x in source) { + if (hasValue) { + if (x > value || Double.IsNaN(value)) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static double? Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + double? value = null; + foreach (double? x in source) { + if (x == null) continue; + if (value == null || x > value || Double.IsNaN((double)value)) value = x; + } + return value; + } + + public static float Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + float value = 0; + bool hasValue = false; + foreach (float x in source) { + if (hasValue) { + if (x > value || Double.IsNaN(value)) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static float? Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + float? value = null; + foreach (float? x in source) { + if (x == null) continue; + if (value == null || x > value || System.Single.IsNaN((float)value)) value = x; + } + return value; + } + + public static decimal Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + decimal value = 0; + bool hasValue = false; + foreach (decimal x in source) { + if (hasValue) { + if (x > value) value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static decimal? Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + decimal? value = null; + foreach (decimal? x in source) { + if (value == null || x > value) value = x; + } + return value; + } + + public static TSource Max(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + Comparer comparer = Comparer.Default; + TSource value = default; + if (value == null) { + foreach (TSource x in source) { + if (x != null && (value == null || comparer.Compare(x, value) > 0)) + value = x; + } + return value; + } + + bool hasValue = false; + foreach (TSource x in source) { + if (hasValue) { + if (comparer.Compare(x, value) > 0) + value = x; + } + else { + value = x; + hasValue = true; + } + } + if (hasValue) return value; + throw new InvalidOperationException("Sequence contains no elements"); + } + + public static int Max(this IPoolingEnumerable source, Func selector) => + Max(source.Select(selector)); + + public static int? Max(this IPoolingEnumerable source, Func selector) => + Max(source.Select(selector)); + + public static long Max(this IPoolingEnumerable source, Func selector) => + Max(source.Select(selector)); + + public static long? Max(this IPoolingEnumerable source, Func selector) => + Max(source.Select(selector)); + + public static float Max(this IPoolingEnumerable source, Func selector) => + Max(source.Select(selector)); + + public static float? Max(this IPoolingEnumerable source, Func selector) => + Max(source.Select(selector)); + + public static double Max(this IPoolingEnumerable source, Func selector) => + Max(source.Select(selector)); + + public static double? Max(this IPoolingEnumerable source, Func selector) => Max(source.Select(selector)); + + public static decimal Max(this IPoolingEnumerable source, Func selector) => + source.Select(selector).Max(); + + public static decimal? Max(this IPoolingEnumerable source, Func selector) => + source.Select(selector).Max(); + + public static TResult Max(this IPoolingEnumerable source, Func selector) => + source.Select(selector).Max(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/OfType.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/OfType.Enumerable.cs new file mode 100644 index 0000000..e9d7b47 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/OfType.Enumerable.cs @@ -0,0 +1,77 @@ +namespace MemoryPools.Collections.Linq +{ + internal class OfTypeExprEnumerable : IPoolingEnumerable + { + private int _count; + + private IPoolingEnumerable _src; + + public OfTypeExprEnumerable Init(IPoolingEnumerable src) + { + _src = src; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(_src.GetEnumerator(), this); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + Pool>.Return(this); + } + } + + internal class OfTypeExprEnumerator : IPoolingEnumerator + { + private IPoolingEnumerator _src; + private OfTypeExprEnumerable _parent; + + public OfTypeExprEnumerator Init(IPoolingEnumerator src, OfTypeExprEnumerable parent) + { + _src = src; + _parent = parent; + return this; + } + + public bool MoveNext() + { + do + { + var next = _src.MoveNext(); + if (!next) return false; + } while (!(_src.Current is T)); + + return true; + } + + public void Reset() + { + _src.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public T Current => (T)_src.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = null; + _src?.Dispose(); + _src = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/OfType.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/OfType.cs new file mode 100644 index 0000000..a4cf1e2 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/OfType.cs @@ -0,0 +1,11 @@ +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable OfType(this IPoolingEnumerable source) + { + if (source is IPoolingEnumerable res) return res; + return Pool>.Get().Init(source); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Prepend.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Prepend.Enumerable.cs new file mode 100644 index 0000000..ad530dd --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Prepend.Enumerable.cs @@ -0,0 +1,88 @@ +namespace MemoryPools.Collections.Linq +{ + internal class PrependExprEnumerable : IPoolingEnumerable + { + private int _count; + + private IPoolingEnumerable _src; + private T _element; + + public PrependExprEnumerable Init(IPoolingEnumerable src, T element) + { + _src = src; + _count = 0; + _element = element; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(_src.GetEnumerator(), this, _element); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _element = default; + Pool>.Return(this); + } + } + + internal class PrependExprEnumerator : IPoolingEnumerator + { + private IPoolingEnumerator _src; + private PrependExprEnumerable _parent; + private T _element; + private bool _first, _shouldReturnElement; + + public PrependExprEnumerator Init(IPoolingEnumerator src, PrependExprEnumerable parent, T element) + { + _src = src; + _parent = parent; + _element = element; + _first = true; + _shouldReturnElement = true; + return this; + } + + public bool MoveNext() + { + if (_first) + { + _first = false; + return true; + } + + _shouldReturnElement = false; + return _src.MoveNext(); + } + + public void Reset() + { + _first = true; + _src.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public T Current => _shouldReturnElement ? _element : (T) _src.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = null; + _src?.Dispose(); + _src = default; + _first = _shouldReturnElement = false; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Reverse.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Reverse.Enumerable.cs new file mode 100644 index 0000000..24bc0f1 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Reverse.Enumerable.cs @@ -0,0 +1,76 @@ +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + internal class ReverseExprEnumerable : IPoolingEnumerable + { + private int _count; + private PoolingList _src; + + public ReverseExprEnumerable Init(PoolingList src) + { + _src = src; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(_src, this); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _src?.Dispose(); + Pool>.Return(_src); + _src = default; + Pool>.Return(this); + } + } + + internal class ReverseExprEnumerator : IPoolingEnumerator + { + private PoolingList _src; + private ReverseExprEnumerable _parent; + private int _position; + + public ReverseExprEnumerator Init(PoolingList src, ReverseExprEnumerable parent) + { + _position = src.Count; + _src = src; + _parent = parent; + return this; + } + + public bool MoveNext() + { + if (_position == 0) return false; + _position--; + return true; + } + + public void Reset() => _position = _src.Count; + + object IPoolingEnumerator.Current => Current; + + public T Current => _src[_position]; + + public void Dispose() + { + _parent?.Dispose(); + _parent = default; + _src = default; + _position = default; + + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Reverse.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Reverse.cs new file mode 100644 index 0000000..dc38d2c --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Reverse.cs @@ -0,0 +1,20 @@ +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + /// + /// Returns sequence with backward direction. Complexity = 2 * O(N) (collect + return) + /// + public static IPoolingEnumerable Reverse(this IPoolingEnumerable source) + { + var list = Pool>.Get().Init(); + foreach (var item in source) + { + list.Add(item); + } + return Pool>.Get().Init(list); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.Enumerable.cs new file mode 100644 index 0000000..f6a3cba --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.Enumerable.cs @@ -0,0 +1,75 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + internal class SelectExprEnumerable : IPoolingEnumerable + { + private IPoolingEnumerable _src; + private Func _mutator; + private int _count; + + public SelectExprEnumerable Init(IPoolingEnumerable src, Func mutator) + { + _src = src; + _count = 0; + _mutator = mutator; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator(), _mutator); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _count = 0; + _mutator = default; + Pool>.Return(this); + } + } + + internal class SelectExprEnumerator : IPoolingEnumerator + { + private Func _mutator; + private SelectExprEnumerable _parent; + private IPoolingEnumerator _src; + + public SelectExprEnumerator Init(SelectExprEnumerable parent, IPoolingEnumerator src, Func mutator) + { + _src = src; + _parent = parent; + _mutator = mutator; + return this; + } + + public bool MoveNext() => _src.MoveNext(); + + public void Reset() => _src.Reset(); + + object IPoolingEnumerator.Current => Current; + + public TR Current => _mutator( _src.Current); + + public void Dispose() + { + _parent?.Dispose(); + _parent = default; + _src?.Dispose(); + _src = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.WithContextEnumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.WithContextEnumerable.cs new file mode 100644 index 0000000..669b6f8 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.WithContextEnumerable.cs @@ -0,0 +1,85 @@ + +using System; + +namespace MemoryPools.Collections.Linq +{ + internal class SelectExprWithContextEnumerable : IPoolingEnumerable + { + private IPoolingEnumerable _src; + private Func _condition; + private TContext _context; + private int _count; + + public SelectExprWithContextEnumerable Init(IPoolingEnumerable src, TContext context, Func condition) + { + _src = src; + _count = 0; + _context = context; + _condition = condition; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator(), _context, _condition); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + + if (_count == 0) + { + _src = default; + _context = default; + _condition = default; + Pool>.Return(this); + } + } + + internal class SelectExprWithContextEnumerator : IPoolingEnumerator + { + private TContext _context; + private Func _condition; + private IPoolingEnumerator _src; + private SelectExprWithContextEnumerable _parent; + + public SelectExprWithContextEnumerator Init( + SelectExprWithContextEnumerable parent, + IPoolingEnumerator src, + TContext context, + Func condition) + { + _src = src; + _parent = parent; + _context = context; + _condition = condition; + return this; + } + + public bool MoveNext() => _src.MoveNext(); + + public void Reset() => _src.Reset(); + object IPoolingEnumerator.Current => Current; + + public TR Current => _condition(_context, _src.Current); + + public void Dispose() + { + _parent?.Dispose(); + _parent = default; + _src?.Dispose(); + _src = default; + _context = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.cs new file mode 100644 index 0000000..befd9d6 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Select.cs @@ -0,0 +1,17 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable Select(this IPoolingEnumerable source, Func mutator) + { + return Pool>.Get().Init(source, mutator); + } + + public static IPoolingEnumerable Select(this IPoolingEnumerable source, TContext context, Func mutator) + { + return Pool>.Get().Init(source, context, mutator); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.ExprEnumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.ExprEnumerable.cs new file mode 100644 index 0000000..85401ac --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.ExprEnumerable.cs @@ -0,0 +1,117 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + internal class SelectManyExprEnumerable : IPoolingEnumerable + { + private IPoolingEnumerable _src; + private Func> _mutator; + private int _count; + + public SelectManyExprEnumerable Init(IPoolingEnumerable src, Func> mutator) + { + _src = src; + _count = 0; + _mutator = mutator; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator(), _mutator); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _count = 0; + _mutator = default; + Pool>.Return(this); + } + } + + internal class SelectManyExprEnumerator : IPoolingEnumerator + { + private Func> _mutator; + private SelectManyExprEnumerable _parent; + private IPoolingEnumerator _src; + private IPoolingEnumerator _currentEnumerator; + private bool _finished; + + public SelectManyExprEnumerator Init( + SelectManyExprEnumerable parent, + IPoolingEnumerator src, + Func> mutator) + { + _src = src; + _finished = false; + _parent = parent; + _mutator = mutator; + _currentEnumerator = default; + return this; + } + + public bool MoveNext() + { + if (_finished) return false; + if (_currentEnumerator == default) + { + if (!_src.MoveNext()) + { + _finished = true; + return false; + } + _currentEnumerator = _mutator(_src.Current).GetEnumerator(); + } + + do + { + var hasValue = _currentEnumerator.MoveNext(); + if (hasValue) return true; + if (!_src.MoveNext()) + { + _finished = true; + return false; + } + _currentEnumerator?.Dispose(); + _currentEnumerator = _mutator(_src.Current).GetEnumerator(); + } while (true); + } + + public void Reset() + { + _currentEnumerator?.Dispose(); + _currentEnumerator = default; + _src.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public TR Current => _currentEnumerator.Current; + + public void Dispose() + { + _currentEnumerator?.Dispose(); + _currentEnumerator = default; + + _parent?.Dispose(); + _parent = default; + + _src.Dispose(); + _src = default; + + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.ExprWithContextEnumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.ExprWithContextEnumerable.cs new file mode 100644 index 0000000..74827cf --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.ExprWithContextEnumerable.cs @@ -0,0 +1,126 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + internal class SelectManyExprWithContextEnumerable : IPoolingEnumerable + { + private IPoolingEnumerable _src; + private Func> _mutator; + private int _count; + private TContext _context; + + public SelectManyExprWithContextEnumerable Init( + IPoolingEnumerable src, + Func> mutator, + TContext context) + { + _src = src; + _count = 0; + _context = context; + _mutator = mutator; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator(), _mutator, _context); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _count = 0; + _context = default; + _mutator = default; + Pool>.Return(this); + } + } + + internal class SelectManyExprWithContextEnumerator : IPoolingEnumerator + { + private TContext _context; + private Func> _mutator; + private SelectManyExprWithContextEnumerable _parent; + private IPoolingEnumerator _src; + private IPoolingEnumerator _currentEnumerator; + private bool _finished; + + public SelectManyExprWithContextEnumerator Init( + SelectManyExprWithContextEnumerable parent, + IPoolingEnumerator src, + Func> mutator, + TContext context) + { + _src = src; + _finished = false; + _parent = parent; + _mutator = mutator; + _context = context; + _currentEnumerator = default; + return this; + } + + public bool MoveNext() + { + if (_finished) return false; + if (_currentEnumerator == default) + { + if (!_src.MoveNext()) + { + _finished = true; + return false; + } + _currentEnumerator = _mutator(_src.Current, _context).GetEnumerator(); + } + + do + { + var hasValue = _currentEnumerator.MoveNext(); + if (hasValue) return true; + if (!_src.MoveNext()) + { + _finished = true; + return false; + } + _currentEnumerator?.Dispose(); + _currentEnumerator = _mutator(_src.Current, _context).GetEnumerator(); + } while (true); + } + + public void Reset() + { + _currentEnumerator?.Dispose(); + _currentEnumerator = default; + _src.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public TR Current => _currentEnumerator.Current; + + public void Dispose() + { + _currentEnumerator?.Dispose(); + _currentEnumerator = default; + + _parent?.Dispose(); + _parent = default; + + _src.Dispose(); + _src = default; + + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.cs new file mode 100644 index 0000000..79a0a5a --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/SelectMany.cs @@ -0,0 +1,22 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable SelectMany( + this IPoolingEnumerable source, + Func> mutator) + { + return Pool>.Get().Init(source, mutator); + } + + public static IPoolingEnumerable SelectMany( + this IPoolingEnumerable source, + TContext context, + Func> mutator) + { + return Pool>.Get().Init(source, mutator, context); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/SimpleExpressions.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/SimpleExpressions.cs new file mode 100644 index 0000000..5f4fc8c --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/SimpleExpressions.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable Empty() => Range(0,0).Select(x => (T)(object)x); + + public static IPoolingEnumerable Range(int startIndex, int count) + { + return Pool.Get().Init(startIndex, count); + } + + public static IPoolingEnumerable Range(int count) + { + return Pool.Get().Init(0, count); + } + + public static IPoolingEnumerable Repeat(T element, int count) => Range(0, count).Select(element, (item, x) => item); + + public static bool Contains(this IPoolingEnumerable self, T element) + { + foreach (var item in self) + { + if (item.Equals(element)) return true; + } + + return false; + } + + public static int Count(this IPoolingEnumerable self) + { + var count = 0; + foreach (var _ in self) + { + count++; + } + return count; + } + + public static long LongCount(this IPoolingEnumerable self) + { + long count = 0; + foreach (var _ in self) + { + count++; + } + return count; + } + + public static T ElementAt(this IPoolingEnumerable self, int position) + { + var i = 0; + foreach (var item in self) + { + if (i == position) return item; + i++; + } + + throw new InvalidOperationException("Sequence is too small. Index not found"); + } + + public static bool SequenceEqual(this IPoolingEnumerable self, IPoolingEnumerable other) + { + var comparer = EqualityComparer.Default; + using (var left = self.GetEnumerator()) + using (var right = other.GetEnumerator()) + { + bool equals, leftHas, rightHas; + + do + { + leftHas = left.MoveNext(); + rightHas = right.MoveNext(); + equals = comparer.Equals(left.Current, right.Current); + + if (leftHas != rightHas || !equals) return false; + } while (leftHas && rightHas); + + return !leftHas && !rightHas; + } + } + } + + internal class RangeExprEnumerable : IPoolingEnumerable + { + private int _start; + private int _workCount; + private int _count; + + public RangeExprEnumerable Init(int start, int count) + { + _start = start; + _workCount = count; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _start, _workCount); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + _start = _workCount = 0; + _count = 0; + Pool.Return(this); + } + } + + internal class RangeExprEnumerator : IPoolingEnumerator + { + private int _start; + private int _current; + private int _workCount; + private RangeExprEnumerable _parent; + + public RangeExprEnumerator Init(RangeExprEnumerable parent, int start, int workCount) + { + _current = -1; + _start = start; + _workCount = workCount; + _parent = parent; + return this; + } + + public bool MoveNext() + { + if (_current == 0) return false; + if (_current == -1) + { + _current = _workCount; + return _workCount != 0; + } + + _current--; + return _current != 0; + } + + public void Reset() => _current = _start; + + object IPoolingEnumerator.Current => _current; + + public int Current => _start + (_workCount - _current); + + public void Dispose() + { + _current = -1; + _parent?.Dispose(); + _parent = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/SingleSingleOrDefault.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/SingleSingleOrDefault.cs new file mode 100644 index 0000000..4929a62 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/SingleSingleOrDefault.cs @@ -0,0 +1,139 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static T Single(this IPoolingEnumerable source) + { + var wasFound = false; + var element = default(T); + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (wasFound) + { + enumerator.Dispose(); + throw new InvalidOperationException("Sequence should contain only one element"); + } + + wasFound = true; + element = enumerator.Current; + } + enumerator.Dispose(); + return element; + } + + public static T Single(this IPoolingEnumerable source, Func condition) + { + var wasFound = false; + var element = default(T); + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (condition(enumerator.Current)) + { + if (wasFound) + { + enumerator.Dispose(); + throw new InvalidOperationException("Sequence should contain only one element"); + } + + wasFound = true; + element = enumerator.Current; + } + } + enumerator.Dispose(); + return element; + } + + public static T Single(this IPoolingEnumerable source, TContext context, Func condition) + { + var wasFound = false; + var element = default(T); + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (condition(context, enumerator.Current)) + { + if (wasFound) + { + enumerator.Dispose(); + throw new InvalidOperationException("Sequence should contain only one element"); + } + + wasFound = true; + element = enumerator.Current; + } + } + enumerator.Dispose(); + return element; + } + + public static T SingleOrDefault(this IPoolingEnumerable source) + { + var wasFound = false; + var element = default(T); + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (wasFound) + { + enumerator.Dispose(); + return default; + } + + wasFound = true; + element = enumerator.Current; + } + enumerator.Dispose(); + return element; + } + + public static T SingleOrDefault(this IPoolingEnumerable source, Func condition) + { + var wasFound = false; + var element = default(T); + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (condition(enumerator.Current)) + { + if (wasFound) + { + enumerator.Dispose(); + return default; + } + + wasFound = true; + element = enumerator.Current; + } + } + enumerator.Dispose(); + return element; + } + + public static T SingleOrDefault(this IPoolingEnumerable source, TContext context, Func condition) + { + var wasFound = false; + var element = default(T); + var enumerator = source.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (condition(context, enumerator.Current)) + { + if (wasFound) + { + enumerator.Dispose(); + return default; + } + + wasFound = true; + element = enumerator.Current; + } + } + enumerator.Dispose(); + return element; + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/SkipTake.ExprPoolingEnumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/SkipTake.ExprPoolingEnumerable.cs new file mode 100644 index 0000000..d3c9821 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/SkipTake.ExprPoolingEnumerable.cs @@ -0,0 +1,99 @@ +namespace MemoryPools.Collections.Linq +{ + internal sealed class SkipTakeExprPoolingEnumerable : IPoolingEnumerable + { + private bool _take; + private int _workCount; + private int _count; + private IPoolingEnumerable _source; + + public SkipTakeExprPoolingEnumerable Init(IPoolingEnumerable source, bool take, int count) + { + _count = 0; + _workCount = count; + _source = source; + _take = take; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _source.GetEnumerator(), _take, _workCount); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + _source = null; + _take = default; + Pool>.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + internal sealed class SkipTakeExprPoolingEnumerator : IPoolingEnumerator + { + private IPoolingEnumerator _source; + private SkipTakeExprPoolingEnumerable _parent; + private bool _take; + private int _pos, _workCount; + + public SkipTakeExprPoolingEnumerator Init(SkipTakeExprPoolingEnumerable parent, IPoolingEnumerator source, bool take, int workCount) + { + _pos = 0; + _take = take; + _source = source; + _parent = parent; + _workCount = workCount; + return this; + } + + public bool MoveNext() + { + if (_take) + { + if (_pos < _workCount) + { + _pos++; + return _source.MoveNext(); + } + + return false; + } + + while (_pos < _workCount) + { + _pos++; + _source.MoveNext(); + } + + return _source.MoveNext(); + } + + public void Reset() + { + _pos = 0; + _source.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public T Current => _source.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = default; + + _source?.Dispose(); + _source = default; + + Pool.Return(this); + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/SkipTake.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/SkipTake.cs new file mode 100644 index 0000000..1a7c552 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/SkipTake.cs @@ -0,0 +1,15 @@ +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable Skip(this IPoolingEnumerable source, int count) + { + return Pool>.Get().Init(source, false, count); + } + + public static IPoolingEnumerable Take(this IPoolingEnumerable source, int count) + { + return Pool>.Get().Init(source, true, count); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Sum.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Sum.cs new file mode 100644 index 0000000..480ee6e --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Sum.cs @@ -0,0 +1,126 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static int Sum(this IPoolingEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + int sum = 0; + checked { + foreach (var v in source) sum += v; + } + return sum; + } + + public static int? Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + int sum = 0; + checked { + foreach (var v in source) { + if (v != null) sum += v.GetValueOrDefault(); + } + } + return sum; + } + + public static long Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + long sum = 0; + checked { + foreach (long v in source) sum += v; + } + return sum; + } + + public static long? Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + long sum = 0; + checked { + foreach (var v in source) { + if (v != null) sum += v.GetValueOrDefault(); + } + } + return sum; + } + + public static float Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + double sum = 0; + foreach (var v in source) sum += v; + return (float)sum; + } + + public static float? Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + double sum = 0; + foreach (var v in source) { + if (v != null) sum += v.GetValueOrDefault(); + } + return (float)sum; + } + + public static double Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + double sum = 0; + foreach (var v in source) sum += v; + return sum; + } + + public static double? Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + double sum = 0; + foreach (var v in source) { + if (v != null) sum += v.GetValueOrDefault(); + } + return sum; + } + + public static decimal Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + decimal sum = 0; + foreach (var v in source) sum += v; + return sum; + } + + public static decimal? Sum(this IPoolingEnumerable source) { + if (source == null) throw new ArgumentNullException(nameof(source)); + decimal sum = 0; + foreach (var v in source) { + if (v != null) sum += v.GetValueOrDefault(); + } + return sum; + } + + public static int Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static int? Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static long Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static long? Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static float Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static float? Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static double Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static double? Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static decimal Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + + public static decimal? Sum(this IPoolingEnumerable source, Func selector) => + Sum(source.Select(selector)); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Union.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Union.Enumerable.cs new file mode 100644 index 0000000..5415115 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Union.Enumerable.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + internal class UnionExprEnumerable : IPoolingEnumerable + { + private int _count; + private PoolingDictionary _src; + + public UnionExprEnumerable Init(PoolingDictionary src) + { + _src = src; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator()); + } + + private void Dispose() + { + if(_count == 0) return; + _count--; + if (_count == 0) + { + _src?.Dispose(); + Pool>.Return(_src); + _src = default; + + Pool>.Return(this); + } + } + internal class UnionExprEnumerator : IPoolingEnumerator + { + private UnionExprEnumerable _parent; + private IPoolingEnumerator> _src; + + public UnionExprEnumerator Init(UnionExprEnumerable parent, IPoolingEnumerator> src) + { + _src = src; + _parent = parent; + return this; + } + + public bool MoveNext() => _src.MoveNext(); + + public void Reset() => _src.Reset(); + + object IPoolingEnumerator.Current => Current; + + public T Current => _src.Current.Key; + + public void Dispose() + { + _src?.Dispose(); + _src = null; + + _parent?.Dispose(); + _parent = default; + + Pool.Return(this); + } + } + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Union.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Union.cs new file mode 100644 index 0000000..0015060 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Union.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using MemoryPools.Collections.Specialized; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable Union(this IPoolingEnumerable source, IPoolingEnumerable second) + { + var set = Pool>.Get().Init(0); + foreach (var item in source) set[item] = 1; + foreach (var item in second) set[item] = 1; + + return Pool>.Get().Init(set); + } + + public static IPoolingEnumerable Union(this IPoolingEnumerable source, IPoolingEnumerable second, IEqualityComparer comparer) + { + var set = Pool>.Get().Init(0, comparer); + foreach (var item in source) set[item] = 1; + foreach (var item in second) set[item] = 1; + + return Pool>.Get().Init(set); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.Enumerable.cs new file mode 100644 index 0000000..0a484e7 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.Enumerable.cs @@ -0,0 +1,88 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + internal class WhereExprEnumerable : IPoolingEnumerable + { + private int _count; + private IPoolingEnumerable _src; + private Func _condition; + + public WhereExprEnumerable Init(IPoolingEnumerable src, Func condition) + { + _count = 0; + _src = src; + _condition = condition; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(_src.GetEnumerator(), this, _condition); + } + + private void Dispose() + { + if (_count == 0) return; + + _count--; + + if (_count == 0) + { + _src = default; + _condition = default; + Pool>.Return(this); + } + } + + internal class WhereExprEnumerator : IPoolingEnumerator + { + private Func _mutator; + private IPoolingEnumerator _src; + private WhereExprEnumerable _parent; + + public WhereExprEnumerator Init(IPoolingEnumerator src, WhereExprEnumerable parent, Func mutator) + { + _src = src; + _mutator = mutator; + _parent = parent; + return this; + } + + public bool MoveNext() + { + do + { + var next = _src.MoveNext(); + if (!next) return false; + } while (!_mutator(_src.Current)); + + return true; + } + + public void Reset() + { + _src.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public T Current => _src.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = default; + _src?.Dispose(); + _src = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.WithContextEnumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.WithContextEnumerable.cs new file mode 100644 index 0000000..313a460 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.WithContextEnumerable.cs @@ -0,0 +1,94 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + internal class WhereExprWithContextEnumerable : IPoolingEnumerable + { + private int _count; + private IPoolingEnumerable _src; + private Func _condition; + private TContext _context; + + public WhereExprWithContextEnumerable Init(IPoolingEnumerable src, TContext context, Func condition) + { + _count = 0; + _src = src; + _context = context; + _condition = condition; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(_src.GetEnumerator(), this, _context, _condition); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + (_condition, _context, _src) = (default, default, default); + Pool>.Return(this); + } + } + + internal class WhereExprWithContextEnumerator : IPoolingEnumerator + { + private TContext _context; + private Func _condition; + private IPoolingEnumerator _src; + private WhereExprWithContextEnumerable _parent; + + public WhereExprWithContextEnumerator Init( + IPoolingEnumerator src, + WhereExprWithContextEnumerable parent, + TContext context, + Func condition) + { + _src = src; + _parent = parent; + _context = context; + _condition = condition; + return this; + } + + public bool MoveNext() + { + do + { + var next = _src.MoveNext(); + if (!next) return false; + } while (!_condition(_context, _src.Current)); + + return true; + } + + public void Reset() + { + _src.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public T Current => _src.Current; + + public void Dispose() + { + _parent?.Dispose(); + _parent = null; + _src?.Dispose(); + _src = default; + _context = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.cs new file mode 100644 index 0000000..f7daae5 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Where.cs @@ -0,0 +1,13 @@ +using System; + +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable Where(this IPoolingEnumerable source, Func condition) => + Pool>.Get().Init(source, condition); + + public static IPoolingEnumerable Where(this IPoolingEnumerable source, TContext context, Func condition) => + Pool>.Get().Init(source, context, condition); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Zip.Enumerable.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Zip.Enumerable.cs new file mode 100644 index 0000000..315a43e --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Zip.Enumerable.cs @@ -0,0 +1,83 @@ +namespace MemoryPools.Collections +{ + internal class ZipExprEnumerable : IPoolingEnumerable<(T, T)> + { + private IPoolingEnumerable _src, _second; + private int _count; + + public ZipExprEnumerable Init(IPoolingEnumerable src, IPoolingEnumerable second) + { + _src = src; + _count = 0; + _second = second; + return this; + } + + public IPoolingEnumerator<(T, T)> GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src.GetEnumerator(), _second.GetEnumerator()); + } + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + _src = default; + _second = default; + Pool>.Return(this); + } + } + + internal class ZipExprEnumerator : IPoolingEnumerator<(T, T)> + { + private ZipExprEnumerable _parent; + private IPoolingEnumerator _src, _second; + private bool _hasResult; + + public ZipExprEnumerator Init( + ZipExprEnumerable parent, IPoolingEnumerator src, IPoolingEnumerator second) + { + _parent = parent; + _src = src; + _second = second; + _hasResult = false; + return this; + } + + public bool MoveNext() + { + _hasResult = _src.MoveNext() && _second.MoveNext(); + return _hasResult; + } + + public void Reset() + { + _src.Reset(); + _second.Reset(); + } + + object IPoolingEnumerator.Current => Current; + + public (T, T) Current => _hasResult ? ( _src.Current, _second.Current) : default; + + public void Dispose() + { + _parent?.Dispose(); + _parent = default; + _src?.Dispose(); + _src = default; + _second?.Dispose(); + _second = default; + Pool.Return(this); + } + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Linq/Zip.cs b/ZeroLevel/Services/MemoryPools/Collections/Linq/Zip.cs new file mode 100644 index 0000000..390c13d --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Linq/Zip.cs @@ -0,0 +1,8 @@ +namespace MemoryPools.Collections.Linq +{ + public static partial class PoolingEnumerable + { + public static IPoolingEnumerable<(T, T)> Zip(this IPoolingEnumerable source, IPoolingEnumerable second) => + Pool>.Get().Init(source, second); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/AsSingleQueryList.EnumerableRef.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/AsSingleQueryList.EnumerableRef.cs new file mode 100644 index 0000000..e1a6924 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/AsSingleQueryList.EnumerableRef.cs @@ -0,0 +1,70 @@ +namespace MemoryPools.Collections.Specialized +{ + public static partial class AsSingleQueryList + { + private class EnumerableShared : IPoolingEnumerable where T : class + { + private PoolingListCanon _src; + private int _count; + + public IPoolingEnumerable Init(PoolingListCanon src) + { + _src = src; + _count = 0; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + _count++; + return Pool.Get().Init(this, _src); + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + + private void Dispose() + { + if (_count == 0) return; + _count--; + if (_count == 0) + { + _src?.Dispose(); + _src = default; + Pool>.Return(this); + } + } + + private class EnumeratorRef : IPoolingEnumerator + { + private IPoolingEnumerator _enumerator; + private EnumerableShared _parent; + + public IPoolingEnumerator Init(EnumerableShared parent, IPoolingEnumerable src) + { + _parent = parent; + _enumerator = src.GetEnumerator(); + return this; + } + + public bool MoveNext() => _enumerator.MoveNext(); + + public void Reset() => _enumerator.Reset(); + + public T Current => _enumerator.Current; + + object IPoolingEnumerator.Current => Current; + + public void Dispose() + { + _enumerator?.Dispose(); + _enumerator = default; + + _parent?.Dispose(); + _parent = default; + + Pool.Return(this); + } + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/AsSingleQueryList.EnumerableVal.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/AsSingleQueryList.EnumerableVal.cs new file mode 100644 index 0000000..9b83589 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/AsSingleQueryList.EnumerableVal.cs @@ -0,0 +1,60 @@ +namespace MemoryPools.Collections.Specialized +{ + public static partial class AsSingleQueryList + { + private class EnumerableTyped : IPoolingEnumerable + { + private PoolingList _src; + + public IPoolingEnumerable Init(PoolingList src) + { + _src = src; + return this; + } + + public IPoolingEnumerator GetEnumerator() + { + var src = _src; + _src = default; + Pool>.Return(this); + return Pool.Get().Init(src); + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + + private class EnumeratorVal : IPoolingEnumerator + { + private PoolingList _src; + private IPoolingEnumerator _enumerator; + + public IPoolingEnumerator Init(PoolingList src) + { + _src = src; + _enumerator = _src.GetEnumerator(); + return this; + } + + public bool MoveNext() => _enumerator.MoveNext(); + + public void Reset() + { + _enumerator?.Dispose(); + _enumerator = _src.GetEnumerator(); + } + + public T Current => _enumerator.Current; + + object IPoolingEnumerator.Current => Current; + + public void Dispose() + { + _enumerator?.Dispose(); + _src?.Dispose(); + Pool>.Return(_src); + Pool.Return(this); + _src = default; + } + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/Helpers/HashHelpers.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/Helpers/HashHelpers.cs new file mode 100644 index 0000000..2644023 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/Helpers/HashHelpers.cs @@ -0,0 +1,91 @@ +using System; +using System.Diagnostics.Contracts; + +namespace MemoryPools.Collections.Specialized.Helpers +{ + internal static class HashHelpers + { + private const int HashPrime = 101; + public const int HashCollisionThreshold = 100; + + // Table of prime numbers to use as hash table sizes. + // A typical resize algorithm would pick the smallest prime number in this array + // that is larger than twice the previous capacity. + // Suppose our Hashtable currently has capacity x and enough elements are added + // such that a resize needs to occur. Resizing first computes 2x then finds the + // first prime in the table greater than 2x, i.e. if primes are ordered + // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. + // Doubling is important for preserving the asymptotic complexity of the + // hashtable operations such as add. Having a prime guarantees that double + // hashing does not lead to infinite loops. IE, your hash function will be + // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime. + public static readonly int[] Primes = + { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 + }; + + public static bool IsPrime(int candidate) + { + if ((candidate & 1) != 0) + { + int limit = (int) Math.Sqrt(candidate); + for (int divisor = 3; divisor <= limit; divisor += 2) + { + if ((candidate % divisor) == 0) + return false; + } + + return true; + } + + return (candidate == 2); + } + + public static int GetPrime(int min) + { + for (int i = 0; i < Primes.Length; i++) + { + int prime = Primes[i]; + if (prime >= min) return prime; + } + + //outside of our predefined table. + //compute the hard way. + for (int i = (min | 1); i < Int32.MaxValue; i += 2) + { + if (IsPrime(i) && ((i - 1) % HashPrime != 0)) + return i; + } + + return min; + } + + public static int GetMinPrime() + { + return Primes[0]; + } + + // Returns size of hashtable to grow to. + public static int ExpandPrime(int oldSize) + { + var newSize = oldSize + 1; + + // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint) newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) + { + Contract.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); + return MaxPrimeArrayLength; + } + + return GetPrime(newSize); + } + + // This is the maximum prime smaller than Array.MaxArrayLength + public const int MaxPrimeArrayLength = 0x7FEFFFFD; + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/IPoolingNode.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/IPoolingNode.cs new file mode 100644 index 0000000..8319f6a --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/IPoolingNode.cs @@ -0,0 +1,15 @@ +using System; + +namespace MemoryPools.Collections.Specialized +{ + public interface IPoolingNode : IDisposable + { + public IPoolingNode Next { get; set; } + + T this[int index] { get; set; } + + IPoolingNode Init(int capacity); + + void Clear(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/IdealHashDictionary.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/IdealHashDictionary.cs new file mode 100644 index 0000000..0b47681 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/IdealHashDictionary.cs @@ -0,0 +1,60 @@ +using System; + +namespace MemoryPools.Collections.Specialized +{ + /// + /// Represents ideal dictionary with extra fast access to its items. Items should inherit IdealHashObjectBase to be + /// able to set hashcode. + /// + /// Key of dictionary + /// Corresponding Value + public class IdealHashDictionary : + IDisposable + where TK : IdealHashObjectBase + where TV : class + { + readonly PoolingListCanon _list = Pool>.Get().Init(); + readonly PoolingQueue _freeNodes = new PoolingQueueVal(); + + public TV this[TK key] + { + get + { + var hc = key.IdealHashCode; + if(hc >= _list.Count) + throw new ArgumentOutOfRangeException(nameof(key)); + return _list[hc]; + } + } + + public void Add(TK key, TV value) + { + var index = AcquireHashCode(value); + key.IdealHashCode = index; + } + + public bool Remove(TK key) + { + var index = key.IdealHashCode; + _freeNodes.Enqueue(index); + _list[index] = default; + return true; + } + + private int AcquireHashCode(TV value) + { + if(_freeNodes.Count > 0) + { + return _freeNodes.Dequeue(); + } + _list.Add(value); + return _list.Count - 1; + } + + public void Dispose() + { + _list.Clear(); + _freeNodes.Clear(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/IdealHashObjectBase.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/IdealHashObjectBase.cs new file mode 100644 index 0000000..9c313d6 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/IdealHashObjectBase.cs @@ -0,0 +1,9 @@ +namespace MemoryPools.Collections.Specialized +{ + public abstract class IdealHashObjectBase + { + internal int IdealHashCode { get; set; } + + public override int GetHashCode() => IdealHashCode; + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/LocalList.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/LocalList.cs new file mode 100644 index 0000000..6b9f13b --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/LocalList.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Specialized +{ + /// + /// Use this class when you need to store 1 or 2 items in class fields and in very rare scenarios + /// more then 2 items. In super-rare scenarios - to pass it as IList, ICollection or as IEnumerable. + /// + public struct LocalList : IList + { + private static readonly EqualityComparer ItemComparer = EqualityComparer.Default; + + private (T, T) _items; + private IList _other; + + // Static lists to store real length (-1 field in struct) + private static readonly IList LengthIs1 = new List {default}; + private static readonly IList LengthIs2 = new List {default, default}; + + private const int DefaultListCapacity = 8; + public const int LocalStoreCapacity = 2; + + public IEnumerator GetEnumerator() + { + // empty + if (_other == null) yield break; + if (_other.Count <= LocalStoreCapacity) + { + yield return _items.Item1; + if (ReferenceEquals(_other, LengthIs2)) yield return _items.Item2; + yield break; + } + + using(var enumerator = _other.GetEnumerator()) + { + while (enumerator.MoveNext()) yield return enumerator.Current; + } + } + + public void Add(T item) + { + // empty + if (_other == null) + { + _items.Item1 = item; + _other = LengthIs1; + } + // count=1 + else if (ReferenceEquals(_other, LengthIs1)) + { + _items.Item2 = item; + _other = LengthIs2; + } + // count=2 + else + { + if (ReferenceEquals(_other, LengthIs2)) + { + _other = new List(DefaultListCapacity); + _other.Add(_items.Item1); + _items.Item1 = default; + + _other.Add(_items.Item2); + _items.Item2 = default; + } + + _other.Add(item); + } + } + + public void Clear() + { + _other = null; + _items.Item1 = default; + _items.Item2 = default; + } + + public bool Contains(T item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (_other == null) return; + + if (_other.Count > LocalStoreCapacity) + { + _other.CopyTo(array, arrayIndex); + } + else + { + if (_other.Count > 0) array[arrayIndex++] = _items.Item1; + if (_other.Count > 1) array[arrayIndex] = _items.Item2; + } + } + + /// + /// Removes first occurrence of given item + /// + public bool Remove(T item) + { + if (_other == null) return false; + + if (_other.Count > LocalStoreCapacity) + { + var done = _other.Remove(item); + if (done && _other.Count == 2) + { + _items.Item1 = _other[0]; + _items.Item2 = _other[1]; + _other = LengthIs2; + } + + return done; + } + + if (ReferenceEquals(_other, LengthIs1) && ItemComparer.Equals(_items.Item1, item)) + { + _items.Item1 = default; + _other = null; + return true; + } + + if (ReferenceEquals(_other, LengthIs2)) + { + var done = false; + if (ItemComparer.Equals(_items.Item2, item)) + { + _items.Item2 = default; + _other = LengthIs1; + return true; + } + + if (ItemComparer.Equals(_items.Item1, item)) + { + _items.Item1 = _items.Item2; + _other = LengthIs1; + return true; + } + } + + return false; + } + + public int Count => _other?.Count ?? 0; + + public bool IsReadOnly => false; + + public int IndexOf(T item) + { + if (_other == null) + return -1; + + if (_other.Count > LocalStoreCapacity) return _other.IndexOf(item); + + if (_other.Count > 0 && ItemComparer.Equals(_items.Item1, item)) return 0; + if (_other.Count > 1 && ItemComparer.Equals(_items.Item2, item)) return 1; + + return -1; + } + + public void Insert(int index, T item) + { + // 2nd of 1, 3rd of 2, 4th of 3.. + if (index == Count) + { + Add(item); + return; + } + + // Asked non-first when empty + if (_other == null) throw new IndexOutOfRangeException(); + + // If list already created + if (_other.Count > LocalStoreCapacity) _other.Insert(index, item); + + if (index == 0) + { + if (ReferenceEquals(_other, LengthIs1)) + { + _items = (item, _items.Item1); + _other = LengthIs2; + } + else + { + (_items.Item1, _items.Item2, item) = (item, _items.Item1, _items.Item2); + Add(item); + } + + return; + } + + // (item0, item1), list(nothing) ->> (def, def), list(item0, item ,item1) + if (index == 1) + { + (_items.Item2, item) = (item, _items.Item2); + Add(item); + } + } + + public void RemoveAt(int index) + { + if (_other == null || _other.Count <= index || index < 0) + throw new IndexOutOfRangeException(); + + if (_other.Count < LocalStoreCapacity) + { + if (index == 0) + { + _items.Item1 = _items.Item2; + _other = ReferenceEquals(_other, LengthIs1) ? null : LengthIs1; + } + else + { + _items.Item2 = default; + _other = LengthIs1; + } + } + else + { + _other.RemoveAt(index); + if (_other.Count == 2) + { + _items.Item1 = _other[0]; + _items.Item2 = _other[1]; + _other = LengthIs2; + } + } + } + + public T this[int index] + { + get + { + if (_other == null || index >= Count || index < 0) + throw new IndexOutOfRangeException(); + + if (_other?.Count > LocalStoreCapacity) return _other[index]; + if (_other.Count > 0 && index == 0) return _items.Item1; + if (_other.Count > 1 && index == 1) return _items.Item2; + + throw new InvalidOperationException("Uncovered branch"); + } + set + { + if (_other == null || index >= Count || index < 0) + throw new IndexOutOfRangeException(); + + if (_other.Count > LocalStoreCapacity) _other[index] = value; + if (_other.Count > 0 && index == 0) _items.Item1 = value; + if (_other.Count > 1 && index == 1) _items.Item2 = value; + + throw new InvalidOperationException("Uncovered branch"); + } + } + + public override int GetHashCode() + { + return (_items.GetHashCode() * 37) & _other.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is LocalList other && Equals(other); + } + + public bool Equals(LocalList other) + { + return _items.Equals(other._items) && Equals(_other, other._other); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/LongLocalList.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/LongLocalList.cs new file mode 100644 index 0000000..21f3e15 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/LongLocalList.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Specialized +{ + /// + /// Contains 8 elements as struct fields as Maximum. Use it when you have guaranteed count of elements <= 8 + /// + public struct LongLocalList + { + private static readonly EqualityComparer ItemComparer = EqualityComparer.Default; + + private (T, T, T, T, T, T, T, T) _items; + + public const int Capacity = 8; + + public void Add(T item) + { + Count++; + if (Count >= Capacity) throw new ArgumentOutOfRangeException(); + + this[Count - 1] = item; + } + + public void Clear() + { + Count = 0; + _items = default; + } + + public bool Contains(T item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(T[] array, int arrayIndex) + { + for (var i = 0; i < Capacity; i++) array[i] = this[i]; + } + + public bool Remove(T item) + { + var i = 0; + var j = 0; + + for (; i < Capacity; i++) + { + if (ItemComparer.Equals(this[i], item) != true) j++; + else continue; + this[j] = this[i]; + } + + return j != i; + } + + public int Count { get; private set; } + + public bool IsReadOnly => false; + + public int IndexOf(T item) + { + for (var i = 0; i < Capacity; i++) + if (ItemComparer.Equals(this[i], item)) + return i; + return -1; + } + + public T this[int index] + { + get + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + + switch (index) + { + case 0: return _items.Item1; + case 1: return _items.Item2; + case 2: return _items.Item3; + case 3: return _items.Item4; + case 4: return _items.Item5; + case 5: return _items.Item6; + case 6: return _items.Item7; + case 7: return _items.Item8; + default: throw new IndexOutOfRangeException(); + } + } + set + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + + switch (index) + { + case 0: + _items.Item1 = value; + break; + case 1: + _items.Item2 = value; + break; + case 2: + _items.Item3 = value; + break; + case 3: + _items.Item4 = value; + break; + case 4: + _items.Item5 = value; + break; + case 5: + _items.Item6 = value; + break; + case 6: + _items.Item7 = value; + break; + case 7: + _items.Item8 = value; + break; + default: throw new IndexOutOfRangeException(); + } + } + } + + public override int GetHashCode() + { + return _items.GetHashCode(); + } + + public override bool Equals(object obj) + { + return obj is LongLocalList other && Equals(other); + } + + public bool Equals(LongLocalList other) + { + return _items.Equals(other._items); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.KeysCollection.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.KeysCollection.cs new file mode 100644 index 0000000..8907d35 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.KeysCollection.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Specialized +{ + public partial class PoolingDictionary + { + internal class KeysCollection : ICollection + { + private PoolingDictionary _src; + + public KeysCollection Init(PoolingDictionary src) + { + _src = src; + return this; + } + + public IEnumerator GetEnumerator() + { + return Pool.Get().Init(_src); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(TKey item) => throw new NotImplementedException(); + public void Clear() => throw new NotImplementedException(); + public bool Contains(TKey item) => _src.FindEntry(item) >= 0; + + public void CopyTo(TKey[] array, int arrayIndex) + { + if (array.Length - arrayIndex < _src._entries.Count) + { + throw new IndexOutOfRangeException("Cannot copy keys into array. Target array is too small."); + } + + for (var index = 0; index < _src._entries.Count; index++) + { + var entry = _src._entries[index]; + array[arrayIndex + index] = entry.key; + } + } + + public bool Remove(TKey item) => throw new NotImplementedException("Removal ops are disallowed."); + public int Count => _src.Count; + public bool IsReadOnly => _src.IsReadOnly; + + private class Enumerator : IEnumerator + { + private PoolingDictionary _src; + private int _pos; + + public Enumerator Init(PoolingDictionary src) + { + _pos = -1; + _src = src; + return this; + } + + public bool MoveNext() + { + if (_pos >= _src.Count) return false; + _pos++; + return _pos < _src.Count; + } + + public void Reset() + { + _pos = -1; + } + + public TKey Current => _src._entries[_pos].key; + + object IEnumerator.Current => Current; + + public void Dispose() + { + _src = default; + Pool.Return(this); + } + } + } + } + + +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.ValuesCollection.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.ValuesCollection.cs new file mode 100644 index 0000000..d71917f --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.ValuesCollection.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace MemoryPools.Collections.Specialized +{ + public partial class PoolingDictionary + { + internal class ValuesCollection : ICollection + { + private PoolingDictionary _src; + + public ValuesCollection Init(PoolingDictionary src) + { + _src = src; + return this; + } + + public IEnumerator GetEnumerator() + { + return Pool.Get().Init(_src); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Add(TValue item) => throw new NotImplementedException(); + + public void Clear() => throw new NotImplementedException(); + + public bool Contains(TValue item) + { + foreach (var entry in _src._entries) + { + if (entry.Equals(item)) return true; + } + + return false; + } + + public void CopyTo(TValue[] array, int arrayIndex) + { + if (array.Length - arrayIndex < _src._entries.Count) + { + throw new IndexOutOfRangeException("Cannot copy keys into array. Target array is too small."); + } + + for (var index = 0; index < _src._entries.Count; index++) + { + var entry = _src._entries[index]; + array[arrayIndex + index] = entry.value; + } + } + + public bool Remove(TValue item) => throw new NotImplementedException("Removal ops are disallowed."); + public int Count => _src.Count; + public bool IsReadOnly => _src.IsReadOnly; + + private class Enumerator : IEnumerator + { + private PoolingDictionary _src; + private int _pos; + + public Enumerator Init(PoolingDictionary src) + { + _pos = -1; + _src = src; + return this; + } + + public bool MoveNext() + { + if (_pos >= _src.Count) return false; + _pos++; + return _pos < _src.Count; + } + + public void Reset() + { + _pos = -1; + } + + public TValue Current => _src._entries[_pos].value; + + object IEnumerator.Current => Current; + + public void Dispose() + { + _src = default; + Pool.Return(this); + } + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.cs new file mode 100644 index 0000000..e1c34b3 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingDictionary.cs @@ -0,0 +1,365 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using MemoryPools.Collections.Specialized.Helpers; + +namespace MemoryPools.Collections.Specialized +{ + /// + /// Pooling dictionary contains two PoolingLists and should be disposed at end of life. + /// Disallowed: removing elements and getting non-generic IEnumerable. + /// When get IEnumerable[TKey, TValue], you need to dispose it (foreach Expr do it automatically). + /// You can safe add any count of elements to dispose them: all collections stores data in 128-sized chunks. + /// These chunks are reusable btw all Pooling* collections. All operations have O(1) complexity. + /// Primary rets IPoolingEnumerable. But you can cast to IEnumerable to work in common manner. + /// + public partial class PoolingDictionary : + IDictionary, + IReadOnlyDictionary, + IPoolingEnumerable>, + IDisposable + { + [DebuggerDisplay("Key: {key}, Value: {value}")] + private struct Entry { + public int hashCode; // Lower 31 bits of hash code, -1 if unused + public int next; // Index of next entry, -1 if last + public TKey key; // Key of entry + public TValue value; // Value of entry + } + + private IEqualityComparer _comparer; + private PoolingList _buckets; + private PoolingList _entries; + private int _freeList; + private int _version; + private int _freeCount; + private int _count; + private int _complexity; + private bool _refType; + private const int EndOfChain = -1; + + public PoolingDictionary() => Init(); + + public PoolingDictionary Init(int capacity = 0, IEqualityComparer comparer = default) + { + if (_buckets != default) + { + return this; + } + _refType = typeof(TKey).IsClass; + var size = HashHelpers.GetPrime(capacity); + _buckets = Pool>.Get().Init(); + for (var i = 0; i < size; i++) + { + _buckets.Add(EndOfChain); + } + _entries = Pool>.Get().Init(); + _freeList = EndOfChain; + _comparer = comparer ?? EqualityComparer.Default; + return this; + } + + public bool TryGetValue(TKey key, out TValue value) + { + var i = FindEntry(key); + if (i >= 0) + { + value = _entries[i].value; + return true; + } + + value = default; + return false; + } + + public TValue this[TKey key] + { + get { + var i = FindEntry(key); + if (i >= 0) return _entries[i].value; + throw new KeyNotFoundException(); + } + set => Insert(key, value, false); + } + + IEnumerable IReadOnlyDictionary.Keys => Keys; + + IEnumerable IReadOnlyDictionary.Values => Values; + + public ICollection Keys => throw new NotImplementedException(); // _keys ??= Pool.Get().Init(this); + + public ICollection Values => throw new NotImplementedException(); // _values ??= Pool.Get().Init(this); + + private int FindEntry(TKey key) + { + if(_refType && key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (_buckets == null) return -1; + var hashCode = key.GetHashCode() & 0x7FFFFFFF; + for (var i = _buckets[hashCode % _buckets.Count]; i >= 0; i = _entries[i].next) + { + if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) return i; + } + return -1; + } + + public int Complexity => _complexity; + + public void Add(TKey key, TValue value) => Insert(key, value, true); + + public bool ContainsKey(TKey key) => FindEntry(key) >= 0; + + public bool Remove(TKey key) + { + throw new NotImplementedException(); + } + + private void Insert(TKey key, TValue value, bool add) { + + if (_refType && key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (_buckets == null) Init(PoolsDefaults.DefaultPoolBucketSize); + var hashCode = key.GetHashCode() & 0x7FFFFFFF; + var targetBucket = hashCode % _buckets.Count; + var complexity = 0; + + for (var i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) + { + if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) + { + if (add) { + throw new ArgumentException("Duplicating key found in dictionary"); + } + + var entrym = _entries[i]; + entrym.value = value; + _entries[i] = entrym; + + unchecked + { + _version++; + } + + return; + } + + complexity++; + } + + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeList = _entries[index].next; + _freeCount--; + } + else + { + if (_count == _entries.Count) + { + Resize(); + targetBucket = hashCode % _buckets.Count; + } + index = _count; + _count++; + } + + var entry = _entries[index]; + entry.hashCode = hashCode; + entry.next = _buckets[targetBucket]; + entry.key = key; + entry.value = value; + _entries[index] = entry; + _buckets[targetBucket] = index; + + unchecked + { + _version++; + } + + _complexity = Math.Max(_complexity, complexity); + } + + private void Resize() + { + Resize(HashHelpers.ExpandPrime(_count), false); + } + + private void Resize(int newSize, bool forceNewHashCodes) + { + var newBuckets = Pool>.Get().Init(); + + while(newBuckets.Count < newSize) newBuckets.Add(EndOfChain); + while(_entries.Count < newSize) _entries.Add(new Entry {hashCode = EndOfChain, next = EndOfChain}); + + + if (forceNewHashCodes) + { + for (var i = 0; i < _count; i++) + { + if (_entries[i].hashCode != -1) + { + var entry = _entries[i]; + entry.hashCode = _entries[i].key.GetHashCode() & 0x7FFFFFFF; + _entries[i] = entry; + } + } + } + + for (int i = 0; i < newSize; i++) { + if (_entries[i].hashCode >= 0) { + int bucket = _entries[i].hashCode % newSize; + var entry = _entries[i]; + entry.next = newBuckets[bucket]; + _entries[i] = entry; + newBuckets[bucket] = i; + } + } + _buckets.Dispose(); + Pool>.Return(_buckets); + _buckets = newBuckets; + } + + public void Dispose() + { + unchecked + { + _version++; + } + + _buckets?.Dispose(); + Pool>.Return(_buckets); + + _entries?.Dispose(); + Pool>.Return(_entries); + + _buckets = default; + _entries = default; + _comparer = default; + _complexity = _count = _version = _freeCount = _freeList = default; + } + + public void Add(KeyValuePair item) => + Insert(item.Key, item.Value, true); + + public void Clear() + { + _buckets.Clear(); + _entries.Clear(); + _complexity = 0; + _count = _freeList = _freeCount = 0; + + unchecked + { + _version++; + } + } + + public bool Contains(KeyValuePair item) + { + var keyHash = item.Key.GetHashCode() & 0x7FFFFFFF; + for (var i = 0; i < _entries.Count; i++) + { + if(_entries[i].hashCode == keyHash && _comparer.Equals(_entries[i].key, item.Key) && + _entries[i].value.Equals(item.Value)) + { + return true; + } + } + + return false; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array.Length - arrayIndex < _entries.Count) + { + throw new IndexOutOfRangeException("Dictionary size bigger than array"); + } + + for (var i = 0; i < _entries.Count; i++) + { + array[arrayIndex + i] = new KeyValuePair(_entries[i].key, _entries[i].value); + } + } + + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public int Count => _count; + public bool IsReadOnly => false; + + internal class Enumerator : IEnumerator>, IPoolingEnumerator> + { + private PoolingDictionary _src; + private int _pos; + private int _ver; + + public Enumerator Init(PoolingDictionary src) + { + _pos = -1; + _src = src; + _ver = _src._version; + return this; + } + + public bool MoveNext() + { + if (_pos >= _src.Count) return false; + if (_ver != _src._version) + { + throw new InvalidOperationException("Version of collection was changed while enumeration"); + } + _pos++; + return _pos < _src._count; + } + + public void Reset() + { + _ver = _src._version; + _pos = -1; + } + + object IPoolingEnumerator.Current => Current; + + public KeyValuePair Current + { + get + { + if (_ver != _src._version) + { + throw new InvalidOperationException("Version of collection was changed while enumeration"); + } + return new KeyValuePair(_src._entries[_pos].key, _src._entries[_pos].value); + } + } + + object IEnumerator.Current => throw new InvalidOperationException("Boxing disallowed"); + + public void Dispose() + { + Pool.Return(this); + } + } + + public IPoolingEnumerator> GetEnumerator() => + Pool.Get().Init(this); + + IEnumerator> IEnumerable>.GetEnumerator() => + (IEnumerator>)GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)GetEnumerator(); + + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingList.SingleQuery.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingList.SingleQuery.cs new file mode 100644 index 0000000..62e8e11 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingList.SingleQuery.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; + +namespace MemoryPools.Collections.Specialized +{ + public static partial class AsSingleQueryList + { + public static IPoolingEnumerable AsSingleEnumerableList(this IEnumerable src) + { + var list = Pool>.Get().Init(); + foreach (var item in src) + { + list.Add(item); + } + return Pool>.Get().Init(list); + } + + public static IPoolingEnumerable AsSingleEnumerableSharedList(this IEnumerable src) where T : class + { + var list = Pool>.Get().Init(); + foreach (var item in src) + { + list.Add(item); + } + return Pool>.Get().Init(list); + } + public static IPoolingEnumerable AsSingleEnumerableList(this IPoolingEnumerable src) + { + var list = Pool>.Get().Init(); + foreach (var item in src) + { + list.Add(item); + } + return Pool>.Get().Init(list); + } + + public static IPoolingEnumerable AsSingleEnumerableSharedList(this IPoolingEnumerable src) where T : class + { + var list = Pool>.Get().Init(); + foreach (var item in src) + { + list.Add(item); + } + return Pool>.Get().Init(list); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingList.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingList.cs new file mode 100644 index 0000000..db9ee05 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingList.cs @@ -0,0 +1,19 @@ +namespace MemoryPools.Collections.Specialized +{ + public sealed class PoolingList : PoolingListBase + { + public PoolingList() => Init(); + + public PoolingList Init() + { + _root = Pool.GetBuffer>(PoolsDefaults.DefaultPoolBucketSize); + _ver = 0; + return this; + } + + protected override IPoolingNode CreateNodeHolder() + { + return Pool>.Get().Init(PoolsDefaults.DefaultPoolBucketSize); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingListBase.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingListBase.cs new file mode 100644 index 0000000..ce65626 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingListBase.cs @@ -0,0 +1,314 @@ +using System; +using System.Buffers; + +namespace MemoryPools.Collections.Specialized +{ + public abstract class PoolingListBase : IDisposable, IPoolingEnumerable + { + protected IMemoryOwner> _root; + protected int _count; + protected int _ver; + + public IPoolingEnumerator GetEnumerator() + { + return Pool.Get().Init(this); + } + + IPoolingEnumerator IPoolingEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + protected abstract IPoolingNode CreateNodeHolder(); + + public void Add(T item) + { + var bn = _count >> PoolsDefaults.DefaultPoolBucketDegree; + var bi = _count & PoolsDefaults.DefaultPoolBucketMask; + + _root.Memory.Span[bn] ??= CreateNodeHolder(); + + _root.Memory.Span[bn][bi] = item; + + _count++; + + unchecked + { + _ver++; + } + } + + public void Clear() + { + for (int i = 0, len = _root.Memory.Span.Length; i < len; i++) + { + if (_root.Memory.Span[i] == null) break; + _root.Memory.Span[i].Clear(); + _root.Memory.Span[i].Dispose(); + _root.Memory.Span[i] = default; + } + + _count = default; + + unchecked + { + _ver++; + } + } + + public bool Contains(T item) => IndexOf(item) != -1; + + public void CopyTo(T[] array, int arrayIndex) + { + var len = 0; + for (var i = 0; i <= PoolsDefaults.DefaultPoolBucketSize; i++) + for (var j = 0; j < PoolsDefaults.DefaultPoolBucketSize && len < _count; j++, len++) + { + array[len] = _root.Memory.Span[i][j]; + } + } + + public bool Remove(T item) + { + int i, j; + for (i = 0, j = 0; i < _count; i++) + { + var bfn = i >> PoolsDefaults.DefaultPoolBucketDegree; + var bfi = i & PoolsDefaults.DefaultPoolBucketMask; + var btn = j >> PoolsDefaults.DefaultPoolBucketDegree; + var bti = j & PoolsDefaults.DefaultPoolBucketMask; + + if (!_root.Memory.Span[bfn][bfi].Equals(item)) + { + _root.Memory.Span[btn][bti] = _root.Memory.Span[bfn][bfi]; + j++; + } + else + { + _count--; + } + } + + unchecked + { + _ver++; + } + + return i != j && i != 0; + } + + public int Count => _count; + + public bool IsReadOnly => false; + + public int IndexOf(T item) + { + var len = 0; + + for (var i = 0; i <= PoolsDefaults.DefaultPoolBucketSize; i++) + for (var j = 0; j < PoolsDefaults.DefaultPoolBucketSize && len < _count; j++, len++) + { + if (item.Equals(_root.Memory.Span[i][j])) return len; + } + + return -1; + } + + public void Insert(int index, T item) + { + if (index < _count) + { + throw new IndexOutOfRangeException(nameof(index)); + } + + for (var i = index; i <= _count; i++) + { + var j = i + 1; + + var bn = i >> PoolsDefaults.DefaultPoolBucketDegree; + var bi = i & PoolsDefaults.DefaultPoolBucketMask; + + var bjn = j >> PoolsDefaults.DefaultPoolBucketDegree; + var bji = j & PoolsDefaults.DefaultPoolBucketMask; + + var copy = _root.Memory.Span[bn][bi]; + _root.Memory.Span[bjn][bji] = item; + item = copy; + } + + _count++; + unchecked + { + _ver++; + } + } + + public void RemoveAt(int index) + { + if (index >= _count) + { + throw new IndexOutOfRangeException(nameof(index)); + } + + for (int i = index, j = i + 1; i <= _count; i++) + { + var bn = i >> PoolsDefaults.DefaultPoolBucketDegree; + var bi = i & PoolsDefaults.DefaultPoolBucketMask; + + var bjn = j >> PoolsDefaults.DefaultPoolBucketDegree; + var bji = j & PoolsDefaults.DefaultPoolBucketMask; + + _root.Memory.Span[bn][bi] = _root.Memory.Span[bjn][bji]; + } + + _count--; + unchecked + { + _ver++; + } + } + + public void Resize(int size) + { + if (size == _count) return; + if (size < _count) + { + var cbn = _count >> PoolsDefaults.DefaultPoolBucketDegree; + var sbn = size >> PoolsDefaults.DefaultPoolBucketDegree; + var sbi = size & PoolsDefaults.DefaultPoolBucketMask; + + for (var bn = sbn + 1; bn <= cbn; bn++) + { + _root.Memory.Span[bn].Dispose(); + _root.Memory.Span[bn] = default; + } + + var span = _root.Memory.Span[sbn]; + for (var i = sbi; i <= PoolsDefaults.DefaultPoolBucketSize; i++) + { + span[i] = default; + } + + _count = size; + } + else + { + var cbn = _count >> PoolsDefaults.DefaultPoolBucketDegree; + var sbn = size >> PoolsDefaults.DefaultPoolBucketDegree; + + for (var bn = cbn + 1; bn <= sbn; bn++) + { + _root.Memory.Span[bn] = CreateNodeHolder(); + } + + _count = size; + } + } + + public T this[int index] + { + get + { + if (index >= _count) + { + throw new IndexOutOfRangeException(nameof(index)); + } + + var bn = index >> PoolsDefaults.DefaultPoolBucketDegree; + var bi = index & PoolsDefaults.DefaultPoolBucketMask; + return _root.Memory.Span[bn][bi]; + } + set + { + if (index >= _count) + { + throw new IndexOutOfRangeException(nameof(index)); + } + + var bn = index >> PoolsDefaults.DefaultPoolBucketDegree; + var bi = index & PoolsDefaults.DefaultPoolBucketMask; + _root.Memory.Span[bn][bi] = value; + + unchecked + { + _ver++; + } + } + } + + public void Dispose() + { + Clear(); + _root?.Dispose(); + _root = default; + } + + private class Enumerator : IPoolingEnumerator + { + private PoolingListBase _src; + private int _bucket, _index, _ver; + + public Enumerator Init(PoolingListBase src) + { + _bucket = 0; + _index = -1; + _src = src; + _ver = _src._ver; + return this; + } + + public bool MoveNext() + { + if (_index >= _src.Count) return false; + if (_ver != _src._ver) + { + throw new InvalidOperationException("Collection was changed while enumeration"); + } + + _index++; + var tb = _src._count >> PoolsDefaults.DefaultPoolBucketDegree; + var ti = _src._count & PoolsDefaults.DefaultPoolBucketMask; + + if (_index == PoolsDefaults.DefaultPoolBucketSize) + { + _index = 0; + _bucket++; + } + + if ((_bucket < tb && _index < PoolsDefaults.DefaultPoolBucketSize) || + (_bucket == tb && _index < ti)) + { + return true; + } + + return false; + } + + public void Reset() + { + _index = -1; + _bucket = 0; + _ver = _src._ver; + } + + public T Current + { + get + { + if (_ver != _src._ver) + { + throw new InvalidOperationException("Collection was changed while enumeration"); + } + return _src._root.Memory.Span[_bucket][_index]; + } + } + + object IPoolingEnumerator.Current => Current; + + public void Dispose() + { + Pool.Return(this); + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingListCanon.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingListCanon.cs new file mode 100644 index 0000000..bd894e4 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingListCanon.cs @@ -0,0 +1,24 @@ +namespace MemoryPools.Collections.Specialized +{ + /// + /// List of elements (should be disposed to avoid memory traffic). Max size = 128*128 = 16,384 elements. + /// The best for scenarios, where you need to collect list of elements, use them and forget (w/out removal or inserts). + /// Add: O(1), Insert, Removal: O(N) + /// + public sealed class PoolingListCanon : PoolingListBase where T : class + { + public PoolingListCanon() => Init(); + + public PoolingListCanon Init() + { + _root = Pool.GetBuffer>(PoolsDefaults.DefaultPoolBucketSize); + _ver = 0; + return this; + } + + protected override IPoolingNode CreateNodeHolder() + { + return (IPoolingNode) Pool>.Get().Init(PoolsDefaults.DefaultPoolBucketSize); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNode.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNode.cs new file mode 100644 index 0000000..ce9086d --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNode.cs @@ -0,0 +1,18 @@ +namespace MemoryPools.Collections.Specialized +{ + internal sealed class PoolingNode : PoolingNodeBase + { + public override void Dispose() + { + base.Dispose(); + Pool>.Return(this); + } + + public override IPoolingNode Init(int capacity) + { + Next = null; + _buf = Pool.GetBuffer(capacity); + return this; + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNodeBase.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNodeBase.cs new file mode 100644 index 0000000..9be70b5 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNodeBase.cs @@ -0,0 +1,33 @@ +using System.Buffers; + +namespace MemoryPools.Collections.Specialized +{ + internal abstract class PoolingNodeBase : IPoolingNode + { + protected IMemoryOwner _buf; + + public int Length => _buf.Memory.Length; + + public virtual T this[int index] + { + get => _buf.Memory.Span[index]; + set => _buf.Memory.Span[index] = value; + } + + public virtual void Dispose() + { + _buf.Dispose(); + _buf = null; + Next = null; + } + + public IPoolingNode Next { get; set; } + + public abstract IPoolingNode Init(int capacity); + + public void Clear() + { + _buf.Memory.Span.Clear(); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNodeCanon.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNodeCanon.cs new file mode 100644 index 0000000..9d0d559 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingNodeCanon.cs @@ -0,0 +1,36 @@ +namespace MemoryPools.Collections.Specialized +{ + internal sealed class PoolingNodeCanon : PoolingNodeBase, IPoolingNode where T : class + { + IPoolingNode IPoolingNode.Next + { + get => (IPoolingNode) Next; + set => Next = (IPoolingNode) value; + } + + T IPoolingNode.this[int index] + { + get => (T)_buf.Memory.Span[index]; + set => _buf.Memory.Span[index] = value; + } + + IPoolingNode IPoolingNode.Init(int capacity) + { + this.Init(capacity); + return this; + } + + public override void Dispose() + { + base.Dispose(); + Pool>.Return(this); + } + + public override IPoolingNode Init(int capacity) + { + Next = null; + _buf = Pool.GetBuffer(capacity); + return this; + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueue.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueue.cs new file mode 100644 index 0000000..d081fe4 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueue.cs @@ -0,0 +1,128 @@ +using System; +using System.Runtime.CompilerServices; + +namespace MemoryPools.Collections.Specialized +{ + public abstract class PoolingQueue : IDisposable + { + private IPoolingNode _enqueueTo; + private IPoolingNode _dequeueFrom; + private int _enqueueIndex, _dequeueIndex; + + protected PoolingQueue() + { + Count = 0; + _enqueueIndex = 0; + _dequeueIndex = 0; + _enqueueTo = _dequeueFrom = null; + } + + public bool IsEmpty => Count == 0; + + public int Count { get; private set; } + + public void Dispose() + { + Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Enqueue(T obj) + { + if (Count == 0 && _enqueueTo == null) + _enqueueTo = _dequeueFrom = CreateNodeHolder(); + + _enqueueTo[_enqueueIndex] = obj; + _enqueueIndex++; + Count++; + + if (_enqueueIndex == PoolsDefaults.DefaultPoolBucketSize) + { + var enqueue = _enqueueTo; + _enqueueTo = CreateNodeHolder(); + enqueue.Next = _enqueueTo; + _enqueueIndex = 0; + } + } + + protected abstract IPoolingNode CreateNodeHolder(); + + /// + /// Tries to return queue element if any available via `val` parameter. + /// + /// + /// true if element found or false otherwise + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryDequeue(out T val) + { + if (IsEmpty) + { + val = default; + return false; + } + + val = Dequeue(); + return true; + } + + /// + /// Returns queue element + /// + /// + /// Returns element or throws IndexOutOfRangeException if no element found + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Dequeue() + { + if (IsEmpty) throw new IndexOutOfRangeException(); + + var obj = _dequeueFrom[_dequeueIndex]; + _dequeueFrom[_dequeueIndex] = default; + + _dequeueIndex++; + Count--; + + if (_dequeueIndex == PoolsDefaults.DefaultPoolBucketSize) + { + var dequeue = _dequeueFrom; + _dequeueFrom = _dequeueFrom.Next; + _dequeueIndex = 0; + dequeue.Dispose(); + } + + if (Count == 0) + { + // return back to pool + if (_enqueueTo != _dequeueFrom) + { + var empty = _dequeueFrom; + _dequeueFrom = _dequeueFrom.Next; + _dequeueIndex = 0; + empty.Dispose(); + } + else + // reset to pool start + { + _enqueueIndex = 0; + _dequeueIndex = 0; + } + } + + return obj; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + while (_enqueueTo != null) + { + var next = _enqueueTo.Next; + _enqueueTo.Dispose(); + _enqueueTo = next; + } + + _dequeueFrom = null; + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueueRef.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueueRef.cs new file mode 100644 index 0000000..21d0ecf --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueueRef.cs @@ -0,0 +1,10 @@ +namespace MemoryPools.Collections.Specialized +{ + public sealed class PoolingQueueRef : PoolingQueue where T : class + { + protected override IPoolingNode CreateNodeHolder() + { + return (IPoolingNode) Pool>.Get().Init(PoolsDefaults.DefaultPoolBucketSize); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueueVal.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueueVal.cs new file mode 100644 index 0000000..abada82 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingQueueVal.cs @@ -0,0 +1,17 @@ +namespace MemoryPools.Collections.Specialized +{ + /// + /// Poolinq queue stores items in buckets of 256 size, who linked with linked list. + /// Nodes of this list and storage (array[256]) + /// ** NOT THREAD SAFE ** + /// Enqueue, dequeue: O(1). + /// + /// Items should be classes because underlying collection stores object type + public sealed class PoolingQueueVal : PoolingQueue where T : struct + { + protected override IPoolingNode CreateNodeHolder() + { + return Pool>.Get().Init(PoolsDefaults.DefaultPoolBucketSize); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStack.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStack.cs new file mode 100644 index 0000000..90e3adc --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStack.cs @@ -0,0 +1,10 @@ +namespace MemoryPools.Collections.Specialized +{ + public class PoolingStack : PoolingStackBase + { + protected override IPoolingNode CreateNodeHolder() + { + return Pool>.Get().Init(PoolsDefaults.DefaultPoolBucketSize); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStackBase.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStackBase.cs new file mode 100644 index 0000000..486f94c --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStackBase.cs @@ -0,0 +1,108 @@ +using System; +using System.Runtime.CompilerServices; + +namespace MemoryPools.Collections.Specialized +{ + public abstract class PoolingStackBase : IDisposable + { + private IPoolingNode _top; + private int _topIndex; + + protected PoolingStackBase() + { + Count = 0; + _topIndex = 0; + _top = null; + } + + public bool IsEmpty => Count == 0; + + public int Count { get; private set; } + + public void Dispose() + { + Clear(); + } + + protected abstract IPoolingNode CreateNodeHolder(); + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push(T obj) + { + if (Count == 0 && _top == null) + _top = CreateNodeHolder(); + + _top[_topIndex] = obj; + _topIndex++; + Count++; + + if (_topIndex == PoolsDefaults.DefaultPoolBucketSize) + { + var top = _top; + _top = CreateNodeHolder(); + _top.Next = top; + _topIndex = 0; + } + } + + /// + /// Tries to return queue element if any available via `val` parameter. + /// + /// + /// true if element found or false otherwise + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPop(out T val) + { + if (IsEmpty) + { + val = default; + return false; + } + + val = Pop(); + return true; + } + + /// + /// Returns queue element + /// + /// + /// Returns element or throws IndexOutOfRangeException if no element found + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Pop() + { + if (IsEmpty) throw new IndexOutOfRangeException(); + + _topIndex--; + + if (_topIndex < 0) + { + _topIndex = PoolsDefaults.DefaultPoolBucketSize - 1; + var oldTop = _top; + _top = _top.Next; + oldTop.Dispose(); + } + + var obj = _top[_topIndex]; + _top[_topIndex] = default; + + Count--; + + return obj; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + while (_top != null) + { + var next = _top.Next; + _top.Dispose(); + _top = next; + } + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStackCanon.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStackCanon.cs new file mode 100644 index 0000000..751d518 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolingStackCanon.cs @@ -0,0 +1,13 @@ +namespace MemoryPools.Collections.Specialized +{ + /// + /// Collection, which is working on shared btw all Pooling* collections buckets + /// + public class PoolingStackCanon : PoolingStackBase where T : class + { + protected override IPoolingNode CreateNodeHolder() + { + return (IPoolingNode) Pool>.Get().Init(PoolsDefaults.DefaultPoolBucketSize); + } + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolsDefaults.cs b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolsDefaults.cs new file mode 100644 index 0000000..0920f37 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Collections/Specialized/PoolsDefaults.cs @@ -0,0 +1,9 @@ +namespace MemoryPools.Collections.Specialized +{ + public static class PoolsDefaults + { + public const int DefaultPoolBucketDegree = 7; + public const int DefaultPoolBucketSize = 1 << DefaultPoolBucketDegree; + public const int DefaultPoolBucketMask = DefaultPoolBucketSize - 1; + } +} \ No newline at end of file diff --git a/ZeroLevel/Services/MemoryPools/DefaultObjectPool.cs b/ZeroLevel/Services/MemoryPools/DefaultObjectPool.cs new file mode 100644 index 0000000..4c8f37f --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/DefaultObjectPool.cs @@ -0,0 +1,101 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +/*https://github.com/dotnet/aspnetcore/blob/main/src/ObjectPool*/ + +namespace MemoryPools +{ + /// + /// Default implementation of . + /// + /// The type to pool objects for. + /// This implementation keeps a cache of retained objects. This means that if objects are returned when the pool has already reached "maximumRetained" objects they will be available to be Garbage Collected. + public class DefaultObjectPool : ObjectPool where T : class + { + private protected readonly ObjectWrapper[] _items; + private protected readonly IPooledObjectPolicy _policy; + private protected readonly bool _isDefaultPolicy; + private protected T? _firstItem; + + // This class was introduced in 2.1 to avoid the interface call where possible + private protected readonly PooledObjectPolicy? _fastPolicy; + + /// + /// Creates an instance of . + /// + /// The pooling policy to use. + public DefaultObjectPool(IPooledObjectPolicy policy) + : this(policy, Environment.ProcessorCount * 2) + { + } + + /// + /// Creates an instance of . + /// + /// The pooling policy to use. + /// The maximum number of objects to retain in the pool. + public DefaultObjectPool(IPooledObjectPolicy policy, int maximumRetained) + { + _policy = policy ?? throw new ArgumentNullException(nameof(policy)); + _fastPolicy = policy as PooledObjectPolicy; + _isDefaultPolicy = IsDefaultPolicy(); + + // -1 due to _firstItem + _items = new ObjectWrapper[maximumRetained - 1]; + + bool IsDefaultPolicy() + { + var type = policy.GetType(); + + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(DefaultPooledObjectPolicy<>); + } + } + + /// + public override T Get() + { + var item = _firstItem; + if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) + { + var items = _items; + for (var i = 0; i < items.Length; i++) + { + item = items[i].Element; + if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item) + { + return item; + } + } + + item = Create(); + } + + return item; + } + + // Non-inline to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + private T Create() => _fastPolicy?.Create() ?? _policy.Create(); + + /// + public override void Return(T obj) + { + if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) + { + if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) + { + var items = _items; + for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i) + { + } + } + } + } + + private protected struct ObjectWrapper + { + public T? Element; + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/DefaultObjectPoolProvider.cs b/ZeroLevel/Services/MemoryPools/DefaultObjectPoolProvider.cs new file mode 100644 index 0000000..bfaf118 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/DefaultObjectPoolProvider.cs @@ -0,0 +1,34 @@ +using System; + +/*https://github.com/dotnet/aspnetcore/blob/main/src/ObjectPool*/ + +namespace MemoryPools +{ + /// + /// The default . + /// + public class DefaultObjectPoolProvider + : ObjectPoolProvider + { + /// + /// The maximum number of objects to retain in the pool. + /// + public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2; + + /// + public override ObjectPool Create(IPooledObjectPolicy policy) + { + if (policy == null) + { + throw new ArgumentNullException(nameof(policy)); + } + + if (typeof(IDisposable).IsAssignableFrom(typeof(T))) + { + return new DisposableObjectPool(policy, MaximumRetained); + } + + return new DefaultObjectPool(policy, MaximumRetained); + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/DefaultPooledObjectPolicy.cs b/ZeroLevel/Services/MemoryPools/DefaultPooledObjectPolicy.cs new file mode 100644 index 0000000..034b519 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/DefaultPooledObjectPolicy.cs @@ -0,0 +1,26 @@ +/*https://github.com/dotnet/aspnetcore/blob/main/src/ObjectPool*/ + +namespace MemoryPools +{ + /// + /// Default implementation for . + /// + /// The type of object which is being pooled. + public class DefaultPooledObjectPolicy + : PooledObjectPolicy where T : class, new() + { + /// + public override T Create() + { + return new T(); + } + + /// + public override bool Return(T obj) + { + // DefaultObjectPool doesn't call 'Return' for the default policy. + // So take care adding any logic to this method, as it might require changes elsewhere. + return true; + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/DisposableObjectPool.cs b/ZeroLevel/Services/MemoryPools/DisposableObjectPool.cs new file mode 100644 index 0000000..afd5567 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/DisposableObjectPool.cs @@ -0,0 +1,92 @@ +using System; +using System.Threading; + +/*https://github.com/dotnet/aspnetcore/blob/main/src/ObjectPool*/ + +namespace MemoryPools +{ + internal sealed class DisposableObjectPool + : DefaultObjectPool, IDisposable where T : class + { + private volatile bool _isDisposed; + + public DisposableObjectPool(IPooledObjectPolicy policy) + : base(policy) + { + } + + public DisposableObjectPool(IPooledObjectPolicy policy, int maximumRetained) + : base(policy, maximumRetained) + { + } + + public override T Get() + { + if (_isDisposed) + { + ThrowObjectDisposedException(); + } + + return base.Get(); + + void ThrowObjectDisposedException() + { + throw new ObjectDisposedException(GetType().Name); + } + } + + public override void Return(T obj) + { + // When the pool is disposed or the obj is not returned to the pool, dispose it + if (_isDisposed || !ReturnCore(obj)) + { + DisposeItem(obj); + } + } + + private bool ReturnCore(T obj) + { + bool returnedTooPool = false; + + if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) + { + if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) + { + returnedTooPool = true; + } + else + { + var items = _items; + for (var i = 0; i < items.Length && !(returnedTooPool = Interlocked.CompareExchange(ref items[i].Element, obj, null) == null); i++) + { + } + } + } + + return returnedTooPool; + } + + public void Dispose() + { + _isDisposed = true; + + DisposeItem(_firstItem); + _firstItem = null; + + ObjectWrapper[] items = _items; + for (var i = 0; i < items.Length; i++) + { + DisposeItem(items[i].Element); + items[i].Element = null; + } + } + + private static void DisposeItem(T? item) + { + if (item is IDisposable disposable) + { + disposable.Dispose(); + } + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/IPooledObjectPolicy.cs b/ZeroLevel/Services/MemoryPools/IPooledObjectPolicy.cs new file mode 100644 index 0000000..d171d35 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/IPooledObjectPolicy.cs @@ -0,0 +1,24 @@ +/*https://github.com/dotnet/aspnetcore/blob/main/src/ObjectPool*/ + +namespace MemoryPools +{ + /// + /// Represents a policy for managing pooled objects. + /// + /// The type of object which is being pooled. + public interface IPooledObjectPolicy where T : notnull + { + /// + /// Create a . + /// + /// The which was created. + T Create(); + + /// + /// Runs some processing when an object was returned to the pool. Can be used to reset the state of an object and indicate if the object should be returned to the pool. + /// + /// The object to return to the pool. + /// if the object should be returned to the pool. if it's not possible/desirable for the pool to keep the object. + bool Return(T obj); + } +} diff --git a/ZeroLevel/Services/MemoryPools/JetPool.cs b/ZeroLevel/Services/MemoryPools/JetPool.cs new file mode 100644 index 0000000..d49e5e0 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/JetPool.cs @@ -0,0 +1,18 @@ +using MemoryPools.Memory; +using System.Runtime.CompilerServices; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools +{ + public class JetPool where T : class, new() + { + private readonly JetStack _freeObjectsQueue = new JetStack(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Get() => _freeObjectsQueue.Count > 0 ? _freeObjectsQueue.Pop() : new T(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Return(T instance) => _freeObjectsQueue.Push(instance); + } +} diff --git a/ZeroLevel/Services/MemoryPools/Memory/CountdownMemoryOwner.cs b/ZeroLevel/Services/MemoryPools/Memory/CountdownMemoryOwner.cs new file mode 100644 index 0000000..5ba8d30 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Memory/CountdownMemoryOwner.cs @@ -0,0 +1,81 @@ +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools.Memory +{ + /// + /// Encapsulates manual memory management mechanism. Holds + /// IMemoryOwner instance goes to GC (link = null) only when + /// all owning entities called Dispose() method. This means, + /// that only this mechanism should be used for covering + /// managed instances. + /// + public sealed class CountdownMemoryOwner : IMemoryOwner + { + private int _length; + private int _offset; + private int _owners; + private T[] _arr; + private CountdownMemoryOwner _parent; + private Memory _memory; + + public Memory Memory + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _memory; + + private set => _memory = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal CountdownMemoryOwner Init(CountdownMemoryOwner parent, int offset, int length, bool defaultOwner = true) + { + _owners = defaultOwner ? 1 : 0; + _offset = offset; + _length = length; + _parent = parent; + _parent.AddOwner(); + Memory = _parent.Memory.Slice(_offset, _length); + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal CountdownMemoryOwner Init(T[] array, int length) + { + _owners = 1; + _offset = 0; + _length = length; + _parent = default; + _arr = array; + Memory = _arr.AsMemory(0, _length); + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddOwner() => _owners++; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + _owners--; + if (_owners > 0) + { + return; + } + + if (_parent != default) + { + _parent.Dispose(); + _parent = null; + } + else + { + ArrayPool.Shared.Return(_arr); + } + Pool>.Return(this); + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Memory/InternalArraysPool.cs b/ZeroLevel/Services/MemoryPools/Memory/InternalArraysPool.cs new file mode 100644 index 0000000..e713574 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Memory/InternalArraysPool.cs @@ -0,0 +1,36 @@ +using MemoryPools.Memory.Pooling; +using System; +using System.Runtime.CompilerServices; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools.Memory +{ + internal sealed class InternalArraysPool + { + private const int MinBufferSize = 128; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CountdownMemoryOwner Rent(int length) + { + return Rent(length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CountdownMemoryOwner Rent(int length, bool noDefaultOwner = false) + { + var realLength = length; + var allocLength = length > MinBufferSize ? length : MinBufferSize; + var owner = BucketsBasedCrossThreadsMemoryPool.Shared.Rent(allocLength); + return owner.AsCountdown(0, realLength, noDefaultOwner); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CountdownMemoryOwner RentFrom(ReadOnlySpan source, bool noDefaultOwner = false) + { + var mem = Rent(source.Length, noDefaultOwner); + source.CopyTo(mem.Memory.Span); + return mem; + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Memory/JetStack.cs b/ZeroLevel/Services/MemoryPools/Memory/JetStack.cs new file mode 100644 index 0000000..560e59b --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Memory/JetStack.cs @@ -0,0 +1,90 @@ +using System; +using System.Runtime.CompilerServices; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools.Memory +{ + internal class JetStack + { + private ObjectWrapper[] _array; + + private int _size; + private T _firstItem; + + private const int DefaultCapacity = 4; + + public JetStack() + { + _array = new ObjectWrapper[DefaultCapacity]; + } + + // Create a stack with a specific initial capacity. The initial capacity + // must be a non-negative number. + public JetStack(int capacity) + { + _array = new ObjectWrapper[capacity]; + } + + public int Count => _size; + + // Removes all Objects from the Stack. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + Array.Clear(_array, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references. + _size = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Peek() => _array[_size - 1].Element; + + // Pops an item from the top of the stack. If the stack is empty, Pop + // throws an InvalidOperationException. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Pop() + { + var item = _firstItem; + if (_firstItem != null) + { + _firstItem = default; + return item; + } + return _array[--_size].Element; + } + + // Pushes an item to the top of the stack. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push(T item) + { + if (_firstItem == null) + { + _firstItem = item; + return; + } + if (_size >= _array.Length) + { + PushWithResize(item); + } + else + { + _array[_size++].Element = item; + } + } + + // Non-inline from Stack.Push to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + private void PushWithResize(T item) + { + Array.Resize(ref _array, _array.Length << 1); + _array[_size].Element = item; + _size++; + } + + // PERF: the struct wrapper avoids array-covariance-checks from the runtime when assigning to elements of the array. + private struct ObjectWrapper + { + public T Element; + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Memory/MemoryEx.cs b/ZeroLevel/Services/MemoryPools/Memory/MemoryEx.cs new file mode 100644 index 0000000..374559d --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Memory/MemoryEx.cs @@ -0,0 +1,35 @@ +using System.Buffers; +using System.Runtime.CompilerServices; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools.Memory +{ + public static class MemoryEx + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Length(this IMemoryOwner that) => + that.Memory.Length; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CountdownMemoryOwner AsCountdown(this CountdownMemoryOwner that, bool noDefaultOwner = false) => + Pool>.Get().Init(that, 0, that.Memory.Length, noDefaultOwner); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CountdownMemoryOwner AsCountdown(this CountdownMemoryOwner that, int offset, + bool noDefaultOwner = false) => + Pool>.Get().Init(that, offset, that.Memory.Length - offset, noDefaultOwner); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CountdownMemoryOwner AsCountdown(this CountdownMemoryOwner that, int offset, int length, bool noDefaultOwner = false) => + Pool>.Get().Init(that, offset, length, noDefaultOwner); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IMemoryOwner Slice(this CountdownMemoryOwner that, int offset) => + Slice(that, offset, that.Memory.Length - offset); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IMemoryOwner Slice(this CountdownMemoryOwner that, int offset, int length) => + that.AsCountdown(offset, length); + } +} diff --git a/ZeroLevel/Services/MemoryPools/Memory/MemoryOwner.cs b/ZeroLevel/Services/MemoryPools/Memory/MemoryOwner.cs new file mode 100644 index 0000000..e7c9884 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Memory/MemoryOwner.cs @@ -0,0 +1,25 @@ +using System; +using System.Buffers; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools.Memory +{ + public class MemoryOwner + : IMemoryOwner + { + public static MemoryOwner Empty = new MemoryOwner(Memory.Empty); + + protected MemoryOwner(Memory memory) + { + Memory = memory; + } + + public void Dispose() + { + Memory = Memory.Empty; + } + + public Memory Memory { get; set; } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Memory/Pooling/BucketsBasedCrossThreadsArrayPool.cs b/ZeroLevel/Services/MemoryPools/Memory/Pooling/BucketsBasedCrossThreadsArrayPool.cs new file mode 100644 index 0000000..3503d9e --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Memory/Pooling/BucketsBasedCrossThreadsArrayPool.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools.Memory.Pooling +{ + public sealed class BucketsBasedCrossThreadsArrayPool + { + [ThreadStatic] + private static BucketsBasedCrossThreadsArrayPool _shared; + private static readonly Queue[] _pool = new Queue[24]; + + static BucketsBasedCrossThreadsArrayPool() + { + for (var i = 0; i < _pool.Length; i++) _pool[i] = new Queue(); + } + + public static BucketsBasedCrossThreadsArrayPool Shared => _shared ??= new BucketsBasedCrossThreadsArrayPool(); + + public T[] Rent(int minimumLength) + { + var queueIndex = Utilities.GetBucket(minimumLength); + var queue = _pool[queueIndex]; + T[] arr; + + if (queue.Count == 0) + { + var length = Utilities.GetMaxSizeForBucket(queueIndex); + arr = new T[length]; + return arr; + } + + arr = queue.Dequeue(); + return arr; + } + + public void Return(T[] array) + { + _pool[Utilities.GetBucket(array.Length)].Enqueue(array); + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Memory/Pooling/BucketsBasedCrossThreadsMemoryPool.cs b/ZeroLevel/Services/MemoryPools/Memory/Pooling/BucketsBasedCrossThreadsMemoryPool.cs new file mode 100644 index 0000000..4b2b437 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Memory/Pooling/BucketsBasedCrossThreadsMemoryPool.cs @@ -0,0 +1,51 @@ +using System; +using System.Runtime.CompilerServices; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools.Memory.Pooling +{ + /// + /// This pool returns NEW instance if pool is empty and old if non-empty. + /// When got, user can return instance back to pool. If not returned, GC will collect it + /// This means, that if you want to detect 'leaks', you should: + /// 1) add [CallerFilePath], [CallerLineNumber] to your Init() method parameters + /// 2) make finalizer in your type and log saved fileName and lineNumber from (1). + /// + /// + /// MyType Init(int arg0, string arg1 + /// #if DEBUG + /// , [CallerFilePath] string fileName = default, [CallerLineNumber] int lineNumber = default + /// #endif + /// ) + /// { + /// #if DEBUG + /// _fileName = fileName; + /// _lineNumber = lineNumber; + /// #endif + /// } + /// #if DEBUG + /// ~MyType() + /// { + /// Console.WriteLine($" - {_fileName}:{_lineNumber}"); + /// } + /// #endif + /// + public sealed class BucketsBasedCrossThreadsMemoryPool + { + private BucketsBasedCrossThreadsArrayPool _pool; + + [ThreadStatic] + private static BucketsBasedCrossThreadsMemoryPool _mempool; + + internal BucketsBasedCrossThreadsArrayPool _arraysPool => _pool ??= new BucketsBasedCrossThreadsArrayPool(); + + public static BucketsBasedCrossThreadsMemoryPool Shared => _mempool ??= new BucketsBasedCrossThreadsMemoryPool(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CountdownMemoryOwner Rent(int minBufferSize = -1) + { + return Pool>.Get().Init(_arraysPool.Rent(minBufferSize), minBufferSize); + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/Memory/Pooling/Utilities.cs b/ZeroLevel/Services/MemoryPools/Memory/Pooling/Utilities.cs new file mode 100644 index 0000000..13d28ee --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Memory/Pooling/Utilities.cs @@ -0,0 +1,26 @@ +using System.Runtime.CompilerServices; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools.Memory.Pooling +{ + internal static class Utilities + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int GetMaxSizeForBucket(int binIndex) => 16 << binIndex; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int GetBucket(int size) + { + if (size == 128 /*default chunk size*/) return 7; + size--; + var length = 0; + while (size >= 16) + { + length++; + size = size >> 1; + } + return length; + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/ObjectPool.cs b/ZeroLevel/Services/MemoryPools/ObjectPool.cs new file mode 100644 index 0000000..9d60a97 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/ObjectPool.cs @@ -0,0 +1,32 @@ +/*https://github.com/dotnet/aspnetcore/blob/main/src/ObjectPool*/ + +namespace MemoryPools +{ + public abstract class ObjectPool where T : class + { + /// + /// Gets an object from the pool if one is available, otherwise creates one. + /// + /// A . + public abstract T Get(); + + /// + /// Return an object to the pool. + /// + /// The object to add to the pool. + public abstract void Return(T obj); + } + + /// + /// Methods for creating instances. + /// + public static class ObjectPool + { + /// + public static ObjectPool Create(IPooledObjectPolicy? policy = null) where T : class, new() + { + var provider = new DefaultObjectPoolProvider(); + return provider.Create(policy ?? new DefaultPooledObjectPolicy()); + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/ObjectPoolProvider.cs b/ZeroLevel/Services/MemoryPools/ObjectPoolProvider.cs new file mode 100644 index 0000000..b541f38 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/ObjectPoolProvider.cs @@ -0,0 +1,25 @@ +/*https://github.com/dotnet/aspnetcore/blob/main/src/ObjectPool*/ + +namespace MemoryPools +{ + /// + /// A provider of instances. + /// + public abstract class ObjectPoolProvider + { + /// + /// Creates an . + /// + /// The type to create a pool for. + public ObjectPool Create() where T : class, new() + { + return Create(new DefaultPooledObjectPolicy()); + } + + /// + /// Creates an with the given . + /// + /// The type to create a pool for. + public abstract ObjectPool Create(IPooledObjectPolicy policy) where T : class; + } +} diff --git a/ZeroLevel/Services/MemoryPools/Pool.cs b/ZeroLevel/Services/MemoryPools/Pool.cs new file mode 100644 index 0000000..c73d391 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/Pool.cs @@ -0,0 +1,52 @@ +using MemoryPools.Memory; +using System; +using System.Runtime.CompilerServices; + +/*https://github.com/sidristij/memory-pools*/ + +namespace MemoryPools +{ + public static class Pool where T : class, new() + { + private static readonly DefaultObjectPool _freeObjectsQueue = new DefaultObjectPool(new DefaultPooledObjectPolicy()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Get() + { + return _freeObjectsQueue.Get(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Return(T1 instance) where T1 : T + { + _freeObjectsQueue.Return(instance); + } + } + + public static class Pool + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Get() where T : class, new() + { + return Pool.Get(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Return(T instance) where T : class, new() + { + Pool.Return(instance); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CountdownMemoryOwner GetBuffer(int size) + { + return InternalArraysPool.Rent(size, false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static CountdownMemoryOwner GetBufferFrom(ReadOnlySpan source) + { + return InternalArraysPool.RentFrom(source, false); + } + } +} diff --git a/ZeroLevel/Services/MemoryPools/PooledObjectPolicy.cs b/ZeroLevel/Services/MemoryPools/PooledObjectPolicy.cs new file mode 100644 index 0000000..60af7b3 --- /dev/null +++ b/ZeroLevel/Services/MemoryPools/PooledObjectPolicy.cs @@ -0,0 +1,17 @@ +/*https://github.com/dotnet/aspnetcore/blob/main/src/ObjectPool*/ + +namespace MemoryPools +{ + /// + /// A base type for . + /// + /// The type of object which is being pooled. + public abstract class PooledObjectPolicy : IPooledObjectPolicy where T : notnull + { + /// + public abstract T Create(); + + /// + public abstract bool Return(T obj); + } +} diff --git a/ZeroLevel/Services/Network/Extensions/ExchangeExtension.cs b/ZeroLevel/Services/Network/Extensions/ExchangeExtension.cs index 9cf1f74..f64aec5 100644 --- a/ZeroLevel/Services/Network/Extensions/ExchangeExtension.cs +++ b/ZeroLevel/Services/Network/Extensions/ExchangeExtension.cs @@ -1,4 +1,5 @@ -using System; +using MemoryPools; +using System; using System.Threading; using ZeroLevel.Services.Pools; @@ -6,23 +7,29 @@ namespace ZeroLevel.Network { public static class ExchangeExtension { - static Pool _mrePool = new Pool(16, (p) => new AutoResetEvent(false)); + private static DefaultObjectPool _mrePool; + + static ExchangeExtension() + { + _mrePool = new DefaultObjectPool(new DefaultPooledObjectPolicy()); + } public static Tresponse Request(this IClientSet exchange, string alias, TimeSpan timeout) { Tresponse response = default; - var ev = _mrePool.Acquire(); + var ev = _mrePool.Get(); + ev.Reset(); try { if (exchange.Request(alias, _response => { response = _response; ev.Set(); })) { - ev.WaitOne(timeout); + ev.Wait(timeout); } } finally { - _mrePool.Release(ev); + _mrePool.Return(ev); } return response; } @@ -30,7 +37,8 @@ namespace ZeroLevel.Network public static Tresponse Request(this IClientSet exchange, string alias, string inbox, TimeSpan timeout) { Tresponse response = default; - var ev = _mrePool.Acquire(); + var ev = _mrePool.Get(); + ev.Reset(); try { if (exchange.Request(alias, inbox, @@ -39,12 +47,12 @@ namespace ZeroLevel.Network ev.Set(); })) { - ev.WaitOne(timeout); + ev.Wait(timeout); } } finally { - _mrePool.Release(ev); + _mrePool.Return(ev); } return response; } @@ -52,18 +60,19 @@ namespace ZeroLevel.Network public static Tresponse Request(this IClientSet exchange, string alias, Trequest request, TimeSpan timeout) { Tresponse response = default; - var ev = _mrePool.Acquire(); + var ev = _mrePool.Get(); + ev.Reset(); try { if (exchange.Request(alias, request, _response => { response = _response; ev.Set(); })) { - ev.WaitOne(timeout); + ev.Wait(timeout); } } finally { - _mrePool.Release(ev); + _mrePool.Return(ev); } return response; } @@ -72,18 +81,19 @@ namespace ZeroLevel.Network , Trequest request, TimeSpan timeout) { Tresponse response = default; - var ev = _mrePool.Acquire(); + var ev = _mrePool.Get(); + ev.Reset(); try { if (exchange.Request(alias, inbox, request, _response => { response = _response; ev.Set(); })) { - ev.WaitOne(timeout); + ev.Wait(timeout); } } finally { - _mrePool.Release(ev); + _mrePool.Return(ev); } return response; } @@ -91,18 +101,19 @@ namespace ZeroLevel.Network public static Tresponse Request(this IClientSet exchange, string alias) { Tresponse response = default; - var ev = _mrePool.Acquire(); + var ev = _mrePool.Get(); + ev.Reset(); try { if (exchange.Request(alias, _response => { response = _response; ev.Set(); })) { - ev.WaitOne(Network.BaseSocket.MAX_REQUEST_TIME_MS); + ev.Wait(Network.BaseSocket.MAX_REQUEST_TIME_MS); } } finally { - _mrePool.Release(ev); + _mrePool.Return(ev); } return response; } @@ -110,7 +121,8 @@ namespace ZeroLevel.Network public static Tresponse Request(this IClientSet exchange, string alias, string inbox) { Tresponse response = default; - var ev = _mrePool.Acquire(); + var ev = _mrePool.Get(); + ev.Reset(); try { if (exchange.Request(alias, inbox, @@ -119,12 +131,12 @@ namespace ZeroLevel.Network ev.Set(); })) { - ev.WaitOne(Network.BaseSocket.MAX_REQUEST_TIME_MS); + ev.Wait(Network.BaseSocket.MAX_REQUEST_TIME_MS); } } finally { - _mrePool.Release(ev); + _mrePool.Return(ev); } return response; } @@ -132,18 +144,19 @@ namespace ZeroLevel.Network public static Tresponse Request(this IClientSet exchange, string alias, Trequest request) { Tresponse response = default; - var ev = _mrePool.Acquire(); + var ev = _mrePool.Get(); + ev.Reset(); try { if (exchange.Request(alias, request, _response => { response = _response; ev.Set(); })) { - ev.WaitOne(Network.BaseSocket.MAX_REQUEST_TIME_MS); + ev.Wait(Network.BaseSocket.MAX_REQUEST_TIME_MS); } } finally { - _mrePool.Release(ev); + _mrePool.Return(ev); } return response; } @@ -152,18 +165,19 @@ namespace ZeroLevel.Network , Trequest request) { Tresponse response = default; - var ev = _mrePool.Acquire(); + var ev = _mrePool.Get(); + ev.Reset(); try { if (exchange.Request(alias, inbox, request, _response => { response = _response; ev.Set(); })) { - ev.WaitOne(Network.BaseSocket.MAX_REQUEST_TIME_MS); + ev.Wait(Network.BaseSocket.MAX_REQUEST_TIME_MS); } } finally { - _mrePool.Release(ev); + _mrePool.Return(ev); } return response; } diff --git a/ZeroLevel/Services/Network/FileTransfer/FileSender.cs b/ZeroLevel/Services/Network/FileTransfer/FileSender.cs index 51e2d64..48890c1 100644 --- a/ZeroLevel/Services/Network/FileTransfer/FileSender.cs +++ b/ZeroLevel/Services/Network/FileTransfer/FileSender.cs @@ -1,16 +1,16 @@ -using System; +using MemoryPools; +using System; using System.Collections.Concurrent; using System.IO; using System.Threading; using ZeroLevel.Models; -using ZeroLevel.Services.Pools; namespace ZeroLevel.Network.FileTransfer { public sealed class FileSender { private BlockingCollection _tasks = new BlockingCollection(); - private Pool _taskPool = new Pool(100, (p) => new FileTransferTask()); + private DefaultObjectPool _taskPool = new DefaultObjectPool(new DefaultPooledObjectPolicy()); private readonly Thread _uploadFileThread; private bool _resendWhenServerError = false; private bool _resendWhenClientError = false; @@ -45,7 +45,7 @@ namespace ZeroLevel.Network.FileTransfer { throw new FileNotFoundException(filePath); } - var task = _taskPool.Acquire(); + var task = _taskPool.Get(); task.CompletedHandler = completeHandler; task.ErrorHandler = errorHandler; task.FilePath = filePath; @@ -69,7 +69,7 @@ namespace ZeroLevel.Network.FileTransfer } finally { - _taskPool.Release(task); + _taskPool.Return(task); } } } diff --git a/ZeroLevel/Services/Network/Model/RequestInfo.cs b/ZeroLevel/Services/Network/Model/RequestInfo.cs index e8fbf1d..c1b2f2d 100644 --- a/ZeroLevel/Services/Network/Model/RequestInfo.cs +++ b/ZeroLevel/Services/Network/Model/RequestInfo.cs @@ -11,6 +11,8 @@ namespace ZeroLevel.Network private bool _sended; public bool Sended { get { return _sended; } } + public RequestInfo() { } + public void Reset(Action handler, Action failHandler) { _sended = false; diff --git a/ZeroLevel/Services/Network/Utils/RequestBuffer.cs b/ZeroLevel/Services/Network/Utils/RequestBuffer.cs index 49bc773..989498a 100644 --- a/ZeroLevel/Services/Network/Utils/RequestBuffer.cs +++ b/ZeroLevel/Services/Network/Utils/RequestBuffer.cs @@ -1,18 +1,18 @@ -using System; +using MemoryPools; +using System; using System.Collections.Concurrent; using System.Collections.Generic; -using ZeroLevel.Services.Pools; namespace ZeroLevel.Network { internal sealed class RequestBuffer { private ConcurrentDictionary _requests = new ConcurrentDictionary(); - private static Pool _ri_pool = new Pool(128, (p) => new RequestInfo()); + private static DefaultObjectPool _ri_pool = new DefaultObjectPool(new DefaultPooledObjectPolicy()); public void RegisterForFrame(int identity, Action callback, Action fail = null) { - var ri = _ri_pool.Acquire(); + var ri = _ri_pool.Get(); ri.Reset(callback, fail); _requests[identity] = ri; } @@ -23,7 +23,7 @@ namespace ZeroLevel.Network if (_requests.TryRemove(frameId, out ri)) { ri.Fail(message); - _ri_pool.Release(ri); + _ri_pool.Return(ri); } } @@ -33,7 +33,7 @@ namespace ZeroLevel.Network if (_requests.TryRemove(frameId, out ri)) { ri.Success(data); - _ri_pool.Release(ri); + _ri_pool.Return(ri); } } @@ -53,7 +53,7 @@ namespace ZeroLevel.Network { if (_requests.TryRemove(frameIds[i], out ri)) { - _ri_pool.Release(ri); + _ri_pool.Return(ri); } } } diff --git a/ZeroLevel/Services/Pools/ObjectPool.cs b/ZeroLevel/Services/Pools/ObjectPool.cs index e26f698..33dbf86 100644 --- a/ZeroLevel/Services/Pools/ObjectPool.cs +++ b/ZeroLevel/Services/Pools/ObjectPool.cs @@ -5,6 +5,7 @@ using System.Threading; namespace ZeroLevel.Services.Pools { + /* public enum LoadingMode { Eager, Lazy, LazyExpanding }; public enum AccessMode { FIFO, LIFO, Circular }; @@ -284,4 +285,5 @@ namespace ZeroLevel.Services.Pools get { return isDisposed; } } } + */ } \ No newline at end of file diff --git a/ZeroLevel/Services/Semantic/WordTokenizer.cs b/ZeroLevel/Services/Semantic/WordTokenizer.cs index 0e15413..9b6c0b4 100644 --- a/ZeroLevel/Services/Semantic/WordTokenizer.cs +++ b/ZeroLevel/Services/Semantic/WordTokenizer.cs @@ -1,18 +1,19 @@ using System; +using System.Buffers; using System.Collections.Generic; -using ZeroLevel.Services.Pools; namespace ZeroLevel.Services.Semantic { public static class WordTokenizer { - static Pool _pool = new Pool(64 ,(p) => new char[2048]); + const int ARRAY_SIZE = 2048; + static ArrayPool _pool = ArrayPool.Create(); public static IEnumerable Tokenize(string text) { int index = 0; bool first = true; - var buffer = _pool.Acquire(); + var buffer = _pool.Rent(ARRAY_SIZE); try { for (int i = 0; i < text?.Length; i++) @@ -40,7 +41,7 @@ namespace ZeroLevel.Services.Semantic } finally { - _pool.Release(buffer); + _pool.Return(buffer); } } }