using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
namespace System
{
private const int Int64Precision = 19;
private const int UInt64Precision = 20;
- private static bool HexNumberToInt32(ref NumberBuffer number, ref int value)
+ /// <summary>256-element map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
+ private static readonly int[] s_charToHexLookup =
{
- uint passedValue = 0;
- bool returnValue = HexNumberToUInt32(ref number, ref passedValue);
- value = (int)passedValue;
- return returnValue;
- }
-
- private static bool HexNumberToInt64(ref NumberBuffer number, ref long value)
- {
- ulong passedValue = 0;
- bool returnValue = HexNumberToUInt64(ref number, ref passedValue);
- value = (long)passedValue;
- return returnValue;
- }
-
- private static unsafe bool HexNumberToUInt32(ref NumberBuffer number, ref uint value)
- {
- int i = number.scale;
- if (i > UInt32Precision || i < number.precision)
- {
- return false;
- }
- char* p = number.digits;
- Debug.Assert(p != null);
-
- uint n = 0;
- while (--i >= 0)
- {
- if (n > ((uint)0xFFFFFFFF / 16))
- {
- return false;
- }
- n *= 16;
- if (*p != '\0')
- {
- uint newN = n;
- if (*p != '\0')
- {
- if (IsDigit(*p))
- {
- newN += (uint)(*p - '0');
- }
- else
- {
- if (*p >= 'A' && *p <= 'F')
- {
- newN += (uint)((*p - 'A') + 10);
- }
- else
- {
- Debug.Assert(*p >= 'a' && *p <= 'f');
- newN += (uint)((*p - 'a') + 10);
- }
- }
- p++;
- }
-
- // Detect an overflow here...
- if (newN < n)
- {
- return false;
- }
- n = newN;
- }
- }
- value = n;
- return true;
- }
-
- private static unsafe bool HexNumberToUInt64(ref NumberBuffer number, ref ulong value)
- {
- int i = number.scale;
- if (i > UInt64Precision || i < number.precision)
- {
- return false;
- }
- char* p = number.digits;
- Debug.Assert(p != null);
-
- ulong n = 0;
- while (--i >= 0)
- {
- if (n > (0xFFFFFFFFFFFFFFFF / 16))
- {
- return false;
- }
- n *= 16;
- if (*p != '\0')
- {
- ulong newN = n;
- if (*p != '\0')
- {
- if (IsDigit(*p))
- {
- newN += (ulong)(*p - '0');
- }
- else
- {
- if (*p >= 'A' && *p <= 'F')
- {
- newN += (ulong)((*p - 'A') + 10);
- }
- else
- {
- Debug.Assert(*p >= 'a' && *p <= 'f');
- newN += (ulong)((*p - 'a') + 10);
- }
- }
- p++;
- }
-
- // Detect an overflow here...
- if (newN < n)
- {
- return false;
- }
- n = newN;
- }
- }
- value = n;
- return true;
- }
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
+ 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
+ 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 255
+ };
private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value)
{
n *= 10;
if (*p != '\0')
{
- n += (int)(*p++ - '0');
+ n += (*p++ - '0');
}
}
if (number.sign)
n *= 10;
if (*p != '\0')
{
- n += (int)(*p++ - '0');
+ n += (*p++ - '0');
}
}
if (number.sign)
return true;
}
- internal static unsafe int ParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
+ internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- int i = 0;
-
- if ((style & ~NumberStyles.Integer) == 0)
+ if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
bool overflow = false;
- if (!TryParseInt32IntegerStyle(s, style, info, out i, ref overflow))
+ if (!TryParseInt32IntegerStyle(value, styles, info, out int intResult, ref overflow))
{
- throw overflow ?
- new OverflowException(SR.Overflow_Int32) :
- (Exception)new FormatException(SR.Format_InvalidString);
+ ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int32));
}
- return i;
+ return intResult;
}
- NumberBuffer number = default;
- StringToNumber(s, style, ref number, info, false);
-
- if ((style & NumberStyles.AllowHexSpecifier) != 0)
+ if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- if (!HexNumberToInt32(ref number, ref i))
+ bool overflow = false;
+ if (!TryParseUInt32HexNumberStyle(value, styles, info, out uint hexResult, ref overflow))
{
- throw new OverflowException(SR.Overflow_Int32);
+ ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int32));
}
+ return (int)hexResult;
}
- else
+
+ NumberBuffer number = default;
+ int result = 0;
+ StringToNumber(value, styles, ref number, info, false);
+ if (!NumberToInt32(ref number, ref result))
{
- if (!NumberToInt32(ref number, ref i))
- {
- throw new OverflowException(SR.Overflow_Int32);
- }
+ ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Int32));
}
- return i;
+ return result;
}
- internal static unsafe long ParseInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+ internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- long i = 0;
-
- if ((options & ~NumberStyles.Integer) == 0)
+ if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
bool overflow = false;
- if (!TryParseInt64IntegerStyle(value, options, numfmt, out i, ref overflow))
+ if (!TryParseInt64IntegerStyle(value, styles, info, out long intResult, ref overflow))
{
- throw overflow ?
- new OverflowException(SR.Overflow_Int64) :
- (Exception)new FormatException(SR.Format_InvalidString);
+ ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int64));
}
- return i;
+ return intResult;
}
- NumberBuffer number = default;
- StringToNumber(value, options, ref number, numfmt, false);
-
- if ((options & NumberStyles.AllowHexSpecifier) != 0)
+ if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- if (!HexNumberToInt64(ref number, ref i))
+ bool overflow = false;
+ if (!TryParseUInt64HexNumberStyle(value, styles, info, out ulong hexResult, ref overflow))
{
- throw new OverflowException(SR.Overflow_Int64);
+ ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int64));
}
+ return (long)hexResult;
}
- else
+
+ NumberBuffer number = default;
+ long result = 0;
+ StringToNumber(value, styles, ref number, info, false);
+ if (!NumberToInt64(ref number, ref result))
{
- if (!NumberToInt64(ref number, ref i))
- {
- throw new OverflowException(SR.Overflow_Int64);
- }
+ ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Int64));
}
- return i;
+ return result;
}
- internal static unsafe uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+ internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- uint i = 0;
+ uint result = 0;
- if ((options & ~NumberStyles.Integer) == 0)
+ if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
bool overflow = false;
- if (!TryParseUInt32IntegerStyle(value, options, numfmt, out i, ref overflow))
+ if (!TryParseUInt32IntegerStyle(value, styles, info, out result, ref overflow))
{
- throw overflow ?
- new OverflowException(SR.Overflow_UInt32) :
- (Exception)new FormatException(SR.Format_InvalidString);
+ ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt32));
}
- return i;
+ return result;
}
- NumberBuffer number = default;
- StringToNumber(value, options, ref number, numfmt, false);
-
- if ((options & NumberStyles.AllowHexSpecifier) != 0)
+ if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- if (!HexNumberToUInt32(ref number, ref i))
+ bool overflow = false;
+ if (!TryParseUInt32HexNumberStyle(value, styles, info, out result, ref overflow))
{
- throw new OverflowException(SR.Overflow_UInt32);
+ ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt32));
}
+ return result;
}
- else
+
+ NumberBuffer number = default;
+ StringToNumber(value, styles, ref number, info, false);
+ if (!NumberToUInt32(ref number, ref result))
{
- if (!NumberToUInt32(ref number, ref i))
- {
- throw new OverflowException(SR.Overflow_UInt32);
- }
+ ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_UInt32));
}
-
- return i;
+ return result;
}
- internal static unsafe ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+ internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- ulong i = 0;
+ ulong result = 0;
- if ((options & ~NumberStyles.Integer) == 0)
+ if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
bool overflow = false;
- if (!TryParseUInt64IntegerStyle(value, options, numfmt, out i, ref overflow))
+ if (!TryParseUInt64IntegerStyle(value, styles, info, out result, ref overflow))
{
- throw overflow ?
- new OverflowException(SR.Overflow_UInt64) :
- (Exception)new FormatException(SR.Format_InvalidString);
+ ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt64));
}
- return i;
+ return result;
}
- NumberBuffer number = default;
- StringToNumber(value, options, ref number, numfmt, false);
- if ((options & NumberStyles.AllowHexSpecifier) != 0)
+ if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- if (!HexNumberToUInt64(ref number, ref i))
+ bool overflow = false;
+ if (!TryParseUInt64HexNumberStyle(value, styles, info, out result, ref overflow))
{
- throw new OverflowException(SR.Overflow_UInt64);
+ ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt64));
}
+ return result;
}
- else
+
+ NumberBuffer number = default;
+ StringToNumber(value, styles, ref number, info, false);
+ if (!NumberToUInt64(ref number, ref result))
{
- if (!NumberToUInt64(ref number, ref i))
- {
- throw new OverflowException(SR.Overflow_UInt64);
- }
+ ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_UInt64));
}
- return i;
+ return result;
}
- private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
+ private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
{
Debug.Assert(str != null);
Debug.Assert(strEnd != null);
Debug.Assert(str <= strEnd);
+ Debug.Assert((styles & NumberStyles.AllowHexSpecifier) == 0);
const int StateSign = 0x0001;
const int StateParens = 0x0002;
string currSymbol = null; // currency symbol from NumberFormatInfo.
bool parsingCurrency = false;
- if ((options & NumberStyles.AllowCurrencySymbol) != 0)
+ if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
{
- currSymbol = numfmt.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 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;
+ decSep = info.CurrencyDecimalSeparator;
+ groupSep = info.CurrencyGroupSeparator;
parsingCurrency = true;
}
else
{
- decSep = numfmt.NumberDecimalSeparator;
- groupSep = numfmt.NumberGroupSeparator;
+ decSep = info.NumberDecimalSeparator;
+ groupSep = info.NumberGroupSeparator;
}
int state = 0;
{
// 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 (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
{
- if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, numfmt.PositiveSign)) != null || ((next = MatchChars(p, strEnd, numfmt.NegativeSign)) != null && (number.sign = true))))
+ if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchChars(p, strEnd, info.NegativeSign)) != null && (number.sign = true))))
{
state |= StateSign;
p = next - 1;
}
- else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
+ else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
{
state |= StateSign | StateParens;
number.sign = true;
int digEnd = 0;
while (true)
{
- if (IsDigit(ch) || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))))
+ if (IsDigit(ch))
{
state |= StateDigits;
number.scale--;
}
}
- else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, numfmt.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;
p = next - 1;
}
- else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, numfmt.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;
}
number.digits[digEnd] = '\0';
if ((state & StateDigits) != 0)
{
- if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0))
+ if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
{
char* temp = p;
ch = ++p < strEnd ? *p : '\0';
- if ((next = MatchChars(p, strEnd, numfmt.positiveSign)) != null)
+ if ((next = MatchChars(p, strEnd, info.positiveSign)) != null)
{
ch = (p = next) < strEnd ? *p : '\0';
}
- else if ((next = MatchChars(p, strEnd, numfmt.negativeSign)) != null)
+ else if ((next = MatchChars(p, strEnd, info.negativeSign)) != null)
{
ch = (p = next) < strEnd ? *p : '\0';
negExp = true;
}
while (true)
{
- if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0)
+ if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
{
- if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, numfmt.PositiveSign)) != null || (((next = MatchChars(p, strEnd, numfmt.NegativeSign)) != null) && (number.sign = true))))
+ if (((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchChars(p, strEnd, info.NegativeSign)) != null) && (number.sign = true))))
{
state |= StateSign;
p = next - 1;
return false;
}
- internal static bool TryParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out int result)
+ internal static bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
{
- NumberBuffer number = default;
- result = 0;
-
- if ((style & ~NumberStyles.Integer) == 0)
+ if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
bool overflow = false;
- return TryParseInt32IntegerStyle(s, style, info, out result, ref overflow);
+ return TryParseInt32IntegerStyle(value, styles, info, out result, ref overflow);
}
- if (!TryStringToNumber(s, style, ref number, info, false))
- {
- return false;
- }
+ result = 0;
- if ((style & NumberStyles.AllowHexSpecifier) != 0)
+ if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- if (!HexNumberToInt32(ref number, ref result))
- {
- return false;
- }
- }
- else
- {
- if (!NumberToInt32(ref number, ref result))
- {
- return false;
- }
+ bool overflow = false;
+ return TryParseUInt32HexNumberStyle(value, styles, info, out Unsafe.As<int, uint>(ref result), ref overflow);
}
- return true;
+
+ NumberBuffer number = default;
+ return
+ TryStringToNumber(value, styles, ref number, info, false) &&
+ NumberToInt32(ref number, ref result);
}
/// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
- private static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
+ private static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
{
Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((uint)source.Length < 1) goto FalseExit;
+ if ((uint)value.Length < 1) goto FalseExit;
bool overflow = false;
int sign = 1;
int index = 0;
- int num = source[0];
+ int num = value[0];
// Skip past any whitespace at the beginning.
if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
{
- index++;
- while (true)
+ do
{
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
- if (!IsWhite(num)) break;
index++;
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
+ while (IsWhite(num));
}
// Parse leading sign.
{
sign = -1;
index++;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
else if (num == '+')
{
index++;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
}
else
{
- source = source.Slice(index);
+ value = value.Slice(index);
index = 0;
- if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+ if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
- else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+ else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
sign = -1;
index += negativeSign.Length;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
}
}
do
{
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
} while (num == '0');
if (!IsDigit(num)) goto HasTrailingChars;
}
index++;
for (int i = 0; i < 8; i++) // next 8 digits can't overflow
{
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
if (!IsDigit(num)) goto HasTrailingChars;
index++;
answer = 10 * answer + num - '0';
}
// Potential overflow now processing the 10th digit.
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
if (!IsDigit(num)) goto HasTrailingChars;
index++;
if (answer > int.MaxValue / 10)
{
overflow = true;
}
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
// At this point, we're either overflowing or hitting a formatting error.
// Format errors take precedence for compatibility.
- num = source[index];
+ num = value[index];
while (IsDigit(num))
{
overflow = true;
index++;
- if ((uint)index >= (uint)source.Length)
+ if ((uint)index >= (uint)value.Length)
goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ num = value[index];
}
goto HasTrailingChars;
}
if (IsWhite(num))
{
if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
- for (index++; index < source.Length; index++)
+ for (index++; index < value.Length; index++)
{
- if (!IsWhite(source[index])) break;
+ if (!IsWhite(value[index])) break;
}
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(source, index)) goto FalseExit;
+ if (!TrailingZeros(value, index)) goto FalseExit;
goto DoneAtEndButPotentialOverflow;
}
/// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
private static bool TryParseInt64IntegerStyle(
- ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
+ ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
{
Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((uint)source.Length < 1) goto FalseExit;
+ if ((uint)value.Length < 1) goto FalseExit;
bool overflow = false;
int sign = 1;
int index = 0;
- int num = source[0];
+ int num = value[0];
// Skip past any whitespace at the beginning.
if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
{
- index++;
- while (true)
+ do
{
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
- if (!IsWhite(num)) break;
index++;
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
+ while (IsWhite(num));
}
// Parse leading sign.
{
sign = -1;
index++;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
else if (num == '+')
{
index++;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
}
else
{
- source = source.Slice(index);
+ value = value.Slice(index);
index = 0;
- if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+ if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
- else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+ else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
sign = -1;
index += negativeSign.Length;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
}
}
do
{
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
} while (num == '0');
if (!IsDigit(num)) goto HasTrailingChars;
}
index++;
for (int i = 0; i < 17; i++) // next 17 digits can't overflow
{
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
if (!IsDigit(num)) goto HasTrailingChars;
index++;
answer = 10 * answer + num - '0';
}
// Potential overflow now processing the 19th digit.
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
if (!IsDigit(num)) goto HasTrailingChars;
index++;
if (answer > long.MaxValue / 10)
{
overflow = true;
}
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
// At this point, we're either overflowing or hitting a formatting error.
// Format errors take precedence for compatibility.
- num = source[index];
+ num = value[index];
while (IsDigit(num))
{
overflow = true;
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
}
goto HasTrailingChars;
}
if (IsWhite(num))
{
if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
- for (index++; index < source.Length; index++)
+ for (index++; index < value.Length; index++)
{
- if (!IsWhite(source[index])) break;
+ if (!IsWhite(value[index])) break;
}
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(source, index)) goto FalseExit;
+ if (!TrailingZeros(value, index)) goto FalseExit;
goto DoneAtEndButPotentialOverflow;
}
- internal static unsafe bool TryParseInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out long result)
+ internal static bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
{
- result = 0;
-
- if ((style & ~NumberStyles.Integer) == 0)
+ if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
bool overflow = false;
- return TryParseInt64IntegerStyle(s, style, info, out result, ref overflow);
+ return TryParseInt64IntegerStyle(value, styles, info, out result, ref overflow);
}
- NumberBuffer number = default;
- if (!TryStringToNumber(s, style, ref number, info, false))
- {
- return false;
- }
+ result = 0;
- if ((style & NumberStyles.AllowHexSpecifier) != 0)
- {
- if (!HexNumberToInt64(ref number, ref result))
- {
- return false;
- }
- }
- else
+ if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- if (!NumberToInt64(ref number, ref result))
- {
- return false;
- }
+ bool overflow = false;
+ return TryParseUInt64HexNumberStyle(value, styles, info, out Unsafe.As<long, ulong>(ref result), ref overflow);
}
- return true;
- }
- internal static unsafe bool TryParseUInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out uint result)
- {
NumberBuffer number = default;
- result = 0;
+ return
+ TryStringToNumber(value, styles, ref number, info, false) &&
+ NumberToInt64(ref number, ref result);
+ }
- if ((style & ~NumberStyles.Integer) == 0)
+ internal static bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+ {
+ if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
bool overflow = false;
- return TryParseUInt32IntegerStyle(s, style, info, out result, ref overflow);
+ return TryParseUInt32IntegerStyle(value, styles, info, out result, ref overflow);
}
- if (!TryStringToNumber(s, style, ref number, info, false))
+ if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- return false;
+ bool overflow = false;
+ return TryParseUInt32HexNumberStyle(value, styles, info, out result, ref overflow);
}
- if ((style & NumberStyles.AllowHexSpecifier) != 0)
- {
- if (!HexNumberToUInt32(ref number, ref result))
- {
- return false;
- }
- }
- else
- {
- if (!NumberToUInt32(ref number, ref result))
- {
- return false;
- }
- }
- return true;
+ NumberBuffer number = default;
+ result = 0;
+ return
+ TryStringToNumber(value, styles, ref number, info, false) &&
+ NumberToUInt32(ref number, ref result);
}
/// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
private static bool TryParseUInt32IntegerStyle(
- ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
+ ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
{
Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((uint)source.Length < 1) goto FalseExit;
+ if ((uint)value.Length < 1) goto FalseExit;
bool overflow = false;
bool hasNegativeSign = false;
int index = 0;
- int num = source[0];
+ int num = value[0];
// Skip past any whitespace at the beginning.
if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
{
- index++;
- while (true)
+ do
{
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
- if (!IsWhite(num)) break;
index++;
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
+ while (IsWhite(num));
}
// Parse leading sign.
if (num == '+')
{
index++;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
else if (num == '-')
{
hasNegativeSign = true;
index++;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
}
else
{
- source = source.Slice(index);
+ value = value.Slice(index);
index = 0;
- if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+ if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
- else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+ else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
hasNegativeSign = true;
index += negativeSign.Length;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
}
}
do
{
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
} while (num == '0');
if (!IsDigit(num)) goto HasTrailingChars;
}
index++;
for (int i = 0; i < 8; i++) // next 8 digits can't overflow
{
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
if (!IsDigit(num)) goto HasTrailingChars;
index++;
answer = 10 * answer + num - '0';
}
// Potential overflow now processing the 10th digit.
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
if (!IsDigit(num)) goto HasTrailingChars;
index++;
if ((uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5'))
overflow = true;
}
answer = answer * 10 + num - '0';
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
// At this point, we're either overflowing or hitting a formatting error.
// Format errors take precedence for compatibility.
- num = source[index];
+ num = value[index];
while (IsDigit(num))
{
overflow = true;
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
}
goto HasTrailingChars;
}
if (IsWhite(num))
{
if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
- for (index++; index < source.Length; index++)
+ for (index++; index < value.Length; index++)
{
- if (!IsWhite(source[index])) break;
+ if (!IsWhite(value[index])) break;
}
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(source, index)) goto FalseExit;
+ if (!TrailingZeros(value, index)) goto FalseExit;
goto DoneAtEndButPotentialOverflow;
}
- internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out ulong result)
+ /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
+ private static bool TryParseUInt32HexNumberStyle(
+ ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
{
- NumberBuffer number = default;
- result = 0;
+ Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
+ Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((style & ~NumberStyles.Integer) == 0)
- {
- // Optimized path for the common case of anything that's allowed for integer style.
- bool overflow = false;
- return TryParseUInt64IntegerStyle(s, style, info, out result, ref overflow);
- }
+ if ((uint)value.Length < 1) goto FalseExit;
- if (!TryStringToNumber(s, style, ref number, info, false))
+ bool overflow = false;
+ int index = 0;
+ int num = value[0];
+ int numValue = 0;
+
+ // Skip past any whitespace at the beginning.
+ if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
{
- return false;
+ do
+ {
+ index++;
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
+ }
+ while (IsWhite(num));
}
- if ((style & NumberStyles.AllowHexSpecifier) != 0)
+ int answer = 0;
+ int[] charToHexLookup = s_charToHexLookup;
+
+ if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
{
- if (!HexNumberToUInt64(ref number, ref result))
+ // Skip past leading zeros.
+ if (num == '0')
{
- return false;
+ do
+ {
+ index++;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+ num = value[index];
+ } while (num == '0');
+ if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF) goto HasTrailingChars;
}
+
+ // Parse up through 8 digits, as no overflow is possible
+ answer = charToHexLookup[num]; // first digit
+ index++;
+ for (int i = 0; i < 7; i++) // next 7 digits can't overflow
+ {
+ if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+ num = value[index];
+ if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+ index++;
+ answer = 16 * answer + numValue;
+ }
+
+ // If there's another digit, it's an overflow.
+ if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+ num = value[index];
+ if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+ index++;
+ overflow = true;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+
+ // At this point, we're either overflowing or hitting a formatting error.
+ // Format errors take precedence for compatibility. Read through any remaining digits.
+ num = value[index];
+ while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
+ {
+ index++;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
+ }
+ goto HasTrailingChars;
}
- else
+
+ FalseExit: // parsing failed
+ result = 0;
+ return false;
+
+ DoneAtEndButPotentialOverflow:
+ if (overflow)
+ {
+ failureIsOverflow = true;
+ goto FalseExit;
+ }
+ DoneAtEnd:
+ result = (uint)answer;
+ return true;
+
+ HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
+ // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
+ if (IsWhite(num))
{
- if (!NumberToUInt64(ref number, ref result))
+ if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+ for (index++; index < value.Length; index++)
{
- return false;
+ if (!IsWhite(value[index])) break;
}
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
}
- return true;
+
+ if (!TrailingZeros(value, index)) goto FalseExit;
+
+ goto DoneAtEndButPotentialOverflow;
+ }
+
+ internal static bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+ {
+ if ((styles & ~NumberStyles.Integer) == 0)
+ {
+ // Optimized path for the common case of anything that's allowed for integer style.
+ bool overflow = false;
+ return TryParseUInt64IntegerStyle(value, styles, info, out result, ref overflow);
+ }
+
+ if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+ {
+ bool overflow = false;
+ return TryParseUInt64HexNumberStyle(value, styles, info, out result, ref overflow);
+ }
+
+ NumberBuffer number = default;
+ result = 0;
+ return
+ TryStringToNumber(value, styles, ref number, info, false) &&
+ NumberToUInt64(ref number, ref result);
}
/// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
private static bool TryParseUInt64IntegerStyle(
- ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
+ ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
{
Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((uint)source.Length < 1) goto FalseExit;
+ if ((uint)value.Length < 1) goto FalseExit;
bool overflow = false;
bool hasNegativeSign = false;
int index = 0;
- int num = source[0];
+ int num = value[0];
// Skip past any whitespace at the beginning.
if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
{
- index++;
- while (true)
+ do
{
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
- if (!IsWhite(num)) break;
index++;
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
+ while (IsWhite(num));
}
// Parse leading sign.
if (num == '+')
{
index++;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
else if (num == '-')
{
hasNegativeSign = true;
index++;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
}
else
{
- source = source.Slice(index);
+ value = value.Slice(index);
index = 0;
- if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+ if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
- else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+ else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
hasNegativeSign = true;
index += negativeSign.Length;
- if ((uint)index >= (uint)source.Length) goto FalseExit;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
}
}
}
do
{
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
} while (num == '0');
if (!IsDigit(num)) goto HasTrailingChars;
}
index++;
for (int i = 0; i < 18; i++) // next 18 digits can't overflow
{
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
if (!IsDigit(num)) goto HasTrailingChars;
index++;
answer = 10 * answer + num - '0';
}
// Potential overflow now processing the 20th digit.
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
if (!IsDigit(num)) goto HasTrailingChars;
index++;
if ((ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5'))
overflow = true;
}
answer = answer * 10 + num - '0';
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
// At this point, we're either overflowing or hitting a formatting error.
// Format errors take precedence for compatibility.
- num = source[index];
+ num = value[index];
while (IsDigit(num))
{
overflow = true;
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- num = source[index];
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
}
goto HasTrailingChars;
}
if (IsWhite(num))
{
if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
- for (index++; index < source.Length; index++)
+ for (index++; index < value.Length; index++)
{
- if (!IsWhite(source[index])) break;
+ if (!IsWhite(value[index])) break;
}
- if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(source, index)) goto FalseExit;
+ if (!TrailingZeros(value, index)) goto FalseExit;
goto DoneAtEndButPotentialOverflow;
}
- internal static unsafe decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+ /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
+ private static bool TryParseUInt64HexNumberStyle(
+ ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
+ {
+ Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
+ Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
+
+ if ((uint)value.Length < 1) goto FalseExit;
+
+ bool overflow = false;
+ int index = 0;
+ int num = value[0];
+ int numValue = 0;
+
+ // Skip past any whitespace at the beginning.
+ if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
+ {
+ do
+ {
+ index++;
+ if ((uint)index >= (uint)value.Length) goto FalseExit;
+ num = value[index];
+ }
+ while (IsWhite(num));
+ }
+
+ long answer = 0;
+ int[] charToHexLookup = s_charToHexLookup;
+
+ if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
+ {
+ // Skip past leading zeros.
+ if (num == '0')
+ {
+ do
+ {
+ index++;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+ num = value[index];
+ } while (num == '0');
+ if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF) goto HasTrailingChars;
+ }
+
+ // Parse up through 16 digits, as no overflow is possible
+ answer = charToHexLookup[num]; // first digit
+ index++;
+ for (int i = 0; i < 15; i++) // next 15 digits can't overflow
+ {
+ if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+ num = value[index];
+ if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+ index++;
+ answer = 16 * answer + numValue;
+ }
+
+ // If there's another digit, it's an overflow.
+ if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+ num = value[index];
+ if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+ index++;
+ overflow = true;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+
+ // At this point, we're either overflowing or hitting a formatting error.
+ // Format errors take precedence for compatibility. Read through any remaining digits.
+ num = value[index];
+ while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
+ {
+ index++;
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ num = value[index];
+ }
+ goto HasTrailingChars;
+ }
+
+ FalseExit: // parsing failed
+ result = 0;
+ return false;
+
+ DoneAtEndButPotentialOverflow:
+ if (overflow)
+ {
+ failureIsOverflow = true;
+ goto FalseExit;
+ }
+ DoneAtEnd:
+ result = (ulong)answer;
+ return true;
+
+ HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
+ // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
+ if (IsWhite(num))
+ {
+ if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+ for (index++; index < value.Length; index++)
+ {
+ if (!IsWhite(value[index])) break;
+ }
+ if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ }
+
+ if (!TrailingZeros(value, index)) goto FalseExit;
+
+ goto DoneAtEndButPotentialOverflow;
+ }
+
+ internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
NumberBuffer number = default;
decimal result = 0;
- StringToNumber(value, options, ref number, numfmt, true);
+ StringToNumber(value, styles, ref number, info, true);
if (!NumberBufferToDecimal(ref number, ref result))
{
- throw new OverflowException(SR.Overflow_Decimal);
+ ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Decimal));
}
return result;
}
- internal static unsafe double ParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+ internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
NumberBuffer number = default;
double d = 0;
- if (!TryStringToNumber(value, options, ref number, numfmt, false))
+ if (!TryStringToNumber(value, styles, ref number, info, false))
{
//If we failed TryStringToNumber, it may be from one of our special strings.
//Check the three with which we're concerned and rethrow if it's not one of
//those strings.
ReadOnlySpan<char> sTrim = value.Trim();
- if (sTrim.EqualsOrdinal(numfmt.PositiveInfinitySymbol))
+ if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
{
return double.PositiveInfinity;
}
- if (sTrim.EqualsOrdinal(numfmt.NegativeInfinitySymbol))
+ if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
{
return double.NegativeInfinity;
}
- if (sTrim.EqualsOrdinal(numfmt.NaNSymbol))
+ if (sTrim.EqualsOrdinal(info.NaNSymbol))
{
return double.NaN;
}
- throw new FormatException(SR.Format_InvalidString);
+ ThrowOverflowOrFormatException(overflow: false, null);
}
if (!NumberBufferToDouble(ref number, ref d))
{
- throw new OverflowException(SR.Overflow_Double);
+ ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Double));
}
return d;
}
- internal static unsafe float ParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+ internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
NumberBuffer number = default;
double d = 0;
- if (!TryStringToNumber(value, options, ref number, numfmt, false))
+ if (!TryStringToNumber(value, styles, ref number, info, false))
{
//If we failed TryStringToNumber, it may be from one of our special strings.
//Check the three with which we're concerned and rethrow if it's not one of
//those strings.
ReadOnlySpan<char> sTrim = value.Trim();
- if (sTrim.EqualsOrdinal(numfmt.PositiveInfinitySymbol))
+ if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
{
return float.PositiveInfinity;
}
- if (sTrim.EqualsOrdinal(numfmt.NegativeInfinitySymbol))
+ if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
{
return float.NegativeInfinity;
}
- if (sTrim.EqualsOrdinal(numfmt.NaNSymbol))
+ if (sTrim.EqualsOrdinal(info.NaNSymbol))
{
return float.NaN;
}
- throw new FormatException(SR.Format_InvalidString);
+ ThrowOverflowOrFormatException(overflow: false, null);
}
if (!NumberBufferToDouble(ref number, ref d))
{
- throw new OverflowException(SR.Overflow_Single);
+ ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Single));
}
float castSingle = (float)d;
if (float.IsInfinity(castSingle))
{
- throw new OverflowException(SR.Overflow_Single);
+ ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Single));
}
return castSingle;
}
- internal static unsafe bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out decimal result)
+ internal static bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
{
NumberBuffer number = default;
result = 0;
- if (!TryStringToNumber(value, options, ref number, numfmt, true))
+ if (!TryStringToNumber(value, styles, ref number, info, true))
{
return false;
}
return true;
}
- internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out double result)
+ internal static bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
{
NumberBuffer number = default;
result = 0;
-
- if (!TryStringToNumber(value, options, ref number, numfmt, false))
+ if (!TryStringToNumber(value, styles, ref number, info, false))
{
return false;
}
return true;
}
- internal static unsafe bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out float result)
+ internal static bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
{
NumberBuffer number = default;
result = 0;
double d = 0;
- if (!TryStringToNumber(value, options, ref number, numfmt, false))
+ if (!TryStringToNumber(value, styles, ref number, info, false))
{
return false;
}
return true;
}
- private static unsafe void StringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
+ private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
{
Debug.Assert(info != null);
- fixed (char* stringPointer = &MemoryMarshal.GetReference(str))
+ fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
{
char* p = stringPointer;
- if (!ParseNumber(ref p, p + str.Length, options, ref number, info, parseDecimal)
- || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
+ if (!ParseNumber(ref p, p + value.Length, styles, ref number, info, parseDecimal)
+ || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
{
- throw new FormatException(SR.Format_InvalidString);
+ ThrowOverflowOrFormatException(overflow: false, null);
}
}
}
- internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
+ internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
{
- Debug.Assert(numfmt != null);
- fixed (char* stringPointer = &MemoryMarshal.GetReference(str))
+ Debug.Assert(info != null);
+ fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
{
char* p = stringPointer;
- if (!ParseNumber(ref p, p + str.Length, options, ref number, numfmt, parseDecimal)
- || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
+ if (!ParseNumber(ref p, p + value.Length, styles, ref number, info, parseDecimal)
+ || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
{
return false;
}
return true;
}
- private static bool TrailingZeros(ReadOnlySpan<char> s, int index)
+ private static bool TrailingZeros(ReadOnlySpan<char> value, int index)
{
// For compatibility, we need to allow trailing zeros at the end of a number string
- for (int i = index; i < s.Length; i++)
+ for (int i = index; i < value.Length; i++)
{
- if (s[i] != '\0')
+ if (value[i] != '\0')
{
return false;
}
return true;
}
- private static unsafe char* MatchChars(char* p, char* pEnd, string str)
+ private static unsafe char* MatchChars(char* p, char* pEnd, string value)
{
- fixed (char* stringPointer = str)
- {
- return MatchChars(p, pEnd, stringPointer);
- }
- }
-
- private static unsafe char* MatchChars(char* p, char* pEnd, char* str)
- {
- Debug.Assert(p != null && pEnd != null && p <= pEnd && str != null);
-
- if (*str == '\0')
- {
- return null;
- }
-
- // We only hurt the failure case
- // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
- // space character we use 0x20 space character instead to mean the same.
- while (true)
+ Debug.Assert(p != null && pEnd != null && p <= pEnd && value != null);
+ fixed (char* stringPointer = value)
{
- char cp = p < pEnd ? *p : '\0';
- if (cp != *str && !(*str == '\u00a0' && cp == '\u0020'))
+ char* str = stringPointer;
+ if (*str != '\0')
{
- break;
+ // We only hurt the failure case
+ // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
+ // space character we use 0x20 space character instead to mean the same.
+ while (true)
+ {
+ char cp = p < pEnd ? *p : '\0';
+ if (cp != *str && !(*str == '\u00a0' && cp == '\u0020'))
+ {
+ break;
+ }
+ p++;
+ str++;
+ if (*str == '\0') return p;
+ }
}
- p++;
- str++;
- if (*str == '\0') return p;
}
return null;
private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
+ private static void ThrowOverflowOrFormatException(bool overflow, string overflowResourceKey)
+ {
+ throw overflow ?
+ new OverflowException(SR.GetResourceString(overflowResourceKey)) :
+ (Exception)new FormatException(SR.Format_InvalidString);
+ }
+
private static bool NumberBufferToDouble(ref NumberBuffer number, ref double value)
{
double d = NumberToDouble(ref number);
- if (!Double.IsFinite(d))
+ if (!double.IsFinite(d))
{
value = default;
return false;