Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Add Span<T> Binary Reader/Writer APIs (#24400)
Browse files Browse the repository at this point in the history
* Adding skeleton for Binary APIs.

* Adding Binary APIs and tests.

* Adding performance tests.

* Fix reference and add perf test project to solution.

* Addressing feedback from API Review

* Renaming Read/Write and verifying T is blittable.

* Adding helper which validates that the type T is blittable.

* Move Binary APIs from Buffers.dll to Memory.dll.

* Fix Write APIs and use correct condition for is partial facade.

* Add reference to S.R.Extensions for BitConverter for netstandard1.1

* Adding test for reading ROSpan into a struct with references.
  • Loading branch information
ahsonkhan authored Oct 12, 2017
1 parent c910a3d commit 563ce5f
Show file tree
Hide file tree
Showing 15 changed files with 2,197 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/System.Buffers/ref/System.Buffers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ public abstract class ArrayPool<T>
public abstract T[] Rent(int minimumLength);
public abstract void Return(T[] array, bool clearArray = false);
}
}
}
77 changes: 77 additions & 0 deletions src/System.Memory/ref/System.Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,81 @@ public abstract class OwnedMemory<T> : IDisposable, IRetainable
public abstract void Retain();
protected internal abstract bool TryGetArray(out ArraySegment<T> arraySegment);
}
}

