Skip to content

Commit

Permalink
Add MemoryExtensions Reverse API to get parity with array (dotnet#26381)
Browse files Browse the repository at this point in the history
* Add MemoryExtensions Reverse API to get parity with array

* Fix tests and add large byte array test

* Remove some redundant tests and the big memory test.
  • Loading branch information
ahsonkhan authored Jan 20, 2018
1 parent 91a8331 commit ac70074
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/System.Memory/ref/System.Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static void CopyTo<T>(this T[] array, System.Span<T> destination) { }
public static bool Overlaps<T>(this System.ReadOnlySpan<T> first, System.ReadOnlySpan<T> second, out int elementOffset) { throw null; }
public static bool Overlaps<T>(this System.Span<T> first, System.ReadOnlySpan<T> second) { throw null; }
public static bool Overlaps<T>(this System.Span<T> first, System.ReadOnlySpan<T> second, out int elementOffset) { throw null; }
public static void Reverse<T>(this System.Span<T> span) { }
public static int SequenceCompareTo<T>(this System.ReadOnlySpan<T> first, System.ReadOnlySpan<T> second) where T : System.IComparable<T> { throw null; }
public static int SequenceCompareTo<T>(this System.Span<T> first, System.ReadOnlySpan<T> second) where T : System.IComparable<T> { throw null; }
public static bool SequenceEqual<T>(this System.ReadOnlySpan<T> first, System.ReadOnlySpan<T> second) where T : System.IEquatable<T> { throw null; }
Expand Down
18 changes: 18 additions & 0 deletions src/System.Memory/src/System/MemoryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,24 @@ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)),
return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(first), first.Length, ref MemoryMarshal.GetReference(second), second.Length);
}

/// <summary>
/// Reverses the sequence of the elements in the entire span.
/// </summary>
public static void Reverse<T>(this Span<T> span)
{
ref T p = ref MemoryMarshal.GetReference(span);
int i = 0;
int j = span.Length - 1;
while (i < j)
{
T temp = Unsafe.Add(ref p, i);
Unsafe.Add(ref p, i) = Unsafe.Add(ref p, j);
Unsafe.Add(ref p, j) = temp;
i++;
j--;
}
}

/// <summary>
/// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
/// </summary>
Expand Down
12 changes: 6 additions & 6 deletions src/System.Memory/tests/Span/Clear.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ public static void ClearByteUnaligned()
var actualSpan = new Span<byte>(actualFull, start, length - start - 1);
actualSpan.Clear();

var actual = actualSpan.ToArray();
var expected = expectedSpan.ToArray();
byte[] actual = actualSpan.ToArray();
byte[] expected = expectedSpan.ToArray();
Assert.Equal<byte>(expected, actual);
Assert.Equal(initial, actualFull[0]);
Assert.Equal(initial, actualFull[length - 1]);
Expand All @@ -83,8 +83,8 @@ public unsafe static void ClearByteUnalignedFixed()
var actualSpan = new Span<byte>(p + start, length - start - 1);
actualSpan.Clear();

var actual = actualSpan.ToArray();
var expected = expectedSpan.ToArray();
byte[] actual = actualSpan.ToArray();
byte[] expected = expectedSpan.ToArray();
Assert.Equal<byte>(expected, actual);
Assert.Equal(initial, actualFull[0]);
Assert.Equal(initial, actualFull[length - 1]);
Expand All @@ -108,8 +108,8 @@ public static void ClearIntPtrOffset()
var actualSpan = new Span<IntPtr>(actualFull, start, length - start - 1);
actualSpan.Clear();

var actual = actualSpan.ToArray();
var expected = expectedSpan.ToArray();
IntPtr[] actual = actualSpan.ToArray();
IntPtr[] expected = expectedSpan.ToArray();
Assert.Equal<IntPtr>(expected, actual);
Assert.Equal(initial, actualFull[0]);
Assert.Equal(initial, actualFull[length - 1]);
Expand Down
4 changes: 2 additions & 2 deletions src/System.Memory/tests/Span/Fill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public static void FillByteUnaligned()
var actualSpan = new Span<byte>(actualFull, start, length - start - 1);
actualSpan.Fill(fill);

var actual = actualSpan.ToArray();
var expected = expectedSpan.ToArray();
byte[] actual = actualSpan.ToArray();
byte[] expected = expectedSpan.ToArray();
Assert.Equal<byte>(expected, actual);
Assert.Equal(0, actualFull[0]);
Assert.Equal(0, actualFull[length - 1]);
Expand Down
219 changes: 219 additions & 0 deletions src/System.Memory/tests/Span/Reverse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Xunit;
using System.Runtime.CompilerServices;
using static System.TestHelpers;

