TryParseNumber_char

Created Diff never expires
7 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
258 lines
14 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
265 lines
private static unsafe bool TryParseNumber(scoped ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
private static unsafe bool TryParseNumber(scoped ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
{
{
Debug.Assert(str != null);
Debug.Assert(str != null);
Debug.Assert(strEnd != null);
Debug.Assert(strEnd != null);
Debug.Assert(str <= strEnd);
Debug.Assert(str <= strEnd);
Debug.Assert((styles & (NumberStyles.AllowHexSpecifier | NumberStyles.AllowBinarySpecifier)) == 0);
Debug.Assert((styles & (NumberStyles.AllowHexSpecifier | NumberStyles.AllowBinarySpecifier)) == 0);


const int StateSign = 0x0001;
const int StateSign = 0x0001;
const int StateParens = 0x0002;
const int StateParens = 0x0002;
const int StateDigits = 0x0004;
const int StateDigits = 0x0004;
const int StateNonZero = 0x0008;
const int StateNonZero = 0x0008;
const int StateDecimal = 0x0010;
const int StateDecimal = 0x0010;
const int StateCurrency = 0x0020;
const int StateCurrency = 0x0020;


Debug.Assert(number.DigitsCount == 0);
Debug.Assert(number.DigitsCount == 0);
Debug.Assert(number.Scale == 0);
Debug.Assert(number.Scale == 0);
Debug.Assert(!number.IsNegative);
Debug.Assert(!number.IsNegative);
Debug.Assert(!number.HasNonZeroTail);
Debug.Assert(!number.HasNonZeroTail);


number.CheckConsistency();
number.CheckConsistency();


string decSep; // decimal separator from NumberFormatInfo.
string decSep; // decimal separator from NumberFormatInfo.
string groupSep; // group separator from NumberFormatInfo.
string groupSep; // group separator from NumberFormatInfo.
string? currSymbol = null; // currency symbol from NumberFormatInfo.
string? currSymbol = null; // currency symbol from NumberFormatInfo.


bool parsingCurrency = false;
bool parsingCurrency = false;
if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
{
{
currSymbol = info.CurrencySymbol;
currSymbol = info.CurrencySymbol;


// The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast.
// The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast.
// The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
// The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
decSep = info.CurrencyDecimalSeparator;
decSep = info.CurrencyDecimalSeparator;
groupSep = info.CurrencyGroupSeparator;
groupSep = info.CurrencyGroupSeparator;
parsingCurrency = true;
parsingCurrency = true;
}
}
else
else
{
{
decSep = info.NumberDecimalSeparator;
decSep = info.NumberDecimalSeparator;
groupSep = info.NumberGroupSeparator;
groupSep = info.NumberGroupSeparator;
}
}


int state = 0;
int state = 0;
char* p = str;
char* p = str;
char ch = p < strEnd ? *p : '\0';
char ch = p < strEnd ? *p : '\0';
char* next;
char* next;


while (true)
while (true)
{
{
// Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
// Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
// "-Kr 1231.47" is legal but "- 1231.47" is not.
// "-Kr 1231.47" is legal but "- 1231.47" is not.
if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
{
{
if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchNegativeSignChars(p, strEnd, info)) != null && (number.IsNegative = true))))
if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchNegativeSignChars(p, strEnd, info)) != null && (number.IsNegative = true))))
{
{
state |= StateSign;
state |= StateSign;
p = next - 1;
p = next - 1;
}
}
else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
{
{
state |= StateSign | StateParens;
state |= StateSign | StateParens;
number.IsNegative = true;
number.IsNegative = true;
}
}
else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
{
{
state |= StateCurrency;
state |= StateCurrency;
currSymbol = null;
currSymbol = null;
// We already found the currency symbol. There should not be more currency symbols. Set
// We already found the currency symbol. There should not be more currency symbols. Set
// currSymbol to NULL so that we won't search it again in the later code path.
// currSymbol to NULL so that we won't search it again in the later code path.
p = next - 1;
p = next - 1;
}
}
else
else
{
{
break;
break;
}
}
}
}
ch = ++p < strEnd ? *p : '\0';
ch = ++p < strEnd ? *p : '\0';
}
}


