using System; using NUnit.Framework; using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Collections.Tests; using Unity.Jobs; internal class UnsafeAppendBufferTests : CollectionsTestCommonBase { struct TestHeader { public int Type; public int PayloadSize; } [Test] public void UnsafeAppendBuffer_DisposeAllocated() { var buffer = new UnsafeAppendBuffer(1, 8, Allocator.Temp); Assert.True(buffer.IsCreated); Assert.True(buffer.IsEmpty); buffer.Dispose(); } [Test] unsafe public void UnsafeAppendBuffer_DisposeExternal() { var data = stackalloc int[1]; var buffer = new UnsafeAppendBuffer(data, sizeof(int)); buffer.Add(5); buffer.Dispose(); Assert.AreEqual(5, data[0]); } [Test] [TestRequiresDotsDebugOrCollectionChecks] public void UnsafeAppendBuffer_ThrowZeroAlignment() { Assert.Throws(() => { var buffer = new UnsafeAppendBuffer(0, 0, Allocator.Temp); }); } void AddAndVerify(ref UnsafeAppendBuffer buffer, T value, ref int expectedLength) where T : unmanaged { buffer.Add(value); expectedLength += UnsafeUtility.SizeOf(); Assert.AreEqual(expectedLength, buffer.Length); } [Test] public unsafe void UnsafeAppendBuffer_AddAndPop_UnalignedRead() { int expectedLength = 0; byte value = 0; var buffer = new UnsafeAppendBuffer(0, 8, Allocator.Temp); Assert.IsTrue(buffer.IsEmpty); Assert.AreEqual(expectedLength, buffer.Length); AddAndVerify(ref buffer, value++, ref expectedLength); AddAndVerify(ref buffer, value++, ref expectedLength); AddAndVerify(ref buffer, value++, ref expectedLength); Assert.AreEqual(9, buffer.Length); AddAndVerify(ref buffer, value++, ref expectedLength); AddAndVerify(ref buffer, value++, ref expectedLength); Assert.AreEqual(14, buffer.Length); { var array = new NativeArray(3, Allocator.Temp); for (int i = 0; i < array.Length; ++i) array[i] = value++; int oldLen = buffer.Length; buffer.AddArray(array.GetUnsafePtr(), 3); Assert.AreEqual(30, buffer.Length); } { var array = new NativeArray(3, Allocator.Temp); for (int i = 0; i < array.Length; ++i) array[i] = value++; int oldLen = buffer.Length; buffer.AddArray(array.GetUnsafePtr(), 3); Assert.AreEqual(46, buffer.Length); } // popping // array elements + count for (int i = 0; i < 3; ++i) { Assert.AreEqual(--value, buffer.Pop()); } Assert.AreEqual(3, buffer.Pop()); // array elements + count for (int i = 0; i < 3; ++i) { Assert.AreEqual(--value, buffer.Pop()); } Assert.AreEqual(3, buffer.Pop()); Assert.AreEqual(--value, buffer.Pop()); Assert.AreEqual(--value, buffer.Pop()); Assert.AreEqual(--value, buffer.Pop()); Assert.AreEqual(--value, buffer.Pop()); Assert.AreEqual(--value, buffer.Pop()); Assert.IsTrue(buffer.IsEmpty); Assert.AreEqual(0, buffer.Length); } [Test] public unsafe void UnsafeAppendBuffer_AddAndRead_UnalignedRead() { var buffer = new UnsafeAppendBuffer(0, 8, Allocator.Temp); buffer.Add(1); buffer.Add(1.0f); var reader = buffer.AsReader(); Assert.AreEqual(reader.ReadNext(), 1); Assert.AreEqual(reader.ReadNext(), 1.0f); buffer.Dispose(); } [Test] public unsafe void UnsafeAppendBuffer_PushHeadersWithPackets() { var buffer = new UnsafeAppendBuffer(0, 8, Allocator.Temp); var scratchPayload = stackalloc byte[1024]; var expectedSize = 0; for (int i = 0; i < 1024; i++) { var packeType = i; var packetSize = i; buffer.Add(new TestHeader { Type = packeType, PayloadSize = packetSize }); expectedSize += UnsafeUtility.SizeOf(); buffer.Add(scratchPayload, i); expectedSize += i; } Assert.True(expectedSize == buffer.Length); buffer.Dispose(); } [Test] public unsafe void UnsafeAppendBuffer_ReadHeadersWithPackets() { var buffer = new UnsafeAppendBuffer(0, 8, Allocator.Temp); var scratchPayload = stackalloc byte[1024]; for (int i = 0; i < 1024; i++) { var packeType = i; var packetSize = i; buffer.Add(new TestHeader { Type = packeType, PayloadSize = packetSize }); UnsafeUtility.MemSet(scratchPayload, (byte)(i & 0xff), packetSize); buffer.Add(scratchPayload, i); } var reader = buffer.AsReader(); for (int i = 0; i < 1024; i++) { var packetHeader = reader.ReadNext(); Assert.AreEqual(i, packetHeader.Type); Assert.AreEqual(i, packetHeader.PayloadSize); if (packetHeader.PayloadSize > 0) { var packetPayload = reader.ReadNext(packetHeader.PayloadSize); Assert.AreEqual((byte)(i & 0xff), *(byte*)packetPayload); } } Assert.True(reader.EndOfBuffer); buffer.Dispose(); } [Test] public unsafe void UnsafeAppendBuffer_AddAndPop() { var buffer = new UnsafeAppendBuffer(0, 8, Allocator.Temp); buffer.Add(123); buffer.Add(234); buffer.Add(345); { var array = new NativeArray(3, Allocator.Temp); buffer.Pop(array.GetUnsafePtr(), 3 * UnsafeUtility.SizeOf()); CollectionAssert.AreEqual(new[] {123, 234, 345}, array); } { var array = new NativeArray(4, Allocator.Temp); array.CopyFrom(new[] {987, 876, 765, 654}); buffer.Add(array.GetUnsafePtr(), 4 * UnsafeUtility.SizeOf()); } Assert.AreEqual(654, buffer.Pop()); Assert.AreEqual(765, buffer.Pop()); Assert.AreEqual(876, buffer.Pop()); Assert.AreEqual(987, buffer.Pop()); buffer.Dispose(); } [Test] public unsafe void UnsafeAppendBuffer_ReadNextArray() { var values = new NativeArray(new[] {123, 234, 345}, Allocator.Temp); var buffer = new UnsafeAppendBuffer(0, 8, Allocator.Temp); buffer.Add(values); var array = (int*)buffer.AsReader().ReadNextArray(out var count); Assert.AreEqual(values.Length, count); for (int i = 0; i < count; ++i) { Assert.AreEqual(values[i], array[i]); } values.Dispose(); buffer.Dispose(); } [Test] public unsafe void UnsafeAppendBuffer_DisposeJob() { var sizeOf = UnsafeUtility.SizeOf(); var alignOf = UnsafeUtility.AlignOf(); var container = new UnsafeAppendBuffer(5, 16, Allocator.Persistent); var disposeJob = container.Dispose(default); Assert.IsTrue(container.Ptr == null); disposeJob.Complete(); } [Test] public void UnsafeAppendBuffer_CustomAllocatorTest() { AllocatorManager.Initialize(); var allocatorHelper = new AllocatorHelper(AllocatorManager.Persistent); ref var allocator = ref allocatorHelper.Allocator; allocator.Initialize(); using (var container = new UnsafeAppendBuffer(1, 1, allocator.Handle)) { } Assert.IsTrue(allocator.WasUsed); allocator.Dispose(); allocatorHelper.Dispose(); AllocatorManager.Shutdown(); } [BurstCompile] struct BurstedCustomAllocatorJob : IJob { [NativeDisableUnsafePtrRestriction] public unsafe CustomAllocatorTests.CountingAllocator* Allocator; public void Execute() { unsafe { using (var container = new UnsafeAppendBuffer(1, 1, Allocator->Handle)) { } } } } [Test] public unsafe void UnsafeAppendBuffer_BurstedCustomAllocatorTest() { AllocatorManager.Initialize(); var allocatorHelper = new AllocatorHelper(AllocatorManager.Persistent); ref var allocator = ref allocatorHelper.Allocator; allocator.Initialize(); var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf(ref allocator); unsafe { var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr}.Schedule(); handle.Complete(); } Assert.IsTrue(allocator.WasUsed); allocator.Dispose(); allocatorHelper.Dispose(); AllocatorManager.Shutdown(); } }