From 7ea7474a08c8307b3217d2c9e9776e4c3cba30ec Mon Sep 17 00:00:00 2001 From: jfd16 Date: Tue, 2 Feb 2021 23:38:11 -0800 Subject: [PATCH 1/4] Optimize BigInteger parsing --- .../src/System/Numerics/BigInteger.cs | 20 +- .../src/System/Numerics/BigNumber.cs | 308 ++++++++++++++---- 2 files changed, 256 insertions(+), 72 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 58ac3ee501202c..3acada17b9efe1 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -683,7 +683,8 @@ public static BigInteger Parse(string value, IFormatProvider? provider) public static BigInteger Parse(string value, NumberStyles style, IFormatProvider? provider) { - return BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); + (int sign, uint[]? bits) = BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); + return new BigInteger(sign, bits); } public static bool TryParse([NotNullWhen(true)] string? value, out BigInteger result) @@ -693,22 +694,31 @@ public static bool TryParse([NotNullWhen(true)] string? value, out BigInteger re public static bool TryParse([NotNullWhen(true)] string? value, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result); + bool success = BigNumber.TryParseBigInteger( + value, style, NumberFormatInfo.GetInstance(provider), out int sign, out uint[]? bits); + + result = new BigInteger(sign, bits); + return success; } public static BigInteger Parse(ReadOnlySpan value, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { - return BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); + (int sign, uint[]? bits) = BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); + return new BigInteger(sign, bits); } public static bool TryParse(ReadOnlySpan value, out BigInteger result) { - return BigNumber.TryParseBigInteger(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(ReadOnlySpan value, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result); + bool success = BigNumber.TryParseBigInteger( + value, style, NumberFormatInfo.GetInstance(provider), out int sign, out uint[]? bits); + + result = new BigInteger(sign, bits); + return success; } public static int Compare(BigInteger left, BigInteger right) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index 726e759def658a..bc984c96f7d51f 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -286,6 +286,8 @@ internal static class BigNumber | NumberStyles.AllowThousands | NumberStyles.AllowExponent | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier); + private static readonly uint[] s_uint32PowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + private struct BigNumberBuffer { public StringBuilder digits; @@ -322,49 +324,49 @@ internal static bool TryValidateParseStyleInteger(NumberStyles style, [NotNullWh return true; } - internal static bool TryParseBigInteger(string? value, NumberStyles style, NumberFormatInfo info, out BigInteger result) + internal static bool TryParseBigInteger(string? value, NumberStyles style, NumberFormatInfo info, out int sign, out uint[]? bits) { if (value == null) { - result = default(BigInteger); + sign = 0; + bits = null; return false; } - return TryParseBigInteger(value.AsSpan(), style, info, out result); + return TryParseBigInteger(value.AsSpan(), style, info, out sign, out bits); } - internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result) + internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out int sign, out uint[]? bits) { - unsafe - { - result = BigInteger.Zero; - ArgumentException? e; - if (!TryValidateParseStyleInteger(style, out e)) - throw e; // TryParse still throws ArgumentException on invalid NumberStyles + sign = 0; + bits = null; - BigNumberBuffer bignumber = BigNumberBuffer.Create(); - if (!FormatProvider.TryStringToBigInteger(value, style, info, bignumber.digits, out bignumber.precision, out bignumber.scale, out bignumber.sign)) - return false; + ArgumentException? e; + if (!TryValidateParseStyleInteger(style, out e)) + throw e; // TryParse still throws ArgumentException on invalid NumberStyles + + BigNumberBuffer bignumber = BigNumberBuffer.Create(); + if (!FormatProvider.TryStringToBigInteger(value, style, info, bignumber.digits, out bignumber.precision, out bignumber.scale, out bignumber.sign)) + return false; - if ((style & NumberStyles.AllowHexSpecifier) != 0) + if ((style & NumberStyles.AllowHexSpecifier) != 0) + { + if (!HexNumberToBigInteger(ref bignumber, out sign, out bits)) { - if (!HexNumberToBigInteger(ref bignumber, ref result)) - { - return false; - } + return false; } - else + } + else + { + if (!NumberToBigInteger(ref bignumber, out sign, out bits)) { - if (!NumberToBigInteger(ref bignumber, ref result)) - { - return false; - } + return false; } - return true; } + return true; } - internal static BigInteger ParseBigInteger(string value, NumberStyles style, NumberFormatInfo info) + internal static (int sign, uint[]? bits) ParseBigInteger(string value, NumberStyles style, NumberFormatInfo info) { if (value == null) { @@ -374,85 +376,257 @@ internal static BigInteger ParseBigInteger(string value, NumberStyles style, Num return ParseBigInteger(value.AsSpan(), style, info); } - internal static BigInteger ParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info) + internal static (int sign, uint[]? bits) ParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info) { ArgumentException? e; if (!TryValidateParseStyleInteger(style, out e)) throw e; - BigInteger result = BigInteger.Zero; - if (!TryParseBigInteger(value, style, info, out result)) + if (!TryParseBigInteger(value, style, info, out int sign, out uint[]? bits)) { throw new FormatException(SR.Overflow_ParseBigInteger); } - return result; + return (sign, bits); } - private static unsafe bool HexNumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) + private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int sign, out uint[]? bits) { + sign = 0; + bits = null; + if (number.digits == null || number.digits.Length == 0) return false; - int len = number.digits.Length - 1; // Ignore trailing '\0' - byte[] bits = new byte[(len / 2) + (len % 2)]; + const int DigitsPerBlock = 8; - bool shift = false; - bool isNegative = false; - int bitIndex = 0; + int totalDigitCount = number.digits.Length - 1; // Ignore trailing '\0' + int blockCount, partialDigitCount; - // Parse the string into a little-endian two's complement byte array - // string value : O F E B 7 \0 - // string index (i) : 0 1 2 3 4 5 <-- - // byte[] (bitIndex): 2 1 1 0 0 <-- - // - for (int i = len - 1; i > -1; i--) + if (totalDigitCount % DigitsPerBlock == 0) + { + blockCount = totalDigitCount / DigitsPerBlock; + partialDigitCount = 0; + } + else { - char c = number.digits[i]; - int b = HexConverter.FromChar(c); - Debug.Assert(b != 0xFF); - if (i == 0 && (b & 0x08) == 0x08) - isNegative = true; + blockCount = (totalDigitCount / DigitsPerBlock) + 1; + partialDigitCount = DigitsPerBlock - (totalDigitCount % DigitsPerBlock); + } + + bool isNegative = HexConverter.FromChar(number.digits[0]) >= 8; + uint partialValue = (isNegative && partialDigitCount > 0) ? 0xFFFFFFFFu : 0; + + uint[]? arrayFromPool = null; + + Span bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit) + ? stackalloc uint[blockCount] + : (arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount); - if (shift) + int bitsBufferPos = blockCount - 1; + + try + { + foreach (ReadOnlyMemory digitsChunk in number.digits.GetChunks()) + ProcessChunk(digitsChunk.Span, bitsBuffer); + + Debug.Assert(partialDigitCount == 0 && bitsBufferPos == -1); + + // BigInteger requires leading zero blocks to be truncated. + int blockCountNoLeadingZeros = blockCount; + while (blockCountNoLeadingZeros > 0 && bitsBuffer[blockCountNoLeadingZeros - 1] == 0) + blockCountNoLeadingZeros--; + + if (blockCountNoLeadingZeros == 0) { - bits[bitIndex] = (byte)(bits[bitIndex] | (b << 4)); - bitIndex++; + sign = 0; + bits = null; + } + else if (blockCountNoLeadingZeros == 1) + { + sign = (int)bitsBuffer[0]; + bits = null; + + if ((!isNegative && sign < 0) || sign == int.MinValue) + { + sign = isNegative ? -1 : 1; + bits = new[] { (uint)sign }; + } } else { - bits[bitIndex] = (byte)(isNegative ? (b | 0xF0) : (b)); + sign = isNegative ? -1 : 1; + bits = bitsBuffer.Slice(0, blockCountNoLeadingZeros).ToArray(); + + if (isNegative) + NumericsHelpers.DangerousMakeTwosComplement(bits); } - shift = !shift; + + return true; + } + finally + { + if (arrayFromPool != null) + ArrayPool.Shared.Return(arrayFromPool); } - value = new BigInteger(bits); - return true; + void ProcessChunk(ReadOnlySpan chunkDigits, Span _bitsBuffer) + { + for (int i = 0; i < chunkDigits.Length; i++) + { + char digitChar = chunkDigits[i]; + if (digitChar == '\0') + return; + + int hexValue = HexConverter.FromChar(digitChar); + Debug.Assert(hexValue != 0xFF); + + partialValue = (partialValue << 4) | (uint)hexValue; + partialDigitCount++; + + if (partialDigitCount == DigitsPerBlock) + { + _bitsBuffer[bitsBufferPos] = partialValue; + bitsBufferPos--; + partialValue = 0; + partialDigitCount = 0; + } + } + } } - private static unsafe bool NumberToBigInteger(ref BigNumberBuffer number, ref BigInteger value) + private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, out uint[]? bits) { - int i = number.scale; - int cur = 0; + sign = 0; + bits = null; + + Span stackBuffer = stackalloc uint[BigInteger.StackallocUInt32Limit]; + Span currentBuffer = stackBuffer; + int currentBufferSize = 0; + uint[]? arrayFromPool = null; + + uint partialValue = 0; + int partialDigitCount = 0; + int totalDigitCount = 0; + int numberScale = number.scale; + + const int MaxPartialDigits = 9; + const uint TenPowMaxPartial = 1000000000; - BigInteger ten = 10; - value = 0; - while (--i >= 0) + try { - value *= ten; - if (number.digits[cur] != '\0') + foreach (ReadOnlyMemory digitsChunk in number.digits.GetChunks()) + { + bool isValid = ProcessChunk(digitsChunk.Span, ref currentBuffer); + if (!isValid) + return false; + } + + if (partialDigitCount > 0) + MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[partialDigitCount], partialValue); + + int trailingZeroCount = numberScale - totalDigitCount; + + while (trailingZeroCount >= MaxPartialDigits) { - value += number.digits[cur++] - '0'; + MultiplyAdd(ref currentBuffer, TenPowMaxPartial, 0); + trailingZeroCount -= MaxPartialDigits; } + + if (trailingZeroCount > 0) + MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[trailingZeroCount], 0); + + if (currentBufferSize == 0) + { + sign = 0; + bits = null; + } + else if (currentBufferSize == 1 && currentBuffer[0] <= int.MaxValue) + { + sign = (int)(number.sign ? -currentBuffer[0] : currentBuffer[0]); + bits = null; + } + else + { + sign = number.sign ? -1 : 1; + bits = currentBuffer.Slice(0, currentBufferSize).ToArray(); + } + + return true; } - while (number.digits[cur] != '\0') + finally { - if (number.digits[cur++] != '0') return false; // Disallow non-zero trailing decimal places + if (arrayFromPool != null) + ArrayPool.Shared.Return(arrayFromPool); } - if (number.sign) + + bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span _currentBuffer) { - value = -value; + int remainingIntDigitCount = Math.Max(numberScale - totalDigitCount, 0); + ReadOnlySpan intDigitsSpan = chunkDigits.Slice(0, Math.Min(remainingIntDigitCount, chunkDigits.Length)); + + for (int i = 0; i < intDigitsSpan.Length; i++) + { + char digitChar = chunkDigits[i]; + if (digitChar == '\0') + return true; + + partialValue = partialValue * 10 + (uint)(digitChar - '0'); + partialDigitCount++; + totalDigitCount++; + + // Update the buffer when enough partial digits have been accumulated. + if (partialDigitCount == MaxPartialDigits) + { + MultiplyAdd(ref _currentBuffer, TenPowMaxPartial, partialValue); + partialValue = 0; + partialDigitCount = 0; + } + } + + // Check for nonzero digits after the decimal point. + ReadOnlySpan fracDigitsSpan = chunkDigits.Slice(intDigitsSpan.Length); + for (int i = 0; i < fracDigitsSpan.Length; i++) + { + char digitChar = fracDigitsSpan[i]; + if (digitChar == '\0') + return true; + if (digitChar != '0') + return false; + } + + return true; + } + + void MultiplyAdd(ref Span _currentBuffer, uint multiplier, uint addValue) + { + Span curBits = _currentBuffer.Slice(0, currentBufferSize); + uint carry = addValue; + + for (int i = 0; i < curBits.Length; i++) + { + ulong p = (ulong)multiplier * curBits[i] + carry; + curBits[i] = (uint)p; + carry = (uint)(p >> 32); + } + + if (carry == 0) + return; + + if (currentBufferSize == _currentBuffer.Length) + { + uint[]? arrayToReturn = arrayFromPool; + + arrayFromPool = ArrayPool.Shared.Rent(checked(currentBufferSize * 2)); + _currentBuffer.CopyTo(arrayFromPool); + _currentBuffer = arrayFromPool; + + if (arrayToReturn != null) + ArrayPool.Shared.Return(arrayToReturn); + } + + _currentBuffer[currentBufferSize] = carry; + currentBufferSize++; } - return true; } // This function is consistent with VM\COMNumber.cpp!COMNumber::ParseFormatSpecifier From 5657cfc0568ea12113ad388d26e35ddad61ed0b5 Mon Sep 17 00:00:00 2001 From: jfd16 Date: Thu, 4 Feb 2021 22:42:59 -0800 Subject: [PATCH 2/4] Use ArrayPool instead of ArrayPool. --- .../src/System/Numerics/BigNumber.cs | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index bc984c96f7d51f..d63c295908a95d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -274,6 +274,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.InteropServices; using System.Text; namespace System.Numerics @@ -416,11 +417,11 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int si bool isNegative = HexConverter.FromChar(number.digits[0]) >= 8; uint partialValue = (isNegative && partialDigitCount > 0) ? 0xFFFFFFFFu : 0; - uint[]? arrayFromPool = null; + int[]? arrayFromPool = null; - Span bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit) - ? stackalloc uint[blockCount] - : (arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount); + Span bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit) + ? stackalloc int[blockCount] + : (arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount); int bitsBufferPos = blockCount - 1; @@ -443,7 +444,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int si } else if (blockCountNoLeadingZeros == 1) { - sign = (int)bitsBuffer[0]; + sign = bitsBuffer[0]; bits = null; if ((!isNegative && sign < 0) || sign == int.MinValue) @@ -455,7 +456,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int si else { sign = isNegative ? -1 : 1; - bits = bitsBuffer.Slice(0, blockCountNoLeadingZeros).ToArray(); + bits = MemoryMarshal.Cast(bitsBuffer).Slice(0, blockCountNoLeadingZeros).ToArray(); if (isNegative) NumericsHelpers.DangerousMakeTwosComplement(bits); @@ -466,10 +467,10 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int si finally { if (arrayFromPool != null) - ArrayPool.Shared.Return(arrayFromPool); + ArrayPool.Shared.Return(arrayFromPool); } - void ProcessChunk(ReadOnlySpan chunkDigits, Span _bitsBuffer) + void ProcessChunk(ReadOnlySpan chunkDigits, Span _bitsBuffer) { for (int i = 0; i < chunkDigits.Length; i++) { @@ -485,7 +486,7 @@ void ProcessChunk(ReadOnlySpan chunkDigits, Span _bitsBuffer) if (partialDigitCount == DigitsPerBlock) { - _bitsBuffer[bitsBufferPos] = partialValue; + _bitsBuffer[bitsBufferPos] = (int)partialValue; bitsBufferPos--; partialValue = 0; partialDigitCount = 0; @@ -499,10 +500,10 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, sign = 0; bits = null; - Span stackBuffer = stackalloc uint[BigInteger.StackallocUInt32Limit]; - Span currentBuffer = stackBuffer; + Span stackBuffer = stackalloc int[BigInteger.StackallocUInt32Limit]; + Span currentBuffer = stackBuffer; int currentBufferSize = 0; - uint[]? arrayFromPool = null; + int[]? arrayFromPool = null; uint partialValue = 0; int partialDigitCount = 0; @@ -542,13 +543,13 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, } else if (currentBufferSize == 1 && currentBuffer[0] <= int.MaxValue) { - sign = (int)(number.sign ? -currentBuffer[0] : currentBuffer[0]); + sign = number.sign ? -currentBuffer[0] : currentBuffer[0]; bits = null; } else { sign = number.sign ? -1 : 1; - bits = currentBuffer.Slice(0, currentBufferSize).ToArray(); + bits = MemoryMarshal.Cast(currentBuffer).Slice(0, currentBufferSize).ToArray(); } return true; @@ -556,10 +557,10 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, finally { if (arrayFromPool != null) - ArrayPool.Shared.Return(arrayFromPool); + ArrayPool.Shared.Return(arrayFromPool); } - bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span _currentBuffer) + bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span _currentBuffer) { int remainingIntDigitCount = Math.Max(numberScale - totalDigitCount, 0); ReadOnlySpan intDigitsSpan = chunkDigits.Slice(0, Math.Min(remainingIntDigitCount, chunkDigits.Length)); @@ -597,15 +598,15 @@ bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span _currentBuffer) return true; } - void MultiplyAdd(ref Span _currentBuffer, uint multiplier, uint addValue) + void MultiplyAdd(ref Span _currentBuffer, uint multiplier, uint addValue) { - Span curBits = _currentBuffer.Slice(0, currentBufferSize); + Span curBits = _currentBuffer.Slice(0, currentBufferSize); uint carry = addValue; for (int i = 0; i < curBits.Length; i++) { - ulong p = (ulong)multiplier * curBits[i] + carry; - curBits[i] = (uint)p; + ulong p = (ulong)multiplier * (uint)curBits[i] + carry; + curBits[i] = (int)p; carry = (uint)(p >> 32); } @@ -614,17 +615,17 @@ void MultiplyAdd(ref Span _currentBuffer, uint multiplier, uint addValue) if (currentBufferSize == _currentBuffer.Length) { - uint[]? arrayToReturn = arrayFromPool; + int[]? arrayToReturn = arrayFromPool; - arrayFromPool = ArrayPool.Shared.Rent(checked(currentBufferSize * 2)); + arrayFromPool = ArrayPool.Shared.Rent(checked(currentBufferSize * 2)); _currentBuffer.CopyTo(arrayFromPool); _currentBuffer = arrayFromPool; if (arrayToReturn != null) - ArrayPool.Shared.Return(arrayToReturn); + ArrayPool.Shared.Return(arrayToReturn); } - _currentBuffer[currentBufferSize] = carry; + _currentBuffer[currentBufferSize] = (int)carry; currentBufferSize++; } } From 7b5eb623761cff778c1ddc878f983dba0352ea8a Mon Sep 17 00:00:00 2001 From: jfd16 Date: Mon, 8 Feb 2021 15:49:27 -0800 Subject: [PATCH 3/4] Additional changes --- .../src/System/Numerics/BigInteger.cs | 20 +-- .../src/System/Numerics/BigNumber.cs | 138 +++++++++--------- 2 files changed, 75 insertions(+), 83 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 3acada17b9efe1..c9c4e4997bbca1 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -464,7 +464,7 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE AssertValid(); } - private BigInteger(int n, uint[]? rgu) + internal BigInteger(int n, uint[]? rgu) { _sign = n; _bits = rgu; @@ -683,8 +683,7 @@ public static BigInteger Parse(string value, IFormatProvider? provider) public static BigInteger Parse(string value, NumberStyles style, IFormatProvider? provider) { - (int sign, uint[]? bits) = BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); - return new BigInteger(sign, bits); + return BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? value, out BigInteger result) @@ -694,17 +693,12 @@ public static bool TryParse([NotNullWhen(true)] string? value, out BigInteger re public static bool TryParse([NotNullWhen(true)] string? value, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - bool success = BigNumber.TryParseBigInteger( - value, style, NumberFormatInfo.GetInstance(provider), out int sign, out uint[]? bits); - - result = new BigInteger(sign, bits); - return success; + return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result); } public static BigInteger Parse(ReadOnlySpan value, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { - (int sign, uint[]? bits) = BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); - return new BigInteger(sign, bits); + return BigNumber.ParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse(ReadOnlySpan value, out BigInteger result) @@ -714,11 +708,7 @@ public static bool TryParse(ReadOnlySpan value, out BigInteger result) public static bool TryParse(ReadOnlySpan value, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - bool success = BigNumber.TryParseBigInteger( - value, style, NumberFormatInfo.GetInstance(provider), out int sign, out uint[]? bits); - - result = new BigInteger(sign, bits); - return success; + return BigNumber.TryParseBigInteger(value, style, NumberFormatInfo.GetInstance(provider), out result); } public static int Compare(BigInteger left, BigInteger right) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index d63c295908a95d..d6fc0bd4201aaa 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -325,41 +325,40 @@ internal static bool TryValidateParseStyleInteger(NumberStyles style, [NotNullWh return true; } - internal static bool TryParseBigInteger(string? value, NumberStyles style, NumberFormatInfo info, out int sign, out uint[]? bits) + internal static bool TryParseBigInteger(string? value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { if (value == null) { - sign = 0; - bits = null; + result = default; return false; } - return TryParseBigInteger(value.AsSpan(), style, info, out sign, out bits); + return TryParseBigInteger(value.AsSpan(), style, info, out result); } - internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out int sign, out uint[]? bits) + internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { - sign = 0; - bits = null; - ArgumentException? e; if (!TryValidateParseStyleInteger(style, out e)) throw e; // TryParse still throws ArgumentException on invalid NumberStyles BigNumberBuffer bignumber = BigNumberBuffer.Create(); if (!FormatProvider.TryStringToBigInteger(value, style, info, bignumber.digits, out bignumber.precision, out bignumber.scale, out bignumber.sign)) + { + result = default; return false; + } if ((style & NumberStyles.AllowHexSpecifier) != 0) { - if (!HexNumberToBigInteger(ref bignumber, out sign, out bits)) + if (!HexNumberToBigInteger(ref bignumber, out result)) { return false; } } else { - if (!NumberToBigInteger(ref bignumber, out sign, out bits)) + if (!NumberToBigInteger(ref bignumber, out result)) { return false; } @@ -367,7 +366,7 @@ internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles s return true; } - internal static (int sign, uint[]? bits) ParseBigInteger(string value, NumberStyles style, NumberFormatInfo info) + internal static BigInteger ParseBigInteger(string value, NumberStyles style, NumberFormatInfo info) { if (value == null) { @@ -377,41 +376,41 @@ internal static (int sign, uint[]? bits) ParseBigInteger(string value, NumberSty return ParseBigInteger(value.AsSpan(), style, info); } - internal static (int sign, uint[]? bits) ParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info) + internal static BigInteger ParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info) { ArgumentException? e; if (!TryValidateParseStyleInteger(style, out e)) throw e; - if (!TryParseBigInteger(value, style, info, out int sign, out uint[]? bits)) + if (!TryParseBigInteger(value, style, info, out BigInteger result)) { throw new FormatException(SR.Overflow_ParseBigInteger); } - return (sign, bits); + return result; } - private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int sign, out uint[]? bits) + private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { - sign = 0; - bits = null; - if (number.digits == null || number.digits.Length == 0) + { + result = default; return false; + } const int DigitsPerBlock = 8; int totalDigitCount = number.digits.Length - 1; // Ignore trailing '\0' int blockCount, partialDigitCount; - if (totalDigitCount % DigitsPerBlock == 0) + if ((uint)totalDigitCount % DigitsPerBlock == 0) { blockCount = totalDigitCount / DigitsPerBlock; partialDigitCount = 0; } else { - blockCount = (totalDigitCount / DigitsPerBlock) + 1; - partialDigitCount = DigitsPerBlock - (totalDigitCount % DigitsPerBlock); + blockCount = Math.DivRem(totalDigitCount, DigitsPerBlock, out int mod) + 1; + partialDigitCount = DigitsPerBlock - mod; } bool isNegative = HexConverter.FromChar(number.digits[0]) >= 8; @@ -427,22 +426,45 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int si try { - foreach (ReadOnlyMemory digitsChunk in number.digits.GetChunks()) - ProcessChunk(digitsChunk.Span, bitsBuffer); + foreach (ReadOnlyMemory digitsChunkMem in number.digits.GetChunks()) + { + ReadOnlySpan chunkDigits = digitsChunkMem.Span; + for (int i = 0; i < chunkDigits.Length; i++) + { + char digitChar = chunkDigits[i]; + if (digitChar == '\0') + break; + + int hexValue = HexConverter.FromChar(digitChar); + Debug.Assert(hexValue != 0xFF); + + partialValue = (partialValue << 4) | (uint)hexValue; + partialDigitCount++; + + if (partialDigitCount == DigitsPerBlock) + { + bitsBuffer[bitsBufferPos] = (int)partialValue; + bitsBufferPos--; + partialValue = 0; + partialDigitCount = 0; + } + } + } Debug.Assert(partialDigitCount == 0 && bitsBufferPos == -1); // BigInteger requires leading zero blocks to be truncated. - int blockCountNoLeadingZeros = blockCount; - while (blockCountNoLeadingZeros > 0 && bitsBuffer[blockCountNoLeadingZeros - 1] == 0) - blockCountNoLeadingZeros--; + bitsBuffer = bitsBuffer.TrimEnd(0); - if (blockCountNoLeadingZeros == 0) + int sign; + uint[]? bits; + + if (bitsBuffer.IsEmpty) { sign = 0; bits = null; } - else if (blockCountNoLeadingZeros == 1) + else if (bitsBuffer.Length == 1) { sign = bitsBuffer[0]; bits = null; @@ -456,12 +478,13 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int si else { sign = isNegative ? -1 : 1; - bits = MemoryMarshal.Cast(bitsBuffer).Slice(0, blockCountNoLeadingZeros).ToArray(); + bits = MemoryMarshal.Cast(bitsBuffer).ToArray(); if (isNegative) NumericsHelpers.DangerousMakeTwosComplement(bits); } + result = new BigInteger(sign, bits); return true; } finally @@ -469,37 +492,10 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out int si if (arrayFromPool != null) ArrayPool.Shared.Return(arrayFromPool); } - - void ProcessChunk(ReadOnlySpan chunkDigits, Span _bitsBuffer) - { - for (int i = 0; i < chunkDigits.Length; i++) - { - char digitChar = chunkDigits[i]; - if (digitChar == '\0') - return; - - int hexValue = HexConverter.FromChar(digitChar); - Debug.Assert(hexValue != 0xFF); - - partialValue = (partialValue << 4) | (uint)hexValue; - partialDigitCount++; - - if (partialDigitCount == DigitsPerBlock) - { - _bitsBuffer[bitsBufferPos] = (int)partialValue; - bitsBufferPos--; - partialValue = 0; - partialDigitCount = 0; - } - } - } } - private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, out uint[]? bits) + private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { - sign = 0; - bits = null; - Span stackBuffer = stackalloc int[BigInteger.StackallocUInt32Limit]; Span currentBuffer = stackBuffer; int currentBufferSize = 0; @@ -517,9 +513,11 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, { foreach (ReadOnlyMemory digitsChunk in number.digits.GetChunks()) { - bool isValid = ProcessChunk(digitsChunk.Span, ref currentBuffer); - if (!isValid) + if (!ProcessChunk(digitsChunk.Span, ref currentBuffer)) + { + result = default; return false; + } } if (partialDigitCount > 0) @@ -536,12 +534,15 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, if (trailingZeroCount > 0) MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[trailingZeroCount], 0); + int sign; + uint[]? bits; + if (currentBufferSize == 0) { sign = 0; bits = null; } - else if (currentBufferSize == 1 && currentBuffer[0] <= int.MaxValue) + else if (currentBufferSize == 1 && (uint)currentBuffer[0] <= int.MaxValue) { sign = number.sign ? -currentBuffer[0] : currentBuffer[0]; bits = null; @@ -552,6 +553,7 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, bits = MemoryMarshal.Cast(currentBuffer).Slice(0, currentBufferSize).ToArray(); } + result = new BigInteger(sign, bits); return true; } finally @@ -560,7 +562,7 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out int sign, ArrayPool.Shared.Return(arrayFromPool); } - bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span _currentBuffer) + bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span currentBuffer) { int remainingIntDigitCount = Math.Max(numberScale - totalDigitCount, 0); ReadOnlySpan intDigitsSpan = chunkDigits.Slice(0, Math.Min(remainingIntDigitCount, chunkDigits.Length)); @@ -578,7 +580,7 @@ bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span _currentBuffer) // Update the buffer when enough partial digits have been accumulated. if (partialDigitCount == MaxPartialDigits) { - MultiplyAdd(ref _currentBuffer, TenPowMaxPartial, partialValue); + MultiplyAdd(ref currentBuffer, TenPowMaxPartial, partialValue); partialValue = 0; partialDigitCount = 0; } @@ -598,9 +600,9 @@ bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span _currentBuffer) return true; } - void MultiplyAdd(ref Span _currentBuffer, uint multiplier, uint addValue) + void MultiplyAdd(ref Span currentBuffer, uint multiplier, uint addValue) { - Span curBits = _currentBuffer.Slice(0, currentBufferSize); + Span curBits = currentBuffer.Slice(0, currentBufferSize); uint carry = addValue; for (int i = 0; i < curBits.Length; i++) @@ -613,19 +615,19 @@ void MultiplyAdd(ref Span _currentBuffer, uint multiplier, uint addValue) if (carry == 0) return; - if (currentBufferSize == _currentBuffer.Length) + if (currentBufferSize == currentBuffer.Length) { int[]? arrayToReturn = arrayFromPool; arrayFromPool = ArrayPool.Shared.Rent(checked(currentBufferSize * 2)); - _currentBuffer.CopyTo(arrayFromPool); - _currentBuffer = arrayFromPool; + currentBuffer.CopyTo(arrayFromPool); + currentBuffer = arrayFromPool; if (arrayToReturn != null) ArrayPool.Shared.Return(arrayToReturn); } - _currentBuffer[currentBufferSize] = (int)carry; + currentBuffer[currentBufferSize] = (int)carry; currentBufferSize++; } } From e5b0dd9765be541a60166a9e590715fd59471849 Mon Sep 17 00:00:00 2001 From: jfd16 Date: Tue, 9 Feb 2021 12:14:21 -0800 Subject: [PATCH 4/4] Additional changes --- .../src/System/Numerics/BigNumber.cs | 142 +++++++++++------- 1 file changed, 87 insertions(+), 55 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index d6fc0bd4201aaa..553144797a27d0 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -338,12 +338,13 @@ internal static bool TryParseBigInteger(string? value, NumberStyles style, Numbe internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result) { - ArgumentException? e; - if (!TryValidateParseStyleInteger(style, out e)) + if (!TryValidateParseStyleInteger(style, out ArgumentException? e)) + { throw e; // TryParse still throws ArgumentException on invalid NumberStyles + } - BigNumberBuffer bignumber = BigNumberBuffer.Create(); - if (!FormatProvider.TryStringToBigInteger(value, style, info, bignumber.digits, out bignumber.precision, out bignumber.scale, out bignumber.sign)) + BigNumberBuffer bigNumber = BigNumberBuffer.Create(); + if (!FormatProvider.TryStringToBigInteger(value, style, info, bigNumber.digits, out bigNumber.precision, out bigNumber.scale, out bigNumber.sign)) { result = default; return false; @@ -351,19 +352,12 @@ internal static bool TryParseBigInteger(ReadOnlySpan value, NumberStyles s if ((style & NumberStyles.AllowHexSpecifier) != 0) { - if (!HexNumberToBigInteger(ref bignumber, out result)) - { - return false; - } + return HexNumberToBigInteger(ref bigNumber, out result); } else { - if (!NumberToBigInteger(ref bignumber, out result)) - { - return false; - } + return NumberToBigInteger(ref bigNumber, out result); } - return true; } internal static BigInteger ParseBigInteger(string value, NumberStyles style, NumberFormatInfo info) @@ -378,10 +372,10 @@ internal static BigInteger ParseBigInteger(string value, NumberStyles style, Num internal static BigInteger ParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info) { - ArgumentException? e; - if (!TryValidateParseStyleInteger(style, out e)) + if (!TryValidateParseStyleInteger(style, out ArgumentException? e)) + { throw e; - + } if (!TryParseBigInteger(value, style, info, out BigInteger result)) { throw new FormatException(SR.Overflow_ParseBigInteger); @@ -402,15 +396,15 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt int totalDigitCount = number.digits.Length - 1; // Ignore trailing '\0' int blockCount, partialDigitCount; - if ((uint)totalDigitCount % DigitsPerBlock == 0) + blockCount = Math.DivRem(totalDigitCount, DigitsPerBlock, out int remainder); + if (remainder == 0) { - blockCount = totalDigitCount / DigitsPerBlock; partialDigitCount = 0; } else { - blockCount = Math.DivRem(totalDigitCount, DigitsPerBlock, out int mod) + 1; - partialDigitCount = DigitsPerBlock - mod; + blockCount += 1; + partialDigitCount = DigitsPerBlock - remainder; } bool isNegative = HexConverter.FromChar(number.digits[0]) >= 8; @@ -418,9 +412,9 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt int[]? arrayFromPool = null; - Span bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit) - ? stackalloc int[blockCount] - : (arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount); + Span bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit) + ? stackalloc uint[blockCount] + : MemoryMarshal.Cast((arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount)); int bitsBufferPos = blockCount - 1; @@ -433,7 +427,9 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt { char digitChar = chunkDigits[i]; if (digitChar == '\0') + { break; + } int hexValue = HexConverter.FromChar(digitChar); Debug.Assert(hexValue != 0xFF); @@ -443,7 +439,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt if (partialDigitCount == DigitsPerBlock) { - bitsBuffer[bitsBufferPos] = (int)partialValue; + bitsBuffer[bitsBufferPos] = partialValue; bitsBufferPos--; partialValue = 0; partialDigitCount = 0; @@ -454,7 +450,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt Debug.Assert(partialDigitCount == 0 && bitsBufferPos == -1); // BigInteger requires leading zero blocks to be truncated. - bitsBuffer = bitsBuffer.TrimEnd(0); + bitsBuffer = bitsBuffer.TrimEnd(0u); int sign; uint[]? bits; @@ -466,7 +462,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt } else if (bitsBuffer.Length == 1) { - sign = bitsBuffer[0]; + sign = (int)bitsBuffer[0]; bits = null; if ((!isNegative && sign < 0) || sign == int.MinValue) @@ -478,10 +474,12 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt else { sign = isNegative ? -1 : 1; - bits = MemoryMarshal.Cast(bitsBuffer).ToArray(); + bits = bitsBuffer.ToArray(); if (isNegative) + { NumericsHelpers.DangerousMakeTwosComplement(bits); + } } result = new BigInteger(sign, bits); @@ -490,14 +488,16 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt finally { if (arrayFromPool != null) + { ArrayPool.Shared.Return(arrayFromPool); + } } } private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { - Span stackBuffer = stackalloc int[BigInteger.StackallocUInt32Limit]; - Span currentBuffer = stackBuffer; + Span stackBuffer = stackalloc uint[BigInteger.StackallocUInt32Limit]; + Span currentBuffer = stackBuffer; int currentBufferSize = 0; int[]? arrayFromPool = null; @@ -521,7 +521,9 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigIntege } if (partialDigitCount > 0) + { MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[partialDigitCount], partialValue); + } int trailingZeroCount = numberScale - totalDigitCount; @@ -532,7 +534,9 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigIntege } if (trailingZeroCount > 0) + { MultiplyAdd(ref currentBuffer, s_uint32PowersOfTen[trailingZeroCount], 0); + } int sign; uint[]? bits; @@ -542,15 +546,15 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigIntege sign = 0; bits = null; } - else if (currentBufferSize == 1 && (uint)currentBuffer[0] <= int.MaxValue) + else if (currentBufferSize == 1 && currentBuffer[0] <= int.MaxValue) { - sign = number.sign ? -currentBuffer[0] : currentBuffer[0]; + sign = (int)(number.sign ? -currentBuffer[0] : currentBuffer[0]); bits = null; } else { sign = number.sign ? -1 : 1; - bits = MemoryMarshal.Cast(currentBuffer).Slice(0, currentBufferSize).ToArray(); + bits = currentBuffer.Slice(0, currentBufferSize).ToArray(); } result = new BigInteger(sign, bits); @@ -559,75 +563,103 @@ private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigIntege finally { if (arrayFromPool != null) + { ArrayPool.Shared.Return(arrayFromPool); + } } - bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span currentBuffer) + bool ProcessChunk(ReadOnlySpan chunkDigits, ref Span currentBuffer) { int remainingIntDigitCount = Math.Max(numberScale - totalDigitCount, 0); ReadOnlySpan intDigitsSpan = chunkDigits.Slice(0, Math.Min(remainingIntDigitCount, chunkDigits.Length)); + bool endReached = false; + + // Storing these captured variables in locals for faster access in the loop. + uint _partialValue = partialValue; + int _partialDigitCount = partialDigitCount; + int _totalDigitCount = totalDigitCount; + for (int i = 0; i < intDigitsSpan.Length; i++) { char digitChar = chunkDigits[i]; if (digitChar == '\0') - return true; + { + endReached = true; + break; + } - partialValue = partialValue * 10 + (uint)(digitChar - '0'); - partialDigitCount++; - totalDigitCount++; + _partialValue = _partialValue * 10 + (uint)(digitChar - '0'); + _partialDigitCount++; + _totalDigitCount++; // Update the buffer when enough partial digits have been accumulated. - if (partialDigitCount == MaxPartialDigits) + if (_partialDigitCount == MaxPartialDigits) { - MultiplyAdd(ref currentBuffer, TenPowMaxPartial, partialValue); - partialValue = 0; - partialDigitCount = 0; + MultiplyAdd(ref currentBuffer, TenPowMaxPartial, _partialValue); + _partialValue = 0; + _partialDigitCount = 0; } } // Check for nonzero digits after the decimal point. - ReadOnlySpan fracDigitsSpan = chunkDigits.Slice(intDigitsSpan.Length); - for (int i = 0; i < fracDigitsSpan.Length; i++) + if (!endReached) { - char digitChar = fracDigitsSpan[i]; - if (digitChar == '\0') - return true; - if (digitChar != '0') - return false; + ReadOnlySpan fracDigitsSpan = chunkDigits.Slice(intDigitsSpan.Length); + for (int i = 0; i < fracDigitsSpan.Length; i++) + { + char digitChar = fracDigitsSpan[i]; + if (digitChar == '\0') + { + break; + } + if (digitChar != '0') + { + return false; + } + } } + partialValue = _partialValue; + partialDigitCount = _partialDigitCount; + totalDigitCount = _totalDigitCount; + return true; } - void MultiplyAdd(ref Span currentBuffer, uint multiplier, uint addValue) + void MultiplyAdd(ref Span currentBuffer, uint multiplier, uint addValue) { - Span curBits = currentBuffer.Slice(0, currentBufferSize); + Span curBits = currentBuffer.Slice(0, currentBufferSize); uint carry = addValue; for (int i = 0; i < curBits.Length; i++) { - ulong p = (ulong)multiplier * (uint)curBits[i] + carry; - curBits[i] = (int)p; + ulong p = (ulong)multiplier * curBits[i] + carry; + curBits[i] = (uint)p; carry = (uint)(p >> 32); } if (carry == 0) + { return; + } if (currentBufferSize == currentBuffer.Length) { int[]? arrayToReturn = arrayFromPool; arrayFromPool = ArrayPool.Shared.Rent(checked(currentBufferSize * 2)); - currentBuffer.CopyTo(arrayFromPool); - currentBuffer = arrayFromPool; + Span newBuffer = MemoryMarshal.Cast(arrayFromPool); + currentBuffer.CopyTo(newBuffer); + currentBuffer = newBuffer; if (arrayToReturn != null) + { ArrayPool.Shared.Return(arrayToReturn); + } } - currentBuffer[currentBufferSize] = (int)carry; + currentBuffer[currentBufferSize] = carry; currentBufferSize++; } }