Skip to content
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
16 changes: 16 additions & 0 deletions src/libraries/Common/tests/System/GenericMathHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,28 @@ public static class BinaryIntegerHelper<TSelf>

public static TSelf PopCount(TSelf value) => TSelf.PopCount(value);

public static TSelf ReadBigEndian(byte[] source, bool isUnsigned) => TSelf.ReadBigEndian(source, isUnsigned);

public static TSelf ReadBigEndian(byte[] source, int startIndex, bool isUnsigned) => TSelf.ReadBigEndian(source, startIndex, isUnsigned);

public static TSelf ReadBigEndian(Span<byte> source, bool isUnsigned) => TSelf.ReadBigEndian(source, isUnsigned);

public static TSelf ReadLittleEndian(byte[] source, bool isUnsigned) => TSelf.ReadLittleEndian(source, isUnsigned);

public static TSelf ReadLittleEndian(byte[] source, int startIndex, bool isUnsigned) => TSelf.ReadLittleEndian(source, startIndex, isUnsigned);

public static TSelf ReadLittleEndian(Span<byte> source, bool isUnsigned) => TSelf.ReadLittleEndian(source, isUnsigned);

public static TSelf RotateLeft(TSelf value, int rotateAmount) => TSelf.RotateLeft(value, rotateAmount);

public static TSelf RotateRight(TSelf value, int rotateAmount) => TSelf.RotateRight(value, rotateAmount);

public static TSelf TrailingZeroCount(TSelf value) => TSelf.TrailingZeroCount(value);

public static bool TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out TSelf value) => TSelf.TryReadBigEndian(source, isUnsigned, out value);

public static bool TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out TSelf value) => TSelf.TryReadLittleEndian(source, isUnsigned, out value);

public static int GetByteCount(TSelf value) => value.GetByteCount();

public static int GetShortestBitLength(TSelf value) => value.GetShortestBitLength();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
using System.Numerics;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR, but we should rename this file at some point.

using System.Runtime.CompilerServices;

#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
{
/// <summary>
Expand Down Expand Up @@ -44,6 +54,24 @@ public static partial class BinaryPrimitives
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long ReverseEndianness(long value) => (long)ReverseEndianness((ulong)value);

/// <summary>
/// Reverses a signed native-sized integral value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static nint ReverseEndianness(nint value) => (nint)ReverseEndianness((nint_t)value);

/// <summary>
/// Reverses a signed 128-bit integral value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Int128 ReverseEndianness(Int128 value)
{
return new Int128(
ReverseEndianness(value.Lower),
ReverseEndianness(value.Upper)
);
}

/// <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
Expand All @@ -70,6 +98,12 @@ public static ushort ReverseEndianness(ushort value)
return (ushort)((value >> 8) + (value << 8));
}

/// <summary>
/// Reverses a 16-bit character value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static char ReverseEndianness(char value) => (char)ReverseEndianness((ushort)value);

/// <summary>
/// Reverses a primitive value - performs an endianness swap
/// </summary>
Expand Down Expand Up @@ -116,5 +150,23 @@ public static ulong ReverseEndianness(ulong value)
return ((ulong)ReverseEndianness((uint)value) << 32)
+ ReverseEndianness((uint)(value >> 32));
}

/// <summary>
/// Reverses an unsigned native-sized integral value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static nuint ReverseEndianness(nuint value) => (nuint)ReverseEndianness((nuint_t)value);

/// <summary>
/// Reverses an unsigned 128-bit integral value - performs an endianness swap
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static UInt128 ReverseEndianness(UInt128 value)
{
return new UInt128(
ReverseEndianness(value.Lower),
ReverseEndianness(value.Upper)
);
}
}
}
71 changes: 69 additions & 2 deletions src/libraries/System.Private.CoreLib/src/System/Byte.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
Expand Down Expand Up @@ -332,6 +333,72 @@ object IConvertible.ToType(Type type, IFormatProvider? provider)
/// <inheritdoc cref="IBinaryInteger{TSelf}.TrailingZeroCount(TSelf)" />
public static byte TrailingZeroCount(byte value) => (byte)(BitOperations.TrailingZeroCount(value << 24) - 24);

/// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadBigEndian(ReadOnlySpan{byte}, bool, out TSelf)" />
static bool IBinaryInteger<byte>.TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out byte value)
{
byte result = default;

if (source.Length != 0)
{
if (!isUnsigned && sbyte.IsNegative((sbyte)source[0]))
{
// When we are signed and the sign bit is set we are negative and therefore
// definitely out of range

value = result;
return false;
}

if ((source.Length > sizeof(byte)) && (source[..^sizeof(byte)].IndexOfAnyExcept((byte)0x00) >= 0))
{
// When we have any non-zero leading data, we are a large positive and therefore
// definitely out of range

value = result;
return false;
}

// We only have 1-byte so read it directly
result = Unsafe.Add(ref MemoryMarshal.GetReference(source), source.Length - sizeof(byte));
}

value = result;
return true;
}

/// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadLittleEndian(ReadOnlySpan{byte}, bool, out TSelf)" />
static bool IBinaryInteger<byte>.TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out byte value)
{
byte result = default;

if (source.Length != 0)
{
if (!isUnsigned && sbyte.IsNegative((sbyte)source[^1]))
{
// When we are signed and the sign bit is set, we are negative and therefore
// definitely out of range

value = result;
return false;
}

if ((source.Length > sizeof(byte)) && (source[sizeof(byte)..].IndexOfAnyExcept((byte)0x00) >= 0))
{
// When we have any non-zero leading data, we are a large positive and therefore
// definitely out of range

value = result;
return false;
}

// We only have 1-byte so read it directly
result = MemoryMarshal.GetReference(source);
}

value = result;
return true;
}

/// <inheritdoc cref="IBinaryInteger{TSelf}.GetShortestBitLength()" />
int IBinaryInteger<byte>.GetShortestBitLength() => (sizeof(byte) * 8) - LeadingZeroCount(m_value);

