From a276e1444426f06cec8585cbb2d84eb960430ff7 Mon Sep 17 00:00:00 2001 From: ikpil Date: Sun, 31 May 2026 17:42:27 +0900 Subject: [PATCH] [upstream] Macro clean up (erincatto/box2d#1045) Remove some unhelpful macros. Removed Neon FMA usage. #1033 Windows ARM64 support #1042 --- .../Benchmarks/BenchmarkShapeDistance.cs | 10 +- src/Box2D.NET/B2Arrays.cs | 22 +- src/Box2D.NET/B2Bodies.cs | 45 +- src/Box2D.NET/B2Islands.cs | 30 +- src/Box2D.NET/B2SolverSets.cs | 22 +- src/Box2D.NET/B2StackArray.cs | 15 - src/Box2D.NET/B2StackArrays.cs | 127 ---- test/Box2D.NET.Test/B2ArrayTests.cs | 708 ++++++++++-------- test/Box2D.NET.Test/B2StackTests.cs | 133 ---- 9 files changed, 439 insertions(+), 673 deletions(-) delete mode 100644 src/Box2D.NET/B2StackArray.cs delete mode 100644 src/Box2D.NET/B2StackArrays.cs delete mode 100644 test/Box2D.NET.Test/B2StackTests.cs diff --git a/src/Box2D.NET.Samples/Samples/Benchmarks/BenchmarkShapeDistance.cs b/src/Box2D.NET.Samples/Samples/Benchmarks/BenchmarkShapeDistance.cs index 71d175b5..d24b56c3 100644 --- a/src/Box2D.NET.Samples/Samples/Benchmarks/BenchmarkShapeDistance.cs +++ b/src/Box2D.NET.Samples/Samples/Benchmarks/BenchmarkShapeDistance.cs @@ -26,7 +26,6 @@ public class BenchmarkShapeDistance : Sample private B2Polygon m_polygonB; private float m_minMilliseconds; private int m_drawIndex; - private int m_minCycles; // for draw private int totalIterations = 0; @@ -87,7 +86,6 @@ public BenchmarkShapeDistance(SampleContext context) : base(context) } m_drawIndex = 0; - m_minCycles = int.MaxValue; m_minMilliseconds = float.MaxValue; } @@ -123,7 +121,6 @@ public override void Step() totalIterations = 0; ulong start = b2GetTicks(); - ulong startCycles = b2GetTicks(); for (int i = 0; i < m_count; ++i) { B2SimplexCache cache = new B2SimplexCache(); @@ -133,10 +130,7 @@ public override void Step() totalIterations += m_outputs[i].iterations; } - ulong endCycles = b2GetTicks(); - float ms = b2GetMilliseconds(start); - m_minCycles = b2MinInt(m_minCycles, (int)endCycles - (int)startCycles); m_minMilliseconds = b2MinFloat(m_minMilliseconds, ms); } @@ -151,8 +145,6 @@ public override void Draw() if (m_context.pause == false || m_context.singleStep == true) { DrawTextLine($"count = {m_count}"); - DrawTextLine($"min cycles = {m_minCycles}"); - DrawTextLine($"ave cycles = {(float)m_minCycles / m_count}"); DrawTextLine($"min ms = {m_minMilliseconds}, ave us = {1000.0f * m_minMilliseconds / (float)m_count}"); DrawTextLine($"average iterations = {totalIterations / (float)m_count}"); } @@ -168,4 +160,4 @@ public override void Draw() DrawLine(m_draw, output.pointA, output.pointA + 0.5f * output.normal, B2HexColor.b2_colorYellow); DrawTextLine($"distance = {output.distance}"); } -} \ No newline at end of file +} diff --git a/src/Box2D.NET/B2Arrays.cs b/src/Box2D.NET/B2Arrays.cs index c05fbccc..73bcce16 100644 --- a/src/Box2D.NET/B2Arrays.cs +++ b/src/Box2D.NET/B2Arrays.cs @@ -183,26 +183,6 @@ public static int b2Array_ByteCount(ref B2Array a) return a; } - // Remove an element from an int arrayA by swapping with the last element. This updates the index contained - // in the moved element in arrayB. Assumes the integers in arrayA index into arrayB. Assumes - // the elements of arrayB have an indexName member that is the index in arrayA. - public static void b2RemoveUpdate(ref B2Array arrayA, ref B2Array arrayB, int indexB, Func getIndexName, Action setIndexName) - { - int lastIndex = (arrayA).count - 1; - B2_ASSERT(0 <= (indexB) && (indexB) < (arrayB).count); - int indexA = getIndexName.Invoke((arrayB).data[indexB]); - B2_ASSERT(0 <= indexA && indexA < (arrayA).count); - if (indexA != lastIndex) - { - int movedIndex = (arrayA).data[lastIndex]; - (arrayA).data[indexA] = movedIndex; - setIndexName.Invoke((arrayB).data[movedIndex], indexA); - } - - (arrayA).count -= 1; - } - - /* Reserve */ [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void b2Array_Reserve(ref B2Array a, int newCapacity) where T : new() @@ -226,4 +206,4 @@ public static void b2Array_Destroy(ref B2Array a) a.capacity = 0; } } -} \ No newline at end of file +} diff --git a/src/Box2D.NET/B2Bodies.cs b/src/Box2D.NET/B2Bodies.cs index 3e2fc75d..fbb66ad9 100644 --- a/src/Box2D.NET/B2Bodies.cs +++ b/src/Box2D.NET/B2Bodies.cs @@ -57,6 +57,22 @@ public static void b2LimitVelocity(B2BodyState state, float maxLinearSpeed) } } + public static void b2RemoveBodySim(ref B2Array bodySims, ref B2Array bodies, int localIndex) + { + B2_ASSERT(0 <= localIndex && localIndex < bodySims.count); + int lastIndex = bodySims.count - 1; + bodySims.data[localIndex].CopyFrom(bodySims.data[lastIndex]); + B2Body movedBody = b2Array_Get(ref bodies, bodySims.data[localIndex].bodyId); + B2_ASSERT(movedBody.localIndex == lastIndex); + movedBody.localIndex = localIndex; + if (localIndex != lastIndex) + { + bodySims.data[lastIndex] = new B2BodySim(); + } + + bodySims.count -= 1; + } + // Get a validated body from a world using an id. public static B2Body b2GetBodyFullId(B2World world, B2BodyId bodyId) { @@ -127,8 +143,14 @@ internal static void b2RemoveBodyFromIsland(B2World world, B2Body body) int islandId = body.islandId; B2Island island = b2Array_Get(ref world.islands, islandId); - - b2RemoveUpdate(ref island.bodies, ref world.bodies, body.id, x => x.islandIndex, (x, idx) => x.islandIndex = idx); + { + int localIndex = body.islandIndex; + int movedBodyId = island.bodies.data[island.bodies.count - 1]; + island.bodies.data[localIndex] = movedBodyId; + B2_VALIDATE(world.bodies.data[movedBodyId].islandIndex == island.bodies.count - 1); + world.bodies.data[movedBodyId].islandIndex = localIndex; + island.bodies.count -= 1; + } if (island.bodies.count == 0) { @@ -399,27 +421,16 @@ public static void b2DestroyBody(B2BodyId bodyId) // Remove body sim from solver set that owns it B2SolverSet set = b2Array_Get(ref world.solverSets, body.setIndex); - int movedIndex = b2Array_RemoveSwap(ref set.bodySims, body.localIndex); - if (movedIndex != B2_NULL_INDEX) - { - // Fix moved body index - B2BodySim movedSim = set.bodySims.data[body.localIndex]; - int movedId = movedSim.bodyId; - B2Body movedBody = b2Array_Get(ref world.bodies, movedId); - B2_ASSERT(movedBody.localIndex == movedIndex); - movedBody.localIndex = body.localIndex; - } + b2RemoveBodySim(ref set.bodySims, ref world.bodies, body.localIndex); // Remove body state from awake set if (body.setIndex == (int)B2SolverSetType.b2_awakeSet) { - int result = b2Array_RemoveSwap(ref set.bodyStates, body.localIndex); - B2_ASSERT(result == movedIndex); - B2_UNUSED(result); + b2Array_RemoveSwap(ref set.bodyStates, body.localIndex); } else if (set.setIndex >= (int)B2SolverSetType.b2_firstSleepingSet && set.bodySims.count == 0) { - // Remove solver set if it's now an orphan. + // Remove solver set if it is empty b2DestroySolverSet(world, set.setIndex); } @@ -2053,4 +2064,4 @@ internal static bool b2ShouldBodiesCollide(B2World world, B2Body bodyA, B2Body b return true; } } -} \ No newline at end of file +} diff --git a/src/Box2D.NET/B2Islands.cs b/src/Box2D.NET/B2Islands.cs index ee58b825..d3e9a658 100644 --- a/src/Box2D.NET/B2Islands.cs +++ b/src/Box2D.NET/B2Islands.cs @@ -68,15 +68,19 @@ public static void b2DestroyIsland(B2World world, int islandId) // assume island is empty B2Island island = b2Array_Get(ref world.islands, islandId); B2SolverSet set = b2Array_Get(ref world.solverSets, island.setIndex); - int movedIndex = b2Array_RemoveSwap(ref set.islandSims, island.localIndex); - if (movedIndex != B2_NULL_INDEX) { - // Fix index on moved element - B2IslandSim movedElement = set.islandSims.data[island.localIndex]; - int movedId = movedElement.islandId; - B2Island movedIsland = b2Array_Get(ref world.islands, movedId); - B2_ASSERT(movedIsland.localIndex == movedIndex); - movedIsland.localIndex = island.localIndex; + int localIndex = island.localIndex; + int lastIndex = set.islandSims.count - 1; + B2_ASSERT(0 <= localIndex && localIndex <= lastIndex); + int moveIslandId = set.islandSims.data[lastIndex].islandId; + set.islandSims.data[localIndex].CopyFrom(set.islandSims.data[lastIndex]); + world.islands.data[moveIslandId].localIndex = localIndex; + if (localIndex != lastIndex) + { + set.islandSims.data[lastIndex] = new B2IslandSim(); + } + + set.islandSims.count -= 1; } // Free island and id (preserve island revision) @@ -84,9 +88,11 @@ public static void b2DestroyIsland(B2World world, int islandId) b2Array_Destroy(ref island.contacts); b2Array_Destroy(ref island.joints); island.constraintRemoveCount = 0; + island.localIndex = B2_NULL_INDEX; island.islandId = B2_NULL_INDEX; island.setIndex = B2_NULL_INDEX; - island.localIndex = B2_NULL_INDEX; + + B2_VALIDATE(island.localIndex == B2_NULL_INDEX); b2FreeId(world.islandIdPool, islandId); } @@ -268,10 +274,9 @@ public static void b2UnlinkContact(B2World world, B2Contact contact) movedContact.islandIndex = removeIndex; } - island.constraintRemoveCount += 1; - contact.islandId = B2_NULL_INDEX; contact.islandIndex = B2_NULL_INDEX; + island.constraintRemoveCount += 1; b2ValidateIsland(world, islandId); } @@ -350,10 +355,9 @@ public static void b2UnlinkJoint(B2World world, B2Joint joint) movedJoint.islandIndex = removeIndex; } - island.constraintRemoveCount += 1; - joint.islandId = B2_NULL_INDEX; joint.islandIndex = B2_NULL_INDEX; + island.constraintRemoveCount += 1; b2ValidateIsland(world, islandId); } diff --git a/src/Box2D.NET/B2SolverSets.cs b/src/Box2D.NET/B2SolverSets.cs index 2bc72231..7b833ba4 100644 --- a/src/Box2D.NET/B2SolverSets.cs +++ b/src/Box2D.NET/B2SolverSets.cs @@ -241,16 +241,7 @@ internal static void b2TrySleepIsland(B2World world, int islandId) //memcpy( sleepBodySim, awakeSim, sizeof( b2BodySim ) ); sleepBodySim.CopyFrom(awakeSim); - int movedIndex = b2Array_RemoveSwap(ref awakeSet.bodySims, awakeBodyIndex); - if (movedIndex != B2_NULL_INDEX) - { - // fix local index on moved element - B2BodySim movedSim = awakeSet.bodySims.data[awakeBodyIndex]; - int movedId = movedSim.bodyId; - B2Body movedBody = b2Array_Get(ref world.bodies, movedId); - B2_ASSERT(movedBody.localIndex == movedIndex); - movedBody.localIndex = awakeBodyIndex; - } + b2RemoveBodySim(ref awakeSet.bodySims, ref world.bodies, awakeBodyIndex); // destroy state, no need to clone b2Array_RemoveSwap(ref awakeSet.bodyStates, awakeBodyIndex); @@ -568,16 +559,7 @@ internal static void b2TransferBody(B2World world, B2SolverSet targetSet, B2Solv targetSim.flags &= ~((uint)B2BodyFlags.b2_isFast | (uint)B2BodyFlags.b2_isSpeedCapped | (uint)B2BodyFlags.b2_hadTimeOfImpact); // Remove body sim from solver set that owns it - int movedIndex = b2Array_RemoveSwap(ref sourceSet.bodySims, sourceIndex); - if (movedIndex != B2_NULL_INDEX) - { - // Fix moved body index - B2BodySim movedSim = sourceSet.bodySims.data[sourceIndex]; - int movedId = movedSim.bodyId; - B2Body movedBody = b2Array_Get(ref world.bodies, movedId); - B2_ASSERT(movedBody.localIndex == movedIndex); - movedBody.localIndex = sourceIndex; - } + b2RemoveBodySim(ref sourceSet.bodySims, ref world.bodies, sourceIndex); if (sourceSet.setIndex == (int)B2SolverSetType.b2_awakeSet) { diff --git a/src/Box2D.NET/B2StackArray.cs b/src/Box2D.NET/B2StackArray.cs deleted file mode 100644 index 60a8c4ed..00000000 --- a/src/Box2D.NET/B2StackArray.cs +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Erin Catto -// SPDX-FileCopyrightText: 2026 Ikpil Choi(ikpil@naver.com) -// SPDX-License-Identifier: MIT - -namespace Box2D.NET -{ - // A stack array uses a fixed size buffer but can grow on the heap if necessary. - public struct B2StackArray - { - public T[] stackData; - public T[] data; - public int count; - public int capacity; - } -} diff --git a/src/Box2D.NET/B2StackArrays.cs b/src/Box2D.NET/B2StackArrays.cs deleted file mode 100644 index 81daf98d..00000000 --- a/src/Box2D.NET/B2StackArrays.cs +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Erin Catto -// SPDX-FileCopyrightText: 2026 Ikpil Choi(ikpil@naver.com) -// SPDX-License-Identifier: MIT - -using System; -using System.Runtime.CompilerServices; -using static Box2D.NET.B2Buffers; -using static Box2D.NET.B2Diagnostics; - -namespace Box2D.NET -{ - public static class B2StackArrays - { - // Used to define a stack array instance - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static B2StackArray b2StackArray_Create(int stackCapacity) where T : new() - { - B2_ASSERT(stackCapacity > 0); - - var a = new B2StackArray(); - a.stackData = new T[stackCapacity]; - InitializeReferenceElements(a.stackData, 0, stackCapacity); - a.data = a.stackData; - a.count = 0; - a.capacity = stackCapacity; - return a; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void b2StackArray_Destroy(ref B2StackArray a) - { - if (a.data == null) - { - return; - } - - if (a.data != null && ReferenceEquals(a.data, a.stackData) == false) - { - b2Free(a.data, a.capacity); - } - - a.stackData = null; - a.data = null; - a.count = 0; - a.capacity = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void b2StackArray_Reserve(ref B2StackArray a, int newCapacity) where T : new() - { - B2_ASSERT(a.data != null && a.capacity > 0); - if (a.capacity >= newCapacity) - { - return; - } - - int oldCapacity = a.capacity; - if (ReferenceEquals(a.data, a.stackData)) - { - T[] newData = b2GrowAlloc(null, 0, newCapacity); - Array.Copy(a.stackData, newData, oldCapacity); - a.data = newData; - } - else - { - a.data = b2GrowAlloc(a.data, oldCapacity, newCapacity); - } - - a.capacity = newCapacity; - } - - // Push a new element by value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void b2StackArray_Push(ref B2StackArray a, T value) where T : new() - { - B2_ASSERT(a.data != null && a.capacity > 0); - if (a.count >= a.capacity) - { - b2StackArray_Reserve(ref a, 2 * a.capacity); - } - - a.data[a.count] = value; - a.count += 1; - } - - // Get a pointer to an element - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T b2StackArray_Get(ref B2StackArray a, int index) - { - B2_ASSERT(0 <= index && index < a.count); - return ref a.data[index]; - } - - // Remove an element from an int arrayA by swapping with the last element. This updates the index contained - // in the moved element in arrayB. Assumes the integers in arrayA index into arrayB. Assumes - // the elements of arrayB have an indexName member that is the index in arrayA. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void b2RemoveUpdate(ref B2StackArray arrayA, ref B2Array arrayB, int indexB, Func getIndexName, Action setIndexName) - { - int lastIndex = arrayA.count - 1; - B2_ASSERT(0 <= indexB && indexB < arrayB.count); - int indexA = getIndexName.Invoke(arrayB.data[indexB]); - B2_ASSERT(0 <= indexA && indexA < arrayA.count); - if (indexA != lastIndex) - { - int movedIndex = arrayA.data[lastIndex]; - arrayA.data[indexA] = movedIndex; - setIndexName.Invoke(arrayB.data[movedIndex], indexA); - } - - arrayA.count -= 1; - } - - private static void InitializeReferenceElements(T[] data, int startIndex, int count) where T : new() - { - if (typeof(T).IsValueType) - { - return; - } - - for (int i = startIndex; i < count; ++i) - { - data[i] = new T(); - } - } - } -} \ No newline at end of file diff --git a/test/Box2D.NET.Test/B2ArrayTests.cs b/test/Box2D.NET.Test/B2ArrayTests.cs index 2d0c88ac..16e9a05b 100644 --- a/test/Box2D.NET.Test/B2ArrayTests.cs +++ b/test/Box2D.NET.Test/B2ArrayTests.cs @@ -1,11 +1,8 @@ -// SPDX-FileCopyrightText: 2025 Erin Catto -// SPDX-FileCopyrightText: 2025 Ikpil Choi(ikpil@naver.com) +// SPDX-FileCopyrightText: 2026 Erin Catto +// SPDX-FileCopyrightText: 2026 Ikpil Choi(ikpil@naver.com) // SPDX-License-Identifier: MIT -using System; -using System.Runtime.InteropServices; using NUnit.Framework; -using Box2D.NET.Test.Primitives; using static Box2D.NET.B2Arrays; using static Box2D.NET.B2Constants; @@ -13,424 +10,499 @@ namespace Box2D.NET.Test; public class B2ArrayTests { - [Test] - public void Test_b2Array_Destroy_Should_ClearDataAndResetCountAndCapacity() + private struct Foo { - // Create array - var array = b2Array_Create(10); - array.count = 5; // simulate added elements - - // Destroy it - b2Array_Destroy(ref array); + public int a; + public float b; + } - // Validate reset - Assert.That(array.data, Is.Null); - Assert.That(array.count, Is.EqualTo(0)); - Assert.That(array.capacity, Is.EqualTo(0)); + private struct Bar + { + public B2Array a; } - [Test] - public void Test_b2Array_Create_Should_InitializeArrayCorrectly_ForAllTypes() + private struct Owner { - // Test with value type (int) - var intArray = b2Array_Create(5); - Assert.That(intArray.capacity, Is.EqualTo(5)); - Assert.That(intArray.count, Is.EqualTo(0)); - Assert.That(intArray.data, Is.Not.Null); - for (int i = 0; i < 5; ++i) - { - Assert.That(intArray.data[i], Is.EqualTo(0)); // default(int) - } + public int index; + } - // Test with reference type (DummyObject) - var objArray = b2Array_Create>(3); - Assert.That(objArray.capacity, Is.EqualTo(3)); - Assert.That(objArray.count, Is.EqualTo(0)); - Assert.That(objArray.data, Is.Not.Null); - for (int i = 0; i < 3; ++i) - { - Assert.That(objArray.data[i], Is.Not.Null); // should be new DummyObject() - Assert.That(objArray.data[i].Value, Is.EqualTo(0)); - } + private struct Entity + { + public int bodyIndex; + public int id; + } - // Test with zero capacity - var emptyArray = b2Array_Create(0); - Assert.That(emptyArray.capacity, Is.EqualTo(0)); - Assert.That(emptyArray.count, Is.EqualTo(0)); - Assert.That(emptyArray.data, Is.Null); // should remain null + private struct Body + { + public int entityIndex; + public float mass; } [Test] - public void Test_b2Array_Reserve_Should_ExpandOnlyIfNewCapacityIsGreater() + public void TestCreateDestroy() { - // Create array with initial capacity - var array = b2Array_Create(8); - array.count = 5; - var originalData = array.data; - - // Reserve with smaller value — should do nothing - b2Array_Reserve(ref array, 4); - - // Validate that nothing changed - Assert.That(array.capacity, Is.EqualTo(8)); - Assert.That(array.count, Is.EqualTo(5)); - Assert.That(array.data, Is.SameAs(originalData)); - - // Reserve with larger value — should grow - b2Array_Reserve(ref array, 16); - - // Validate updated capacity and data - Assert.That(array.capacity, Is.EqualTo(16)); - Assert.That(array.count, Is.EqualTo(5)); - Assert.That(array.data, Is.Not.Null); - Assert.That(array.data.Length, Is.GreaterThanOrEqualTo(16)); - Assert.That(array.data, Is.Not.SameAs(originalData)); + B2Array a = b2Array_Create(); + b2Array_Destroy(ref a); } [Test] - public void Test_b2Array_Clear_Should_ResetCountWithoutTouchingDataOrCapacity() + public void TestAccess() { - // Create and simulate filled array - var array = b2Array_Create(6); - array.count = 4; - array.data[0] = 1; - array.data[1] = 2; - - var originalData = array.data; - - // Clear it - b2Array_Clear(ref array); - - // Validate - Assert.That(array.count, Is.EqualTo(0)); - Assert.That(array.capacity, Is.EqualTo(6)); - Assert.That(array.data, Is.SameAs(originalData)); - Assert.That(array.data[0], Is.EqualTo(1)); // data not zeroed - Assert.That(array.data[1], Is.EqualTo(2)); + B2Array a = b2Array_Create(); + b2Array_Push(ref a, 42); + ref int element = ref b2Array_Get(ref a, 0); + Assert.That(element, Is.EqualTo(42)); + b2Array_Destroy(ref a); } - [Test] - public void Test_b2Array_ByteCount() + public void TestIteration() { - // Test for ValueType (struct) + B2Array a = new B2Array(); + b2Array_Push(ref a, 1); + b2Array_Push(ref a, 2); + b2Array_Push(ref a, 3); + + int sum = 0; + for (int i = 0; i < a.count; ++i) { - // Create array of value type (KeyValuePair) - var array = b2Array_Create(5); + sum += a.data[i]; + } - // Validate byte count - int byteCount = b2Array_ByteCount(ref array); - Assert.That(byteCount, Is.EqualTo(8 * 5)); + Assert.That(sum, Is.EqualTo(6)); + b2Array_Destroy(ref a); + } + + [Test] + public void TestArrayOfStruct() + { + B2Array a = b2Array_Create(); + b2Array_Push(ref a, new Foo { a = 1, b = 5.0f }); + b2Array_Push(ref a, new Foo { a = 2, b = 6.0f }); + b2Array_Push(ref a, new Foo { a = 3, b = 7.0f }); + + int sum1 = 0; + float sum2 = 0.0f; + for (int i = 0; i < a.count; ++i) + { + sum1 += a.data[i].a; + sum2 += a.data[i].b; } - // Test for ReferenceType (object) + Assert.That(sum1, Is.EqualTo(6)); + Assert.That(sum2, Is.EqualTo(18.0f)); + + b2Array_Destroy(ref a); + } + + [Test] + public void TestStructWithArray() + { + Bar a = new Bar(); + a.a = b2Array_Create(); + b2Array_Push(ref a.a, 1); + b2Array_Push(ref a.a, 2); + b2Array_Push(ref a.a, 3); + + int sum1 = 0; + for (int i = 0; i < a.a.count; ++i) { - // Create array of reference type (object) - var array = b2Array_Create(3); + sum1 += a.a.data[i]; + } - // For reference type, the byte count should return -1 - int byteCount = b2Array_ByteCount(ref array); + Assert.That(sum1, Is.EqualTo(6)); - // Validate that byte count returns -1 for reference types - Assert.That(byteCount, Is.EqualTo(-1)); + b2Array_Destroy(ref a.a); + } + + [Test] + public void TestArrayEmplace() + { + B2Array a = new B2Array(); + + for (int i = 0; i < 100; ++i) + { + ref ulong j = ref b2Array_Add(ref a); + j = (ulong)i; } - // Test for PrimitiveType (int) + ulong sum = 0; + for (int i = 0; i < a.count; ++i) { - // Create array of primitive type (int) - var array = b2Array_Create(10); + sum += a.data[i]; + } - // Size of int is 4 bytes - int expectedByteCount = 10 * Marshal.SizeOf(); + Assert.That(sum, Is.EqualTo(100ul * 99ul / 2ul)); - // Validate byte count - int byteCount = b2Array_ByteCount(ref array); - Assert.That(byteCount, Is.EqualTo(expectedByteCount)); - } + b2Array_Destroy(ref a); } [Test] - public void Test_b2Array_Push() + public void TestArrayRemove() { - // Test for pushing a value when the array has sufficient capacity - { - // Create array of int with initial capacity of 5 - var array = b2Array_Create(5); - array.count = 3; // set count to 3 (some values are already added) + B2Array a = new B2Array(); - // Push a new value into the array - b2Array_Push(ref array, 10); + int n = 10; + b2Array_Reserve(ref a, n); + Assert.That(a.capacity == n && a.count == 0, Is.True); - // Validate that the new value was added - Assert.That(array.data[array.count - 1], Is.EqualTo(10)); - Assert.That(array.count, Is.EqualTo(4)); // count should have increased by 1 + for (short i = 0; i < n; ++i) + { + b2Array_Push(ref a, i); } - // Test for pushing a value when the array needs to expand capacity + Assert.That(a.count, Is.EqualTo(n)); + + int sum = 0; + for (int i = 0; i < n; ++i) { - // Create array of int with initial capacity of 2 (small capacity) - var array = b2Array_Create(2); - array.count = 2; // set count to 2 (array is full) + int temp = b2Array_RemoveSwap(ref a, 0); + sum += temp; + } + + Assert.That(sum, Is.EqualTo(n * (n - 1) / 2 - 1)); - // Push a new value, which should trigger a resize of the array - b2Array_Push(ref array, 20); + b2Array_Destroy(ref a); + } - // Validate that the new value was added and the capacity increased - Assert.That(array.data[array.count - 1], Is.EqualTo(20)); - Assert.That(array.count, Is.EqualTo(3)); // count should have increased by 1 - Assert.That(array.capacity, Is.GreaterThan(2)); // capacity should be larger than before + [Test] + public void TestArrayPop() + { + B2Array a = new B2Array(); + + int n = 100; + b2Array_Resize(ref a, n); + Assert.That(a.capacity == n && a.count == n, Is.True); + + for (byte i = 0; i < n; ++i) + { + b2Array_Push(ref a, i); } - // Test for pushing a value into an empty array + int sum = 0; + for (int i = 0; i < n; ++i) { - // Create an empty array of int with initial capacity of 5 - var array = b2Array_Create(5); - array.count = 0; // no elements in the array yet + sum += b2Array_Pop(ref a); + } - // Push a new value into the array - b2Array_Push(ref array, 30); + Assert.That(sum, Is.EqualTo(100 * 99 / 2)); - // Validate that the new value was added - Assert.That(array.data[array.count - 1], Is.EqualTo(30)); - Assert.That(array.count, Is.EqualTo(1)); // count should be 1 after pushing the value - } + b2Array_Destroy(ref a); } [Test] - public void Test_b2Array_Add() + public void TestEmptyArrayProperties() { - // Test when the array has sufficient capacity - { - // Create array of int with initial capacity of 5 - var array = b2Array_Create(5); - array.count = 3; // set count to 3 (some values are already added) - - // Add a new value to the array - ref var addedValue = ref b2Array_Add(ref array); - addedValue = 10; + B2Array a = b2Array_Create(); + Assert.That(a.count, Is.EqualTo(0)); + b2Array_Destroy(ref a); + Assert.That(a.count, Is.EqualTo(0)); + Assert.That(a.capacity, Is.EqualTo(0)); + Assert.That(a.data, Is.Null); + } - // Validate that the new value was added - Assert.That(array.data[array.count - 1], Is.EqualTo(10)); - Assert.That(array.count, Is.EqualTo(4)); // count should have increased by 1 - } + [Test] + public void TestArrayReserveNoop() + { + B2Array a = new B2Array(); + b2Array_Reserve(ref a, 16); + Assert.That(a.capacity, Is.GreaterThanOrEqualTo(16)); + int oldCapacity = a.capacity; + // Reserve with smaller or equal capacity should be a no-op + b2Array_Reserve(ref a, 8); + Assert.That(a.capacity, Is.EqualTo(oldCapacity)); + b2Array_Reserve(ref a, oldCapacity); + Assert.That(a.capacity, Is.EqualTo(oldCapacity)); + b2Array_Destroy(ref a); + } - // Test when the array needs to expand capacity + [Test] + public void TestArrayResizeDown() + { + B2Array a = new B2Array(); + for (int i = 0; i < 10; ++i) { - // Create array of int with initial capacity of 2 (small capacity) - var array = b2Array_Create(2); - array.count = 2; // set count to 2 (array is full) - - // Add a new value, which should trigger a resize of the array - ref var addedValue = ref b2Array_Add(ref array); - addedValue = 20; - - // Validate that the new value was added and the capacity increased - Assert.That(array.data[array.count - 1], Is.EqualTo(20)); - Assert.That(array.count, Is.EqualTo(3)); // count should have increased by 1 - Assert.That(array.capacity, Is.GreaterThan(2)); // capacity should be larger than before + b2Array_Push(ref a, i * 10); } - // Test when adding to an empty array + Assert.That(a.count, Is.EqualTo(10)); + + b2Array_Resize(ref a, 5); + Assert.That(a.count, Is.EqualTo(5)); + + // First 5 elements should be unchanged + for (int i = 0; i < 5; ++i) { - // Create an empty array of int with initial capacity of 5 - var array = b2Array_Create(5); - array.count = 0; // no elements in the array yet - - // Add a new value to the array - ref var addedValue = ref b2Array_Add(ref array); - addedValue = 30; - - // Validate that the new value was added - Assert.That(array.data[array.count - 1], Is.EqualTo(30)); - Assert.That(array.count, Is.EqualTo(1)); // count should be 1 after adding the value - Assert.That(array.capacity, Is.EqualTo(5)); + Assert.That(b2Array_Get(ref a, i), Is.EqualTo(i * 10)); } + + b2Array_Destroy(ref a); } [Test] - public void Test_b2Array_Get() + public void TestArrayResizeUp() { - // Test for valid index within the array's range - { - // Create an array with initial capacity of 5 and set count to 3 - var array = b2Array_Create(5); - array.count = 3; - - // Set some values - array.data[0] = 1; - array.data[1] = 2; - array.data[2] = 3; + B2Array a = new B2Array(); - // Get value at index 1 (valid index) - ref var value = ref b2Array_Get(ref array, 1); + b2Array_Resize(ref a, 10); + Assert.That(a.count, Is.EqualTo(10)); + Assert.That(a.capacity, Is.GreaterThanOrEqualTo(10)); - // Validate that the value is correct - Assert.That(value, Is.EqualTo(2)); // The value at index 1 should be 2 + // Write values into resized slots + for (int i = 0; i < 10; ++i) + { + a.data[i] = i + 1; } - // Test for invalid index (out of range) - Expect IndexOutOfRangeException + // Resize larger, original values preserved + b2Array_Resize(ref a, 20); + Assert.That(a.count, Is.EqualTo(20)); + for (int i = 0; i < 10; ++i) { - // Create an array with initial capacity of 3 and set count to 3 - var array = b2Array_Create(3); - array.count = 3; + Assert.That(a.data[i], Is.EqualTo(i + 1)); + } - // Set some values - array.data[0] = 10; - array.data[1] = 20; - array.data[2] = 30; + b2Array_Destroy(ref a); + } - // Invalid index (out of range) - Assert.Throws((Action)(() => b2Array_Get(ref array, 5))); - } + [Test] + public void TestArrayPopOrder() + { + B2Array a = new B2Array(); + b2Array_Push(ref a, 10); + b2Array_Push(ref a, 20); + b2Array_Push(ref a, 30); + + // Pop returns the last element (LIFO) + Assert.That(b2Array_Pop(ref a), Is.EqualTo(30)); + Assert.That(a.count, Is.EqualTo(2)); + Assert.That(b2Array_Pop(ref a), Is.EqualTo(20)); + Assert.That(a.count, Is.EqualTo(1)); + Assert.That(b2Array_Pop(ref a), Is.EqualTo(10)); + Assert.That(a.count, Is.EqualTo(0)); + + b2Array_Destroy(ref a); } [Test] - public void Test_b2Array_Resize() + public void TestArrayRemoveSwapContents() { - // Test Resize when increasing the array size - { - // Create an array with initial capacity of 3 - var array = b2Array_Create(3); - array.count = 3; + B2Array a = new B2Array(); + b2Array_Push(ref a, 100); + b2Array_Push(ref a, 200); + b2Array_Push(ref a, 300); + b2Array_Push(ref a, 400); + + // Remove middle element: last element swaps into its place + b2Array_RemoveSwap(ref a, 1); + Assert.That(a.count, Is.EqualTo(3)); + Assert.That(b2Array_Get(ref a, 0), Is.EqualTo(100)); + Assert.That(b2Array_Get(ref a, 1), Is.EqualTo(400)); // swapped from end + Assert.That(b2Array_Get(ref a, 2), Is.EqualTo(300)); + + // Remove last element: no swap needed + b2Array_RemoveSwap(ref a, 2); + Assert.That(a.count, Is.EqualTo(2)); + Assert.That(b2Array_Get(ref a, 0), Is.EqualTo(100)); + Assert.That(b2Array_Get(ref a, 1), Is.EqualTo(400)); + + // Remove first element + b2Array_RemoveSwap(ref a, 0); + Assert.That(a.count, Is.EqualTo(1)); + Assert.That(b2Array_Get(ref a, 0), Is.EqualTo(400)); + + // Remove sole element + b2Array_RemoveSwap(ref a, 0); + Assert.That(a.count, Is.EqualTo(0)); + + b2Array_Destroy(ref a); + } - // Set some values - array.data[0] = 1; - array.data[1] = 2; - array.data[2] = 3; + [Test] + public void TestArrayRemoveSwapAllFromEnd() + { + B2Array a = new B2Array(); + b2Array_Push(ref a, 1); + b2Array_Push(ref a, 2); + b2Array_Push(ref a, 3); - // Resize the array to a larger size (5) - b2Array_Resize(ref array, 5); + // Always remove the last element (no swap path) + b2Array_RemoveSwap(ref a, a.count - 1); + Assert.That(a.count, Is.EqualTo(2)); + Assert.That(b2Array_Get(ref a, 0), Is.EqualTo(1)); + Assert.That(b2Array_Get(ref a, 1), Is.EqualTo(2)); - // Validate that the new count is correct - Assert.That(array.count, Is.EqualTo(5)); // After resizing, count should be 5 + b2Array_RemoveSwap(ref a, a.count - 1); + Assert.That(a.count, Is.EqualTo(1)); + Assert.That(b2Array_Get(ref a, 0), Is.EqualTo(1)); - // Validate that the previous data is still intact - Assert.That(array.data[0], Is.EqualTo(1)); - Assert.That(array.data[1], Is.EqualTo(2)); - Assert.That(array.data[2], Is.EqualTo(3)); + b2Array_RemoveSwap(ref a, a.count - 1); + Assert.That(a.count, Is.EqualTo(0)); - // Validate that new slots are initialized - Assert.That(array.data[3], Is.EqualTo(0)); // New slots should be default (0 for int) - Assert.That(array.data[4], Is.EqualTo(0)); - } + b2Array_Destroy(ref a); + } - // Test Resize when decreasing the array size + [Test] + public void TestArrayGrowthIntegrity() + { + B2Array a = new B2Array(); + + // Push many elements to trigger multiple reallocations + for (int i = 0; i < 1000; ++i) { - // Create an array with initial capacity of 5 - var array = b2Array_Create(5); - array.count = 5; - - // Set some values - array.data[0] = 1; - array.data[1] = 2; - array.data[2] = 3; - array.data[3] = 4; - array.data[4] = 5; - - // Resize the array to a smaller size (3) - b2Array_Resize(ref array, 3); - - // Validate that the new count is correct - Assert.That(array.count, Is.EqualTo(3)); // After resizing, count should be 3 - - // Validate that the previous data is still intact - Assert.That(array.data[0], Is.EqualTo(1)); - Assert.That(array.data[1], Is.EqualTo(2)); - Assert.That(array.data[2], Is.EqualTo(3)); - - // Validate that data beyond the resized count is not accessible (should be out of bounds) - Assert.Throws((Action)(() => b2Array_Get(ref array, 3))); + b2Array_Push(ref a, i); } - // Test Resize when setting the count to 0 + Assert.That(a.count, Is.EqualTo(1000)); + Assert.That(a.capacity, Is.GreaterThanOrEqualTo(1000)); + + // Verify every element survived the reallocations + for (int i = 0; i < 1000; ++i) { - // Create an array with initial capacity of 5 - var array = b2Array_Create(5); - array.count = 3; + Assert.That(b2Array_Get(ref a, i), Is.EqualTo(i)); + } + + b2Array_Destroy(ref a); + } + + [Test] + public void TestArrayInterleavedPushPop() + { + B2Array a = new B2Array(); + + b2Array_Push(ref a, 1); + b2Array_Push(ref a, 2); + Assert.That(b2Array_Pop(ref a), Is.EqualTo(2)); + Assert.That(a.count, Is.EqualTo(1)); + + b2Array_Push(ref a, 3); + b2Array_Push(ref a, 4); + Assert.That(a.count, Is.EqualTo(3)); + Assert.That(b2Array_Pop(ref a), Is.EqualTo(4)); + Assert.That(b2Array_Pop(ref a), Is.EqualTo(3)); + Assert.That(b2Array_Pop(ref a), Is.EqualTo(1)); + Assert.That(a.count, Is.EqualTo(0)); + + // Re-use after emptying + b2Array_Push(ref a, 99); + Assert.That(a.count, Is.EqualTo(1)); + Assert.That(b2Array_Get(ref a, 0), Is.EqualTo(99)); + + b2Array_Destroy(ref a); + } + + [Test] + public void TestArrayEmplaceStruct() + { + B2Array a = new B2Array(); - // Resize the array to 0 - b2Array_Resize(ref array, 0); + for (int i = 0; i < 50; ++i) + { + ref Foo f = ref b2Array_Add(ref a); + f.a = i; + f.b = (float)i * 2.0f; + } - // Validate that the count is set to 0 - Assert.That(array.count, Is.EqualTo(0)); // After resizing, count should be 0 + Assert.That(a.count, Is.EqualTo(50)); - // Validate that the array data is not accessible - Assert.Throws((Action)(() => b2Array_Get(ref array, 0))); + for (int i = 0; i < 50; ++i) + { + ref Foo f = ref b2Array_Get(ref a, i); + Assert.That(f.a, Is.EqualTo(i)); + Assert.That(f.b, Is.EqualTo((float)i * 2.0f)); } + + b2Array_Destroy(ref a); } [Test] - public void Test_b2Array_Set() + public void TestArrayPushAfterReserve() { - // Test setting a valid index - var array = b2Array_Create>(5); - array.count = 3; - array.data[0] = new DummyObject(1); - array.data[1] = new DummyObject(2); - array.data[2] = new DummyObject(3); + B2Array a = new B2Array(); - var expectedValue = new DummyObject(99); + // Reserve doesn't change count + b2Array_Reserve(ref a, 50); + Assert.That(a.count, Is.EqualTo(0)); + Assert.That(a.capacity, Is.GreaterThanOrEqualTo(50)); - b2Array_Set(ref array, 1, expectedValue); - Assert.That(array.data[1], Is.EqualTo(expectedValue)); // Verify that value at index 1 is updated to 99 + // Push within reserved capacity (no reallocation expected) + for (int i = 0; i < 50; ++i) + { + b2Array_Push(ref a, i * 3); + } - // Test invalid index (negative index) - Assert.Throws((Action)(() => b2Array_Set(ref array, -1, expectedValue))); + Assert.That(a.count, Is.EqualTo(50)); + for (int i = 0; i < 50; ++i) + { + Assert.That(b2Array_Get(ref a, i), Is.EqualTo(i * 3)); + } - // Test invalid index (out of bounds) - Assert.Throws((Action)(() => b2Array_Set(ref array, 5, expectedValue))); + b2Array_Destroy(ref a); } [Test] - public void Test_b2Array_RemoveSwap() + public void TestArraySingleElement() { - // Test valid removal and swap - var array = b2Array_Create(5); - array.count = 5; - array.data[0] = 1; - array.data[1] = 2; - array.data[2] = 3; - array.data[3] = 4; - array.data[4] = 5; - - int movedIndex = b2Array_RemoveSwap(ref array, 2); - - Assert.That(movedIndex, Is.EqualTo(4)); // The moved index should be the last element's index (4) - Assert.That(array.data[2], Is.EqualTo(5)); // The value at index 2 should now be 5 (swapped with last element) - Assert.That(array.count, Is.EqualTo(4)); // The count should decrease by 1 - - // Test removing from the last index - movedIndex = b2Array_RemoveSwap(ref array, 3); - Assert.That(movedIndex, Is.EqualTo(B2_NULL_INDEX)); // No swap should occur when removing the last element - Assert.That(array.count, Is.EqualTo(3)); // The count should decrease by 1 - - // Test invalid index (negative index) - Assert.Throws((Action)(() => b2Array_RemoveSwap(ref array, -1))); - - // Test invalid index (out of bounds) - Assert.Throws((Action)(() => b2Array_RemoveSwap(ref array, 5))); + B2Array a = new B2Array(); + + ref Foo f = ref b2Array_Add(ref a); + f.a = 7; + f.b = 3.14f; + + Assert.That(a.count, Is.EqualTo(1)); + Assert.That(b2Array_Get(ref a, 0).a, Is.EqualTo(7)); + Assert.That(b2Array_Get(ref a, 0).b, Is.EqualTo(3.14f)); + + Foo popped = b2Array_Pop(ref a); + Assert.That(popped.a, Is.EqualTo(7)); + Assert.That(popped.b, Is.EqualTo(3.14f)); + Assert.That(a.count, Is.EqualTo(0)); + + b2Array_Destroy(ref a); } [Test] - public void Test_b2Array_Pop() + public void TestArrayCreateN() { - // Test popping an element from a non-empty array - var array = b2Array_Create>(5); - array.count = 3; - array.data[0] = new DummyObject(1); - array.data[1] = new DummyObject(2); - array.data[2] = new DummyObject(3); + B2Array a = b2Array_Create(16); + Assert.That(a.count, Is.EqualTo(0)); + Assert.That(a.capacity, Is.EqualTo(16)); + Assert.That(a.data, Is.Not.Null); - DummyObject poppedValue = b2Array_Pop(ref array); + // Verify it behaves like a normal array after creation + for (int i = 0; i < 16; ++i) + { + b2Array_Push(ref a, i * 5); + } - Assert.That(poppedValue.Value, Is.EqualTo(3)); // The popped value should be 3 (last element) - Assert.That(array.count, Is.EqualTo(2)); // The count should decrease by 1 - Assert.That(array.data[2].Value, Is.EqualTo(0)); // The last element should be reset to default (0 for int) + Assert.That(a.count, Is.EqualTo(16)); + for (int i = 0; i < 16; ++i) + { + Assert.That(b2Array_Get(ref a, i), Is.EqualTo(i * 5)); + } - // Test popping from an empty array (should throw an exception) - var emptyArray = b2Array_Create(5); - emptyArray.count = 0; + b2Array_Destroy(ref a); + } - Assert.Throws((Action)(() => b2Array_Pop(ref emptyArray))); + [Test] + public void TestDeclaredArrayTypes() + { + B2Array owners = b2Array_Create(); + B2Array entities = b2Array_Create(); + B2Array bodies = b2Array_Create(); + + b2Array_Push(ref owners, new Owner { index = 1 }); + b2Array_Push(ref entities, new Entity { bodyIndex = 2, id = 3 }); + b2Array_Push(ref bodies, new Body { entityIndex = 4, mass = 5.0f }); + + Assert.That(b2Array_Get(ref owners, 0).index, Is.EqualTo(1)); + Assert.That(b2Array_Get(ref entities, 0).bodyIndex, Is.EqualTo(2)); + Assert.That(b2Array_Get(ref entities, 0).id, Is.EqualTo(3)); + Assert.That(b2Array_Get(ref bodies, 0).entityIndex, Is.EqualTo(4)); + Assert.That(b2Array_Get(ref bodies, 0).mass, Is.EqualTo(5.0f)); + + b2Array_Destroy(ref owners); + b2Array_Destroy(ref entities); + b2Array_Destroy(ref bodies); } -} \ No newline at end of file +} diff --git a/test/Box2D.NET.Test/B2StackTests.cs b/test/Box2D.NET.Test/B2StackTests.cs deleted file mode 100644 index 00e6365b..00000000 --- a/test/Box2D.NET.Test/B2StackTests.cs +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Erin Catto -// SPDX-FileCopyrightText: 2025 Ikpil Choi(ikpil@naver.com) -// SPDX-License-Identifier: MIT - -using NUnit.Framework; -using Box2D.NET.Test.Primitives; -using static Box2D.NET.B2Arrays; - -namespace Box2D.NET.Test; - -public class B2StackTests -{ - [Test] - public void Test_b2StackArray_CreatePushReserveDestroy() - { - var stack = B2StackArrays.b2StackArray_Create(3); - var stackData = stack.stackData; - - B2StackArrays.b2StackArray_Push(ref stack, 1); - B2StackArrays.b2StackArray_Push(ref stack, 2); - B2StackArrays.b2StackArray_Push(ref stack, 3); - - Assert.That(stack.data, Is.SameAs(stackData)); - Assert.That(stack.count, Is.EqualTo(3)); - Assert.That(stack.capacity, Is.EqualTo(3)); - - B2StackArrays.b2StackArray_Push(ref stack, 4); - - Assert.That(stack.data, Is.Not.SameAs(stackData)); - Assert.That(stack.count, Is.EqualTo(4)); - Assert.That(stack.capacity, Is.EqualTo(6)); - Assert.That(stack.data[0], Is.EqualTo(1)); - Assert.That(stack.data[3], Is.EqualTo(4)); - - ref int value = ref B2StackArrays.b2StackArray_Get(ref stack, 2); - Assert.That(value, Is.EqualTo(3)); - - B2StackArrays.b2StackArray_Destroy(ref stack); - - Assert.That(stack.stackData, Is.Null); - Assert.That(stack.data, Is.Null); - Assert.That(stack.count, Is.EqualTo(0)); - Assert.That(stack.capacity, Is.EqualTo(0)); - } - - [Test] - public void Test_b2StackArray_RemoveUpdate() - { - var a = B2StackArrays.b2StackArray_Create(8); - var owners = b2Array_Create(); - - int n = 21; - for (int i = 0; i < n; ++i) - { - ref DummyClass dummyClass = ref b2Array_Add(ref owners); - dummyClass.index = i; - B2StackArrays.b2StackArray_Push(ref a, i); - } - - B2StackArrays.b2RemoveUpdate(ref a, ref owners, 0, x => x.index, (x, idx) => x.index = idx); - b2Array_Get(ref owners, 0).index = -1; - B2StackArrays.b2RemoveUpdate(ref a, ref owners, 3, x => x.index, (x, idx) => x.index = idx); - b2Array_Get(ref owners, 3).index = -1; - B2StackArrays.b2RemoveUpdate(ref a, ref owners, 8, x => x.index, (x, idx) => x.index = idx); - b2Array_Get(ref owners, 8).index = -1; - B2StackArrays.b2RemoveUpdate(ref a, ref owners, 5, x => x.index, (x, idx) => x.index = idx); - b2Array_Get(ref owners, 5).index = -1; - B2StackArrays.b2RemoveUpdate(ref a, ref owners, owners.count - 1, x => x.index, (x, idx) => x.index = idx); - b2Array_Get(ref owners, owners.count - 1).index = -1; - - int count = 0; - for (int i = 0; i < owners.count; ++i) - { - DummyClass dummyClass = b2Array_Get(ref owners, i); - if (dummyClass.index == -1) - { - continue; - } - - int indexA = B2StackArrays.b2StackArray_Get(ref a, dummyClass.index); - Assert.That(indexA, Is.EqualTo(i)); - count += 1; - } - - Assert.That(count, Is.EqualTo(a.count)); - - B2StackArrays.b2StackArray_Destroy(ref a); - b2Array_Destroy(ref owners); - } - - [Test] - public void Test_b2StackArray_RemoveUpdateAll() - { - var a = B2StackArrays.b2StackArray_Create(8); - var owners = b2Array_Create(); - - int n = 5; - for (int i = 0; i < n; ++i) - { - ref DummyClass dummyClass = ref b2Array_Add(ref owners); - dummyClass.index = i; - B2StackArrays.b2StackArray_Push(ref a, i); - } - - // Remove all elements one by one - for (int i = 0; i < n; ++i) - { - // Find a valid owner to remove - int removeIdx = -1; - for (int j = 0; j < owners.count; ++j) - { - if (b2Array_Get(ref owners, j).index != -1) - { - removeIdx = j; - break; - } - } - - if (removeIdx == -1) - { - break; - } - - B2StackArrays.b2RemoveUpdate(ref a, ref owners, removeIdx, x => x.index, (x, idx) => x.index = idx); - b2Array_Get(ref owners, removeIdx).index = -1; - } - - Assert.That(a.count, Is.EqualTo(0)); - - B2StackArrays.b2StackArray_Destroy(ref a); - b2Array_Destroy(ref owners); - } -} \ No newline at end of file