275 lines
10 KiB
C#
275 lines
10 KiB
C#
|
using System;
|
||
|
using NUnit.Framework;
|
||
|
using Unity.Burst;
|
||
|
using Unity.Collections;
|
||
|
using Unity.Mathematics;
|
||
|
using Unity.Collections.LowLevel.Unsafe;
|
||
|
using Unity.Jobs;
|
||
|
|
||
|
internal class ConcurrentMaskTests
|
||
|
{
|
||
|
internal struct Test
|
||
|
{
|
||
|
internal long value;
|
||
|
internal int bits;
|
||
|
internal long expectedModified;
|
||
|
internal int offset;
|
||
|
internal bool expectedWorked;
|
||
|
internal Test(ulong uValue, ulong uExpectedModified, bool w)
|
||
|
{
|
||
|
this = default;
|
||
|
value = (long)uValue;
|
||
|
expectedModified = (long)uExpectedModified;
|
||
|
expectedWorked = w;
|
||
|
}
|
||
|
internal Test(ulong uValue, int bits, ulong uExpectedModified, bool w)
|
||
|
{
|
||
|
this = default;
|
||
|
value = (long)uValue;
|
||
|
this.bits = bits;
|
||
|
expectedModified = (long)uExpectedModified;
|
||
|
expectedWorked = w;
|
||
|
}
|
||
|
internal Test(ulong uValue, ulong uExpectedModified, int offset, bool w)
|
||
|
{
|
||
|
this = default;
|
||
|
value = (long)uValue;
|
||
|
expectedModified = (long)uExpectedModified;
|
||
|
this.offset = offset;
|
||
|
expectedWorked = w;
|
||
|
}
|
||
|
internal Test(ulong uValue, int bits, ulong uExpectedModified, int offset, bool w)
|
||
|
{
|
||
|
value = (long)uValue;
|
||
|
this.bits = bits;
|
||
|
expectedModified = (long)uExpectedModified;
|
||
|
this.offset = offset;
|
||
|
expectedWorked = w;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AllocatesOneBitFromLong()
|
||
|
{
|
||
|
foreach(var test in new Test[] {
|
||
|
new Test(0x0000000000000000UL,0x0000000000000001UL, true),
|
||
|
new Test(0x0000000000000001UL,0x0000000000000003UL, true),
|
||
|
new Test(0x00000000000000FFUL,0x00000000000001FFUL, true),
|
||
|
new Test(0x0000000000000100UL,0x0000000000000101UL, true),
|
||
|
new Test(0x7FFFFFFFFFFFFFFFUL,0xFFFFFFFFFFFFFFFFUL, true),
|
||
|
new Test(0x8000000000000000UL,0x8000000000000001UL, true),
|
||
|
}) {
|
||
|
long value = test.value;
|
||
|
long expectedModified = test.expectedModified;
|
||
|
long modified = value;
|
||
|
var worked = ConcurrentMask.TryAllocate(ref modified, out int _, 1);
|
||
|
Assert.AreEqual(expectedModified, modified);
|
||
|
Assert.AreEqual(test.expectedWorked, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void FailsToAllocateOneBitFromLong()
|
||
|
{
|
||
|
foreach(var test in new Test[] {
|
||
|
new Test(0xFFFFFFFFFFFFFFFFUL,0xFFFFFFFFFFFFFFFFUL, false)
|
||
|
}) {
|
||
|
long value = test.value;
|
||
|
long expectedModified = test.expectedModified;
|
||
|
long modified = value;
|
||
|
var worked = ConcurrentMask.TryAllocate(ref modified, out int _, 1);
|
||
|
Assert.AreEqual(expectedModified, modified);
|
||
|
Assert.AreEqual(test.expectedWorked, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AllocatesMultipleBitsFromLong()
|
||
|
{
|
||
|
foreach(var test in new Test[] {
|
||
|
new Test(0x0000000000000000UL, 64,0xFFFFFFFFFFFFFFFFUL, true),
|
||
|
new Test(0x0000000000000000UL, 2,0x0000000000000003UL, true),
|
||
|
new Test(0x0000000000000001UL,63,0xFFFFFFFFFFFFFFFFUL, true),
|
||
|
new Test(0x00000000000000FFUL,8, 0x000000000000FFFFUL, true),
|
||
|
new Test(0x00000000000FF0FFUL,8, 0x000000000FFFF0FFUL, true),
|
||
|
}) {
|
||
|
long value = test.value;
|
||
|
long expectedModified = test.expectedModified;
|
||
|
long modified = value;
|
||
|
var worked = ConcurrentMask.TryAllocate(ref modified, out int _, test.bits);
|
||
|
Assert.AreEqual(expectedModified, modified);
|
||
|
Assert.AreEqual(test.expectedWorked, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void FailsToAllocateMultipleBitsFromLong()
|
||
|
{
|
||
|
foreach(var test in new Test[] {
|
||
|
new Test(0x0000000000000000UL, 65,0x0000000000000000UL, false),
|
||
|
new Test(0x0000000000000001UL,64,0x0000000000000001UL, false),
|
||
|
new Test(0xFF000000000000FFUL,49, 0xFF000000000000FFUL, false),
|
||
|
}) {
|
||
|
long value = test.value;
|
||
|
long expectedModified = test.expectedModified;
|
||
|
long modified = value;
|
||
|
var worked = ConcurrentMask.TryAllocate(ref modified, out int _, test.bits);
|
||
|
Assert.AreEqual(expectedModified, modified);
|
||
|
Assert.AreEqual(test.expectedWorked, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void FreesOneBitFromLong()
|
||
|
{
|
||
|
foreach(var test in new Test[] {
|
||
|
new Test(0x0000000000000000UL,0x0000000000000001UL, 0, true),
|
||
|
new Test(0x0000000000000001UL,0x0000000000000003UL, 1, true),
|
||
|
new Test(0x00000000000000FFUL,0x00000000000001FFUL, 8, true),
|
||
|
new Test(0x0000000000000100UL,0x0000000000000101UL, 0, true),
|
||
|
new Test(0x7FFFFFFFFFFFFFFFUL,0xFFFFFFFFFFFFFFFFUL, 63, true),
|
||
|
new Test(0x8000000000000000UL,0x8000000000000001UL, 0, true),
|
||
|
}) {
|
||
|
long expectedModified = (long)test.value;
|
||
|
long modified = (long)test.expectedModified;
|
||
|
var worked = ConcurrentMask.TryFree(ref modified, test.offset, 1);
|
||
|
Assert.AreEqual(expectedModified, modified);
|
||
|
Assert.AreEqual(test.expectedWorked, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void FreesMultipleBitsFromLong()
|
||
|
{
|
||
|
foreach(var test in new Test[] {
|
||
|
new Test(0x0000000000000000UL, 64,0xFFFFFFFFFFFFFFFFUL, 0, true),
|
||
|
new Test(0x0000000000000000UL, 2,0x0000000000000003UL, 0, true),
|
||
|
new Test(0x0000000000000001UL,63,0xFFFFFFFFFFFFFFFFUL, 1, true),
|
||
|
new Test(0x00000000000000FFUL,8, 0x000000000000FFFFUL, 8, true),
|
||
|
new Test(0x00000000000FF0FFUL,8, 0x000000000FFFF0FFUL, 20, true),
|
||
|
}) {
|
||
|
long expectedModified = test.value;
|
||
|
long modified = test.expectedModified;
|
||
|
var worked = ConcurrentMask.TryFree(ref modified, test.offset, test.bits);
|
||
|
Assert.AreEqual(expectedModified, modified);
|
||
|
Assert.AreEqual(test.expectedWorked, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AllocatesOneBitFromArray()
|
||
|
{
|
||
|
var storage = new NativeList<long>(3, Allocator.Persistent);
|
||
|
storage.Length = 3;
|
||
|
for(var i = 0; i < 64; ++i)
|
||
|
{
|
||
|
var worked = ConcurrentMask.TryAllocate(ref storage, out int offset, 1);
|
||
|
Assert.AreEqual(true, ConcurrentMask.Succeeded(worked));
|
||
|
Assert.AreEqual(i, offset);
|
||
|
}
|
||
|
Assert.AreEqual(-1L, storage[0]);
|
||
|
Assert.AreEqual(0L, storage[1]);
|
||
|
Assert.AreEqual(0L, storage[2]);
|
||
|
for(var i = 0; i < 64; ++i)
|
||
|
{
|
||
|
var worked = ConcurrentMask.TryAllocate(ref storage, out int offset, 1);
|
||
|
Assert.AreEqual(true, ConcurrentMask.Succeeded(worked));
|
||
|
Assert.AreEqual(64+i, offset);
|
||
|
}
|
||
|
Assert.AreEqual(-1L, storage[0]);
|
||
|
Assert.AreEqual(-1L, storage[1]);
|
||
|
Assert.AreEqual(0L, storage[2]);
|
||
|
storage.Dispose();
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AllocatesMultipleBitsFromArray()
|
||
|
{
|
||
|
var storage = new NativeList<long>(3, Allocator.Persistent);
|
||
|
storage.Length = 3;
|
||
|
for(var i = 0; i < 3; ++i)
|
||
|
{
|
||
|
var worked = ConcurrentMask.TryAllocate(ref storage, out int offset, 33);
|
||
|
Assert.AreEqual(true, ConcurrentMask.Succeeded(worked));
|
||
|
Assert.AreEqual(i * 64, offset);
|
||
|
}
|
||
|
{
|
||
|
var worked = ConcurrentMask.TryAllocate(ref storage, out int offset, 33);
|
||
|
Assert.AreEqual(false, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
storage.Dispose();
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void FreesOneBitFromArray()
|
||
|
{
|
||
|
var storage = new NativeList<long>(3, Allocator.Persistent);
|
||
|
storage.Length = 3;
|
||
|
ConcurrentMask.TryAllocate(ref storage, out int _, 64);
|
||
|
ConcurrentMask.TryAllocate(ref storage, out int _, 64);
|
||
|
ConcurrentMask.TryAllocate(ref storage, out int _, 64);
|
||
|
for(var i = 0; i < 64 * 3; ++i)
|
||
|
{
|
||
|
var worked = ConcurrentMask.TryFree(ref storage, i, 1);
|
||
|
Assert.AreEqual(true, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
Assert.AreEqual(0L, storage[0]);
|
||
|
Assert.AreEqual(0L, storage[1]);
|
||
|
Assert.AreEqual(0L, storage[2]);
|
||
|
storage.Dispose();
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void FreesMultipleBitsFromArray()
|
||
|
{
|
||
|
var storage = new NativeList<long>(3, Allocator.Persistent);
|
||
|
storage.Length = 3;
|
||
|
ConcurrentMask.TryAllocate(ref storage, out int _, 64);
|
||
|
ConcurrentMask.TryAllocate(ref storage, out int _, 64);
|
||
|
ConcurrentMask.TryAllocate(ref storage, out int _, 64);
|
||
|
for(var i = 0; i < 3; ++i)
|
||
|
{
|
||
|
var worked = ConcurrentMask.TryFree(ref storage, i * 64 + 1, 63);
|
||
|
Assert.AreEqual(true, ConcurrentMask.Succeeded(worked));
|
||
|
}
|
||
|
Assert.AreEqual(1L, storage[0]);
|
||
|
Assert.AreEqual(1L, storage[1]);
|
||
|
Assert.AreEqual(1L, storage[2]);
|
||
|
storage.Dispose();
|
||
|
}
|
||
|
|
||
|
#if !UNITY_DOTSRUNTIME
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
#endif
|
||
|
struct AllocateJob : IJobParallelFor
|
||
|
{
|
||
|
[NativeDisableContainerSafetyRestriction] public NativeList<long> m_storage;
|
||
|
public void Execute(int index)
|
||
|
{
|
||
|
ConcurrentMask.TryAllocate(ref m_storage, out int _, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AllocatesFromJob()
|
||
|
{
|
||
|
const int kLengthInWords = 10;
|
||
|
const int kLengthInBits = kLengthInWords * 64;
|
||
|
var storage = new NativeList<long>(kLengthInWords, Allocator.Persistent);
|
||
|
storage.Length = kLengthInWords;
|
||
|
|
||
|
for (int i = 0; i < kLengthInWords; ++i)
|
||
|
Assert.AreEqual(0L, storage[i]);
|
||
|
|
||
|
var allocateJob = new AllocateJob();
|
||
|
allocateJob.m_storage = storage;
|
||
|
allocateJob.Schedule(kLengthInBits, 1).Complete();
|
||
|
|
||
|
for (int i = 0; i < kLengthInWords; ++i)
|
||
|
Assert.AreEqual(~0L, storage[i]);
|
||
|
|
||
|
storage.Dispose();
|
||
|
}
|
||
|
|
||
|
}
|