int digCount = 0;
int digCount = 0;
int digEnd = 0;
int digEnd = 0;
int maxDigCount = number.Digits.Length - 1;
int maxDigCount = number.Digits.Length - 1;
int numberOfTrailingZeros = 0;
int numberOfTrailingZeros = 0;


while (true)
while (true)
{
{
if (IsDigit(ch))
if (IsDigit(ch))
{
{
state |= StateDigits;
state |= StateDigits;


if (ch != '0' || (state & StateNonZero) != 0)
if (ch != '0' || (state & StateNonZero) != 0)
{
{
if (digCount < maxDigCount)
if (digCount < maxDigCount)
{
{
number.Digits[digCount] = (byte)(ch);
number.Digits[digCount] = (byte)(ch);
if ((ch != '0') || (number.Kind != NumberBufferKind.Integer))
if ((ch != '0') || (number.Kind != NumberBufferKind.Integer))
{
{
digEnd = digCount + 1;
digEnd = digCount + 1;
}
}
}
}
else if (ch != '0')
else if (ch != '0')
{
{
// For decimal and binary floating-point numbers, we only
// For decimal and binary floating-point numbers, we only
// need to store digits up to maxDigCount. However, we still
// need to store digits up to maxDigCount. However, we still
// need to keep track of whether any additional digits past
// need to keep track of whether any additional digits past
// maxDigCount were non-zero, as that can impact rounding
// maxDigCount were non-zero, as that can impact rounding
// for an input that falls evenly between two representable
// for an input that falls evenly between two representable
// results.
// results.


number.HasNonZeroTail = true;
number.HasNonZeroTail = true;
}
}


if ((state & StateDecimal) == 0)
if ((state & StateDecimal) == 0)
{
{
number.Scale++;
number.Scale++;
}
}


if (digCount < maxDigCount)
if (digCount < maxDigCount)
{
{
// Handle a case like "53.0". We need to ignore trailing zeros in the fractional part for floating point numbers, so we keep a count of the number of trailing zeros and update digCount later
// Handle a case like "53.0". We need to ignore trailing zeros in the fractional part for floating point numbers, so we keep a count of the number of trailing zeros and update digCount later
if (ch == '0')
if (ch == '0')
{
{
numberOfTrailingZeros++;
numberOfTrailingZeros++;
}
}
else
else
{
{
numberOfTrailingZeros = 0;
numberOfTrailingZeros = 0;
}
}
}
}
digCount++;
digCount++;
state |= StateNonZero;
state |= StateNonZero;
}
}
else if ((state & StateDecimal) != 0)
else if ((state & StateDecimal) != 0)
{
{
number.Scale--;
number.Scale--;
}
}
}
}
else if (((styles & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || (parsingCurrency && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberDecimalSeparator)) != null))
else if (((styles & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || (parsingCurrency && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberDecimalSeparator)) != null))
{
{
state |= StateDecimal;
state |= StateDecimal;
p = next - 1;
p = next - 1;
}
}
else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || (parsingCurrency && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null))
else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || (parsingCurrency && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null))
{
{
p = next - 1;
p = next - 1;
}
}
else
else
{
{
break;
break;
}
}
ch = ++p < strEnd ? *p : '\0';
ch = ++p < strEnd ? *p : '\0';
}
}


