From 91d76add127bc9ab6a31fe4cd296e01be8bcb083 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 7 May 2026 16:08:31 +0200 Subject: [PATCH 1/2] Reduce Unsafe usage in TryWriteSignificand/TryHash helpers Replace MemoryMarshal.GetReference + Unsafe.WriteUnaligned with BinaryPrimitives.WriteXxxBigEndian/LittleEndian (or MemoryMarshal.Write for NFloat where the underlying type is arch-conditional) in: * Decimal.IFloatingPoint.TryWriteSignificand{Big,Little}Endian * NFloat.IFloatingPoint.TryWrite{Exponent,Significand}{Big,Little}Endian * XxHash3.TryHash * XxHash128.WriteBigEndian128 (used by TryHash / GetCurrentHash) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/System/IO/Hashing/XxHash128.cs | 13 ++------ .../src/System/IO/Hashing/XxHash3.cs | 7 +---- .../src/System/Decimal.cs | 30 +++---------------- .../System/Runtime/InteropServices/NFloat.cs | 8 ++--- 4 files changed, 11 insertions(+), 47 deletions(-) diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs index 23ff9fce02a671..2c34010d87bfde 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs @@ -214,17 +214,8 @@ public UInt128 GetCurrentHashAsUInt128() [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WriteBigEndian128(in Hash128 hash, Span destination) { - ulong low = hash.Low64; - ulong high = hash.High64; - if (BitConverter.IsLittleEndian) - { - low = BinaryPrimitives.ReverseEndianness(low); - high = BinaryPrimitives.ReverseEndianness(high); - } - - ref byte dest0 = ref MemoryMarshal.GetReference(destination); - Unsafe.WriteUnaligned(ref dest0, high); - Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref dest0, new IntPtr(sizeof(ulong))), low); + BinaryPrimitives.WriteUInt64BigEndian(destination, hash.High64); + BinaryPrimitives.WriteUInt64BigEndian(destination.Slice(sizeof(ulong)), hash.Low64); } private static Hash128 HashLength0To16(byte* source, uint length, ulong seed) diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs index c7985efc0e66b7..c70d8ff53db86a 100644 --- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs +++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs @@ -104,12 +104,7 @@ public static bool TryHash(ReadOnlySpan source, Span destination, ou if (destination.Length >= sizeof(long)) { ulong hash = HashToUInt64(source, seed); - - if (BitConverter.IsLittleEndian) - { - hash = BinaryPrimitives.ReverseEndianness(hash); - } - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), hash); + BinaryPrimitives.WriteUInt64BigEndian(destination, hash); bytesWritten = HashLengthInBytes; return true; diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs index 1a85cd80202461..a5ff30b93be1b3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs @@ -1175,19 +1175,8 @@ bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination { if (destination.Length >= (sizeof(uint) + sizeof(ulong))) { - uint hi32 = _hi32; - ulong lo64 = _lo64; - - if (BitConverter.IsLittleEndian) - { - hi32 = BinaryPrimitives.ReverseEndianness(hi32); - lo64 = BinaryPrimitives.ReverseEndianness(lo64); - } - - ref byte address = ref MemoryMarshal.GetReference(destination); - - Unsafe.WriteUnaligned(ref address, hi32); - Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref address, sizeof(uint)), lo64); + BinaryPrimitives.WriteUInt32BigEndian(destination, _hi32); + BinaryPrimitives.WriteUInt64BigEndian(destination.Slice(sizeof(uint)), _lo64); bytesWritten = sizeof(uint) + sizeof(ulong); return true; @@ -1204,19 +1193,8 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinat { if (destination.Length >= (sizeof(ulong) + sizeof(uint))) { - ulong lo64 = _lo64; - uint hi32 = _hi32; - - if (!BitConverter.IsLittleEndian) - { - lo64 = BinaryPrimitives.ReverseEndianness(lo64); - hi32 = BinaryPrimitives.ReverseEndianness(hi32); - } - - ref byte address = ref MemoryMarshal.GetReference(destination); - - Unsafe.WriteUnaligned(ref address, lo64); - Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref address, sizeof(ulong)), hi32); + BinaryPrimitives.WriteUInt64LittleEndian(destination, _lo64); + BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(sizeof(ulong)), _hi32); bytesWritten = sizeof(ulong) + sizeof(uint); return true; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs index d542d13b39f07a..1c15859e2bc404 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs @@ -1049,7 +1049,7 @@ bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, ou exponent = BinaryPrimitives.ReverseEndianness(exponent); } - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), exponent); + MemoryMarshal.Write(destination, in exponent); bytesWritten = sizeof(NativeExponentType); return true; @@ -1073,7 +1073,7 @@ bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, exponent = BinaryPrimitives.ReverseEndianness(exponent); } - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), exponent); + MemoryMarshal.Write(destination, in exponent); bytesWritten = sizeof(NativeExponentType); return true; @@ -1097,7 +1097,7 @@ bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, significand = BinaryPrimitives.ReverseEndianness(significand); } - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), significand); + MemoryMarshal.Write(destination, in significand); bytesWritten = sizeof(NativeSignificandType); return true; @@ -1121,7 +1121,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati significand = BinaryPrimitives.ReverseEndianness(significand); } - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), significand); + MemoryMarshal.Write(destination, in significand); bytesWritten = sizeof(NativeSignificandType); return true; From 2fab70c731d66e795d4a5069df47cf636b04d6ae Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 7 May 2026 18:12:06 +0200 Subject: [PATCH 2/2] Forward NFloat IFloatingPoint TryWriteXxx to inner Single/Double helpers Per review feedback, expose internal TryWrite{Exponent,Significand}{Big,Little}Endian methods on Single/Double next to the explicit interface implementations, and have NFloat's IFloatingPoint.TryWriteXxx forward directly to _value.TryWriteXxx. This removes the TARGET_32BIT ifdefs, manual ReverseEndianness, and the BitConverter.TryWriteBytes + ThrowHelper boilerplate from NFloat. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/System/Double.cs | 36 +++++++-- .../System/Runtime/InteropServices/NFloat.cs | 81 +------------------ .../src/System/Single.cs | 36 +++++++-- 3 files changed, 60 insertions(+), 93 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 94d7a627d3f70b..2ff9c3662e42d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -721,8 +721,7 @@ int IFloatingPoint.GetExponentShortestBitLength() /// int IFloatingPoint.GetSignificandBitLength() => 53; - /// - bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + internal bool TryWriteExponentBigEndian(Span destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteInt16BigEndian(destination, Exponent)) { @@ -734,8 +733,13 @@ bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, ou return false; } - /// - bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + /// + bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + { + return TryWriteExponentBigEndian(destination, out bytesWritten); + } + + internal bool TryWriteExponentLittleEndian(Span destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteInt16LittleEndian(destination, Exponent)) { @@ -747,8 +751,13 @@ bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, return false; } - /// - bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + /// + bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + { + return TryWriteExponentLittleEndian(destination, out bytesWritten); + } + + internal bool TryWriteSignificandBigEndian(Span destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteUInt64BigEndian(destination, Significand)) { @@ -760,8 +769,13 @@ bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, return false; } - /// - bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + /// + bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + { + return TryWriteSignificandBigEndian(destination, out bytesWritten); + } + + internal bool TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteUInt64LittleEndian(destination, Significand)) { @@ -773,6 +787,12 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati return false; } + /// + bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + { + return TryWriteSignificandLittleEndian(destination, out bytesWritten); + } + // // IFloatingPointConstants // diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs index 1c15859e2bc404..fd875adcc67b1f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs @@ -1,7 +1,6 @@ // 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; @@ -1040,97 +1039,25 @@ int IFloatingPoint.GetSignificandBitLength() /// bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) { - if (destination.Length >= sizeof(NativeExponentType)) - { - NativeExponentType exponent = _value.Exponent; - - if (BitConverter.IsLittleEndian) - { - exponent = BinaryPrimitives.ReverseEndianness(exponent); - } - - MemoryMarshal.Write(destination, in exponent); - - bytesWritten = sizeof(NativeExponentType); - return true; - } - else - { - bytesWritten = 0; - return false; - } + return _value.TryWriteExponentBigEndian(destination, out bytesWritten); } /// bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) { - if (destination.Length >= sizeof(NativeExponentType)) - { - NativeExponentType exponent = _value.Exponent; - - if (!BitConverter.IsLittleEndian) - { - exponent = BinaryPrimitives.ReverseEndianness(exponent); - } - - MemoryMarshal.Write(destination, in exponent); - - bytesWritten = sizeof(NativeExponentType); - return true; - } - else - { - bytesWritten = 0; - return false; - } + return _value.TryWriteExponentLittleEndian(destination, out bytesWritten); } /// bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) { - if (destination.Length >= sizeof(NativeSignificandType)) - { - NativeSignificandType significand = _value.Significand; - - if (BitConverter.IsLittleEndian) - { - significand = BinaryPrimitives.ReverseEndianness(significand); - } - - MemoryMarshal.Write(destination, in significand); - - bytesWritten = sizeof(NativeSignificandType); - return true; - } - else - { - bytesWritten = 0; - return false; - } + return _value.TryWriteSignificandBigEndian(destination, out bytesWritten); } /// bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) { - if (destination.Length >= sizeof(NativeSignificandType)) - { - NativeSignificandType significand = _value.Significand; - - if (!BitConverter.IsLittleEndian) - { - significand = BinaryPrimitives.ReverseEndianness(significand); - } - - MemoryMarshal.Write(destination, in significand); - - bytesWritten = sizeof(NativeSignificandType); - return true; - } - else - { - bytesWritten = 0; - return false; - } + return _value.TryWriteSignificandLittleEndian(destination, out bytesWritten); } // diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index cfad70514b4dc9..7a106630b6c841 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -716,8 +716,7 @@ int IFloatingPoint.GetExponentShortestBitLength() /// int IFloatingPoint.GetSignificandBitLength() => 24; - /// - bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + internal bool TryWriteExponentBigEndian(Span destination, out int bytesWritten) { if (destination.Length >= sizeof(sbyte)) { @@ -730,8 +729,13 @@ bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out return false; } - /// - bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + /// + bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + { + return TryWriteExponentBigEndian(destination, out bytesWritten); + } + + internal bool TryWriteExponentLittleEndian(Span destination, out int bytesWritten) { if (destination.Length >= sizeof(sbyte)) { @@ -744,8 +748,13 @@ bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, return false; } - /// - bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + /// + bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + { + return TryWriteExponentLittleEndian(destination, out bytesWritten); + } + + internal bool TryWriteSignificandBigEndian(Span destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteUInt32BigEndian(destination, Significand)) { @@ -757,8 +766,13 @@ bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, return false; } - /// - bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + /// + bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + { + return TryWriteSignificandBigEndian(destination, out bytesWritten); + } + + internal bool TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteUInt32LittleEndian(destination, Significand)) { @@ -770,6 +784,12 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio return false; } + /// + bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + { + return TryWriteSignificandLittleEndian(destination, out bytesWritten); + } + // // IFloatingPointConstants //