Skip to content
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

Fix assertion at System.Numerics.BigIntegerCalculator.Multiply #96744

Merged
merged 5 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -659,9 +659,9 @@ internal static ParsingStatus TryParseBigIntegerBinaryNumberStyle(ReadOnlySpan<c
//
#if DEBUG
// Mutable for unit testing...
private static
internal static
#else
private const
internal const
#endif
int s_naiveThreshold = 20000;
private static ParsingStatus NumberToBigInteger(ref NumberBuffer number, out BigInteger result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,9 @@ stackalloc uint[StackAllocThreshold]

#if DEBUG
// Mutable for unit testing...
private static
internal static
#else
private const
internal const
#endif
int ReducerThreshold = 32;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ internal static partial class BigIntegerCalculator
{
#if DEBUG
// Mutable for unit testing...
private static
internal static
#else
private const
internal const
#endif
int SquareThreshold = 32;

Expand Down Expand Up @@ -160,7 +160,8 @@ internal const
public static void Multiply(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right, Span<uint> bits)
{
Debug.Assert(left.Length >= right.Length);
Debug.Assert(bits.Length == left.Length + right.Length);
Debug.Assert(bits.Length >= left.Length + right.Length);
Debug.Assert(!bits.ContainsAnyExcept(0u));

if (left.Length - right.Length < 3)
{
Expand All @@ -175,7 +176,8 @@ public static void Multiply(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right, S
private static void MultiplyFarLength(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right, Span<uint> bits)
{
Debug.Assert(left.Length - right.Length >= 3);
Debug.Assert(bits.Length == left.Length + right.Length);
Debug.Assert(bits.Length >= left.Length + right.Length);
Debug.Assert(!bits.ContainsAnyExcept(0u));

// Executes different algorithms for computing z = a * b
// based on the actual length of b. If b is "small" enough
Expand Down Expand Up @@ -370,7 +372,8 @@ stackalloc uint[StackAllocThreshold]
private static void MultiplyNearLength(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right, Span<uint> bits)
{
Debug.Assert(left.Length - right.Length < 3);
Debug.Assert(bits.Length == left.Length + right.Length);
Debug.Assert(bits.Length >= left.Length + right.Length);
Debug.Assert(!bits.ContainsAnyExcept(0u));

// Executes different algorithms for computing z = a * b
// based on the actual length of b. If b is "small" enough
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;

namespace BigIntTools
Expand Down Expand Up @@ -34,43 +33,29 @@ public static string BuildRandomNumber(int maxdigits, int seed)
}
}

private static TypeInfo InternalCalculator
public static void RunWithFakeThreshold(in int field, int value, Action action)
{
get
{
if (s_lazyInternalCalculator == null)
{
Type t = typeof(BigInteger).Assembly.GetType("System.Numerics.BigIntegerCalculator");
if (t != null)
{
s_lazyInternalCalculator = t.GetTypeInfo();
}
}
return s_lazyInternalCalculator;
}
}

private static volatile TypeInfo s_lazyInternalCalculator;

public static void RunWithFakeThreshold(string name, int value, Action action)
{
TypeInfo internalCalculator = InternalCalculator;
if (internalCalculator == null)
return; // Internal frame types are not reflectable on AoT platforms. Skip the test.
int lastValue = field;

FieldInfo field = internalCalculator.GetDeclaredField(name);
if (field is null || field.IsLiteral)
return; // in Release config the field may be const
// This is tricky hack. If only DEBUG build is targeted,
// `RunWithFakeThreshold(ref int field, int value, Action action action)` would be more appropriate.
// However, in RELEASE build, the code should be like this.
// This is because const fields cannot be passed as ref arguments.
// When a const field is passed to the in argument, a local
// variable reference is implicitly passed to the in argument
// so that the original const field value is never rewritten.
ref int reference = ref Unsafe.AsRef(in field);

int lastValue = (int)field.GetValue(null);
field.SetValue(null, value);
try
{
reference = value;
if (field != value)
return; // In release build, the test will be skipped.
action();
}
finally
{
field.SetValue(null, lastValue);
reference = lastValue;
}
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public static void ModPow1Large2SmallInt()
public static void ModPow1Large2SmallInt_Threshold()
{
// Again, with lower threshold
BigIntTools.Utils.RunWithFakeThreshold("ReducerThreshold", 8, ModPow1Large2SmallInt);
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.ReducerThreshold, 8, ModPow1Large2SmallInt);
}

[Fact]
Expand All @@ -166,7 +166,7 @@ public static void ModPow2Large1SmallInt()
public static void ModPow2Large1SmallInt_Threshold()
{
// Again, with lower threshold
BigIntTools.Utils.RunWithFakeThreshold("ReducerThreshold", 8, ModPow2Large1SmallInt);
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.ReducerThreshold, 8, ModPow2Large1SmallInt);
}

// InlineData randomly generated using a new Random(0) and the same logic as is used in MyBigIntImp
Expand Down Expand Up @@ -194,7 +194,7 @@ public static void ModPow3LargeInt(string value, string exponent, string modulus
Assert.Equal(resultInt, BigInteger.ModPow(valueInt, exponentInt, modulusInt));

// Once with reduced threshold
BigIntTools.Utils.RunWithFakeThreshold("ReducerThreshold", 8, () =>
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.ReducerThreshold, 8, () =>
{
Assert.Equal(resultInt, BigInteger.ModPow(valueInt, exponentInt, modulusInt));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ public static void RunMultiply_TwoLargeBigIntegers()
public static void RunMultiply_TwoLargeBigIntegers_Threshold()
{
// Again, with lower threshold
BigIntTools.Utils.RunWithFakeThreshold("SquareThreshold", 8, () =>
BigIntTools.Utils.RunWithFakeThreshold("MultiplyThreshold", 8, RunMultiply_TwoLargeBigIntegers)
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.SquareThreshold, 8, () =>
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.MultiplyThreshold, 8, RunMultiply_TwoLargeBigIntegers)
);

// Again, with lower threshold
BigIntTools.Utils.RunWithFakeThreshold("SquareThreshold", 8, () =>
BigIntTools.Utils.RunWithFakeThreshold("MultiplyThreshold", 8, () =>
BigIntTools.Utils.RunWithFakeThreshold("StackAllocThreshold", 8, RunMultiply_TwoLargeBigIntegers)
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.SquareThreshold, 8, () =>
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.MultiplyThreshold, 8, () =>
BigIntTools.Utils.RunWithFakeThreshold(BigIntegerCalculator.StackAllocThreshold, 8, RunMultiply_TwoLargeBigIntegers)
)
);
}
Expand Down
39 changes: 35 additions & 4 deletions src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public static IEnumerable<object[]> Cultures
public static void RunParseToStringTests(CultureInfo culture)
{
Test();
BigNumberTools.Utils.RunWithFakeThreshold("s_naiveThreshold", 0, Test);
BigIntTools.Utils.RunWithFakeThreshold(Number.s_naiveThreshold, 0, Test);

void Test()
{
byte[] tempByteArray1 = new byte[0];
Expand Down Expand Up @@ -101,7 +102,9 @@ void Test()
public void Parse_Subspan_Success(string input, int offset, int length, string expected)
{
Test();
BigNumberTools.Utils.RunWithFakeThreshold("s_naiveThreshold", 0, Test);

BigIntTools.Utils.RunWithFakeThreshold(Number.s_naiveThreshold, 0, Test);

void Test()
{
Eval(BigInteger.Parse(input.AsSpan(offset, length)), expected);
Expand All @@ -114,7 +117,8 @@ void Test()
public void Parse_EmptySubspan_Fails()
{
Test();
BigNumberTools.Utils.RunWithFakeThreshold("s_naiveThreshold", 0, Test);
BigIntTools.Utils.RunWithFakeThreshold(Number.s_naiveThreshold, 0, Test);

void Test()
{
Assert.False(BigInteger.TryParse("12345".AsSpan(0, 0), out BigInteger result));
Expand Down Expand Up @@ -150,6 +154,33 @@ public void Parse_Hex32Bits()
});
}

public static IEnumerable<object[]> RegressionIssueRuntime94610_TestData()
{
yield return new object[]
{
new string('9', 865),
};

yield return new object[]
{
new string('9', 20161),
};
}

[Theory]
[MemberData(nameof(RegressionIssueRuntime94610_TestData))]
public void RegressionIssueRuntime94610(string text)
{
// Regression test for: https://github.com/dotnet/runtime/issues/94610
Test();
BigIntTools.Utils.RunWithFakeThreshold(Number.s_naiveThreshold, 0, Test);

void Test()
{
VerifyParseToString(text, NumberStyles.Integer, true);
}
}

private static void RunFormatProviderParseStrings()
{
NumberFormatInfo nfi = new NumberFormatInfo();
Expand Down Expand Up @@ -782,7 +813,7 @@ private static string GetBinaryDigitSequence(int min, int max, Random random)
{
string result = string.Empty;
int size = random.Next(min, max);

for (int i = 0; i < size; i++)
{
result += random.Next(0, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<Compile Include="BigInteger\BigIntegerToStringTests.cs" />
<Compile Include="BigInteger\BigInteger.AddTests.cs" />
<Compile Include="BigInteger\BigInteger.SubtractTests.cs" />
<Compile Include="BigInteger\BigNumberTools.cs" />
<Compile Include="BigInteger\BigIntTools.cs" />
<Compile Include="BigInteger\cast_from.cs" />
<Compile Include="BigInteger\cast_to.cs" />
Expand Down
Loading