-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Restrict the maximum length of BigInteger #102874
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,19 +29,21 @@ public static void BigShiftsTest() | |
| public static void LargeNegativeBigIntegerShiftTest() | ||
| { | ||
| // Create a very large negative BigInteger | ||
| BigInteger bigInt = new BigInteger(-1) << int.MaxValue; | ||
| Assert.Equal(2147483647, bigInt.GetBitLength()); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this become a test that validates an exception is thrown?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potentially. There's some general cleanup and additional validation I'd like to do here long term, but I'll get this back up to validate it throws in a follow up PR. |
||
| int bitsPerElement = 8 * sizeof(uint); | ||
| int maxBitLength = ((Array.MaxLength / bitsPerElement) * bitsPerElement); | ||
| BigInteger bigInt = new BigInteger(-1) << (maxBitLength - 1); | ||
| Assert.Equal(maxBitLength - 1, bigInt.GetBitLength()); | ||
| Assert.Equal(-1, bigInt.Sign); | ||
|
|
||
| // Validate internal representation. | ||
| // At this point, bigInt should be a 1 followed by int.MaxValue zeros. | ||
| // At this point, bigInt should be a 1 followed by maxBitLength - 1 zeros. | ||
| // Given this, bigInt._bits is expected to be structured as follows: | ||
| // - _bits.Length == (int.MaxValue + 1) / (8 * sizeof(uint)) | ||
| // - _bits.Length == ceil(maxBitLength / bitsPerElement) | ||
| // - First (_bits.Length - 1) elements: 0x00000000 | ||
| // - Last element: 0x80000000 | ||
| // ^------ (There's the leading '1') | ||
|
|
||
| Assert.Equal(((uint)int.MaxValue + 1) / (8 * sizeof(uint)), (uint)bigInt._bits.Length); | ||
| Assert.Equal((maxBitLength + (bitsPerElement - 1)) / bitsPerElement, bigInt._bits.Length); | ||
|
|
||
| uint i = 0; | ||
| for (; i < (bigInt._bits.Length - 1); i++) { | ||
|
|
@@ -52,18 +54,18 @@ public static void LargeNegativeBigIntegerShiftTest() | |
|
|
||
| // Right shift the BigInteger | ||
| BigInteger shiftedBigInt = bigInt >> 1; | ||
| Assert.Equal(2147483646, shiftedBigInt.GetBitLength()); | ||
| Assert.Equal(maxBitLength - 2, shiftedBigInt.GetBitLength()); | ||
| Assert.Equal(-1, shiftedBigInt.Sign); | ||
|
|
||
| // Validate internal representation. | ||
| // At this point, shiftedBigInt should be a 1 followed by int.MaxValue - 1 zeros. | ||
| // At this point, shiftedBigInt should be a 1 followed by maxBitLength - 2 zeros. | ||
| // Given this, shiftedBigInt._bits is expected to be structured as follows: | ||
| // - _bits.Length == (int.MaxValue + 1) / (8 * sizeof(uint)) | ||
| // - _bits.Length == ceil((maxBitLength - 1) / bitsPerElement) | ||
| // - First (_bits.Length - 1) elements: 0x00000000 | ||
| // - Last element: 0x40000000 | ||
| // ^------ (the '1' is now one position to the right) | ||
|
|
||
| Assert.Equal(((uint)int.MaxValue + 1) / (8 * sizeof(uint)), (uint)shiftedBigInt._bits.Length); | ||
| Assert.Equal(((maxBitLength - 1) + (bitsPerElement - 1)) / bitsPerElement, shiftedBigInt._bits.Length); | ||
|
|
||
| i = 0; | ||
| for (; i < (shiftedBigInt._bits.Length - 1); i++) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why we have two separate implementations one for
Span<uint>and one forReadOnlySpan<uint>+ isNegative. Could this ctor just do the checks to compute isNegative and then delegate to the other in order to avoid duplication? Or are they dealing with completely different formats?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly because
BigIntegeris an old type in desperate need of a rewrite 😆. I imagine we could, with a bit more refactoring, share some or most of the implementation here.private BigInteger(ReadOnlySpan<uint> value, bool negative)is an optimized implementation that assumes we are already in almost the right shape. Sovalueis the unsigned storage data (the absolute value) and we're really just trimming unnecessary leading zeros and deciding whether to store it in_bitsor compress it into_sign.private BigInteger(Span<uint> value)on the other hand is taking a two's complement little-endian span. So it has to detect if its positive or negative and if its negative fix it up to be the absolute value for storage purposes. So I expect we could implement it as:0xFFif negative,0x00if positive)private BigInteger(ReadOnlySpan<uint> value, bool negative)