feat(queue): improve Resize logic and add unit tests
Refactored UnsafeQueue<T>.Resize to prevent shrinking below current count and handle wrap-around copying correctly. Exposed AllocationHandle in UnsafeArray<T> via a new property. Bumped project version to 1.6.10. Added TestUnsafeQueue class to cover core queue operations.
This commit is contained in:
@@ -81,6 +81,8 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
#endif
|
#endif
|
||||||
private AllocationHandle _allocationHandle;
|
private AllocationHandle _allocationHandle;
|
||||||
|
|
||||||
|
internal readonly AllocationHandle AllocationHandle => _allocationHandle;
|
||||||
|
|
||||||
public readonly int Count => _count;
|
public readonly int Count => _count;
|
||||||
public readonly int Length => _count;
|
public readonly int Length => _count;
|
||||||
|
|
||||||
|
|||||||
@@ -183,12 +183,39 @@ public unsafe struct UnsafeQueue<T> : IUnsafeCollection<T>
|
|||||||
|
|
||||||
public void Resize(int newSize, AllocationOption option = AllocationOption.None)
|
public void Resize(int newSize, AllocationOption option = AllocationOption.None)
|
||||||
{
|
{
|
||||||
_array.Resize(newSize, option);
|
if (newSize < _count)
|
||||||
|
|
||||||
if (_count > newSize)
|
|
||||||
{
|
{
|
||||||
_count = newSize;
|
newSize = _count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newArray = new UnsafeArray<T>(newSize, _array.AllocationHandle, option);
|
||||||
|
|
||||||
|
if (_count > 0)
|
||||||
|
{
|
||||||
|
if (_offset + _count <= Capacity)
|
||||||
|
{
|
||||||
|
// No wrap-around, single copy
|
||||||
|
var sizeToCopy = (uint)(_count * sizeof(T));
|
||||||
|
MemCpy(newArray.GetUnsafePtr(), (byte*)_array.GetUnsafePtr() + _offset * sizeof(T), sizeToCopy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Wrap-around, two copies required
|
||||||
|
var firstPartElements = Capacity - _offset;
|
||||||
|
var secondPartElements = _count - firstPartElements;
|
||||||
|
|
||||||
|
// Copy from _offset to the end of the old array
|
||||||
|
MemCpy(newArray.GetUnsafePtr(), (byte*)_array.GetUnsafePtr() + _offset * sizeof(T), (uint)(firstPartElements * sizeof(T)));
|
||||||
|
|
||||||
|
// Copy from the start of the old array to the remaining count
|
||||||
|
MemCpy((byte*)newArray.GetUnsafePtr() + firstPartElements * sizeof(T), _array.GetUnsafePtr(), (uint)(secondPartElements * sizeof(T)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_array.Dispose();
|
||||||
|
_array = newArray;
|
||||||
|
|
||||||
|
_offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Authors>Misaki</Authors>
|
<Authors>Misaki</Authors>
|
||||||
<AssemblyVersion>1.6.9</AssemblyVersion>
|
<AssemblyVersion>1.6.10</AssemblyVersion>
|
||||||
<Version>$(AssemblyVersion)</Version>
|
<Version>$(AssemblyVersion)</Version>
|
||||||
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
<RepositoryUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</RepositoryUrl>
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Misaki.HighPerformance.Test.UnitTest.Collections;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class TestUnsafeQueue
|
||||||
|
{
|
||||||
|
private UnsafeQueue<int> _queue;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_queue = new UnsafeQueue<int>(4, LowLevel.Buffer.Allocator.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCleanup]
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
_queue.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestEnqueueDequeue()
|
||||||
|
{
|
||||||
|
_queue.Enqueue(1);
|
||||||
|
_queue.Enqueue(2);
|
||||||
|
_queue.Enqueue(3);
|
||||||
|
Assert.AreEqual(3, _queue.Count);
|
||||||
|
Assert.AreEqual(1, _queue.Dequeue());
|
||||||
|
Assert.AreEqual(2, _queue.Dequeue());
|
||||||
|
Assert.AreEqual(3, _queue.Dequeue());
|
||||||
|
Assert.AreEqual(0, _queue.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestPeek()
|
||||||
|
{
|
||||||
|
_queue.Enqueue(10);
|
||||||
|
_queue.Enqueue(20);
|
||||||
|
Assert.AreEqual(10, _queue.Peek());
|
||||||
|
Assert.AreEqual(10, _queue.Dequeue());
|
||||||
|
Assert.AreEqual(20, _queue.Peek());
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestTryPeek()
|
||||||
|
{
|
||||||
|
Assert.IsFalse(_queue.TryPeek(out _));
|
||||||
|
_queue.Enqueue(5);
|
||||||
|
Assert.IsTrue(_queue.TryPeek(out int value));
|
||||||
|
Assert.AreEqual(5, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestResize()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
_queue.Enqueue(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(10, _queue.Count);
|
||||||
|
|
||||||
|
_queue.Dequeue();
|
||||||
|
_queue.Dequeue();
|
||||||
|
|
||||||
|
for (int i = 10; i < 20; i++)
|
||||||
|
{
|
||||||
|
_queue.Enqueue(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 2; i < 20; i++)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(i, _queue.Dequeue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user