From 5b9fdf1b2f40a62ef562dad9d3b89bfa6605d316 Mon Sep 17 00:00:00 2001 From: skydevlab Date: Wed, 7 Oct 2026 23:11:02 +0530 Subject: [PATCH] Add Array.GetValue and SetValue overloads taking ReadOnlySpan Fixes #125325 --- .../src/System/Array.cs | 12 ++ .../System.Runtime/ref/System.Runtime.cs | 2 + .../System.Runtime.Tests/System/ArrayTests.cs | 171 ++++++++++++++++++ 3 files changed, 185 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index c364a6b1c67f19..fc2c8f8248311d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -815,6 +815,12 @@ public int GetLowerBound(int dimension) { if (indices == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices); + + return GetValue(new ReadOnlySpan(indices)); + } + + public object? GetValue(params ReadOnlySpan indices) + { if (Rank != indices.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices); @@ -873,6 +879,12 @@ public void SetValue(object? value, params int[] indices) { if (indices == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices); + + SetValue(value, new ReadOnlySpan(indices)); + } + + public void SetValue(object? value, params ReadOnlySpan indices) + { if (Rank != indices.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 7b2fd5e2ea9641..2489f8cd6df48a 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -568,6 +568,7 @@ public static void ForEach(T[] array, System.Action action) { } public object? GetValue(long index1, long index2) { throw null; } public object? GetValue(long index1, long index2, long index3) { throw null; } public object? GetValue(params long[] indices) { throw null; } + public object? GetValue(params System.ReadOnlySpan indices) { throw null; } public static int IndexOf(System.Array array, object? value) { throw null; } public static int IndexOf(System.Array array, object? value, int startIndex) { throw null; } public static int IndexOf(System.Array array, object? value, int startIndex, int count) { throw null; } @@ -594,6 +595,7 @@ public void SetValue(object? value, long index) { } public void SetValue(object? value, long index1, long index2) { } public void SetValue(object? value, long index1, long index2, long index3) { } public void SetValue(object? value, params long[] indices) { } + public void SetValue(object? value, params System.ReadOnlySpan indices) { } public static void Sort(System.Array array) { } public static void Sort(System.Array keys, System.Array? items) { } public static void Sort(System.Array keys, System.Array? items, System.Collections.IComparer? comparer) { } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs index 621185f3a32c6c..eaa93ba957d048 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs @@ -5057,5 +5057,176 @@ private static void InvokeAllocatingLargeArray(Func body) Assert.Equal(RemoteExecutor.SuccessExitCode, exitCode); } + + [Fact] + public static void GetValue_ReadOnlySpan_1D() + { + int[] array = [10, 20, 30]; + ReadOnlySpan index0 = [0]; + ReadOnlySpan index1 = [1]; + ReadOnlySpan index2 = [2]; + + Assert.Equal(10, array.GetValue(index0)); + Assert.Equal(20, array.GetValue(index1)); + Assert.Equal(30, array.GetValue(index2)); + + // stackalloc + Span stackSpan = stackalloc int[1]; + stackSpan[0] = 1; + Assert.Equal(20, array.GetValue(stackSpan)); + + // Rank mismatch (rank 1 vs 0 or 2 indices) + AssertExtensions.Throws(null, () => array.GetValue(ReadOnlySpan.Empty)); + AssertExtensions.Throws(null, () => array.GetValue((ReadOnlySpan)[0, 1])); + + // Out of bounds + Assert.Throws(() => array.GetValue((ReadOnlySpan)[-1])); + Assert.Throws(() => array.GetValue((ReadOnlySpan)[3])); + } + + [Fact] + public static void GetValue_ReadOnlySpan_MultiDimensional() + { + int[,] array2D = new int[2, 3] + { + { 1, 2, 3 }, + { 4, 5, 6 } + }; + + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 3; j++) + { + Assert.Equal(array2D[i, j], array2D.GetValue((ReadOnlySpan)[i, j])); + Span indices = stackalloc int[2] { i, j }; + Assert.Equal(array2D[i, j], array2D.GetValue(indices)); + } + } + + // Rank mismatch (rank 2 vs 1 or 3 indices) + AssertExtensions.Throws(null, () => array2D.GetValue((ReadOnlySpan)[0])); + AssertExtensions.Throws(null, () => array2D.GetValue((ReadOnlySpan)[0, 1, 2])); + + // Out of bounds + Assert.Throws(() => array2D.GetValue((ReadOnlySpan)[-1, 0])); + Assert.Throws(() => array2D.GetValue((ReadOnlySpan)[2, 0])); + Assert.Throws(() => array2D.GetValue((ReadOnlySpan)[0, -1])); + Assert.Throws(() => array2D.GetValue((ReadOnlySpan)[0, 3])); + + // 3D array + int[,,] array3D = new int[2, 2, 2]; + array3D[1, 0, 1] = 42; + Assert.Equal(42, array3D.GetValue((ReadOnlySpan)[1, 0, 1])); + AssertExtensions.Throws(null, () => array3D.GetValue((ReadOnlySpan)[1, 0])); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNonZeroLowerBoundArraySupported))] + public static void GetValue_ReadOnlySpan_NonZeroLowerBound() + { + Array array = Array.CreateInstance(typeof(int), [2, 3], [1, 5]); + array.SetValue(99, 1, 5); + array.SetValue(100, 2, 7); + + Assert.Equal(99, array.GetValue((ReadOnlySpan)[1, 5])); + Assert.Equal(100, array.GetValue((ReadOnlySpan)[2, 7])); + + Assert.Throws(() => array.GetValue((ReadOnlySpan)[0, 5])); + Assert.Throws(() => array.GetValue((ReadOnlySpan)[1, 4])); + Assert.Throws(() => array.GetValue((ReadOnlySpan)[3, 5])); + Assert.Throws(() => array.GetValue((ReadOnlySpan)[1, 8])); + } + + [Fact] + public static void SetValue_ReadOnlySpan_1D() + { + int[] array = new int[3]; + array.SetValue(10, (ReadOnlySpan)[0]); + array.SetValue(20, (ReadOnlySpan)[1]); + array.SetValue(30, (ReadOnlySpan)[2]); + + Assert.Equal(10, array[0]); + Assert.Equal(20, array[1]); + Assert.Equal(30, array[2]); + + // stackalloc + Span stackSpan = stackalloc int[1] { 1 }; + array.SetValue(99, stackSpan); + Assert.Equal(99, array[1]); + + // Null value to value type + array.SetValue(null, (ReadOnlySpan)[1]); + Assert.Equal(0, array[1]); + + // Reference type array + string?[] stringArray = new string?[2]; + stringArray.SetValue("hello", (ReadOnlySpan)[0]); + Assert.Equal("hello", stringArray[0]); + stringArray.SetValue(null, (ReadOnlySpan)[0]); + Assert.Null(stringArray[0]); + + // Incompatible type + Assert.Throws(() => array.SetValue("invalid", (ReadOnlySpan)[0])); + + // Rank mismatch (rank 1 vs 0 or 2 indices) + AssertExtensions.Throws(null, () => array.SetValue(1, ReadOnlySpan.Empty)); + AssertExtensions.Throws(null, () => array.SetValue(1, (ReadOnlySpan)[0, 1])); + + // Out of bounds + Assert.Throws(() => array.SetValue(1, (ReadOnlySpan)[-1])); + Assert.Throws(() => array.SetValue(1, (ReadOnlySpan)[3])); + } + + [Fact] + public static void SetValue_ReadOnlySpan_MultiDimensional() + { + int[,] array2D = new int[2, 3]; + int counter = 1; + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 3; j++) + { + array2D.SetValue(counter, (ReadOnlySpan)[i, j]); + Assert.Equal(counter, array2D[i, j]); + counter++; + } + } + + // stackalloc + Span indices = stackalloc int[2] { 1, 2 }; + array2D.SetValue(42, indices); + Assert.Equal(42, array2D[1, 2]); + + // Rank mismatch (rank 2 vs 1 or 3 indices) + AssertExtensions.Throws(null, () => array2D.SetValue(1, (ReadOnlySpan)[0])); + AssertExtensions.Throws(null, () => array2D.SetValue(1, (ReadOnlySpan)[0, 1, 2])); + + // Out of bounds + Assert.Throws(() => array2D.SetValue(1, (ReadOnlySpan)[-1, 0])); + Assert.Throws(() => array2D.SetValue(1, (ReadOnlySpan)[2, 0])); + Assert.Throws(() => array2D.SetValue(1, (ReadOnlySpan)[0, -1])); + Assert.Throws(() => array2D.SetValue(1, (ReadOnlySpan)[0, 3])); + + // 3D array + int[,,] array3D = new int[2, 2, 2]; + array3D.SetValue(88, (ReadOnlySpan)[1, 0, 1]); + Assert.Equal(88, array3D[1, 0, 1]); + AssertExtensions.Throws(null, () => array3D.SetValue(88, (ReadOnlySpan)[1, 0])); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNonZeroLowerBoundArraySupported))] + public static void SetValue_ReadOnlySpan_NonZeroLowerBound() + { + Array array = Array.CreateInstance(typeof(int), [2, 3], [1, 5]); + array.SetValue(99, (ReadOnlySpan)[1, 5]); + array.SetValue(100, (ReadOnlySpan)[2, 7]); + + Assert.Equal(99, array.GetValue(1, 5)); + Assert.Equal(100, array.GetValue(2, 7)); + + Assert.Throws(() => array.SetValue(1, (ReadOnlySpan)[0, 5])); + Assert.Throws(() => array.SetValue(1, (ReadOnlySpan)[1, 4])); + Assert.Throws(() => array.SetValue(1, (ReadOnlySpan)[3, 5])); + Assert.Throws(() => array.SetValue(1, (ReadOnlySpan)[1, 8])); + } } }