File: src\libraries\System.Private.CoreLib\src\System\Buffers\Binary\BinaryPrimitives.ReverseEndianness.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
 
#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
 
#if TARGET_64BIT
using nint_t = System.Int64;
using nuint_t = System.UInt64;
#else
using nint_t = System.Int32;
using nuint_t = System.UInt32;
#endif
 
namespace System.Buffers.Binary
{
    public static partial class BinaryPrimitives
    {
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="sbyte" /> value, which effectively does nothing for an <see cref="sbyte" />.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The passed-in value, unmodified.</returns>
        /// <remarks>This method effectively does nothing and was added only for consistency.</remarks>
        [CLSCompliant(false)]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static sbyte ReverseEndianness(sbyte value) => value;
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="short" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [Intrinsic]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static short ReverseEndianness(short value) => (short)ReverseEndianness((ushort)value);
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="int" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [Intrinsic]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int ReverseEndianness(int value) => (int)ReverseEndianness((uint)value);
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="long" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [Intrinsic]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static long ReverseEndianness(long value) => (long)ReverseEndianness((ulong)value);
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="nint" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static nint ReverseEndianness(nint value) => (nint)ReverseEndianness((nint_t)value);
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="Int128" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Int128 ReverseEndianness(Int128 value)
        {
            return new Int128(
                ReverseEndianness(value.Lower),
                ReverseEndianness(value.Upper)
            );
        }
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="byte" /> value, which effectively does nothing for an <see cref="byte" />.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The passed-in value, unmodified.</returns>
        /// <remarks>This method effectively does nothing and was added only for consistency.</remarks>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static byte ReverseEndianness(byte value) => value;
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="ushort" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [CLSCompliant(false)]
        [Intrinsic]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ushort ReverseEndianness(ushort value)
        {
            // Don't need to AND with 0xFF00 or 0x00FF since the final
            // cast back to ushort will clear out all bits above [ 15 .. 00 ].
            // This is normally implemented via "movzx eax, ax" on the return.
            // Alternatively, the compiler could elide the movzx instruction
            // entirely if it knows the caller is only going to access "ax"
            // instead of "eax" / "rax" when the function returns.
 
            return (ushort)((value >> 8) + (value << 8));
        }
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="char" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static char ReverseEndianness(char value) => (char)ReverseEndianness((ushort)value);
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="uint" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [CLSCompliant(false)]
        [Intrinsic]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static uint ReverseEndianness(uint value)
        {
            // This takes advantage of the fact that the JIT can detect
            // ROL32 / ROR32 patterns and output the correct intrinsic.
            //
            // Input: value = [ ww xx yy zz ]
            //
            // First line generates : [ ww xx yy zz ]
            //                      & [ 00 FF 00 FF ]
            //                      = [ 00 xx 00 zz ]
            //             ROR32(8) = [ zz 00 xx 00 ]
            //
            // Second line generates: [ ww xx yy zz ]
            //                      & [ FF 00 FF 00 ]
            //                      = [ ww 00 yy 00 ]
            //             ROL32(8) = [ 00 yy 00 ww ]
            //
            //                (sum) = [ zz yy xx ww ]
            //
            // Testing shows that throughput increases if the AND
            // is performed before the ROL / ROR.
 
            return BitOperations.RotateRight(value & 0x00FF00FFu, 8) // xx zz
                + BitOperations.RotateLeft(value & 0xFF00FF00u, 8); // ww yy
        }
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="ulong" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [CLSCompliant(false)]
        [Intrinsic]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ulong ReverseEndianness(ulong value)
        {
            // Operations on 32-bit values have higher throughput than
            // operations on 64-bit values, so decompose.
 
            return ((ulong)ReverseEndianness((uint)value) << 32)
                + ReverseEndianness((uint)(value >> 32));
        }
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="nuint" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [CLSCompliant(false)]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static nuint ReverseEndianness(nuint value) => (nuint)ReverseEndianness((nuint_t)value);
 
        /// <summary>
        /// Reverses a primitive value by performing an endianness swap of the specified <see cref="UInt128" /> value.
        /// </summary>
        /// <param name="value">The value to reverse.</param>
        /// <returns>The reversed value.</returns>
        [CLSCompliant(false)]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static UInt128 ReverseEndianness(UInt128 value)
        {
            return new UInt128(
                ReverseEndianness(value.Lower),
                ReverseEndianness(value.Upper)
            );
        }
 
