Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,12 @@ public int GetLowerBound(int dimension)
{
if (indices == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);

return GetValue(new ReadOnlySpan<int>(indices));
}

public object? GetValue(params ReadOnlySpan<int> indices)
{
if (Rank != indices.Length)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);

Expand Down Expand Up @@ -873,6 +879,12 @@ public void SetValue(object? value, params int[] indices)
{
if (indices == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);

SetValue(value, new ReadOnlySpan<int>(indices));
}

public void SetValue(object? value, params ReadOnlySpan<int> indices)
{
if (Rank != indices.Length)
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);

Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ public static void ForEach<T>(T[] array, System.Action<T> 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<int> 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; }
Expand All @@ -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<int> 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) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5057,5 +5057,176 @@ private static void InvokeAllocatingLargeArray(Func<int> body)

Assert.Equal(RemoteExecutor.SuccessExitCode, exitCode);
}

[Fact]
public static void GetValue_ReadOnlySpan_1D()
{
int[] array = [10, 20, 30];
ReadOnlySpan<int> index0 = [0];
ReadOnlySpan<int> index1 = [1];
ReadOnlySpan<int> index2 = [2];

Assert.Equal(10, array.GetValue(index0));
Assert.Equal(20, array.GetValue(index1));
Assert.Equal(30, array.GetValue(index2));

// stackalloc
Span<int> stackSpan = stackalloc int[1];
stackSpan[0] = 1;
Assert.Equal(20, array.GetValue(stackSpan));

// Rank mismatch (rank 1 vs 0 or 2 indices)
AssertExtensions.Throws<ArgumentException>(null, () => array.GetValue(ReadOnlySpan<int>.Empty));
AssertExtensions.Throws<ArgumentException>(null, () => array.GetValue((ReadOnlySpan<int>)[0, 1]));

// Out of bounds
Assert.Throws<IndexOutOfRangeException>(() => array.GetValue((ReadOnlySpan<int>)[-1]));
Assert.Throws<IndexOutOfRangeException>(() => array.GetValue((ReadOnlySpan<int>)[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<int>)[i, j]));
Span<int> 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<ArgumentException>(null, () => array2D.GetValue((ReadOnlySpan<int>)[0]));
AssertExtensions.Throws<ArgumentException>(null, () => array2D.GetValue((ReadOnlySpan<int>)[0, 1, 2]));

// Out of bounds
Assert.Throws<IndexOutOfRangeException>(() => array2D.GetValue((ReadOnlySpan<int>)[-1, 0]));
Assert.Throws<IndexOutOfRangeException>(() => array2D.GetValue((ReadOnlySpan<int>)[2, 0]));
Assert.Throws<IndexOutOfRangeException>(() => array2D.GetValue((ReadOnlySpan<int>)[0, -1]));
Assert.Throws<IndexOutOfRangeException>(() => array2D.GetValue((ReadOnlySpan<int>)[0, 3]));

// 3D array
int[,,] array3D = new int[2, 2, 2];
array3D[1, 0, 1] = 42;
Assert.Equal(42, array3D.GetValue((ReadOnlySpan<int>)[1, 0, 1]));
AssertExtensions.Throws<ArgumentException>(null, () => array3D.GetValue((ReadOnlySpan<int>)[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<int>)[1, 5]));
Assert.Equal(100, array.GetValue((ReadOnlySpan<int>)[2, 7]));

Assert.Throws<IndexOutOfRangeException>(() => array.GetValue((ReadOnlySpan<int>)[0, 5]));
Assert.Throws<IndexOutOfRangeException>(() => array.GetValue((ReadOnlySpan<int>)[1, 4]));
Assert.Throws<IndexOutOfRangeException>(() => array.GetValue((ReadOnlySpan<int>)[3, 5]));
Assert.Throws<IndexOutOfRangeException>(() => array.GetValue((ReadOnlySpan<int>)[1, 8]));
}

