Skip to content

Commit 10db735

Browse files
tannergoodingmmitche
authored andcommitted
Merged PR 21170: Fixing the shift-left handling to correctly account for overshifting
Fixing the shift-left handling to correctly account for overshifting
1 parent 5b0b8a0 commit 10db735

File tree

1 file changed

+32
-12
lines changed

1 file changed

+32
-12
lines changed

src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs

+32-12
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ internal unsafe ref struct BigInteger
3131
private const int MaxBits = BitsForLongestBinaryMantissa + BitsForLongestDigitSequence + BitsPerBlock;
3232

3333
private const int BitsPerBlock = sizeof(int) * 8;
34-
private const int MaxBlockCount = (MaxBits + (BitsPerBlock - 1)) / BitsPerBlock;
34+
35+
// We need one extra block to make our shift left algorithm significantly simpler
36+
private const int MaxBlockCount = ((MaxBits + (BitsPerBlock - 1)) / BitsPerBlock) + 1;
3537

3638
private static readonly uint[] s_Pow10UInt32Table = new uint[]
3739
{
@@ -302,7 +304,8 @@ internal unsafe ref struct BigInteger
302304
0xD9D61A05,
303305
0x00000325,
304306

305-
// 9 Trailing blocks to ensure MaxBlockCount
307+
// 10 Trailing blocks to ensure MaxBlockCount
308+
0x00000000,
306309
0x00000000,
307310
0x00000000,
308311
0x00000000,
@@ -1206,19 +1209,21 @@ public void ShiftLeft(uint shift)
12061209
int readIndex = (length - 1);
12071210
int writeIndex = readIndex + (int)(blocksToShift);
12081211

1209-
uint remainingBitsInLastBlock = (uint)BitOperations.LeadingZeroCount(_blocks[readIndex]);
1210-
1211-
if (remainingBitsToShift > remainingBitsInLastBlock)
1212-
{
1213-
// We need an extra block for the partial shift
1214-
writeIndex++;
1215-
}
1216-
1217-
Debug.Assert(unchecked((uint)(writeIndex)) < MaxBlockCount);
1218-
12191212
// Check if the shift is block aligned
12201213
if (remainingBitsToShift == 0)
12211214
{
1215+
Debug.Assert(unchecked((uint)(length)) < MaxBlockCount);
1216+
1217+
if (unchecked((uint)(length)) >= MaxBlockCount)
1218+
{
1219+
// We shouldn't reach here, and the above assert will help flag this
1220+
// during testing, but we'll ensure that we return a safe value of
1221+
// zero in the case we end up overflowing in any way.
1222+
1223+
SetZero(out this);
1224+
return;
1225+
}
1226+
12221227
while (readIndex >= 0)
12231228
{
12241229
_blocks[writeIndex] = _blocks[readIndex];
@@ -1233,6 +1238,21 @@ public void ShiftLeft(uint shift)
12331238
}
12341239
else
12351240
{
1241+
// We need an extra block for the partial shift
1242+
1243+
writeIndex++;
1244+
Debug.Assert(unchecked((uint)(length)) < MaxBlockCount);
1245+
1246+
if (unchecked((uint)(length)) >= MaxBlockCount)
1247+
{
1248+
// We shouldn't reach here, and the above assert will help flag this
1249+
// during testing, but we'll ensure that we return a safe value of
1250+
// zero in the case we end up overflowing in any way.
1251+
1252+
SetZero(out this);
1253+
return;
1254+
}
1255+
12361256
// Set the length to hold the shifted blocks
12371257
_length = writeIndex + 1;
12381258

0 commit comments

Comments
 (0)