        /// <summary>Copies every primitive value from <paramref name="source"/> to <paramref name="destination"/>, reversing each primitive by performing an endianness swap as part of writing each.</summary>
        /// <param name="source">The source span to copy.</param>
        /// <param name="destination">The destination to which the source elements should be copied.</param>
        /// <remarks>The source and destination spans may overlap. The same span may be passed as both the source and the destination in order to reverse each element's endianness in place.</remarks>
        /// <exception cref="ArgumentException">The <paramref name="destination"/>'s length is smaller than that of the <paramref name="source"/>.</exception>
        [CLSCompliant(false)]
        public static void ReverseEndianness(ReadOnlySpan<ushort> source, Span<ushort> destination) =>
            ReverseEndianness<short, Int16EndiannessReverser>(MemoryMarshal.Cast<ushort, short>(source), MemoryMarshal.Cast<ushort, short>(destination));
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        public static void ReverseEndianness(ReadOnlySpan<short> source, Span<short> destination) =>
            ReverseEndianness<short, Int16EndiannessReverser>(source, destination);
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        [CLSCompliant(false)]
        public static void ReverseEndianness(ReadOnlySpan<uint> source, Span<uint> destination) =>
            ReverseEndianness<int, Int32EndiannessReverser>(MemoryMarshal.Cast<uint, int>(source), MemoryMarshal.Cast<uint, int>(destination));
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        public static void ReverseEndianness(ReadOnlySpan<int> source, Span<int> destination) =>
            ReverseEndianness<int, Int32EndiannessReverser>(source, destination);
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        [CLSCompliant(false)]
        public static void ReverseEndianness(ReadOnlySpan<ulong> source, Span<ulong> destination) =>
            ReverseEndianness<long, Int64EndiannessReverser>(MemoryMarshal.Cast<ulong, long>(source), MemoryMarshal.Cast<ulong, long>(destination));
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        public static void ReverseEndianness(ReadOnlySpan<long> source, Span<long> destination) =>
            ReverseEndianness<long, Int64EndiannessReverser>(source, destination);
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        [CLSCompliant(false)]
        public static void ReverseEndianness(ReadOnlySpan<nuint> source, Span<nuint> destination) =>
#if TARGET_64BIT
            ReverseEndianness<long, Int64EndiannessReverser>(MemoryMarshal.Cast<nuint, long>(source), MemoryMarshal.Cast<nuint, long>(destination));
#else
            ReverseEndianness<int, Int32EndiannessReverser>(MemoryMarshal.Cast<nuint, int>(source), MemoryMarshal.Cast<nuint, int>(destination));
#endif
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        public static void ReverseEndianness(ReadOnlySpan<nint> source, Span<nint> destination) =>
#if TARGET_64BIT
            ReverseEndianness<long, Int64EndiannessReverser>(MemoryMarshal.Cast<nint, long>(source), MemoryMarshal.Cast<nint, long>(destination));
#else
            ReverseEndianness<int, Int32EndiannessReverser>(MemoryMarshal.Cast<nint, int>(source), MemoryMarshal.Cast<nint, int>(destination));
#endif
 
        private readonly struct Int16EndiannessReverser : IEndiannessReverser<short>
        {
            public static short Reverse(short value) =>
                ReverseEndianness(value);
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static Vector128<short> Reverse(Vector128<short> vector) =>
                Vector128.ShiftLeft(vector, 8) | Vector128.ShiftRightLogical(vector, 8);
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static Vector256<short> Reverse(Vector256<short> vector) =>
                Vector256.ShiftLeft(vector, 8) | Vector256.ShiftRightLogical(vector, 8);
        }
 
        private readonly struct Int32EndiannessReverser : IEndiannessReverser<int>
        {
            public static int Reverse(int value) =>
                ReverseEndianness(value);
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static Vector128<int> Reverse(Vector128<int> vector) =>
                Vector128.Shuffle(vector.AsByte(), Vector128.Create((byte)3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12)).AsInt32();
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static Vector256<int> Reverse(Vector256<int> vector) =>
                Vector256.Shuffle(vector.AsByte(), Vector256.Create((byte)3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, 19, 18, 17, 16, 23, 22, 21, 20, 27, 26, 25, 24, 31, 30, 29, 28)).AsInt32();
        }
 
        private readonly struct Int64EndiannessReverser : IEndiannessReverser<long>
        {
            public static long Reverse(long value) =>
                ReverseEndianness(value);
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static Vector128<long> Reverse(Vector128<long> vector) =>
                Vector128.Shuffle(vector.AsByte(), Vector128.Create((byte)7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8)).AsInt64();
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static Vector256<long> Reverse(Vector256<long> vector) =>
                Vector256.Shuffle(vector.AsByte(), Vector256.Create((byte)7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24)).AsInt64();
        }
 
