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(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(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(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(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 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(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(); } }