namespace System.SpanTests
{
public static partial class SpanTests
{
[Fact]
public static void ReverseEmpty()
{
var span = Span<byte>.Empty;
span.Reverse();

byte[] actual = { 1, 2, 3, 4 };
byte[] expected = { 1, 2, 3, 4 };

span = actual;
span.Slice(2, 0).Reverse();

Assert.Equal<byte>(expected, span.ToArray());
}

[Fact]
public static void ReverseEmptyWithReference()
{
var span = Span<string>.Empty;
span.Reverse();

string[] actual = { "a", "b", "c", "d" };
string[] expected = { "a", "b", "c", "d" };

span = actual;
span.Slice(2, 0).Reverse();

Assert.Equal<string>(expected, span.ToArray());
}

[Fact]
public static void ReverseByte()
{
for(int length = 0; length < byte.MaxValue; length++)
{
var actual = new byte[length];
for (int i = 0; i < length; i++)
{
actual[i] = (byte)i;
}
byte[] expected = new byte[length];
Array.Copy(actual, expected, length);
Array.Reverse(expected);

var span = new Span<byte>(actual);
span.Reverse();
Assert.Equal<byte>(expected, actual);
}
}

[Fact]
public static void ReverseByteTwiceReturnsOriginal()
{
byte[] actual = { 1, 2, 3, 4, 5 };
byte[] expected = { 1, 2, 3, 4, 5 };

Span<byte> span = actual;
span.Reverse();
span.Reverse();
Assert.Equal<byte>(expected, actual);
}

[Fact]
public static void ReverseByteUnaligned()
{
const int length = 32;
const int offset = 1;
var actualFull = new byte[length];
for (int i = 0; i < length; i++)
{
actualFull[i] = (byte)i;
}
byte[] expectedFull = new byte[length];
Array.Copy(actualFull, expectedFull, length);
Array.Reverse(expectedFull, offset, length - offset - 1);

var expectedSpan = new Span<byte>(expectedFull, offset, length - offset - 1);
var actualSpan = new Span<byte>(actualFull, offset, length - offset - 1);
actualSpan.Reverse();

byte[] actual = actualSpan.ToArray();
byte[] expected = expectedSpan.ToArray();
Assert.Equal<byte>(expected, actual);
Assert.Equal(expectedFull[0], actualFull[0]);
Assert.Equal(expectedFull[length - 1], actualFull[length - 1]);
}

[Fact]
public static void ReverseIntPtrOffset()
{
const int length = 32;
const int offset = 2;
var actualFull = new IntPtr[length];
for (int i = 0; i < length; i++)
{
actualFull[i] = IntPtr.Zero + i;
}
IntPtr[] expectedFull = new IntPtr[length];
Array.Copy(actualFull, expectedFull, length);
Array.Reverse(expectedFull, offset, length - offset);

var expectedSpan = new Span<IntPtr>(expectedFull, offset, length - offset);
var actualSpan = new Span<IntPtr>(actualFull, offset, length - offset);
actualSpan.Reverse();

IntPtr[] actual = actualSpan.ToArray();
IntPtr[] expected = expectedSpan.ToArray();
Assert.Equal<IntPtr>(expected, actual);
Assert.Equal(expectedFull[0], actualFull[0]);
Assert.Equal(expectedFull[1], actualFull[1]);
}

[Fact]
public static void ReverseValueTypeWithoutReferences()
{
const int length = 2048;
int[] actual = new int[length];
for (int i = 0; i < length; i++)
{
actual[i] = i;
}
int[] expected = new int[length];
Array.Copy(actual, expected, length);
Array.Reverse(expected);

var span = new Span<int>(actual);
span.Reverse();
Assert.Equal<int>(expected, actual);
}

[Fact]
public static void ReverseValueTypeWithoutReferencesPointerSize()
{
const int length = 15;
long[] actual = new long[length];
for (int i = 0; i < length; i++)
{
actual[i] = i;
}
long[] expected = new long[length];
Array.Copy(actual, expected, length);
Array.Reverse(expected);

var span = new Span<long>(actual);
span.Reverse();
Assert.Equal<long>(expected, actual);
}

[Fact]
public static void ReverseReferenceType()
{
const int length = 2048;
string[] actual = new string[length];
for (int i = 0; i < length; i++)
{
actual[i] = i.ToString();
}
string[] expected = new string[length];
Array.Copy(actual, expected, length);
Array.Reverse(expected);

var span = new Span<string>(actual);
span.Reverse();
Assert.Equal<string>(expected, actual);
}

[Fact]
public static void ReverseReferenceTwiceReturnsOriginal()
{
string[] actual = { "a1", "b2", "c3" };
string[] expected = { "a1", "b2", "c3" };

var span = new Span<string>(actual);
span.Reverse();
span.Reverse();
Assert.Equal<string>(expected, actual);
}

[Fact]
public static void ReverseEnumType()
{
TestEnum[] actual = { TestEnum.e0, TestEnum.e1, TestEnum.e2 };
TestEnum[] expected = { TestEnum.e2, TestEnum.e1, TestEnum.e0 };

var span = new Span<TestEnum>(actual);
span.Reverse();
Assert.Equal<TestEnum>(expected, actual);
}

[Fact]
public static void ReverseValueTypeWithReferences()
{
TestValueTypeWithReference[] actual = {
new TestValueTypeWithReference() { I = 1, S = "a" },
new TestValueTypeWithReference() { I = 2, S = "b" },
new TestValueTypeWithReference() { I = 3, S = "c" } };
TestValueTypeWithReference[] expected = {
new TestValueTypeWithReference() { I = 3, S = "c" },
new TestValueTypeWithReference() { I = 2, S = "b" },
new TestValueTypeWithReference() { I = 1, S = "a" } };

var span = new Span<TestValueTypeWithReference>(actual);
span.Reverse();
Assert.Equal<TestValueTypeWithReference>(expected, actual);
}
}
}
1 change: 1 addition & 0 deletions src/System.Memory/tests/System.Memory.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<Compile Include="Span\LastIndexOfSequence.char.cs" />
<Compile Include="Span\NonPortableCast.cs" />
<Compile Include="Span\Overflow.cs" />
<Compile Include="Span\Reverse.cs" />
<Compile Include="Span\SequenceCompareTo.T.cs" />
<Compile Include="Span\SequenceCompareTo.byte.cs" />
<Compile Include="Span\SequenceEqual.T.cs" />
Expand Down

0 comments on commit ac70074

Please sign in to comment.