[Fact]
public static void SetValue_ReadOnlySpan_1D()
{
int[] array = new int[3];
array.SetValue(10, (ReadOnlySpan<int>)[0]);
array.SetValue(20, (ReadOnlySpan<int>)[1]);
array.SetValue(30, (ReadOnlySpan<int>)[2]);

Assert.Equal(10, array[0]);
Assert.Equal(20, array[1]);
Assert.Equal(30, array[2]);

// stackalloc
Span<int> stackSpan = stackalloc int[1] { 1 };
array.SetValue(99, stackSpan);
Assert.Equal(99, array[1]);

// Null value to value type
array.SetValue(null, (ReadOnlySpan<int>)[1]);
Assert.Equal(0, array[1]);

// Reference type array
string?[] stringArray = new string?[2];
stringArray.SetValue("hello", (ReadOnlySpan<int>)[0]);
Assert.Equal("hello", stringArray[0]);
stringArray.SetValue(null, (ReadOnlySpan<int>)[0]);
Assert.Null(stringArray[0]);

// Incompatible type
Assert.Throws<InvalidCastException>(() => array.SetValue("invalid", (ReadOnlySpan<int>)[0]));

// Rank mismatch (rank 1 vs 0 or 2 indices)
AssertExtensions.Throws<ArgumentException>(null, () => array.SetValue(1, ReadOnlySpan<int>.Empty));
AssertExtensions.Throws<ArgumentException>(null, () => array.SetValue(1, (ReadOnlySpan<int>)[0, 1]));

// Out of bounds
Assert.Throws<IndexOutOfRangeException>(() => array.SetValue(1, (ReadOnlySpan<int>)[-1]));
Assert.Throws<IndexOutOfRangeException>(() => array.SetValue(1, (ReadOnlySpan<int>)[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<int>)[i, j]);
Assert.Equal(counter, array2D[i, j]);
counter++;
}
}

// stackalloc
Span<int> 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<ArgumentException>(null, () => array2D.SetValue(1, (ReadOnlySpan<int>)[0]));
AssertExtensions.Throws<ArgumentException>(null, () => array2D.SetValue(1, (ReadOnlySpan<int>)[0, 1, 2]));

// Out of bounds
Assert.Throws<IndexOutOfRangeException>(() => array2D.SetValue(1, (ReadOnlySpan<int>)[-1, 0]));
Assert.Throws<IndexOutOfRangeException>(() => array2D.SetValue(1, (ReadOnlySpan<int>)[2, 0]));
Assert.Throws<IndexOutOfRangeException>(() => array2D.SetValue(1, (ReadOnlySpan<int>)[0, -1]));
Assert.Throws<IndexOutOfRangeException>(() => array2D.SetValue(1, (ReadOnlySpan<int>)[0, 3]));

// 3D array
int[,,] array3D = new int[2, 2, 2];
array3D.SetValue(88, (ReadOnlySpan<int>)[1, 0, 1]);
Assert.Equal(88, array3D[1, 0, 1]);
AssertExtensions.Throws<ArgumentException>(null, () => array3D.SetValue(88, (ReadOnlySpan<int>)[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<int>)[1, 5]);
array.SetValue(100, (ReadOnlySpan<int>)[2, 7]);

Assert.Equal(99, array.GetValue(1, 5));
Assert.Equal(100, array.GetValue(2, 7));

Assert.Throws<IndexOutOfRangeException>(() => array.SetValue(1, (ReadOnlySpan<int>)[0, 5]));
Assert.Throws<IndexOutOfRangeException>(() => array.SetValue(1, (ReadOnlySpan<int>)[1, 4]));
Assert.Throws<IndexOutOfRangeException>(() => array.SetValue(1, (ReadOnlySpan<int>)[3, 5]));
Assert.Throws<IndexOutOfRangeException>(() => array.SetValue(1, (ReadOnlySpan<int>)[1, 8]));
}
}
}
Loading