From cdad4c64262ce2ccc9448f55636a713df9711216 Mon Sep 17 00:00:00 2001 From: Robert Hague Date: Sun, 27 Aug 2023 12:11:40 +0200 Subject: [PATCH 1/4] Use BinaryPrimitives/MemoryMarshal in BigInteger constructor --- .../src/System/Numerics/BigInteger.cs | 49 +++++++------------ 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 5f4cf82f75a941..8fedd26ec3769b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -352,42 +352,31 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE } else { - int unalignedBytes = byteCount % 4; - int dwordCount = byteCount / 4 + (unalignedBytes == 0 ? 0 : 1); - uint[] val = new uint[dwordCount]; - int byteCountMinus1 = byteCount - 1; + int wholeDwordCount = Math.DivRem(byteCount, 4, out int unalignedBytes); + uint[] val = new uint[wholeDwordCount + (unalignedBytes == 0 ? 0 : 1)]; // Copy all dwords, except don't do the last one if it's not a full four bytes - int curDword, curByte; - if (isBigEndian) { - curByte = byteCount - sizeof(int); - for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) + int curByte = byteCount - sizeof(int); + for (int curDword = 0; curDword < wholeDwordCount; curDword++) { - for (int byteInDword = 0; byteInDword < 4; byteInDword++) - { - byte curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; - curByte++; - } + val[curDword] = BinaryPrimitives.ReadUInt32BigEndian(value.Slice(curByte)); - curByte -= 8; + curByte -= 4; } } else { - curByte = sizeof(int) - 1; - for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) - { - for (int byteInDword = 0; byteInDword < 4; byteInDword++) - { - byte curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; - curByte--; - } + ReadOnlySpan dwords = MemoryMarshal.Cast(value.Slice(0, wholeDwordCount * 4)); - curByte += 8; + if (!BitConverter.IsLittleEndian) + { + BinaryPrimitives.ReverseEndianness(dwords, val); + } + else + { + dwords.CopyTo(val); } } @@ -396,23 +385,23 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE { if (isNegative) { - val[dwordCount - 1] = 0xffffffff; + val[wholeDwordCount] = 0xffffffff; } if (isBigEndian) { - for (curByte = 0; curByte < unalignedBytes; curByte++) + for (int curByte = 0; curByte < unalignedBytes; curByte++) { byte curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; + val[wholeDwordCount] = (val[wholeDwordCount] << 8) | curByteValue; } } else { - for (curByte = byteCountMinus1; curByte >= byteCount - unalignedBytes; curByte--) + for (int curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { byte curByteValue = value[curByte]; - val[curDword] = (val[curDword] << 8) | curByteValue; + val[wholeDwordCount] = (val[wholeDwordCount] << 8) | curByteValue; } } } From 388ed17e8dc9ad68eaf514a342a05c87ac62c58f Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Mon, 28 Aug 2023 20:47:12 +0200 Subject: [PATCH 2/4] Revert MemoryMarshal use --- .../src/System/Numerics/BigInteger.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 8fedd26ec3769b..24fd6d8df21740 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -368,15 +368,9 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE } else { - ReadOnlySpan dwords = MemoryMarshal.Cast(value.Slice(0, wholeDwordCount * 4)); - - if (!BitConverter.IsLittleEndian) - { - BinaryPrimitives.ReverseEndianness(dwords, val); - } - else + for (int curDword = 0; curDword < wholeDwordCount; curDword++) { - dwords.CopyTo(val); + val[curDword] = BinaryPrimitives.ReadUInt32LittleEndian(value.Slice(curDword * 4)); } } From 4b21dfa9ff2ec98f456c2551c95d5f91b0cd8b3c Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Mon, 28 Aug 2023 22:56:56 +0200 Subject: [PATCH 3/4] Use MemoryMarshal in the other direction --- .../src/System/Numerics/BigInteger.cs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 24fd6d8df21740..c82b53bb536b3a 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -355,23 +355,33 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE int wholeDwordCount = Math.DivRem(byteCount, 4, out int unalignedBytes); uint[] val = new uint[wholeDwordCount + (unalignedBytes == 0 ? 0 : 1)]; - // Copy all dwords, except don't do the last one if it's not a full four bytes + // Copy all dwords, except don't do the last one if it's not a full four bytes. + // The dwords are stored in 'least significant first' order. if (isBigEndian) { - int curByte = byteCount - sizeof(int); - for (int curDword = 0; curDword < wholeDwordCount; curDword++) - { - val[curDword] = BinaryPrimitives.ReadUInt32BigEndian(value.Slice(curByte)); + // The bytes parameter is in big-endian byte order. + // We need to read the uints out in reverse. - curByte -= 4; - } + Span uintBytes = MemoryMarshal.AsBytes(val.AsSpan(0, wholeDwordCount)); + + // We need to slice off the remainder from the beginning. + value.Slice(unalignedBytes).CopyTo(uintBytes); + + uintBytes.Reverse(); } else { - for (int curDword = 0; curDword < wholeDwordCount; curDword++) - { - val[curDword] = BinaryPrimitives.ReadUInt32LittleEndian(value.Slice(curDword * 4)); - } + // The bytes parameter is in little-endian byte order. + // We can just copy the bytes directly into the uint array. + + value.Slice(0, wholeDwordCount * 4).CopyTo(MemoryMarshal.AsBytes(val)); + } + + // In both of the above cases on big-endian architecture, we need to perform + // an endianness swap on the resulting uints. + if (!BitConverter.IsLittleEndian) + { + BinaryPrimitives.ReverseEndianness(val.AsSpan(0, wholeDwordCount), val); } // Copy the last dword specially if it's not aligned From e7f12afe6efd953ab673a7b435594c4dcf5bc8b7 Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Tue, 31 Oct 2023 20:17:06 +0100 Subject: [PATCH 4/4] Dword -> UInt32 --- .../src/System/Numerics/BigInteger.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 29d6caad65cce1..13122c91562cf4 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -352,17 +352,18 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE } else { - int wholeDwordCount = Math.DivRem(byteCount, 4, out int unalignedBytes); - uint[] val = new uint[wholeDwordCount + (unalignedBytes == 0 ? 0 : 1)]; + int wholeUInt32Count = Math.DivRem(byteCount, 4, out int unalignedBytes); + uint[] val = new uint[wholeUInt32Count + (unalignedBytes == 0 ? 0 : 1)]; - // Copy all dwords, except don't do the last one if it's not a full four bytes. - // The dwords are stored in 'least significant first' order. + // Copy the bytes to the uint array, apart from those which represent the + // most significant uint if it's not a full four bytes. + // The uints are stored in 'least significant first' order. if (isBigEndian) { // The bytes parameter is in big-endian byte order. // We need to read the uints out in reverse. - Span uintBytes = MemoryMarshal.AsBytes(val.AsSpan(0, wholeDwordCount)); + Span uintBytes = MemoryMarshal.AsBytes(val.AsSpan(0, wholeUInt32Count)); // We need to slice off the remainder from the beginning. value.Slice(unalignedBytes).CopyTo(uintBytes); @@ -374,22 +375,22 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE // The bytes parameter is in little-endian byte order. // We can just copy the bytes directly into the uint array. - value.Slice(0, wholeDwordCount * 4).CopyTo(MemoryMarshal.AsBytes(val)); + value.Slice(0, wholeUInt32Count * 4).CopyTo(MemoryMarshal.AsBytes(val)); } // In both of the above cases on big-endian architecture, we need to perform // an endianness swap on the resulting uints. if (!BitConverter.IsLittleEndian) { - BinaryPrimitives.ReverseEndianness(val.AsSpan(0, wholeDwordCount), val); + BinaryPrimitives.ReverseEndianness(val.AsSpan(0, wholeUInt32Count), val); } - // Copy the last dword specially if it's not aligned + // Copy the last uint specially if it's not aligned if (unalignedBytes != 0) { if (isNegative) { - val[wholeDwordCount] = 0xffffffff; + val[wholeUInt32Count] = 0xffffffff; } if (isBigEndian) @@ -397,7 +398,7 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE for (int curByte = 0; curByte < unalignedBytes; curByte++) { byte curByteValue = value[curByte]; - val[wholeDwordCount] = (val[wholeDwordCount] << 8) | curByteValue; + val[wholeUInt32Count] = (val[wholeUInt32Count] << 8) | curByteValue; } } else @@ -405,7 +406,7 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE for (int curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { byte curByteValue = value[curByte]; - val[wholeDwordCount] = (val[wholeDwordCount] << 8) | curByteValue; + val[wholeUInt32Count] = (val[wholeUInt32Count] << 8) | curByteValue; } } }