namespace System.Buffers.Binary
{
public static class BinaryPrimitives
{
public static sbyte ReverseEndianness(sbyte value) { throw null; }
public static byte ReverseEndianness(byte value) { throw null; }
public static short ReverseEndianness(short value) { throw null; }
public static ushort ReverseEndianness(ushort value) { throw null; }
public static int ReverseEndianness(int value) { throw null; }
public static uint ReverseEndianness(uint value) { throw null; }
public static long ReverseEndianness(long value) { throw null; }
public static ulong ReverseEndianness(ulong value) { throw null; }

public static T ReadMachineEndian<T>(ReadOnlySpan<byte> buffer) where T : struct { throw null; }
public static bool TryReadMachineEndian<T>(ReadOnlySpan<byte> buffer, out T value) where T : struct { throw null; }

public static short ReadInt16LittleEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static int ReadInt32LittleEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static long ReadInt64LittleEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static ushort ReadUInt16LittleEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static uint ReadUInt32LittleEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static ulong ReadUInt64LittleEndian(ReadOnlySpan<byte> buffer) { throw null; }

public static bool TryReadInt16LittleEndian(ReadOnlySpan<byte> buffer, out short value) { throw null; }
public static bool TryReadInt32LittleEndian(ReadOnlySpan<byte> buffer, out int value) { throw null; }
public static bool TryReadInt64LittleEndian(ReadOnlySpan<byte> buffer, out long value) { throw null; }
public static bool TryReadUInt16LittleEndian(ReadOnlySpan<byte> buffer, out ushort value) { throw null; }
public static bool TryReadUInt32LittleEndian(ReadOnlySpan<byte> buffer, out uint value) { throw null; }
public static bool TryReadUInt64LittleEndian(ReadOnlySpan<byte> buffer, out ulong value) { throw null; }

public static short ReadInt16BigEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static int ReadInt32BigEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static long ReadInt64BigEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static ushort ReadUInt16BigEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static uint ReadUInt32BigEndian(ReadOnlySpan<byte> buffer) { throw null; }
public static ulong ReadUInt64BigEndian(ReadOnlySpan<byte> buffer) { throw null; }

public static bool TryReadInt16BigEndian(ReadOnlySpan<byte> buffer, out short value) { throw null; }
public static bool TryReadInt32BigEndian(ReadOnlySpan<byte> buffer, out int value) { throw null; }
public static bool TryReadInt64BigEndian(ReadOnlySpan<byte> buffer, out long value) { throw null; }
public static bool TryReadUInt16BigEndian(ReadOnlySpan<byte> buffer, out ushort value) { throw null; }
public static bool TryReadUInt32BigEndian(ReadOnlySpan<byte> buffer, out uint value) { throw null; }
public static bool TryReadUInt64BigEndian(ReadOnlySpan<byte> buffer, out ulong value) { throw null; }

public static void WriteMachineEndian<T>(Span<byte> buffer, ref T value) where T : struct { throw null; }
public static bool TryWriteMachineEndian<T>(Span<byte> buffer, ref T value) where T : struct { throw null; }

public static void WriteInt16LittleEndian(Span<byte> buffer, short value) { throw null; }
public static void WriteInt32LittleEndian(Span<byte> buffer, int value) { throw null; }
public static void WriteInt64LittleEndian(Span<byte> buffer, long value) { throw null; }
public static void WriteUInt16LittleEndian(Span<byte> buffer, ushort value) { throw null; }
public static void WriteUInt32LittleEndian(Span<byte> buffer, uint value) { throw null; }
public static void WriteUInt64LittleEndian(Span<byte> buffer, ulong value) { throw null; }

public static bool TryWriteInt16LittleEndian(Span<byte> buffer, short value) { throw null; }
public static bool TryWriteInt32LittleEndian(Span<byte> buffer, int value) { throw null; }
public static bool TryWriteInt64LittleEndian(Span<byte> buffer, long value) { throw null; }
public static bool TryWriteUInt16LittleEndian(Span<byte> buffer, ushort value) { throw null; }
public static bool TryWriteUInt32LittleEndian(Span<byte> buffer, uint value) { throw null; }
public static bool TryWriteUInt64LittleEndian(Span<byte> buffer, ulong value) { throw null; }

public static void WriteInt16BigEndian(Span<byte> buffer, short value) { throw null; }
public static void WriteInt32BigEndian(Span<byte> buffer, int value) { throw null; }
public static void WriteInt64BigEndian(Span<byte> buffer, long value) { throw null; }
public static void WriteUInt16BigEndian(Span<byte> buffer, ushort value) { throw null; }
public static void WriteUInt32BigEndian(Span<byte> buffer, uint value) { throw null; }
public static void WriteUInt64BigEndian(Span<byte> buffer, ulong value) { throw null; }

public static bool TryWriteInt16BigEndian(Span<byte> buffer, short value) { throw null; }
public static bool TryWriteInt32BigEndian(Span<byte> buffer, int value) { throw null; }
public static bool TryWriteInt64BigEndian(Span<byte> buffer, long value) { throw null; }
public static bool TryWriteUInt16BigEndian(Span<byte> buffer, ushort value) { throw null; }
public static bool TryWriteUInt32BigEndian(Span<byte> buffer, uint value) { throw null; }
public static bool TryWriteUInt64BigEndian(Span<byte> buffer, ulong value) { throw null; }
}
}
10 changes: 9 additions & 1 deletion src/System.Memory/src/System.Memory.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<CLSCompliant>false</CLSCompliant>
<DocumentationFile>$(OutputPath)$(MSBuildProjectName).xml</DocumentationFile>
<IsPartialFacadeAssembly Condition="'$(TargetGroup)' == 'netcoreapp' OR '$(TargetGroup)' == 'uap'">true</IsPartialFacadeAssembly>
<DefineConstants Condition="'$(IsPartialFacadeAssembly)' == 'true'">$(DefineConstants);IsPartialFacade</DefineConstants>
<DefineConstants Condition="'$(TargetGroup)'=='netcoreapp'">$(DefineConstants);netcoreapp</DefineConstants>
<DefineConstants Condition="'$(TargetGroup)'=='netstandard1.1'">$(DefineConstants);netstandard11</DefineConstants>
</PropertyGroup>
Expand All @@ -24,6 +25,12 @@
<Compile Include="System\SpanExtensions.cs" />
<Compile Include="System\SpanHelpers.T.cs" />
<Compile Include="System\SpanHelpers.byte.cs" />
<Compile Include="System\Buffers\Binary\Reader.cs" />
<Compile Include="System\Buffers\Binary\ReaderBigEndian.cs" />
<Compile Include="System\Buffers\Binary\ReaderLittleEndian.cs" />
<Compile Include="System\Buffers\Binary\Writer.cs" />
<Compile Include="System\Buffers\Binary\WriterBigEndian.cs" />
<Compile Include="System\Buffers\Binary\WriterLittleEndian.cs" />
</ItemGroup>
<ItemGroup Condition="'$(IsPartialFacadeAssembly)' != 'true'">
<Compile Include="System\ReadOnlySpan.cs" />
Expand All @@ -46,10 +53,11 @@
<Reference Include="System.Reflection" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.Extensions" />
<Reference Include="System.Runtime.InteropServices" />
<Reference Condition="'$(TargetGroup)' != 'netstandard1.1'" Include="System.Numerics.Vectors" />
<Reference Condition="'$(TargetGroup)' != 'netstandard1.1'" Include="System.Runtime.CompilerServices.Unsafe" />
<ProjectReference Condition="'$(TargetGroup)' == 'netstandard1.1'" Include="..\..\System.Runtime.CompilerServices.Unsafe\ref\System.Runtime.CompilerServices.Unsafe.csproj" />
<ProjectReference Condition="'$(TargetGroup)' == 'netstandard1.1'" Include="..\..\System.Runtime.CompilerServices.Unsafe\src\System.Runtime.CompilerServices.Unsafe.ilproj" />
</ItemGroup>
<ItemGroup Condition="'$(IsPartialFacadeAssembly)' == 'true'">
<Compile Include="System\SpanExtensions.Fast.cs" />
Expand Down
147 changes: 147 additions & 0 deletions src/System.Memory/src/System/Buffers/Binary/Reader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// 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;
using System.Runtime.CompilerServices;

