private unsafe static Boolean ParseNumber(ref char * str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, Boolean parseDecimal) {
const Int32 StateSign = 0x0001;
const Int32 StateParens = 0x0002;
const Int32 StateDigits = 0x0004;
const Int32 StateNonZero = 0x0008;
const Int32 StateDecimal = 0x0010;
const Int32 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.
// The alternative currency symbol used in Win9x ANSI codepage, that can not roundtrip between ANSI and Unicode.
// Currently, only ja-JP and ko-KR has non-null values (which is U+005c, backslash)
string ansicurrSymbol = null; // currency symbol from NumberFormatInfo.
string altdecSep = null; // decimal separator from NumberFormatInfo as a decimal
string altgroupSep = null; // group separator from NumberFormatInfo as a decimal
Boolean parsingCurrency = false;
if ((options & NumberStyles.AllowCurrencySymbol) != 0) {
currSymbol = numfmt.CurrencySymbol;
if (numfmt.ansiCurrencySymbol != null) {
ansicurrSymbol = numfmt.ansiCurrencySymbol;
}
// The idea here to match the curreny 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 seperator (currency in the if part and decimal in the else part).
altdecSep = numfmt.NumberDecimalSeparator;
altgroupSep = numfmt.NumberGroupSeparator;
decSep = numfmt.CurrencyDecimalSeparator;
groupSep = numfmt.CurrencyGroupSeparator;
parsingCurrency = true;
}
else {
decSep = numfmt.NumberDecimalSeparator;
groupSep = numfmt.NumberGroupSeparator;
}
Int32 state = 0;
Boolean signflag = false; // Cache the results of "options & PARSE_LEADINGSIGN && !(state & STATE_SIGN)" to avoid doing this twice
char* p = str;
char ch = *p;
char* next;
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 & StateSign) != 0) && (((state & StateCurrency) != 0) || numfmt.numberNegativePattern == 2)))) {
// Do nothing here. We will increase p at the end of the loop.
}
else if ((signflag = (((options & NumberStyles.AllowLeadingSign) != 0) && ((state & StateSign) == 0))) && ((next = MatchChars(p, numfmt.positiveSign)) != null)) {
state |= StateSign;
p = next - 1;
} else if (signflag && (next = MatchChars(p, numfmt.negativeSign)) != null) {
state |= StateSign;
number.sign = true;
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) || (ansicurrSymbol != null && (next = MatchChars(p, ansicurrSymbol)) != null)) {
state |= StateCurrency;
currSymbol = null;
ansicurrSymbol = 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;
}
Int32 digCount = 0;
Int32 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) {
if (digCount < NumberMaxDigits) {
number.digits[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, altdecSep)) != 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, altgroupSep)) != null)) {
p = next - 1;
}
else {
break;
}
ch = *++p;
}
Boolean negExp = false;
number.precision = digEnd;
number.digits[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') {
Int32 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)) {
}
else if ((signflag = (((options & NumberStyles.AllowTrailingSign) != 0) && ((state & StateSign) == 0))) && (next = MatchChars(p, numfmt.positiveSign)) != null) {
state |= StateSign;
p = next - 1;
} else if (signflag && (next = MatchChars(p, numfmt.negativeSign)) != null) {
state |= StateSign;
number.sign = true;
p = next - 1;
}
else if (ch == ')' && ((state & StateParens) != 0)) {
state &= ~StateParens;
}
else if ((currSymbol != null && (next = MatchChars(p, currSymbol)) != null) || (ansicurrSymbol != null && (next = MatchChars(p, ansicurrSymbol)) != null)) {
currSymbol = null;
ansicurrSymbol = 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;
}