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