namespace System.Buffers.Binary
{
/// <summary>
/// Reads bytes as primitives with specific endianness
/// </summary>
/// <remarks>
/// For native formats, SpanExtensions.Read&lt;T&gt; 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>
[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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ReverseEndianness(ushort value)
{
return (ushort)((value & 0x00FFU) << 8 | (value & 0xFF00U) >> 8);
}

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

/// <summary>
/// Reverses a primitive value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong ReverseEndianness(ulong value)
{
value = (value << 32) | (value >> 32);
value = (value & 0x0000FFFF0000FFFF) << 16 | (value & 0xFFFF0000FFFF0000) >> 16;
value = (value & 0x00FF00FF00FF00FF) << 8 | (value & 0xFF00FF00FF00FF00) >> 8;
return value;
}

/// <summary>
/// Reads a structure of type T out of a read-only span of bytes.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ReadMachineEndian<T>(ReadOnlySpan<byte> buffer)
where T : struct
{
#if IsPartialFacade
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
throw new ArgumentException(SR.Format(SR.Argument_InvalidTypeWithPointersNotSupported, typeof(T)));
}
#else
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
{
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
}
#endif
if (Unsafe.SizeOf<T>() > buffer.Length)
{
throw new ArgumentOutOfRangeException();
}
return Unsafe.ReadUnaligned<T>(ref buffer.DangerousGetPinnableReference());
}

/// <summary>
/// Reads a structure of type T out of a span of bytes.
/// <returns>If the span is too small to contain the type T, return false.</returns>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryReadMachineEndian<T>(ReadOnlySpan<byte> buffer, out T value)
where T : struct
{
#if IsPartialFacade
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
throw new ArgumentException(SR.Format(SR.Argument_InvalidTypeWithPointersNotSupported, typeof(T)));
}
#else
if (SpanHelpers.IsReferenceOrContainsReferences<T>())
{
ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T));
}
#endif
if (Unsafe.SizeOf<T>() > (uint)buffer.Length)
{
value = default;
return false;
}
value = Unsafe.ReadUnaligned<T>(ref buffer.DangerousGetPinnableReference());
return true;
}
}
}
Loading

0 comments on commit 563ce5f

Please sign in to comment.