From 080b1e571eedbbcd78a6982b3bef25fa12acd958 Mon Sep 17 00:00:00 2001 From: Anthony Konzel Date: Thu, 5 Jun 2025 12:57:08 -0500 Subject: [PATCH] Allow `foreach` enumeration over SDLArray --- SDL3-CS.Tests/TestSDLArray.cs | 117 ++++++++++++++++++++++++++ SDL3-CS/SDLArray.cs | 18 ++++ SDL3-CS/SDLConstOpaquePointerArray.cs | 18 ++++ SDL3-CS/SDLOpaquePointerArray.cs | 18 ++++ SDL3-CS/SDLPointerArray.cs | 18 ++++ 5 files changed, 189 insertions(+) create mode 100644 SDL3-CS.Tests/TestSDLArray.cs diff --git a/SDL3-CS.Tests/TestSDLArray.cs b/SDL3-CS.Tests/TestSDLArray.cs new file mode 100644 index 0000000..67b2c93 --- /dev/null +++ b/SDL3-CS.Tests/TestSDLArray.cs @@ -0,0 +1,117 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Runtime.InteropServices; +using NUnit.Framework; + +namespace SDL.Tests +{ + [TestFixture] + public class TestSDLArray + { + private static unsafe T* CopyToSdl(T[] array) + where T : unmanaged + { + UIntPtr size = (UIntPtr)(Marshal.SizeOf() * array.Length); + IntPtr target = SDL3.SDL_malloc(size); + + fixed (T* source = array) + { + SDL3.SDL_memcpy(target, (IntPtr)source, size); + } + + return (T*)target; + } + + private static unsafe T** CopyToSdl(T*[] array) + where T : unmanaged + { + UIntPtr size = (UIntPtr)(sizeof(IntPtr) * array.Length); + IntPtr target = SDL3.SDL_malloc(size); + + fixed (T** source = array) + { + SDL3.SDL_memcpy(target, (IntPtr)source, size); + } + + return (T**)target; + } + + [Test] + public unsafe void TestArrayEnumerator() + { + int[] values = [10, 20, 30, 40]; + int* sdlMemory = CopyToSdl(values); + + using var array = new SDLArray(sdlMemory, values.Length); + int index = 0; + + foreach (int i in array) + { + Assert.AreEqual(values[index++], i); + } + } + + [Test] + public unsafe void TestConstOpaquePointerArrayEnumerator() + { + int a = 10, b = 20, c = 30, d = 40; + int*[] values = [&a, &b, &c, &d]; + int** sdlMemory = null; + + // Const pointer arrays are not freed automatically. Since the + // unit test owns the memory, this must be done at the end of the + // test. + + try + { + sdlMemory = CopyToSdl(values); + + var array = new SDLConstOpaquePointerArray(sdlMemory, values.Length); + int index = 0; + + foreach (int* i in array) + { + Assert.AreEqual((IntPtr)values[index++], (IntPtr)i); + } + } + finally + { + if (sdlMemory != null) + SDL3.SDL_free(sdlMemory); + } + } + + [Test] + public unsafe void TestOpaquePointerArrayEnumerator() + { + int a = 10, b = 20, c = 30, d = 40; + int*[] values = [&a, &b, &c, &d]; + int** sdlMemory = CopyToSdl(values); + + using var array = new SDLOpaquePointerArray(sdlMemory, values.Length); + int index = 0; + + foreach (int* i in array) + { + Assert.AreEqual((IntPtr)values[index++], (IntPtr)i); + } + } + + [Test] + public unsafe void TestPointerArrayEnumerator() + { + int a = 10, b = 20, c = 30, d = 40; + int*[] values = [&a, &b, &c, &d]; + int** memory = CopyToSdl(values); + + using var array = new SDLPointerArray(memory, values.Length); + int index = 0; + + foreach (int i in array) + { + Assert.AreEqual(*values[index++], i); + } + } + } +} diff --git a/SDL3-CS/SDLArray.cs b/SDL3-CS/SDLArray.cs index 36cde5c..03bd028 100644 --- a/SDL3-CS/SDLArray.cs +++ b/SDL3-CS/SDLArray.cs @@ -40,6 +40,24 @@ namespace SDL SDL3.SDL_free(array); } + + public Enumerator GetEnumerator() => new Enumerator(this); + + public ref struct Enumerator + { + private readonly SDLArray array; + private int index; + + internal Enumerator(SDLArray array) + { + this.array = array; + index = -1; + } + + public bool MoveNext() => ++index < array.Count; + + public T Current => array[index]; + } } internal static unsafe class SDLArray diff --git a/SDL3-CS/SDLConstOpaquePointerArray.cs b/SDL3-CS/SDLConstOpaquePointerArray.cs index f4854eb..bf79b12 100644 --- a/SDL3-CS/SDLConstOpaquePointerArray.cs +++ b/SDL3-CS/SDLConstOpaquePointerArray.cs @@ -28,5 +28,23 @@ namespace SDL return array[index]; } } + + public Enumerator GetEnumerator() => new Enumerator(this); + + public ref struct Enumerator + { + private readonly SDLConstOpaquePointerArray array; + private int index; + + internal Enumerator(SDLConstOpaquePointerArray array) + { + this.array = array; + index = -1; + } + + public bool MoveNext() => ++index < array.Count; + + public T* Current => array[index]; + } } } diff --git a/SDL3-CS/SDLOpaquePointerArray.cs b/SDL3-CS/SDLOpaquePointerArray.cs index 4123e91..edb6429 100644 --- a/SDL3-CS/SDLOpaquePointerArray.cs +++ b/SDL3-CS/SDLOpaquePointerArray.cs @@ -42,5 +42,23 @@ namespace SDL SDL3.SDL_free(array); } + + public Enumerator GetEnumerator() => new Enumerator(this); + + public ref struct Enumerator + { + private readonly SDLOpaquePointerArray array; + private int index; + + internal Enumerator(SDLOpaquePointerArray array) + { + this.array = array; + index = -1; + } + + public bool MoveNext() => ++index < array.Count; + + public T* Current => array[index]; + } } } diff --git a/SDL3-CS/SDLPointerArray.cs b/SDL3-CS/SDLPointerArray.cs index 621fd8c..d0be981 100644 --- a/SDL3-CS/SDLPointerArray.cs +++ b/SDL3-CS/SDLPointerArray.cs @@ -43,5 +43,23 @@ namespace SDL SDL3.SDL_free(array); } + + public Enumerator GetEnumerator() => new Enumerator(this); + + public ref struct Enumerator + { + private readonly SDLPointerArray array; + private int index; + + internal Enumerator(SDLPointerArray array) + { + this.array = array; + index = -1; + } + + public bool MoveNext() => ++index < array.Count; + + public T Current => array[index]; + } } }