Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
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
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\TlsOverPerCoreLockedStacksArrayPool.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Utilities.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\Reader.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\ReaderBigEndian.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\ReaderLittleEndian.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\WriterBigEndian.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\WriterLittleEndian.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\FormattingHelpers.CountDigits.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Byte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Char.cs" />
Expand Down
126 changes: 126 additions & 0 deletions src/System.Private.CoreLib/shared/System/Buffers/Binary/Reader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// 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 System.Runtime.CompilerServices;

namespace System.Buffers.Binary
{
/// <summary>
/// Reads bytes as primitives with specific endianness
/// </summary>
/// <remarks>
/// For native formats, MemoryExtensions.Read{T}; should be used.
/// Use these helpers when you need to read specific endinanness.
/// </remarks>
public static partial class BinaryPrimitives
{
/// <summary>
/// This is a no-op and added only for consistency.
/// This allows the caller to read a struct of numeric primitives and reverse each field
/// rather than having to skip sbyte fields.
/// </summary>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static sbyte ReverseEndianness(sbyte value)
{
return value;
}

/// <summary>
/// Reverses a primitive value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short ReverseEndianness(short value)
{
return (short)((value & 0x00FF) << 8 | (value & 0xFF00) >> 8);
}

/// <summary>
/// Reverses a primitive value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReverseEndianness(int value) => (int)ReverseEndianness((uint)value);

/// <summary>
/// Reverses a primitive value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long ReverseEndianness(long value) => (long)ReverseEndianness((ulong)value);

/// <summary>
/// This is a no-op and added only for consistency.
/// This allows the caller to read a struct of numeric primitives and reverse each field
/// rather than having to skip byte fields.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte ReverseEndianness(byte value)
{
return value;
}

/// <summary>
/// Reverses a primitive value - performs an endianness swap
/// </summary>
[CLSCompliant(false)]
[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 - performs an endianness swap
/// </summary>
[CLSCompliant(false)]
[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.

uint mask_xx_zz = (value & 0x00FF00FFU);
uint mask_ww_yy = (value & 0xFF00FF00U);
return ((mask_xx_zz >> 8) | (mask_xx_zz << 24))
+ ((mask_ww_yy << 8) | (mask_ww_yy >> 24));
}

/// <summary>
/// Reverses a primitive value - performs an endianness swap
/// </summary>
[CLSCompliant(false)]
[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));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// 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 System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Buffers.Binary
{
public static partial class BinaryPrimitives
{
/// <summary>
/// Reads an Int16 out of a read-only span of bytes as big endian.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short ReadInt16BigEndian(ReadOnlySpan<byte> source)
{
short result = MemoryMarshal.Read<short>(source);
if (BitConverter.IsLittleEndian)
{
result = ReverseEndianness(result);
}
return result;
}

/// <summary>
/// Reads an Int32 out of a read-only span of bytes as big endian.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReadInt32BigEndian(ReadOnlySpan<byte> source)
{
int result = MemoryMarshal.Read<int>(source);
if (BitConverter.IsLittleEndian)
{
result = ReverseEndianness(result);
}
return result;
}

/// <summary>
/// Reads an Int64 out of a read-only span of bytes as big endian.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long ReadInt64BigEndian(ReadOnlySpan<byte> source)
{
long result = MemoryMarshal.Read<long>(source);
if (BitConverter.IsLittleEndian)
{
result = ReverseEndianness(result);
}
return result;
}

/// <summary>
/// Reads a UInt16 out of a read-only span of bytes as big endian.
/// </summary>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ReadUInt16BigEndian(ReadOnlySpan<byte> source)
{
ushort result = MemoryMarshal.Read<ushort>(source);
if (BitConverter.IsLittleEndian)
{
result = ReverseEndianness(result);
}
return result;
}

/// <summary>
/// Reads a UInt32 out of a read-only span of bytes as big endian.
/// </summary>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ReadUInt32BigEndian(ReadOnlySpan<byte> source)
{
uint result = MemoryMarshal.Read<uint>(source);
if (BitConverter.IsLittleEndian)
{
result = ReverseEndianness(result);
}
return result;
}

/// <summary>
/// Reads a UInt64 out of a read-only span of bytes as big endian.
/// </summary>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong ReadUInt64BigEndian(ReadOnlySpan<byte> source)
{
ulong result = MemoryMarshal.Read<ulong>(source);
if (BitConverter.IsLittleEndian)
{
result = ReverseEndianness(result);
}
return result;
}

/// <summary>
/// Reads an Int16 out of a read-only span of bytes as big endian.
/// <returns>If the span is too small to contain an Int16, return false.</returns>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadInt16BigEndian(ReadOnlySpan<byte> source, out short value)
{
bool success = MemoryMarshal.TryRead(source, out value);
if (BitConverter.IsLittleEndian)
{
value = ReverseEndianness(value);
}
return success;
}

/// <summary>
/// Reads an Int32 out of a read-only span of bytes as big endian.
/// <returns>If the span is too small to contain an Int32, return false.</returns>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadInt32BigEndian(ReadOnlySpan<byte> source, out int value)
{
bool success = MemoryMarshal.TryRead(source, out value);
if (BitConverter.IsLittleEndian)
{
value = ReverseEndianness(value);
}
return success;
}

/// <summary>
/// Reads an Int64 out of a read-only span of bytes as big endian.
/// <returns>If the span is too small to contain an Int64, return false.</returns>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadInt64BigEndian(ReadOnlySpan<byte> source, out long value)
{
bool success = MemoryMarshal.TryRead(source, out value);
if (BitConverter.IsLittleEndian)
{
value = ReverseEndianness(value);
}
return success;
}

/// <summary>
/// Reads a UInt16 out of a read-only span of bytes as big endian.
/// <returns>If the span is too small to contain a UInt16, return false.</returns>
/// </summary>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadUInt16BigEndian(ReadOnlySpan<byte> source, out ushort value)
{
bool success = MemoryMarshal.TryRead(source, out value);
if (BitConverter.IsLittleEndian)
{
value = ReverseEndianness(value);
}
return success;
}

/// <summary>
/// Reads a UInt32 out of a read-only span of bytes as big endian.
/// <returns>If the span is too small to contain a UInt32, return false.</returns>
/// </summary>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadUInt32BigEndian(ReadOnlySpan<byte> source, out uint value)
{
bool success = MemoryMarshal.TryRead(source, out value);
if (BitConverter.IsLittleEndian)
{
value = ReverseEndianness(value);
}
return success;
}

/// <summary>
/// Reads a UInt64 out of a read-only span of bytes as big endian.
/// <returns>If the span is too small to contain a UInt64, return false.</returns>
/// </summary>
[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadUInt64BigEndian(ReadOnlySpan<byte> source, out ulong value)
{
bool success = MemoryMarshal.TryRead(source, out value);
if (BitConverter.IsLittleEndian)
{
value = ReverseEndianness(value);
}
return success;
}
}
}

Loading