Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 55ac59b

Browse files
tannergoodingwtgodbe
authored andcommitted
Merged PR 21172: Fixing the shift-left handling to correctly account for overshifting
Fixing the shift-left handling to correctly account for overshifting
1 parent 0473291 commit 55ac59b

File tree

1 file changed

+33
-13
lines changed

1 file changed

+33
-13
lines changed

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

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ internal unsafe ref struct BigInteger
3232
private const int MaxBits = BitsForLongestBinaryMantissa + BitsForLongestDigitSequence + BitsPerBlock;
3333

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

3739
private static readonly uint[] s_Pow10UInt32Table = new uint[]
3840
{
@@ -300,7 +302,8 @@ internal unsafe ref struct BigInteger
300302
0xD9D61A05,
301303
0x00000325,
302304

303-
// 9 Trailing blocks to ensure MaxBlockCount
305+
// 10 Trailing blocks to ensure MaxBlockCount
306+
0x00000000,
304307
0x00000000,
305308
0x00000000,
306309
0x00000000,
@@ -1304,16 +1307,6 @@ public void ShiftLeft(uint shift)
13041307
int readIndex = (length - 1);
13051308
int writeIndex = readIndex + (int)(blocksToShift);
13061309

1307-
uint remainingBitsInLastBlock = (uint)BitOperations.LeadingZeroCount(_blocks[readIndex]);
1308-
1309-
if (remainingBitsToShift > remainingBitsInLastBlock)
1310-
{
1311-
// We need an extra block for the partial shift
1312-
writeIndex++;
1313-
}
1314-
1315-
Debug.Assert(unchecked((uint)(writeIndex)) < MaxBlockCount);
1316-
13171310
if (unchecked((uint)(writeIndex)) >= MaxBlockCount)
13181311
{
13191312
// We shouldn't reach here, and the above assert will help flag this
@@ -1327,6 +1320,18 @@ public void ShiftLeft(uint shift)
13271320
// Check if the shift is block aligned
13281321
if (remainingBitsToShift == 0)
13291322
{
1323+
Debug.Assert(unchecked((uint)(length)) < MaxBlockCount);
1324+
1325+
if (unchecked((uint)(length)) >= MaxBlockCount)
1326+
{
1327+
// We shouldn't reach here, and the above assert will help flag this
1328+
// during testing, but we'll ensure that we return a safe value of
1329+
// zero in the case we end up overflowing in any way.
1330+
1331+
SetZero();
1332+
return;
1333+
}
1334+
13301335
while (readIndex >= 0)
13311336
{
13321337
_blocks[writeIndex] = _blocks[readIndex];
@@ -1340,7 +1345,22 @@ public void ShiftLeft(uint shift)
13401345
Buffer.ZeroMemory((byte*)(GetBlocksPointer()), (blocksToShift * sizeof(uint)));
13411346
}
13421347
else
1343-
{
1348+
{
1349+
// We need an extra block for the partial shift
1350+
1351+
writeIndex++;
1352+
Debug.Assert(unchecked((uint)(length)) < MaxBlockCount);
1353+
1354+
if (unchecked((uint)(length)) >= MaxBlockCount)
1355+
{
1356+
// We shouldn't reach here, and the above assert will help flag this
1357+
// during testing, but we'll ensure that we return a safe value of
1358+
// zero in the case we end up overflowing in any way.
1359+
1360+
SetZero();
1361+
return;
1362+
}
1363+
13441364
// Set the length to hold the shifted blocks
13451365
_length = writeIndex + 1;
13461366

0 commit comments

Comments
 (0)