@@ -31,7 +31,9 @@ internal unsafe ref struct BigInteger
31
31
private const int MaxBits = BitsForLongestBinaryMantissa + BitsForLongestDigitSequence + BitsPerBlock ;
32
32
33
33
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 ;
35
37
36
38
private static readonly uint [ ] s_Pow10UInt32Table = new uint [ ]
37
39
{
@@ -302,7 +304,8 @@ internal unsafe ref struct BigInteger
302
304
0xD9D61A05 ,
303
305
0x00000325 ,
304
306
305
- // 9 Trailing blocks to ensure MaxBlockCount
307
+ // 10 Trailing blocks to ensure MaxBlockCount
308
+ 0x00000000 ,
306
309
0x00000000 ,
307
310
0x00000000 ,
308
311
0x00000000 ,
@@ -358,11 +361,24 @@ public static void Add(ref BigInteger lhs, ref BigInteger rhs, out BigInteger re
358
361
resultIndex ++ ;
359
362
}
360
363
364
+ int resultLength = largeLength ;
365
+
361
366
// If there's still a carry, append a new block
362
367
if ( carry != 0 )
363
368
{
364
369
Debug . Assert ( carry == 1 ) ;
365
- Debug . Assert ( ( resultIndex == largeLength ) && ( largeLength < MaxBlockCount ) ) ;
370
+ Debug . Assert ( resultIndex == resultLength ) ;
371
+ Debug . Assert ( unchecked ( ( uint ) ( resultLength ) ) < MaxBlockCount ) ;
372
+
373
+ if ( unchecked ( ( uint ) ( resultLength ) ) >= MaxBlockCount )
374
+ {
375
+ // We shouldn't reach here, and the above assert will help flag this
376
+ // during testing, but we'll ensure that we return a safe value of
377
+ // zero in the case we end up overflowing in any way.
378
+
379
+ SetZero ( out result ) ;
380
+ return ;
381
+ }
366
382
367
383
result . _blocks [ resultIndex ] = 1 ;
368
384
result . _length ++ ;
@@ -722,16 +738,27 @@ public static void Multiply(ref BigInteger lhs, uint value, out BigInteger resul
722
738
index ++ ;
723
739
}
724
740
741
+ int resultLength = lhsLength ;
742
+
725
743
if ( carry != 0 )
726
744
{
727
- Debug . Assert ( unchecked ( ( uint ) ( lhsLength ) ) + 1 <= MaxBlockCount ) ;
745
+ Debug . Assert ( unchecked ( ( uint ) ( resultLength ) ) < MaxBlockCount ) ;
746
+
747
+ if ( unchecked ( ( uint ) ( resultLength ) ) >= MaxBlockCount )
748
+ {
749
+ // We shouldn't reach here, and the above assert will help flag this
750
+ // during testing, but we'll ensure that we return a safe value of
751
+ // zero in the case we end up overflowing in any way.
752
+
753
+ SetZero ( out result ) ;
754
+ return ;
755
+ }
756
+
728
757
result . _blocks [ index ] = carry ;
729
- result . _length = ( lhsLength + 1 ) ;
730
- }
731
- else
732
- {
733
- result . _length = lhsLength ;
758
+ resultLength += 1 ;
734
759
}
760
+
761
+ result . _length = resultLength ;
735
762
}
736
763
737
764
public static void Multiply ( ref BigInteger lhs , ref BigInteger rhs , out BigInteger result )
@@ -766,6 +793,16 @@ public static void Multiply(ref BigInteger lhs, ref BigInteger rhs, out BigInteg
766
793
int maxResultLength = smallLength + largeLength ;
767
794
Debug . Assert ( unchecked ( ( uint ) ( maxResultLength ) ) <= MaxBlockCount ) ;
768
795
796
+ if ( unchecked ( ( uint ) ( maxResultLength ) ) > MaxBlockCount )
797
+ {
798
+ // We shouldn't reach here, and the above assert will help flag this
799
+ // during testing, but we'll ensure that we return a safe value of
800
+ // zero in the case we end up overflowing in any way.
801
+
802
+ SetZero ( out result ) ;
803
+ return ;
804
+ }
805
+
769
806
// Zero out result internal blocks.
770
807
result . _length = maxResultLength ;
771
808
result . Clear ( ( uint ) maxResultLength ) ;
@@ -811,7 +848,19 @@ public static void Pow2(uint exponent, out BigInteger result)
811
848
{
812
849
uint blocksToShift = DivRem32 ( exponent , out uint remainingBitsToShift ) ;
813
850
result . _length = ( int ) blocksToShift + 1 ;
851
+
814
852
Debug . Assert ( unchecked ( ( uint ) result . _length ) <= MaxBlockCount ) ;
853
+
854
+ if ( unchecked ( ( uint ) result . _length ) > MaxBlockCount )
855
+ {
856
+ // We shouldn't reach here, and the above assert will help flag this
857
+ // during testing, but we'll ensure that we return a safe value of
858
+ // zero in the case we end up overflowing in any way.
859
+
860
+ SetZero ( out result ) ;
861
+ return ;
862
+ }
863
+
815
864
if ( blocksToShift > 0 )
816
865
{
817
866
result . Clear ( blocksToShift ) ;
@@ -1001,7 +1050,18 @@ public void Add(uint value)
1001
1050
}
1002
1051
}
1003
1052
1004
- Debug . Assert ( unchecked ( ( uint ) ( length ) ) + 1 <= MaxBlockCount ) ;
1053
+ Debug . Assert ( unchecked ( ( uint ) ( length ) ) < MaxBlockCount ) ;
1054
+
1055
+ if ( unchecked ( ( uint ) ( length ) ) >= MaxBlockCount )
1056
+ {
1057
+ // We shouldn't reach here, and the above assert will help flag this
1058
+ // during testing, but we'll ensure that we return a safe value of
1059
+ // zero in the case we end up overflowing in any way.
1060
+
1061
+ SetZero ( out this ) ;
1062
+ return ;
1063
+ }
1064
+
1005
1065
_blocks [ length ] = 1 ;
1006
1066
_length = length + 1 ;
1007
1067
}
@@ -1063,9 +1123,20 @@ public void Multiply10()
1063
1123
1064
1124
if ( carry != 0 )
1065
1125
{
1066
- Debug . Assert ( unchecked ( ( uint ) ( _length ) ) + 1 <= MaxBlockCount ) ;
1126
+ Debug . Assert ( unchecked ( ( uint ) ( length ) ) < MaxBlockCount ) ;
1127
+
1128
+ if ( unchecked ( ( uint ) ( length ) ) >= MaxBlockCount )
1129
+ {
1130
+ // We shouldn't reach here, and the above assert will help flag this
1131
+ // during testing, but we'll ensure that we return a safe value of
1132
+ // zero in the case we end up overflowing in any way.
1133
+
1134
+ SetZero ( out this ) ;
1135
+ return ;
1136
+ }
1137
+
1067
1138
_blocks [ index ] = ( uint ) carry ;
1068
- _length ++ ;
1139
+ _length = length + 1 ;
1069
1140
}
1070
1141
}
1071
1142
@@ -1141,7 +1212,17 @@ public void ShiftLeft(uint shift)
1141
1212
// Check if the shift is block aligned
1142
1213
if ( remainingBitsToShift == 0 )
1143
1214
{
1144
- Debug . Assert ( writeIndex < MaxBlockCount ) ;
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
+ }
1145
1226
1146
1227
while ( readIndex >= 0 )
1147
1228
{
@@ -1158,8 +1239,19 @@ public void ShiftLeft(uint shift)
1158
1239
else
1159
1240
{
1160
1241
// We need an extra block for the partial shift
1242
+
1161
1243
writeIndex ++ ;
1162
- Debug . Assert ( writeIndex < MaxBlockCount ) ;
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
+ }
1163
1255
1164
1256
// Set the length to hold the shifted blocks
1165
1257
_length = writeIndex + 1 ;
0 commit comments