Refactor memory management with MemoryHandle
Replaced `SafeHandle` with a new `MemoryHandle` system for improved memory tracking, safety, and leak detection. Updated allocators (`ArenaAllocator`, `HeapAllocator`, `StackAllocator`) and collections (`UnTypedArray`, `UnsafeArray<T>`, `UnsafeBitSet`) to use `MemoryHandle`. Refactored `AllocationManager` to use `ConcurrentSlotMap` for live allocation tracking and added methods for managing `MemoryHandle` instances. Simplified alignment and padding logic across allocators and collections. Enhanced performance with optimized memory operations (`MemClear`, `MemSet`, `MemCpy`) and vectorized operations in `MemoryUtility` and `UnsafeBitSet`. Fixed alignment issues in vectorized memory operations. Updated tests to reflect the new memory management system and added new tests for `UnsafeBitSet` bitwise operations. Enabled `ENABLE_COLLECTION_CHECKS` for debug builds and improved error messages and documentation. Removed unused `SafeHandle` code and adjusted project configuration to include necessary references.
This commit is contained in:
@@ -82,11 +82,11 @@ public interface IUnTypedCollection : IUnsafeCollection
|
||||
/// <summary>
|
||||
/// The total size of the buffer in bytes.
|
||||
/// </summary>
|
||||
uint Size
|
||||
nuint Size
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
ref T GetElementAt<T>(uint index)
|
||||
ref T GetElementAt<T>(nuint index)
|
||||
where T : unmanaged;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
||||
using Misaki.HighPerformance.LowLevel.Contracts;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
@@ -9,18 +9,16 @@ namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||
public unsafe struct UnTypedArray : IUnTypedCollection
|
||||
{
|
||||
private void* _buffer;
|
||||
private uint _size;
|
||||
private uint _alignment;
|
||||
private nuint _size;
|
||||
private nuint _alignment;
|
||||
|
||||
private AllocationHandle* _handle;
|
||||
private MemoryHandle _memoryHandle;
|
||||
private AllocationHandle* _allocationHandle;
|
||||
|
||||
public readonly uint Size => _size;
|
||||
public readonly uint Alignment => _alignment;
|
||||
public readonly nuint Size => _size;
|
||||
public readonly nuint Alignment => _alignment;
|
||||
|
||||
public readonly bool IsCreated
|
||||
{
|
||||
get => _buffer != null;
|
||||
}
|
||||
public readonly bool IsCreated => _buffer != null && _allocationHandle != null && _memoryHandle.IsValid;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an UnsafeArray with a default size of 1 and uses the Persistent allocator.
|
||||
@@ -30,17 +28,20 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
||||
{
|
||||
}
|
||||
|
||||
public UnTypedArray(uint size, uint alignment, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
||||
public UnTypedArray(nuint size, nuint alignment, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None)
|
||||
{
|
||||
if (size <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(size), "Count must be greater than zero.");
|
||||
}
|
||||
|
||||
_handle = (AllocationHandle*)Unsafe.AsPointer(ref handle);
|
||||
_buffer = handle.Alloc(_handle->Allocator, size, alignment, allocationOption);
|
||||
MemoryHandle memHandle;
|
||||
_buffer = handle.Alloc(_allocationHandle->Allocator, size, alignment, allocationOption, &memHandle);
|
||||
_size = size;
|
||||
_alignment = alignment;
|
||||
|
||||
_memoryHandle = memHandle;
|
||||
_allocationHandle = (AllocationHandle*)Unsafe.AsPointer(ref handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -50,7 +51,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
||||
/// <param name="allocator">Specifies the allocator to use for memory allocation, which determines the memory management strategy.</param>
|
||||
/// <param name="allocationOption">Determines how the memory should be allocated.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified number of elements is less than or equal to zero.</exception>
|
||||
public UnTypedArray(uint size, uint alignment, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
|
||||
public UnTypedArray(nuint size, nuint alignment, Allocator allocator, AllocationOption allocationOption = AllocationOption.None)
|
||||
: this(size, alignment, ref AllocationManager.GetAllocationHandle(allocator), allocationOption)
|
||||
{
|
||||
}
|
||||
@@ -72,7 +73,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly ref T GetElementAt<T>(uint index)
|
||||
public readonly ref T GetElementAt<T>(nuint index)
|
||||
where T : unmanaged
|
||||
{
|
||||
return ref UnsafeUtility.ReadArrayElementRef<T>(_buffer, index);
|
||||
@@ -86,8 +87,10 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
||||
return;
|
||||
}
|
||||
|
||||
_buffer = _handle->Realloc(_handle->Allocator, _buffer, _size, newSize, _alignment, option);
|
||||
MemoryHandle memHandle = _memoryHandle;
|
||||
_buffer = _allocationHandle->Realloc(_allocationHandle->Allocator, _buffer, _size, newSize, _alignment, option, &memHandle);
|
||||
_size = newSize;
|
||||
_memoryHandle = memHandle;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -112,12 +115,12 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
||||
return;
|
||||
}
|
||||
|
||||
if (_handle != null)
|
||||
if (_allocationHandle != null)
|
||||
{
|
||||
_handle->Free(_handle->Allocator, _buffer);
|
||||
_allocationHandle->Free(_allocationHandle->Allocator, _buffer, _memoryHandle);
|
||||
}
|
||||
|
||||
_handle = null;
|
||||
_allocationHandle = null;
|
||||
_buffer = null;
|
||||
_size = 0;
|
||||
_alignment = 0;
|
||||
|
||||
@@ -49,7 +49,8 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
|
||||
private T* _buffer;
|
||||
private int _count;
|
||||
private AllocationHandle* _handle;
|
||||
private MemoryHandle _memoryHandle;
|
||||
private AllocationHandle* _allocationHandle;
|
||||
|
||||
public readonly int Count => _count;
|
||||
|
||||
@@ -73,14 +74,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
}
|
||||
}
|
||||
|
||||
public readonly bool IsCreated
|
||||
{
|
||||
get
|
||||
{
|
||||
var handle = SafeHandle.GetSafeHandle(_buffer, AlignOf<T>());
|
||||
return handle != null && Volatile.Read(ref handle->valid) == 1;
|
||||
}
|
||||
}
|
||||
public readonly bool IsCreated => _buffer != null && _allocationHandle != null && _memoryHandle.IsValid;
|
||||
|
||||
public Enumerator GetEnumerator() => new((UnsafeArray<T>*)UnsafeUtility.AddressOf(ref this));
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
|
||||
@@ -108,15 +102,12 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
throw new ArgumentOutOfRangeException(nameof(count), "Count can not be less than zero.");
|
||||
}
|
||||
|
||||
var tAlign = AlignOf<T>();
|
||||
var headerSize = SafeHandle.GetPaddedHeaderSize(tAlign);
|
||||
var sizeWithHeader = (nuint)(count * sizeof(T)) + headerSize;
|
||||
var alignment = SafeHandle.GetAlignWithHeader(tAlign);
|
||||
MemoryHandle memHandle;
|
||||
var buff = handle.Alloc(handle.Allocator, (nuint)(count * sizeof(T)), AlignOf<T>(), allocationOption, &memHandle);
|
||||
|
||||
var buff = handle.Alloc(handle.Allocator, sizeWithHeader, alignment, allocationOption);
|
||||
|
||||
_buffer = (T*)((byte*)buff + headerSize);
|
||||
_handle = (AllocationHandle*)Unsafe.AsPointer(ref handle);
|
||||
_buffer = (T*)buff;
|
||||
_memoryHandle = memHandle;
|
||||
_allocationHandle = (AllocationHandle*)Unsafe.AsPointer(ref handle);
|
||||
_count = count;
|
||||
}
|
||||
|
||||
@@ -188,8 +179,10 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
return;
|
||||
}
|
||||
|
||||
MemoryHandle memHandle = _memoryHandle;
|
||||
var elemSize = SizeOf<T>();
|
||||
_buffer = (T*)_handle->Realloc(_handle->Allocator, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option);
|
||||
_buffer = (T*)_allocationHandle->Realloc(_allocationHandle->Allocator, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option, &memHandle);
|
||||
_memoryHandle = memHandle;
|
||||
_count = newSize;
|
||||
}
|
||||
|
||||
@@ -238,12 +231,12 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
||||
return;
|
||||
}
|
||||
|
||||
if (_handle != null)
|
||||
if (_allocationHandle != null)
|
||||
{
|
||||
_handle->Free(_handle->Allocator, _buffer);
|
||||
_allocationHandle->Free(_allocationHandle->Allocator, _buffer, _memoryHandle);
|
||||
}
|
||||
|
||||
_handle = null;
|
||||
_allocationHandle = null;
|
||||
_buffer = null;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Contracts;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Collections;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Text;
|
||||
|
||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||
@@ -28,15 +31,10 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
/// </summary>
|
||||
public readonly int HighestBit => _highestBit;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the count of the bitset, how many uints it consists of.
|
||||
/// </summary>
|
||||
public readonly int Count => _bits.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of bits represented by the current instance.
|
||||
/// </summary>
|
||||
public readonly int BitCount => _bits.Count << _INDEX_SIZE;
|
||||
public readonly int Count => _bits.Count << _INDEX_SIZE;
|
||||
|
||||
public readonly bool IsCreated => _bits.IsCreated;
|
||||
|
||||
@@ -55,6 +53,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
{
|
||||
var uints = (minimalLength >> _INDEX_SIZE) + int.Sign(minimalLength & _BIT_SIZE);
|
||||
var length = RoundToPadding(uints);
|
||||
|
||||
_bits = new UnsafeArray<uint>(length, ref handle, option);
|
||||
}
|
||||
|
||||
@@ -69,9 +68,9 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnsafeBitSet" /> class.
|
||||
/// </summary>
|
||||
public UnsafeBitSet(Span<uint> bits, Allocator allocator, AllocationOption option = AllocationOption.None)
|
||||
public UnsafeBitSet(Span<uint> bits, Allocator allocator)
|
||||
{
|
||||
_bits = new UnsafeArray<uint>(bits.Length, allocator, option);
|
||||
_bits = new UnsafeArray<uint>(bits.Length, allocator, AllocationOption.None);
|
||||
_bits.CopyFrom(bits);
|
||||
|
||||
_highestBit = 0;
|
||||
@@ -221,7 +220,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
/// <returns>True if they match, false if not.</returns>
|
||||
public readonly bool All(UnsafeBitSet other)
|
||||
{
|
||||
var min = Math.Min(Math.Min(Count, other.Count), _max);
|
||||
var min = Math.Min(Math.Min(_bits.Count, other._bits.Count), _max);
|
||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||
{
|
||||
var bits = _bits.AsSpan();
|
||||
@@ -282,7 +281,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
/// <returns>True if they match, false if not.</returns>
|
||||
public readonly bool Any(UnsafeBitSet other)
|
||||
{
|
||||
var min = Math.Min(Math.Min(Count, other.Count), _max);
|
||||
var min = Math.Min(Math.Min(_bits.Count, other._bits.Count), _max);
|
||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||
{
|
||||
var bits = _bits.AsSpan();
|
||||
@@ -343,7 +342,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
/// <returns>True if none match, false if not.</returns>
|
||||
public readonly bool None(UnsafeBitSet other)
|
||||
{
|
||||
var min = Math.Min(Math.Min(Count, other.Count), _max);
|
||||
var min = Math.Min(Math.Min(_bits.Count, other._bits.Count), _max);
|
||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||
{
|
||||
var bits = _bits.AsSpan();
|
||||
@@ -385,7 +384,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
/// <returns>True if they match, false if not.</returns>
|
||||
public readonly bool Exclusive(UnsafeBitSet other)
|
||||
{
|
||||
var min = Math.Min(Math.Min(Count, other.Count), _max);
|
||||
var min = Math.Min(Math.Min(_bits.Count, other._bits.Count), _max);
|
||||
|
||||
if (!Vector.IsHardwareAccelerated || min < s_padding)
|
||||
{
|
||||
@@ -440,83 +439,213 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inverts all bits in the current vector, replacing each bit with its logical complement.
|
||||
/// </summary>
|
||||
public void Not()
|
||||
{
|
||||
var thisCount = _bits.Count;
|
||||
if (!Vector.IsHardwareAccelerated || thisCount < s_padding)
|
||||
{
|
||||
for (var i = 0; i < thisCount; i++)
|
||||
{
|
||||
_bits[i] = ~_bits[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var pThis = (byte*)_bits.GetUnsafePtr();
|
||||
|
||||
for (var i = 0; i < thisCount; i += s_padding)
|
||||
{
|
||||
var vector = new Vector<uint>(_bits.AsSpan()[i..]);
|
||||
var resultVector = ~vector;
|
||||
|
||||
Unsafe.WriteUnaligned(pThis + i, resultVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a bitwise AND operation between the current bit set and the specified bit set, updating the current bit
|
||||
/// set in place.
|
||||
/// </summary>
|
||||
/// <param name="other">The bit set to combine with the current bit set using a bitwise AND operation. Must have the same length as the current bit set.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="other"/> does not have the same length as the current bit set.</exception>
|
||||
public void And(UnsafeBitSet other)
|
||||
{
|
||||
if (Count != other.Count)
|
||||
var thisCount = _bits.Count;
|
||||
if (thisCount != other._bits.Count)
|
||||
{
|
||||
throw new ArgumentException("Bitsets must be of the same length for AND operation.");
|
||||
}
|
||||
|
||||
if (!Vector.IsHardwareAccelerated || Count < s_padding)
|
||||
if (!Vector.IsHardwareAccelerated || thisCount < s_padding)
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
for (var i = 0; i < thisCount; i++)
|
||||
{
|
||||
_bits[i] &= other._bits[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < Count; i += s_padding)
|
||||
var pThis = (byte*)_bits.GetUnsafePtr();
|
||||
var pOther = (byte*)other._bits.GetUnsafePtr();
|
||||
|
||||
for (var i = 0; i < thisCount; i += s_padding)
|
||||
{
|
||||
var vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
|
||||
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||
var vectorLeft = Vector.Load(pThis + i);
|
||||
var vectorRight = Vector.Load(pOther + i);
|
||||
var resultVector = Vector.BitwiseAnd(vectorLeft, vectorRight);
|
||||
|
||||
resultVector.CopyTo(_bits.AsSpan(i, s_padding));
|
||||
Unsafe.WriteUnaligned(pThis + i, resultVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Or(UnsafeBitSet other)
|
||||
/// <summary>
|
||||
/// Performs a bitwise NAND operation between the current bit set and the specified bit set, updating the current
|
||||
/// bit set in place.
|
||||
/// </summary>
|
||||
/// <param name="other">The bit set to combine with the current bit set using the NAND operation. Must have the same length as the current bit set.</param>
|
||||
/// <exception cref="ArgumentException">Thrown if <paramref name="other"/> does not have the same length as the current bit set.</exception>
|
||||
public void Nand(UnsafeBitSet other)
|
||||
{
|
||||
if (Count != other.Count)
|
||||
var thisCount = _bits.Count;
|
||||
if (thisCount != other._bits.Count)
|
||||
{
|
||||
throw new ArgumentException("Bitsets must be of the same length for AND operation.");
|
||||
}
|
||||
|
||||
if (!Vector.IsHardwareAccelerated || Count < s_padding)
|
||||
if (!Vector.IsHardwareAccelerated || thisCount < s_padding)
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
for (var i = 0; i < thisCount; i++)
|
||||
{
|
||||
_bits[i] = ~(_bits[i] & other._bits[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var pThis = (byte*)_bits.GetUnsafePtr();
|
||||
var pOther = (byte*)other._bits.GetUnsafePtr();
|
||||
|
||||
for (var i = 0; i < thisCount; i += s_padding)
|
||||
{
|
||||
var vectorLeft = Vector.Load(pThis +i);
|
||||
var vectorRight = Vector.Load(pOther +i);
|
||||
var resultVector = ~Vector.BitwiseAnd(vectorLeft, vectorRight);
|
||||
|
||||
Unsafe.WriteUnaligned(pThis + i, resultVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a bitwise AND NOT operation between the current bit set and the specified bit set, updating the current
|
||||
/// bit set in place.
|
||||
/// </summary>
|
||||
/// <param name="other">The bit set whose bits will be inverted and ANDed with the current bit set. Must have the same length as the current bit set.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when the specified bit set does not have the same length as the current bit set.</exception>
|
||||
public void ANDC(UnsafeBitSet other)
|
||||
{
|
||||
var thisCount = _bits.Count;
|
||||
if (thisCount != other._bits.Count)
|
||||
{
|
||||
throw new ArgumentException("Bitsets must be of the same length for AND operation.");
|
||||
}
|
||||
|
||||
if (!Vector.IsHardwareAccelerated || thisCount < s_padding)
|
||||
{
|
||||
for (var i = 0; i < thisCount; i++)
|
||||
{
|
||||
_bits[i] &= ~other._bits[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var pThis = (byte*)_bits.GetUnsafePtr();
|
||||
var pOther = (byte*)other._bits.GetUnsafePtr();
|
||||
|
||||
for (var i = 0; i < thisCount; i += s_padding)
|
||||
{
|
||||
var vectorLeft = Vector.Load(pThis + i);
|
||||
var vectorRight = Vector.Load(pOther + i);
|
||||
var resultVector = Vector.AndNot(vectorLeft, vectorRight);
|
||||
|
||||
Unsafe.WriteUnaligned(pThis + i, resultVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a bitwise OR operation between the current bit set and the specified bit set, updating the current set
|
||||
/// in place.
|
||||
/// </summary>
|
||||
/// <param name="other">The bit set to combine with the current set using a bitwise OR operation. Must have the same length as the current bit set.</param>
|
||||
/// <exception cref="ArgumentException">Thrown if <paramref name="other"/> does not have the same length as the current bit set.</exception>
|
||||
public void Or(UnsafeBitSet other)
|
||||
{
|
||||
var thisCount = _bits.Count;
|
||||
if (thisCount != other._bits.Count)
|
||||
{
|
||||
throw new ArgumentException("Bitsets must be of the same length for AND operation.");
|
||||
}
|
||||
|
||||
if (!Vector.IsHardwareAccelerated || thisCount < s_padding)
|
||||
{
|
||||
for (var i = 0; i < thisCount; i++)
|
||||
{
|
||||
_bits[i] |= other._bits[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < Count; i += s_padding)
|
||||
var pThis = (byte*)_bits.GetUnsafePtr();
|
||||
var pOther = (byte*)other._bits.GetUnsafePtr();
|
||||
|
||||
for (var i = 0; i < thisCount; i += s_padding)
|
||||
{
|
||||
var vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
|
||||
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||
var vectorLeft = Vector.Load(pThis + i);
|
||||
var vectorRight = Vector.Load(pOther + i);
|
||||
var resultVector = Vector.BitwiseOr(vectorLeft, vectorRight);
|
||||
|
||||
resultVector.CopyTo(_bits.AsSpan(i, s_padding));
|
||||
Unsafe.WriteUnaligned(pThis + i, resultVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a bitwise exclusive OR (XOR) operation between the current bit set and the specified bit set.
|
||||
/// </summary>
|
||||
/// <param name="other">The bit set to XOR with the current instance. Must have the same length as the current bit set.</param>
|
||||
/// <exception cref="ArgumentException">Thrown if <paramref name="other"/> does not have the same length as the current bit set.</exception>
|
||||
public void Xor(UnsafeBitSet other)
|
||||
{
|
||||
if (Count != other.Count)
|
||||
var thisCount = _bits.Count;
|
||||
if (thisCount != other._bits.Count)
|
||||
{
|
||||
throw new ArgumentException("Bitsets must be of the same length for AND operation.");
|
||||
}
|
||||
|
||||
if (!Vector.IsHardwareAccelerated || Count < s_padding)
|
||||
if (!Vector.IsHardwareAccelerated || thisCount < s_padding)
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
for (var i = 0; i < thisCount; i++)
|
||||
{
|
||||
_bits[i] ^= other._bits[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < Count; i += s_padding)
|
||||
var pThis = (byte*)_bits.GetUnsafePtr();
|
||||
var pOther = (byte*)other._bits.GetUnsafePtr();
|
||||
|
||||
for (var i = 0; i < thisCount; i += s_padding)
|
||||
{
|
||||
var vectorLeft = new Vector<uint>(_bits.AsSpan()[i..]);
|
||||
var vectorRight = new Vector<uint>(other._bits.AsSpan()[i..]);
|
||||
var vectorLeft = Vector.Load(pThis + i);
|
||||
var vectorRight = Vector.Load(pOther + i);
|
||||
var resultVector = Vector.Xor(vectorLeft, vectorRight);
|
||||
|
||||
resultVector.CopyTo(_bits.AsSpan(i, s_padding));
|
||||
Unsafe.WriteUnaligned(pThis + i, resultVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -524,7 +653,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Span{T}"/> to access the <see cref="_bits"/>.
|
||||
/// </summary>
|
||||
/// <returns>The hash.</returns>
|
||||
/// <returns>The <see cref="Span{T}"/>.</returns>
|
||||
public readonly Span<uint> AsSpan()
|
||||
{
|
||||
var max = _highestBit / (_BIT_SIZE + 1) + 1;
|
||||
@@ -540,7 +669,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
public readonly Span<uint> AsSpan(Span<uint> span, bool zero = true)
|
||||
{
|
||||
// Copy everything thats possible from one to another
|
||||
var length = Math.Min(Count, span.Length);
|
||||
var length = Math.Min(_bits.Count, span.Length);
|
||||
for (var index = 0; index < length; index++)
|
||||
{
|
||||
span[index] = _bits[index];
|
||||
@@ -552,7 +681,7 @@ public unsafe struct UnsafeBitSet : IDisposable
|
||||
span[index] = 0;
|
||||
}
|
||||
|
||||
return span[..Count];
|
||||
return span[.._bits.Count];
|
||||
}
|
||||
|
||||
public readonly override string ToString()
|
||||
|
||||
Reference in New Issue
Block a user