bool negExp = false;
bool negExp = false;
number.DigitsCount = digEnd;
number.DigitsCount = digEnd;
number.Digits[digEnd] = (byte)('\0');
number.Digits[digEnd] = (byte)('\0');
if ((state & StateDigits) != 0)
if ((state & StateDigits) != 0)
{
{
if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
{
{
char* temp = p;
char* temp = p;
ch = ++p < strEnd ? *p : '\0';
ch = ++p < strEnd ? *p : '\0';
if ((next = MatchChars(p, strEnd, info._positiveSign)) != null)
if ((next = MatchChars(p, strEnd, info.PositiveSign)) != null)
{
{
ch = (p = next) < strEnd ? *p : '\0';
ch = (p = next) < strEnd ? *p : '\0';
}
}
else if ((next = MatchNegativeSignChars(p, strEnd, info)) != null)
else if ((next = MatchNegativeSignChars(p, strEnd, info)) != null)
{
{
ch = (p = next) < strEnd ? *p : '\0';
ch = (p = next) < strEnd ? *p : '\0';
negExp = true;
negExp = true;
}
}
if (IsDigit(ch))
if (IsDigit(ch))
{
{
int exp = 0;
int exp = 0;
do
do
{
{
exp = exp * 10 + (ch - '0');
// Check if we are about to overflow past our limit of 9 digits
ch = ++p < strEnd ? *p : '\0';
if (exp >= 100_000_000)
if (exp > 1000)
{
{
exp = 9999;
// Set exp to Int.MaxValue to signify the requested exponent is too large. This will lead to an OverflowException later.
while (IsDigit(ch))
exp = int.MaxValue;
number.Scale = 0;

// Finish parsing the number, a FormatException could still occur later on.
while (char.IsAsciiDigit(ch))
{
{
ch = ++p < strEnd ? *p : '\0';
ch = ++p < strEnd ? *p : '\0';
}
}
break;
}
}

exp = exp * 10 + (ch - '0');
ch = ++p < strEnd ? *p : '\0';
} while (IsDigit(ch));
} while (IsDigit(ch));
if (negExp)
if (negExp)
{
{
exp = -exp;
exp = -exp;
}
}
number.Scale += exp;
number.Scale += exp;
}
}
else
else
{
{
p = temp;
p = temp;
ch = p < strEnd ? *p : '\0';
ch = p < strEnd ? *p : '\0';
}
}
}
}


if (number.Kind == NumberBufferKind.FloatingPoint && !number.HasNonZeroTail)
if (number.Kind == NumberBufferKind.FloatingPoint && !number.HasNonZeroTail)
{
{
// Adjust the number buffer for trailing zeros
// Adjust the number buffer for trailing zeros
int numberOfFractionalDigits = digEnd - number.Scale;
int numberOfFractionalDigits = digEnd - number.Scale;
if (numberOfFractionalDigits > 0)
if (numberOfFractionalDigits > 0)
{
{
numberOfTrailingZeros = Math.Min(numberOfTrailingZeros, numberOfFractionalDigits);
numberOfTrailingZeros = Math.Min(numberOfTrailingZeros, numberOfFractionalDigits);
Debug.Assert(numberOfTrailingZeros >= 0);
Debug.Assert(numberOfTrailingZeros >= 0);
number.DigitsCount = digEnd - numberOfTrailingZeros;
number.DigitsCount = digEnd - numberOfTrailingZeros;
number.Digits[number.DigitsCount] = (byte)('\0');
number.Digits[number.DigitsCount] = (byte)('\0');
}
}
}
}


while (true)
while (true)
{
{
if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
{
{
if ((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchNegativeSignChars(p, strEnd, info)) != null) && (number.IsNegative = true))))
if ((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchNegativeSignChars(p, strEnd, info)) != null) && (number.IsNegative = true))))
{
{
state |= StateSign;
state |= StateSign;
p = next - 1;
p = next - 1;
}
}
else if (ch == ')' && ((state & StateParens) != 0))
else if (ch == ')' && ((state & StateParens) != 0))
{
{
state &= ~StateParens;
state &= ~StateParens;
}
}
else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
{
{
currSymbol = null;
currSymbol = null;
p = next - 1;
p = next - 1;
}
}
else
else
{
{
break;
break;
}
}
}
}
ch = ++p < strEnd ? *p : '\0';
ch = ++p < strEnd ? *p : '\0';
}
}
if ((state & StateParens) == 0)
if ((state & StateParens) == 0)
{
{
if ((state & StateNonZero) == 0)
if ((state & StateNonZero) == 0)
{
{
if (number.Kind != NumberBufferKind.Decimal)
if (number.Kind != NumberBufferKind.Decimal)
{
{
number.Scale = 0;
number.Scale = 0;
}
}
if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0)
if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0)
{
{
number.IsNegative = false;
number.IsNegative = false;
}
}
}
}
str = p;
str = p;
return true;
return true;
}
}
}
}
str = p;
str = p;
return false;
return false;
}
}