Skip to content

Commit 7de0d40

Browse files
authored
Add BinaryPrimitives.ReverseEndianness for spans of elements (#76339)
1 parent be87e80 commit 7de0d40

File tree

5 files changed

+403
-16
lines changed

5 files changed

+403
-16
lines changed

src/libraries/System.Memory/ref/System.Memory.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,27 @@ public static partial class BinaryPrimitives
442442
public static uint ReverseEndianness(uint value) { throw null; }
443443
[System.CLSCompliantAttribute(false)]
444444
public static ulong ReverseEndianness(ulong value) { throw null; }
445+
public static nint ReverseEndianness(nint value) { throw null; }
446+
[System.CLSCompliantAttribute(false)]
447+
public static nuint ReverseEndianness(nuint value) { throw null; }
448+
public static System.Int128 ReverseEndianness(System.Int128 value) { throw null; }
449+
[System.CLSCompliantAttribute(false)]
450+
public static System.UInt128 ReverseEndianness(System.UInt128 value) { throw null; }
451+
public static void ReverseEndianness(System.ReadOnlySpan<int> source, System.Span<int> destination) { }
452+
public static void ReverseEndianness(System.ReadOnlySpan<Int128> source, System.Span<Int128> destination) { }
453+
public static void ReverseEndianness(System.ReadOnlySpan<long> source, System.Span<long> destination) { }
454+
public static void ReverseEndianness(System.ReadOnlySpan<nint> source, System.Span<nint> destination) { }
455+
public static void ReverseEndianness(System.ReadOnlySpan<short> source, System.Span<short> destination) { }
456+
[System.CLSCompliant(false)]
457+
public static void ReverseEndianness(System.ReadOnlySpan<nuint> source, System.Span<nuint> destination) { }
458+
[System.CLSCompliant(false)]
459+
public static void ReverseEndianness(System.ReadOnlySpan<uint> source, System.Span<uint> destination) { }
460+
[System.CLSCompliant(false)]
461+
public static void ReverseEndianness(System.ReadOnlySpan<UInt128> source, System.Span<UInt128> destination) { }
462+
[System.CLSCompliant(false)]
463+
public static void ReverseEndianness(System.ReadOnlySpan<ulong> source, System.Span<ulong> destination) { }
464+
[System.CLSCompliant(false)]
465+
public static void ReverseEndianness(System.ReadOnlySpan<ushort> source, System.Span<ushort> destination) { }
445466
public static bool TryReadDoubleBigEndian(System.ReadOnlySpan<byte> source, out double value) { throw null; }
446467
public static bool TryReadDoubleLittleEndian(System.ReadOnlySpan<byte> source, out double value) { throw null; }
447468
public static bool TryReadHalfBigEndian(System.ReadOnlySpan<byte> source, out System.Half value) { throw null; }

src/libraries/System.Memory/tests/Binary/ReverseEndiannessUnitTests.cs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Numerics;
7+
using System.Runtime.InteropServices;
48
using Xunit;
59

610
namespace System.Buffers.Binary.Tests
@@ -41,6 +45,13 @@ public void ReverseEndianness_Int32AndUInt32(uint a, uint b)
4145
Assert.Equal((int)a, BinaryPrimitives.ReverseEndianness((int)b));
4246
Assert.Equal(b, BinaryPrimitives.ReverseEndianness(a));
4347
Assert.Equal(a, BinaryPrimitives.ReverseEndianness(b));
48+
if (IntPtr.Size == 4)
49+
{
50+
Assert.Equal((nint)b, BinaryPrimitives.ReverseEndianness((nint)a));
51+
Assert.Equal((nint)a, BinaryPrimitives.ReverseEndianness((nint)b));
52+
Assert.Equal((nuint)b, BinaryPrimitives.ReverseEndianness((nuint)a));
53+
Assert.Equal((nuint)a, BinaryPrimitives.ReverseEndianness((nuint)b));
54+
}
4455
}
4556

