Skip to content

Commit 3065735

Browse files
tannergoodingmmitche
authored andcommitted
Merged PR 21171: Fixing the shift-left handling to correctly account for overshifting
Fixing the shift-left handling to correctly account for overshifting
1 parent f4567ec commit 3065735

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

Lines changed: 32 additions & 12 deletions
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,
@@ -1205,19 +1208,21 @@ public void ShiftLeft(uint shift)
12051208
int readIndex = (length - 1);
12061209
int writeIndex = readIndex + (int)(blocksToShift);
12071210

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

0 commit comments

Comments
 (0)