Add iterators to UnsafeBitSet and SpanBitSet
Introduced `Iterator` structs in `UnsafeBitSet` and `SpanBitSet` to enable efficient traversal of set bits. Added `GetIterator` methods to both structs to return iterator instances. Implemented `NextSetBit` in `SpanBitSet` to support iterator functionality. Changed constants in `UnsafeBitSet` from `private` to `internal` for broader assembly access. Removed redundant methods from `SpanBitSet` to streamline the API in favor of iterator-based operations. Updated constructors in `UnsafeSlotMap` and `UnsafeSparseSet` to conditionally clear arrays based on `AllocationOption.Clear`. Incremented assembly version to 1.2.7 to reflect these updates.
This commit is contained in:
@@ -10,10 +10,29 @@ namespace Misaki.HighPerformance.LowLevel.Collections;
|
|||||||
|
|
||||||
public unsafe struct UnsafeBitSet : IDisposable
|
public unsafe struct UnsafeBitSet : IDisposable
|
||||||
{
|
{
|
||||||
private const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31
|
public ref struct Iterator
|
||||||
private const int _INDEX_SIZE = 5; // log_2(BitSize + 1)
|
{
|
||||||
private const int _MASK = (1 << 5) - 1; // 0x1F, the mask to get the bit index inside a uint
|
private UnsafeBitSet _bitSet;
|
||||||
private static readonly int s_padding = Vector<uint>.Count; // The padding used for vectorization, the amount of uints required for being vectorized basically
|
private int _currentBit;
|
||||||
|
|
||||||
|
public Iterator (UnsafeBitSet bitSet)
|
||||||
|
{
|
||||||
|
_bitSet = bitSet;
|
||||||
|
_currentBit = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Next(out int bitIndex)
|
||||||
|
{
|
||||||
|
_currentBit = _bitSet.NextSetBit(_currentBit + 1);
|
||||||
|
bitIndex = _currentBit;
|
||||||
|
return _currentBit != -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31
|
||||||
|
internal const int _INDEX_SIZE = 5; // log_2(BitSize + 1)
|
||||||
|
internal const int _MASK = (1 << 5) - 1; // 0x1F, the mask to get the bit index inside a uint
|
||||||
|
internal static readonly int s_padding = Vector<uint>.Count; // The padding used for vectorization, the amount of uints required for being vectorized basically
|
||||||
|
|
||||||
private UnsafeArray<uint> _bits;
|
private UnsafeArray<uint> _bits;
|
||||||
private int _highestBit;
|
private int _highestBit;
|
||||||
@@ -97,6 +116,11 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
return (id >> _INDEX_SIZE) + int.Sign(id & _BIT_SIZE);
|
return (id >> _INDEX_SIZE) + int.Sign(id & _BIT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly Iterator GetIterator()
|
||||||
|
{
|
||||||
|
return new Iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether a bit is set at the index.
|
/// Checks whether a bit is set at the index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -717,6 +741,25 @@ public unsafe struct UnsafeBitSet : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly ref struct SpanBitSet
|
public readonly ref struct SpanBitSet
|
||||||
{
|
{
|
||||||
|
public ref struct Iterator
|
||||||
|
{
|
||||||
|
private SpanBitSet _bitSet;
|
||||||
|
private int _currentBit;
|
||||||
|
|
||||||
|
public Iterator(SpanBitSet bitSet)
|
||||||
|
{
|
||||||
|
_bitSet = bitSet;
|
||||||
|
_currentBit = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Next(out int bitIndex)
|
||||||
|
{
|
||||||
|
_currentBit = _bitSet.NextSetBit(_currentBit + 1);
|
||||||
|
bitIndex = _currentBit;
|
||||||
|
return _currentBit != -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31
|
private const int _BIT_SIZE = sizeof(uint) * 8 - 1; // 31
|
||||||
// NOTE: Is a byte not 8 bits?
|
// NOTE: Is a byte not 8 bits?
|
||||||
private const int _BYTE_SIZE = 5; // log_2(BitSize + 1)
|
private const int _BYTE_SIZE = 5; // log_2(BitSize + 1)
|
||||||
@@ -734,12 +777,16 @@ public readonly ref struct SpanBitSet
|
|||||||
_bits = bits;
|
_bits = bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly Iterator GetIterator()
|
||||||
|
{
|
||||||
|
return new Iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether a bit is set at the index.
|
/// Checks whether a bit is set at the index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">The index.</param>
|
/// <param name="index">The index.</param>
|
||||||
/// <returns>True if it is, otherwise false</returns>
|
/// <returns>True if it is, otherwise false</returns>
|
||||||
|
|
||||||
public bool IsSet(int index)
|
public bool IsSet(int index)
|
||||||
{
|
{
|
||||||
var b = index >> _BYTE_SIZE;
|
var b = index >> _BYTE_SIZE;
|
||||||
@@ -756,7 +803,6 @@ public readonly ref struct SpanBitSet
|
|||||||
/// Resizes its internal array if necessary.
|
/// Resizes its internal array if necessary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">The index.</param>
|
/// <param name="index">The index.</param>
|
||||||
|
|
||||||
public void SetBit(int index)
|
public void SetBit(int index)
|
||||||
{
|
{
|
||||||
var b = index >> _BYTE_SIZE;
|
var b = index >> _BYTE_SIZE;
|
||||||
@@ -772,7 +818,6 @@ public readonly ref struct SpanBitSet
|
|||||||
/// Clears the bit at the given index.
|
/// Clears the bit at the given index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">The index.</param>
|
/// <param name="index">The index.</param>
|
||||||
|
|
||||||
public void ClearBit(int index)
|
public void ClearBit(int index)
|
||||||
{
|
{
|
||||||
var b = index >> _BYTE_SIZE;
|
var b = index >> _BYTE_SIZE;
|
||||||
@@ -787,7 +832,6 @@ public readonly ref struct SpanBitSet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets all bits.
|
/// Sets all bits.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public void SetAll()
|
public void SetAll()
|
||||||
{
|
{
|
||||||
var count = _bits.Length;
|
var count = _bits.Length;
|
||||||
@@ -800,12 +844,40 @@ public readonly ref struct SpanBitSet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clears all set bits.
|
/// Clears all set bits.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public void ClearAll()
|
public void ClearAll()
|
||||||
{
|
{
|
||||||
_bits.Clear();
|
_bits.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int NextSetBit(int startIndex)
|
||||||
|
{
|
||||||
|
var wordIndex = startIndex >> _BIT_SIZE;
|
||||||
|
if (wordIndex >= _bits.Length)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask off bits below startIndex in the first word:
|
||||||
|
var word = _bits[wordIndex] & ~0u << (startIndex & UnsafeBitSet._MASK);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (word != 0)
|
||||||
|
{
|
||||||
|
// get the least-significant set bit
|
||||||
|
var bit = BitOperations.TrailingZeroCount(word);
|
||||||
|
return (wordIndex << _BIT_SIZE) + bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
wordIndex++;
|
||||||
|
if (wordIndex >= _bits.Length)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
word = _bits[wordIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="Span{T}"/> to access the <see cref="_bits"/>.
|
/// Creates a <see cref="Span{T}"/> to access the <see cref="_bits"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
_freeSlots = new UnsafeQueue<int>(capacity, ref handle, allocationOption);
|
_freeSlots = new UnsafeQueue<int>(capacity, ref handle, allocationOption);
|
||||||
_validBits = new UnsafeBitSet(GetBitSetCapacity(capacity), ref handle, allocationOption);
|
_validBits = new UnsafeBitSet(GetBitSetCapacity(capacity), ref handle, allocationOption);
|
||||||
|
|
||||||
|
if (!allocationOption.HasFlag(AllocationOption.Clear))
|
||||||
|
{
|
||||||
|
_generations.AsSpan().Clear();
|
||||||
|
}
|
||||||
_validBits.ClearAll();
|
_validBits.ClearAll();
|
||||||
|
|
||||||
_count = 0;
|
_count = 0;
|
||||||
|
|||||||
@@ -94,6 +94,12 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
_reverse = new UnsafeArray<int>(capacity, ref handle, allocationOption);
|
_reverse = new UnsafeArray<int>(capacity, ref handle, allocationOption);
|
||||||
_freeSparse = new UnsafeStack<int>(capacity, ref handle, allocationOption);
|
_freeSparse = new UnsafeStack<int>(capacity, ref handle, allocationOption);
|
||||||
|
|
||||||
|
if (!allocationOption.HasFlag(AllocationOption.Clear))
|
||||||
|
{
|
||||||
|
_generations.AsSpan().Clear();
|
||||||
|
_sparse.AsSpan().Clear();
|
||||||
|
}
|
||||||
|
|
||||||
_count = 0;
|
_count = 0;
|
||||||
_nextId = 0;
|
_nextId = 0;
|
||||||
_capacity = capacity;
|
_capacity = capacity;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
<Authors>Misaki</Authors>
|
<Authors>Misaki</Authors>
|
||||||
<AssemblyVersion>1.2.6</AssemblyVersion>
|
<AssemblyVersion>1.2.7</AssemblyVersion>
|
||||||
<Version>$(AssemblyVersion)</Version>
|
<Version>$(AssemblyVersion)</Version>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
||||||
|
|||||||
Reference in New Issue
Block a user