4657
[Theory]
@@ -52,6 +63,129 @@ public void ReverseEndianness_Int64AndUInt64(ulong a, ulong b)
5263
Assert.Equal((long)a, BinaryPrimitives.ReverseEndianness((long)b));
5364
Assert.Equal(b, BinaryPrimitives.ReverseEndianness(a));
5465
Assert.Equal(a, BinaryPrimitives.ReverseEndianness(b));
66+
if (IntPtr.Size == 8)
67+
{
68+
Assert.Equal((nint)b, BinaryPrimitives.ReverseEndianness((nint)a));
69+
Assert.Equal((nint)a, BinaryPrimitives.ReverseEndianness((nint)b));
70+
Assert.Equal((nuint)b, BinaryPrimitives.ReverseEndianness((nuint)a));
71+
Assert.Equal((nuint)a, BinaryPrimitives.ReverseEndianness((nuint)b));
72+
}
73+
}
74+
75+
[Theory]
76+
[InlineData(0x0123456789ABCDEF, 0xABCDEF0123456789)]
77+
public void ReverseEndianness_Int128AndUInt128(ulong aLower, ulong aUpper)
78+
{
79+
Int128 original = new Int128(aLower, aUpper);
80+
Int128 reversed = new Int128(BinaryPrimitives.ReverseEndianness(aUpper), BinaryPrimitives.ReverseEndianness(aLower));
81+
82+
Assert.Equal(reversed, BinaryPrimitives.ReverseEndianness(original));
83+
Assert.Equal((UInt128)reversed, BinaryPrimitives.ReverseEndianness((UInt128)original));
84+
}
85+
86+
public static IEnumerable<object[]> ReverseEndianness_Span_MemberData()
87+
{
88+
var r = new Random(42);
89+
foreach (int length in Enumerable.Range(0, 36))
90+
{
91+
yield return new object[] { Enumerable.Range(0, length).Select(_ => (ushort)r.Next(int.MinValue, int.MaxValue)).ToArray() };
92+
yield return new object[] { Enumerable.Range(0, length).Select(_ => (short)r.Next(int.MinValue, int.MaxValue)).ToArray() };
93+
yield return new object[] { Enumerable.Range(0, length).Select(_ => (uint)r.Next(int.MinValue, int.MaxValue)).ToArray() };
94+
yield return new object[] { Enumerable.Range(0, length).Select(_ => r.Next(int.MinValue, int.MaxValue)).ToArray() };
95+
yield return new object[] { Enumerable.Range(0, length).Select(_ => (ulong)r.NextInt64(long.MinValue, long.MaxValue)).ToArray() };
96+
yield return new object[] { Enumerable.Range(0, length).Select(_ => r.NextInt64(long.MinValue, long.MaxValue)).ToArray() };
97+
yield return new object[] { Enumerable.Range(0, length).Select(_ => (nuint)r.NextInt64(long.MinValue, long.MaxValue)).ToArray() };
98+
yield return new object[] { Enumerable.Range(0, length).Select(_ => (nint)r.NextInt64(long.MinValue, long.MaxValue)).ToArray() };
99+
yield return new object[] { Enumerable.Range(0, length).Select(_ => new UInt128((ulong)r.NextInt64(long.MinValue, long.MaxValue), (ulong)r.NextInt64(long.MinValue, long.MaxValue))).ToArray() };
100+
yield return new object[] { Enumerable.Range(0, length).Select(_ => new Int128((ulong)r.NextInt64(long.MinValue, long.MaxValue), (ulong)r.NextInt64(long.MinValue, long.MaxValue))).ToArray() };
101+
}
102+
}
103+
104+
[Theory]
105+
[MemberData(nameof(ReverseEndianness_Span_MemberData))]
106+
public void ReverseEndianness_Span_AllElementsReversed<T>(T[] original) where T : struct, INumber<T>
107+
{
108+
T[] expected = original.Select(ReverseEndianness).ToArray();
109+
T[] originalCopy = (T[])original.Clone();
110+
111+
T[] actual1 = (T[])original.Clone();
112+
T[] actual2 = new T[original.Length];
113+
T[] actual3 = new T[original.Length + 1];
114+
115+
// In-place
116+
ReverseEndianness<T>(actual1, actual1);
117+
Assert.Equal(expected, actual1);
118+
119+
// Different destination
120+
ReverseEndianness<T>(original, actual2);
121+
Assert.Equal(originalCopy, original);
122+
Assert.Equal(expected, actual2);
123+
124+
// Different larger destination
125+
ReverseEndianness<T>(original, actual3);
126+
Assert.Equal(originalCopy, original);
127+
Assert.Equal(expected, actual3[0..^1]);
128+
Assert.Equal(default, actual3[^1]);
129+
130+
foreach (int offset in new[] { 1, 2, 3 })
131+
{
132+
if (original.Length > offset)
133+
{
134+
// In-place shifted +offset
135+
expected = original.AsSpan(0, original.Length - offset).ToArray().Select(ReverseEndianness).ToArray();
136+
actual1 = (T[])original.Clone();
137+
ReverseEndianness(actual1.AsSpan(0, actual1.Length - offset), actual1.AsSpan(offset));
138+
Assert.Equal(expected, actual1.AsSpan(offset).ToArray());
139+
for (int i = 0; i < offset; i++)
140+
{
141+
Assert.Equal(original[i], actual1[i]);
142+
}
143+
144+
// In-place shifted -offset
145+
expected = original.AsSpan(offset).ToArray().Select(ReverseEndianness).ToArray();
146+
actual2 = (T[])original.Clone();
147+
ReverseEndianness(actual2.AsSpan(offset), actual2.AsSpan(0, actual2.Length - offset));
148+
Assert.Equal(expected, actual2.AsSpan(0, actual2.Length - offset).ToArray());
149+
Assert.Equal(original[^offset], actual2[^offset]);
150+
}
151+
}
152+
153+
// Throws if the destination is too short
154+
if (original.Length > 0)
155+
{
156+
T[] destination = new T[original.Length - 1];
157+
AssertExtensions.Throws<ArgumentException>("destination", () => ReverseEndianness<T>(original, destination));
158+
}
159+
}
160+
161+
private static T ReverseEndianness<T>(T value)
162+
{
163+
if (typeof(T) == typeof(ushort)) return (T)(object)BinaryPrimitives.ReverseEndianness((ushort)(object)value);
164+
if (typeof(T) == typeof(short)) return (T)(object)BinaryPrimitives.ReverseEndianness((short)(object)value);
165+
if (typeof(T) == typeof(uint)) return (T)(object)BinaryPrimitives.ReverseEndianness((uint)(object)value);
166+
if (typeof(T) == typeof(int)) return (T)(object)BinaryPrimitives.ReverseEndianness((int)(object)value);
167+
if (typeof(T) == typeof(ulong)) return (T)(object)BinaryPrimitives.ReverseEndianness((ulong)(object)value);
168+
if (typeof(T) == typeof(long)) return (T)(object)BinaryPrimitives.ReverseEndianness((long)(object)value);
169+
if (typeof(T) == typeof(nuint)) return (T)(object)BinaryPrimitives.ReverseEndianness((nuint)(object)value);
170+
if (typeof(T) == typeof(nint)) return (T)(object)BinaryPrimitives.ReverseEndianness((nint)(object)value);
171+
if (typeof(T) == typeof(UInt128)) return (T)(object)BinaryPrimitives.ReverseEndianness((UInt128)(object)value);
172+
if (typeof(T) == typeof(Int128)) return (T)(object)BinaryPrimitives.ReverseEndianness((Int128)(object)value);
173+
throw new Exception($"Unexpected type {typeof(T)}");
174+
}
175+
176+
private static void ReverseEndianness<T>(ReadOnlySpan<T> source, Span<T> destination) where T : struct
177+
{
178+
if (typeof(T) == typeof(ushort)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, ushort>(source), MemoryMarshal.Cast<T, ushort>(destination)); return; }
179+
if (typeof(T) == typeof(short)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, short>(source), MemoryMarshal.Cast<T, short>(destination)); return; }
180+
if (typeof(T) == typeof(uint)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, uint>(source), MemoryMarshal.Cast<T, uint>(destination)); return; }
181+
if (typeof(T) == typeof(int)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, int>(source), MemoryMarshal.Cast<T, int>(destination)); return; }
182+
if (typeof(T) == typeof(ulong)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, ulong>(source), MemoryMarshal.Cast<T, ulong>(destination)); return; }
183+
if (typeof(T) == typeof(long)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, long>(source), MemoryMarshal.Cast<T, long>(destination)); return; }
184+
if (typeof(T) == typeof(nuint)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, nuint>(source), MemoryMarshal.Cast<T, nuint>(destination)); return; }
185+
if (typeof(T) == typeof(nint)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, nint>(source), MemoryMarshal.Cast<T, nint>(destination)); return; }
186+
if (typeof(T) == typeof(UInt128)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, UInt128>(source), MemoryMarshal.Cast<T, UInt128>(destination)); return; }
187+
if (typeof(T) == typeof(Int128)) { BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<T, Int128>(source), MemoryMarshal.Cast<T, Int128>(destination)); return; }
188+
throw new Exception($"Unexpected type {typeof(T)}");
55189
}
56190
}
57191
}

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
<Compile Include="$(MSBuildThisFileDirectory)System\Buffer.cs" />
104104
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPool.cs" />
105105
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\ArrayPoolEventSource.cs" />
106+
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\BinaryPrimitives.ReverseEndianness.cs" />
106107
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\Reader.cs" />
107108
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\ReaderBigEndian.cs" />
108109
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\ReaderLittleEndian.cs" />

0 commit comments

Comments
 (0)