runtime: [API Proposal] Expose missing BinaryPrimitives APIs
System.Buffers.Binary.BinaryPrimitives
exposes many APIs but there are few notable ones missing for core number types. We should extend the type to ensure that it supports the basic operations for all basic number types.
namespace System.Buffers.Binary;
public static partial class BinaryPrimitives
{
// Read*BigEndian and Read*LittleEndian
public static nint ReadIntPtrBigEndian(ReadOnlySpan<byte> source);
public static nint ReadIntPtrLittleEndian(ReadOnlySpan<byte> source);
public static Int128 ReadInt128BigEndian(ReadOnlySpan<byte> source);
public static Int128 ReadInt128LittleEndian(ReadOnlySpan<byte> source);
public static nuint ReadUIntPtrBigEndian(ReadOnlySpan<byte> source);
public static nuint ReadUIntPtrLittleEndian(ReadOnlySpan<byte> source);
public static UInt128 ReadUInt128BigEndian(ReadOnlySpan<byte> source);
public static UInt128 ReadUInt128LittleEndian(ReadOnlySpan<byte> source);
// ReverseEndianness
public static nint ReverseEndianness(nint value);
public static Int128 ReverseEndianness(Int128 value);
public static nuint ReverseEndianness(nuint value);
public static UInt128 ReverseEndianness(UInt128 value);
// TryRead*BigEndian and TryRead*LittleEndian
public static bool TryReadIntPtrBigEndian(ReadOnlySpan<byte> source, out nint value);
public static bool TryReadIntPtrLittleEndian(ReadOnlySpan<byte> source, out nint value);
public static bool TryReadInt128BigEndian(ReadOnlySpan<byte> source, out Int128 value);
public static bool TryReadInt128LittleEndian(ReadOnlySpan<byte> source, out Int128 value);
public static bool TryReadUIntPtrBigEndian(ReadOnlySpan<byte> source, out nuint value);
public static bool TryReadUIntPtrLittleEndian(ReadOnlySpan<byte> source, out nuint value);
public static bool TryReadUInt128BigEndian(ReadOnlySpan<byte> source, out UInt128 value);
public static bool TryReadUInt128LittleEndian(ReadOnlySpan<byte> source, out UInt128 value);
// TryWrite*BigEndian and TryWrite*LittleEndian
public static bool TryWriteIntPtrBigEndian(ReadOnlySpan<byte> destination, nint value);
public static bool TryWriteIntPtrLittleEndian(ReadOnlySpan<byte> destination, nint value);
public static bool TryWriteInt128BigEndian(ReadOnlySpan<byte> destination, Int128 value);
public static bool TryWriteInt128LittleEndian(ReadOnlySpan<byte> destination, Int128 value);
public static bool TryWriteUIntPtrBigEndian(ReadOnlySpan<byte> destination, nuint value);
public static bool TryWriteUIntPtrLittleEndian(ReadOnlySpan<byte> destination, nuint value);
public static bool TryWriteUInt128BigEndian(ReadOnlySpan<byte> destination, UInt128 value);
public static bool TryWriteUInt128LittleEndian(ReadOnlySpan<byte> destination, UInt128 value);
// Write*BigEndian and Write*LittleEndian
public static void WriteIntPtrBigEndian(ReadOnlySpan<byte> destination, nint value);
public static void WriteIntPtrLittleEndian(ReadOnlySpan<byte> destination, nint value);
public static void WriteInt128BigEndian(ReadOnlySpan<byte> destination, Int128 value);
public static void WriteInt128LittleEndian(ReadOnlySpan<byte> destination, Int128 value);
public static void WriteUIntPtrBigEndian(ReadOnlySpan<byte> destination, nuint value);
public static void WriteUIntPtrLittleEndian(ReadOnlySpan<byte> destination, nuint value);
public static void WriteUInt128BigEndian(ReadOnlySpan<byte> destination, UInt128 value);
public static void WriteUInt128LittleEndian(ReadOnlySpan<byte> destination, UInt128 value);
}
Additional Notes
Also missing are some APIs around char
, Half
, float
, and double
. However, there are concerns around char
not being a “core” numeric type and around possible normalization issues for the various floating-point types, so they aren’t included.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 33 (33 by maintainers)
I’m handling:
as part of #75901.
Yes, however I do not think you would want to write this code this way, for the structure in your example at least.
The signature of an API optimized for in-place swapping that you are trying to do here would be
void ReverseEndianness(ref float value) => Unsafe.As<float,uint>(ref float) = ReverseEndianess(Unsafe.As<float,uint>(ref float))
. You can implement this as a one-liner helper yourself if you have a system designed around in-place swapping.I want to clarify that I’m not opposed to these APIs. (Or at least the
char
-based ones, which is the area I understand the best.)If there’s a valid power-user scenario for them, I don’t want to stand in the way of enabling that. But from a code review perspective, if I were to see a PR come through which introduced a call to one of these APIs, it would immediately raise a red flag for me. “This call site is suspicious and the caller is probably doing something very subtly incorrect, and I should scrutinize this code very closely.”
There’s always some slight paranoia I feel whenever I see an API that lends itself to that, even if there are legitimate power user scenarios. 😃
A use case I typically have for a
float
/double
ReverseEndianness
is when I’m reading in a bunch of structured data at once, and then want to correct it later if I’m running on a big endian system:What would be the optimal alternative here? Changing the structure to use
int
/uint
and exposing properties that useInt32BitsToSingle
/SingleToInt32Bits
?Copying chars to/from byte streams is not the correct way to perform UTF-16 conversion. We have existing APIs in the framework that do this the correct way. I really don’t want people to think the APIs being proposed here are a blessed (or “fast”) way to do this.