        private static void ReverseEndianness<T, TReverser>(ReadOnlySpan<T> source, Span<T> destination)
            where T : struct
            where TReverser : IEndiannessReverser<T>
        {
            if (destination.Length < source.Length)
            {
                ThrowDestinationTooSmall();
            }
 
            ref T sourceRef = ref MemoryMarshal.GetReference(source);
            ref T destRef = ref MemoryMarshal.GetReference(destination);
 
            if (Unsafe.AreSame(ref sourceRef, ref destRef) ||
                !source.Overlaps(destination, out int elementOffset) ||
                elementOffset < 0)
            {
                // Either there's no overlap between the source and the destination, or there's overlap but the
                // destination starts at or before the source.  That means we can safely iterate from beginning
                // to end of the source and not have to worry about writing into the destination and clobbering
                // source data we haven't yet read.
 
                int i = 0;
 
                if (Vector256.IsHardwareAccelerated)
                {
                    while (i <= source.Length - Vector256<T>.Count)
                    {
                        Vector256.StoreUnsafe(TReverser.Reverse(Vector256.LoadUnsafe(ref sourceRef, (uint)i)), ref destRef, (uint)i);
                        i += Vector256<T>.Count;
                    }
                }
 
                if (Vector128.IsHardwareAccelerated)
                {
                    while (i <= source.Length - Vector128<T>.Count)
                    {
                        Vector128.StoreUnsafe(TReverser.Reverse(Vector128.LoadUnsafe(ref sourceRef, (uint)i)), ref destRef, (uint)i);
                        i += Vector128<T>.Count;
                    }
                }
 
                while (i < source.Length)
                {
                    Unsafe.Add(ref destRef, i) = TReverser.Reverse(Unsafe.Add(ref sourceRef, i));
                    i++;
                }
            }
            else
            {
                // There's overlap between the source and the destination, and the source starts before the destination.
                // That means if we were to iterate from beginning to end, reading from the source and writing to the
                // destination, we'd overwrite source elements not yet read.  To avoid that, we iterate from end to beginning.
 
                int i = source.Length;
 
                if (Vector256.IsHardwareAccelerated)
                {
                    while (i >= Vector256<T>.Count)
                    {
                        i -= Vector256<T>.Count;
                        Vector256.StoreUnsafe(TReverser.Reverse(Vector256.LoadUnsafe(ref sourceRef, (uint)i)), ref destRef, (uint)i);
                    }
                }
 
                if (Vector128.IsHardwareAccelerated)
                {
                    while (i >= Vector128<T>.Count)
                    {
                        i -= Vector128<T>.Count;
                        Vector128.StoreUnsafe(TReverser.Reverse(Vector128.LoadUnsafe(ref sourceRef, (uint)i)), ref destRef, (uint)i);
                    }
                }
 
                while (i > 0)
                {
                    i--;
                    Unsafe.Add(ref destRef, i) = TReverser.Reverse(Unsafe.Add(ref sourceRef, i));
                }
            }
        }
 
        private interface IEndiannessReverser<T> where T : struct
        {
            static abstract T Reverse(T value);
            static abstract Vector128<T> Reverse(Vector128<T> vector);
            static abstract Vector256<T> Reverse(Vector256<T> vector);
        }
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        [CLSCompliant(false)]
        public static void ReverseEndianness(ReadOnlySpan<UInt128> source, Span<UInt128> destination) =>
            ReverseEndianness(MemoryMarshal.Cast<UInt128, Int128>(source), MemoryMarshal.Cast<UInt128, Int128>(destination));
 
        /// <inheritdoc cref="ReverseEndianness(ReadOnlySpan{ushort}, Span{ushort})" />
        public static void ReverseEndianness(ReadOnlySpan<Int128> source, Span<Int128> destination)
        {
            if (destination.Length < source.Length)
            {
                ThrowDestinationTooSmall();
            }
 
            if (Unsafe.AreSame(ref MemoryMarshal.GetReference(source), ref MemoryMarshal.GetReference(destination)) ||
                !source.Overlaps(destination, out int elementOffset) ||
                elementOffset < 0)
            {
                // Iterate from beginning to end
                for (int i = 0; i < source.Length; i++)
                {
                    destination[i] = ReverseEndianness(source[i]);
                }
            }
            else
            {
                // Iterate from end to beginning
                for (int i = source.Length - 1; i >= 0; i--)
                {
                    destination[i] = ReverseEndianness(source[i]);
                }
            }
        }
 
        [DoesNotReturn]
        private static void ThrowDestinationTooSmall() =>
            throw new ArgumentException(SR.Arg_BufferTooSmall, "destination");
    }
}