private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal)
{
const int StateSign = 0x0001;
const int StateParens = 0x0002;
const int StateDigits = 0x0004;
const int StateNonZero = 0x0008;
const int StateDecimal = 0x0010;
const int StateCurrency = 0x0020;
number.scale = 0;
number.sign = false;
string decSep; // Decimal separator from NumberFormatInfo.
string groupSep; // Group separator from NumberFormatInfo.
string currSymbol = null; // Currency symbol from NumberFormatInfo.
bool parsingCurrency = false;
if ((options & NumberStyles.AllowCurrencySymbol) != 0)
{
currSymbol = numfmt.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 values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
decSep = numfmt.CurrencyDecimalSeparator;
groupSep = numfmt.CurrencyGroupSeparator;
parsingCurrency = true;
}
else
{
decSep = numfmt.NumberDecimalSeparator;
groupSep = numfmt.NumberGroupSeparator;
}
int state = 0;
bool bigNumber = (sb != null); // When a StringBuilder is provided then we use it in place of the number.digits char[50]
int maxParseDigits = bigNumber ? int.MaxValue : NumberMaxDigits;
char* p = str;
char ch = *p;
char* next;
char* dig = number.digits;
while (true)
{
// 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.
if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2)))
{
if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true))))
{
state |= StateSign;
p = next - 1;
}
else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
{
state |= StateSign | StateParens;
number.sign = true;
}
else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null)
{
state |= StateCurrency;
currSymbol = null;
// 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.
p = next - 1;
}
else
{
break;
}
}
ch = *++p;
}
int digCount = 0;
int digEnd = 0;
while (true)
{
if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))))
{
state |= StateDigits;
if (ch != '0' || (state & StateNonZero) != 0 || (bigNumber && ((options & NumberStyles.AllowHexSpecifier) != 0)))
{
if (digCount < maxParseDigits)
{
if (bigNumber)
sb.Append(ch);
else
dig[digCount++] = ch;
if (ch != '0' || parseDecimal)
{
digEnd = digCount;
}
}
if ((state & StateDecimal) == 0)
{
number.scale++;
}
state |= StateNonZero;
}
else if ((state & StateDecimal) != 0)
{
number.scale--;
}
}
else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberDecimalSeparator)) != null))
{
state |= StateDecimal;
p = next - 1;
}
else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberGroupSeparator)) != null))
{
p = next - 1;
}
else
{
break;
}
ch = *++p;
}
bool negExp = false;
number.precision = digEnd;
if (bigNumber)
sb.Append('\0');
else
dig[digEnd] = '\0';
if ((state & StateDigits) != 0)
{
if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0))
{
char* temp = p;
ch = *++p;
if ((next = MatchChars(p, numfmt.PositiveSign)) != null)
{
ch = *(p = next);
}
else if ((next = MatchChars(p, numfmt.NegativeSign)) != null)
{
ch = *(p = next);
negExp = true;
}
if (ch >= '0' && ch <= '9')
{
int exp = 0;
do
{
exp = exp * 10 + (ch - '0');
ch = *++p;
if (exp > 1000)
{
exp = 9999;
while (ch >= '0' && ch <= '9')
{
ch = *++p;
}
}
} while (ch >= '0' && ch <= '9');
if (negExp)
{
exp = -exp;
}
number.scale += exp;
}
else
{
p = temp;
ch = *p;
}
}
while (true)
{
if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0)
{
if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true))))
{
state |= StateSign;
p = next - 1;
}
else if (ch == ')' && ((state & StateParens) != 0))
{
state &= ~StateParens;
}
else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null)
{
currSymbol = null;
p = next - 1;
}
else
{
break;
}
}
ch = *++p;
}
if ((state & StateParens) == 0)
{
if ((state & StateNonZero) == 0)
{
if (!parseDecimal)
{
number.scale = 0;
}
if ((state & StateDecimal) == 0)
{
number.sign = false;
}
}
str = p;
return true;
}
}
str = p;
return false;
}