diff --git a/CHANGELOG.md b/CHANGELOG.md index 54b23a339..d52360588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Release Notes ==== +# 05-21-2024 +DotNext.Metaprogramming 5.4.0 +* Smallish performance improvements of `IndexPool` instance methods +* Added ability to instantiate empty `IndexPool` + # 05-15-2024 DotNext.Metaprogramming 5.3.1 * Fixed [234](https://github.com/dotnet/dotNext/issues/234) diff --git a/README.md b/README.md index cf84afddd..e20e8240f 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,11 @@ All these things are implemented in 100% managed code on top of existing .NET AP * [NuGet Packages](https://www.nuget.org/profiles/rvsakno) # What's new -Release Date: 05-15-2024 +Release Date: 05-21-2024 -DotNext.Metaprogramming 5.3.1 -* Fixed [234](https://github.com/dotnet/dotNext/issues/234) -* Updated dependencies +DotNext.Metaprogramming 5.4.0 +* Smallish performance improvements of `IndexPool` instance methods +* Added ability to instantiate empty `IndexPool` Changelog for previous versions located [here](./CHANGELOG.md). diff --git a/src/DotNext.Tests/Collections/Concurrent/IndexPoolTests.cs b/src/DotNext.Tests/Collections/Concurrent/IndexPoolTests.cs index ddad312dc..d7eb27429 100644 --- a/src/DotNext.Tests/Collections/Concurrent/IndexPoolTests.cs +++ b/src/DotNext.Tests/Collections/Concurrent/IndexPoolTests.cs @@ -12,6 +12,20 @@ public static void EmptyPool() Empty(pool); } + [Fact] + public static void EmptyPool2() + { + var pool = new IndexPool() { IsEmpty = true }; + False(pool.TryPeek(out _)); + False(pool.TryTake(out _)); + DoesNotContain(10, pool); + Empty(pool); + + pool.Reset(); + NotEmpty(pool); + Contains(10, pool); + } + [Fact] public static void TakeAll() { diff --git a/src/DotNext.Threading/Collections/Concurrent/IndexPool.cs b/src/DotNext.Threading/Collections/Concurrent/IndexPool.cs index ae36d1d07..9e9471e56 100644 --- a/src/DotNext.Threading/Collections/Concurrent/IndexPool.cs +++ b/src/DotNext.Threading/Collections/Concurrent/IndexPool.cs @@ -15,7 +15,7 @@ namespace DotNext.Collections.Concurrent; /// [EditorBrowsable(EditorBrowsableState.Advanced)] [StructLayout(LayoutKind.Auto)] -public struct IndexPool : ISupplier, IConsumer, IReadOnlyCollection +public struct IndexPool : ISupplier, IConsumer, IReadOnlyCollection, IResettable { private readonly int maxValue; private ulong bitmask; @@ -39,13 +39,22 @@ public IndexPool() /// public IndexPool(int maxValue) { - if (maxValue < 0 || maxValue > MaxValue) + if ((uint)maxValue > (uint)MaxValue) throw new ArgumentOutOfRangeException(nameof(maxValue)); bitmask = ulong.MaxValue; this.maxValue = maxValue; } + /// + /// Gets or sets a value indicating that the pool is empty. + /// + public bool IsEmpty + { + readonly get => Count is 0; + init => bitmask = value ? 0UL : ulong.MaxValue; + } + /// /// Gets the maximum number that can be returned by the pool. /// @@ -109,28 +118,28 @@ public int Take() } /// - /// Takes all available indicies, atomically. + /// Takes all available indices, atomically. /// - /// - /// The buffer to be modified with the indicies taken from the pool. + /// + /// The buffer to be modified with the indices taken from the pool. /// The size of the buffer should not be less than . /// - /// The number of indicies written to the buffer. - /// is too small to place indicies. + /// The number of indices written to the buffer. + /// is too small to place indices. /// - public int Take(Span indicies) + public int Take(Span indices) { - if (indicies.Length < Capacity) - throw new ArgumentOutOfRangeException(nameof(indicies)); + if (indices.Length < Capacity) + throw new ArgumentOutOfRangeException(nameof(indices)); var oldValue = Interlocked.Exchange(ref bitmask, 0UL); var bufferOffset = 0; - for (int bitPosition = 0; bitPosition < Capacity; bitPosition++) + for (var bitPosition = 0; bitPosition < Capacity; bitPosition++) { if (Contains(oldValue, bitPosition)) { - indicies[bufferOffset++] = bitPosition; + indices[bufferOffset++] = bitPosition; } } @@ -148,7 +157,7 @@ public int Take(Span indicies) /// value specified for this pool. public void Return(int value) { - if (value < 0 || value > maxValue) + if ((uint)value > (uint)maxValue) ThrowArgumentOutOfRangeException(); Interlocked.Or(ref bitmask, 1UL << value); @@ -160,14 +169,14 @@ static void ThrowArgumentOutOfRangeException() } /// - /// Returns multiple indicies, atomically. + /// Returns multiple indices, atomically. /// - /// The buffer of indicies to return back to the pool. - public void Return(ReadOnlySpan indicies) + /// The buffer of indices to return back to the pool. + public void Return(ReadOnlySpan indices) { var newValue = 0UL; - foreach (var index in indicies) + foreach (var index in indices) { newValue |= 1UL << index; } @@ -175,6 +184,11 @@ public void Return(ReadOnlySpan indicies) Interlocked.Or(ref bitmask, newValue); } + /// + /// Returns all values to the pool. + /// + public void Reset() => Volatile.Write(ref bitmask, ulong.MaxValue); + /// void IConsumer.Invoke(int value) => Return(value); @@ -184,20 +198,20 @@ public void Return(ReadOnlySpan indicies) /// The value to check. /// if is available for rent; otherwise, . public readonly bool Contains(int value) - => value >= 0 && value <= maxValue && Contains(Volatile.Read(in bitmask), value); + => (uint)value <= (uint)maxValue && Contains(Volatile.Read(in bitmask), value); private static bool Contains(ulong bitmask, int index) => (bitmask & (1UL << index)) is not 0UL; /// - /// Gets the number of available indicies. + /// Gets the number of available indices. /// public readonly int Count => Math.Min(BitOperations.PopCount(bitmask), maxValue + 1); /// - /// Gets an enumerator over available indicies in the pool. + /// Gets an enumerator over available indices in the pool. /// - /// The enumerator over available indicies in this pool. + /// The enumerator over available indices in this pool. public readonly Enumerator GetEnumerator() => new(Volatile.Read(in bitmask), maxValue); /// @@ -207,7 +221,7 @@ private static bool Contains(ulong bitmask, int index) readonly IEnumerator IEnumerable.GetEnumerator() => GetEnumerator().AsClassicEnumerator(); /// - /// Represents an enumerator over available indicies in the pool. + /// Represents an enumerator over available indices in the pool. /// [StructLayout(LayoutKind.Auto)] public struct Enumerator diff --git a/src/DotNext.Threading/DotNext.Threading.csproj b/src/DotNext.Threading/DotNext.Threading.csproj index b13b94472..03dad4d50 100644 --- a/src/DotNext.Threading/DotNext.Threading.csproj +++ b/src/DotNext.Threading/DotNext.Threading.csproj @@ -7,7 +7,7 @@ true true nullablePublicOnly - 5.3.1 + 5.4.0 .NET Foundation and Contributors .NEXT Family of Libraries