Expand All @@ -344,7 +411,7 @@ bool IBinaryInteger<byte>.TryWriteBigEndian(Span<byte> destination, out int byte
if (destination.Length >= sizeof(byte))
{
byte value = m_value;
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value);
MemoryMarshal.GetReference(destination) = value;

bytesWritten = sizeof(byte);
return true;
Expand All @@ -362,7 +429,7 @@ bool IBinaryInteger<byte>.TryWriteLittleEndian(Span<byte> destination, out int b
if (destination.Length >= sizeof(byte))
{
byte value = m_value;
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value);
MemoryMarshal.GetReference(destination) = value;

bytesWritten = sizeof(byte);
return true;
Expand Down
128 changes: 113 additions & 15 deletions src/libraries/System.Private.CoreLib/src/System/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,21 @@ public readonly struct Char
{
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x0E, 0x0E, // U+0000..U+000F
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0010..U+001F
0x8B, 0x18, 0x18, 0x18, 0x1A, 0x18, 0x18, 0x18, 0x14, 0x15, 0x18, 0x19, 0x18, 0x13, 0x18, 0x18, // U+0020..U+002F
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x18, 0x18, 0x19, 0x19, 0x19, 0x18, // U+0030..U+003F
0x18, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+0040..U+004F
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x14, 0x18, 0x15, 0x1B, 0x12, // U+0050..U+005F
0x1B, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+0060..U+006F
0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x14, 0x19, 0x15, 0x19, 0x0E, // U+0070..U+007F
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x8E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0080..U+008F
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0090..U+009F
0x8B, 0x18, 0x1A, 0x1A, 0x1A, 0x1A, 0x1C, 0x18, 0x1B, 0x1C, 0x04, 0x16, 0x19, 0x0F, 0x1C, 0x1B, // U+00A0..U+00AF
0x1C, 0x19, 0x0A, 0x0A, 0x1B, 0x21, 0x18, 0x18, 0x1B, 0x0A, 0x04, 0x17, 0x0A, 0x0A, 0x0A, 0x18, // U+00B0..U+00BF
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+00C0..U+00CF
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x21, // U+00D0..U+00DF
0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+00E0..U+00EF
0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x19, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+00F0..U+00FF
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0001..U+001F
0x8B, 0x18, 0x18, 0x18, 0x1A, 0x18, 0x18, 0x18, 0x14, 0x15, 0x18, 0x19, 0x18, 0x13, 0x18, 0x18, // U+0002..U+002F
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x18, 0x18, 0x19, 0x19, 0x19, 0x18, // U+0003..U+003F
0x18, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+0004..U+004F
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x14, 0x18, 0x15, 0x1B, 0x12, // U+0005..U+005F
0x1B, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+0006..U+006F
0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x14, 0x19, 0x15, 0x19, 0x0E, // U+0007..U+007F
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x8E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0008..U+008F
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0009..U+009F
0x8B, 0x18, 0x1A, 0x1A, 0x1A, 0x1A, 0x1C, 0x18, 0x1B, 0x1C, 0x04, 0x16, 0x19, 0x0F, 0x1C, 0x1B, // U+000A..U+00AF
0x1C, 0x19, 0x0A, 0x0A, 0x1B, 0x21, 0x18, 0x18, 0x1B, 0x0A, 0x04, 0x17, 0x0A, 0x0A, 0x0A, 0x18, // U+000B..U+00BF
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+000C..U+00CF
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x21, // U+000D..U+00DF
0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+000E..U+00EF
0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x19, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+000F..U+00FF
};

// Return true for all characters below or equal U+00ff, which is ASCII + Latin-1 Supplement.
Expand Down Expand Up @@ -1179,6 +1179,104 @@ public static int ConvertToUtf32(string s, int index)
/// <inheritdoc cref="IBinaryInteger{TSelf}.TrailingZeroCount(TSelf)" />
static char IBinaryInteger<char>.TrailingZeroCount(char value) => (char)(BitOperations.TrailingZeroCount(value << 16) - 16);

/// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadBigEndian(ReadOnlySpan{byte}, bool, out TSelf)" />
static bool IBinaryInteger<char>.TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out char value)
{
char result = default;

if (source.Length != 0)
{
if (!isUnsigned && sbyte.IsNegative((sbyte)source[0]))
{
// When we are signed and the sign bit is set, we are negative and therefore
// definitely out of range

value = result;
return false;
}

if ((source.Length > sizeof(char)) && (source[..^sizeof(char)].IndexOfAnyExcept((byte)0x00) >= 0))
{
// When we have any non-zero leading data, we are a large positive and therefore
// definitely out of range

value = result;
return false;
}

ref byte sourceRef = ref MemoryMarshal.GetReference(source);

if (source.Length >= sizeof(char))
{
sourceRef = ref Unsafe.Add(ref sourceRef, source.Length - sizeof(char));

// We have at least 2 bytes, so just read the ones we need directly
result = Unsafe.ReadUnaligned<char>(ref sourceRef);

if (BitConverter.IsLittleEndian)
{
result = BinaryPrimitives.ReverseEndianness(result);
}
}
else
{
// We only have 1-byte so read it directly
result = (char)sourceRef;
}
}

value = result;
return true;
}

/// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadLittleEndian(ReadOnlySpan{byte}, bool, out TSelf)" />
static bool IBinaryInteger<char>.TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out char value)
{
char result = default;

if (source.Length != 0)
{
if (!isUnsigned && sbyte.IsNegative((sbyte)source[^1]))
{
// When we are signed and the sign bit is set, we are negative and therefore
// definitely out of range

value = result;
return false;
}

if ((source.Length > sizeof(char)) && (source[sizeof(char)..].IndexOfAnyExcept((byte)0x00) >= 0))
{
// When we have any non-zero leading data, we are a large positive and therefore
// definitely out of range

value = result;
return false;
}

ref byte sourceRef = ref MemoryMarshal.GetReference(source);

if (source.Length >= sizeof(char))
{
// We have at least 2 bytes, so just read the ones we need directly
result = Unsafe.ReadUnaligned<char>(ref sourceRef);

if (!BitConverter.IsLittleEndian)
{
result = BinaryPrimitives.ReverseEndianness(result);
}
}
else
{
// We only have 1-byte so read it directly
result = (char)sourceRef;
}
}

value = result;
return true;
}

/// <inheritdoc cref="IBinaryInteger{TSelf}.GetShortestBitLength()" />
int IBinaryInteger<char>.GetShortestBitLength() => (sizeof(char) * 8) - ushort.LeadingZeroCount(m_value);

Expand Down
Loading