@@ -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