if ((style & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- if (!TryParseInt32IntegerStyle(s, style, info, out i, out bool overflow))
+ bool overflow = false;
+ if (!TryParseInt32IntegerStyle(s, style, info, out i, ref overflow))
{
throw overflow ?
new OverflowException(SR.Overflow_Int32) :
if ((options & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- if (!TryParseInt64IntegerStyle(value, options, numfmt, out i, out bool overflow))
+ bool overflow = false;
+ if (!TryParseInt64IntegerStyle(value, options, numfmt, out i, ref overflow))
{
throw overflow ?
new OverflowException(SR.Overflow_Int64) :
internal static unsafe uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
- NumberBuffer number = default;
uint i = 0;
+ if ((options & ~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))
+ {
+ throw overflow ?
+ new OverflowException(SR.Overflow_UInt32) :
+ (Exception)new FormatException(SR.Format_InvalidString);
+ }
+ return i;
+ }
+
+ NumberBuffer number = default;
StringToNumber(value, options, ref number, numfmt, false);
if ((options & NumberStyles.AllowHexSpecifier) != 0)
internal static unsafe ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
- NumberBuffer number = default;
ulong i = 0;
+ if ((options & ~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))
+ {
+ throw overflow ?
+ new OverflowException(SR.Overflow_UInt64) :
+ (Exception)new FormatException(SR.Format_InvalidString);
+ }
+ return i;
+ }
+
+ NumberBuffer number = default;
StringToNumber(value, options, ref number, numfmt, false);
if ((options & NumberStyles.AllowHexSpecifier) != 0)
{
if ((style & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- return TryParseInt32IntegerStyle(s, style, info, out result, out _);
+ bool overflow = false;
+ return TryParseInt32IntegerStyle(s, style, info, out result, ref overflow);
}
if (!TryStringToNumber(s, style, ref number, info, false))
}
/// <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, out bool overflow)
+ private static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
{
- overflow = false;
- Debug.Assert((styles & ~NumberStyles.Integer) == 0);
+ 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;
+ bool overflow = false;
int sign = 1;
int index = 0;
int num = source[0];
do
{
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEnd;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
num = source[index];
} while (num == '0');
- if (!IsDigit(num)) goto DoneButRemainingChars;
+ if (!IsDigit(num)) goto HasTrailingChars;
}
- // Parse most digits, up to the potential for overflow, which can't
- // happen until after 9 digits.
+ // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
answer = num - '0'; // first digit
index++;
for (int i = 0; i < 8; i++) // next 8 digits can't overflow
{
- if ((uint)index >= (uint)source.Length) goto DoneAtEnd;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
num = source[index];
- if (!IsDigit(num)) goto DoneButRemainingChars;
+ 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 DoneAtEnd;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
num = source[index];
- if (!IsDigit(num)) goto DoneButRemainingChars;
+ if (!IsDigit(num)) goto HasTrailingChars;
index++;
if (answer > int.MaxValue / 10)
{
overflow = true;
- goto FalseExit;
}
answer = answer * 10 + num - '0';
if ((uint)answer > (uint)int.MaxValue + (-1 * sign + 1) / 2)
{
overflow = true;
- goto FalseExit;
}
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- if ((uint)index >= (uint)source.Length) goto DoneAtEnd;
+ // At this point, we're either overflowing or hitting a formatting error.
+ // Format errors take precedence for compatibility.
num = source[index];
- if (!IsDigit(num)) goto DoneButRemainingChars;
-
- // Anything more than 10 digits is definitely overflow.
- overflow = true;
+ while (IsDigit(num))
+ {
+ overflow = true;
+ index++;
+ if ((uint)index >= (uint)source.Length)
+ goto DoneAtEndButPotentialOverflow;
+ num = source[index];
+ }
+ goto HasTrailingChars;
}
FalseExit: // parsing failed
result = 0;
return false;
- DoneAtEnd: // we've successfully parsed up to the end of the span
+ DoneAtEndButPotentialOverflow:
+ if (overflow)
+ {
+ failureIsOverflow = true;
+ goto FalseExit;
+ }
result = answer * sign;
return true;
- DoneButRemainingChars: // 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.
+ 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;
{
if (!IsWhite(source[index])) break;
}
- if ((uint)index >= (uint)source.Length) goto DoneAtEnd;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
}
if (!TrailingZeros(source, index)) goto FalseExit;
- goto DoneAtEnd;
+ 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, out bool overflow)
+ private static bool TryParseInt64IntegerStyle(
+ ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
{
- overflow = false;
- Debug.Assert((styles & ~NumberStyles.Integer) == 0);
+ 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;
+ bool overflow = false;
int sign = 1;
int index = 0;
int num = source[0];
do
{
index++;
- if ((uint)index >= (uint)source.Length) goto DoneAtEnd;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
num = source[index];
} while (num == '0');
- if (!IsDigit(num)) goto DoneButRemainingChars;
+ if (!IsDigit(num)) goto HasTrailingChars;
}
- // Parse most digits, up to the potential for overflow, which can't
- // happen until after 18 digits.
+ // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
answer = num - '0'; // first digit
index++;
-
for (int i = 0; i < 17; i++) // next 17 digits can't overflow
{
- if ((uint)index >= (uint)source.Length) goto DoneAtEnd;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
num = source[index];
- if (!IsDigit(num)) goto DoneButRemainingChars;
+ 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 DoneAtEnd;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
num = source[index];
- if (!IsDigit(num)) goto DoneButRemainingChars;
+ if (!IsDigit(num)) goto HasTrailingChars;
index++;
if (answer > long.MaxValue / 10)
{
overflow = true;
- goto FalseExit;
}
answer = answer * 10 + num - '0';
if ((ulong)answer > (ulong)long.MaxValue + (ulong)((-1 * sign + 1) / 2)) // + sign => 0, - sign => 1
{
overflow = true;
- goto FalseExit;
}
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
- if ((uint)index >= (uint)source.Length) goto DoneAtEnd;
+ // At this point, we're either overflowing or hitting a formatting error.
+ // Format errors take precedence for compatibility.
num = source[index];
- if (!IsDigit(num)) goto DoneButRemainingChars;
-
- // Anything more than 19 digits is definitely overflow.
- overflow = true;
+ while (IsDigit(num))
+ {
+ overflow = true;
+ index++;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ num = source[index];
+ }
+ goto HasTrailingChars;
}
FalseExit: // parsing failed
result = 0;
return false;
- DoneAtEnd: // we've successfully parsed up to the end of the span
+ DoneAtEndButPotentialOverflow:
+ if (overflow)
+ {
+ failureIsOverflow = true;
+ goto FalseExit;
+ }
result = answer * sign;
return true;
- DoneButRemainingChars: // 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.
+ 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;
{
if (!IsWhite(source[index])) break;
}
- if ((uint)index >= (uint)source.Length) goto DoneAtEnd;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
}
if (!TrailingZeros(source, index)) goto FalseExit;
- goto DoneAtEnd;
+ goto DoneAtEndButPotentialOverflow;
}
internal static unsafe bool TryParseInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out long result)
if ((style & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- return TryParseInt64IntegerStyle(s, style, info, out result, out _);
+ bool overflow = false;
+ return TryParseInt64IntegerStyle(s, style, info, out result, ref overflow);
}
NumberBuffer number = default;
NumberBuffer number = default;
result = 0;
+ if ((style & ~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);
+ }
+
if (!TryStringToNumber(s, style, ref number, info, false))
{
return false;
return true;
}
+ /// <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)
+ {
+ 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;
+
+ bool overflow = false;
+ bool hasNegativeSign = false;
+ int index = 0;
+ int num = source[0];
+
+ // Skip past any whitespace at the beginning.
+ if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
+ {
+ index++;
+ while (true)
+ {
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ if (!IsWhite(num)) break;
+ index++;
+ }
+ }
+
+ // Parse leading sign.
+ if ((styles & NumberStyles.AllowLeadingSign) != 0)
+ {
+ string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
+
+ if (positiveSign == "+" && negativeSign == "-")
+ {
+ if (num == '+')
+ {
+ index++;
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ }
+ else if (num == '-')
+ {
+ hasNegativeSign = true;
+ index++;
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ }
+ }
+ else
+ {
+ source = source.Slice(index);
+ index = 0;
+ if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+ {
+ index += positiveSign.Length;
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ }
+ else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+ {
+ hasNegativeSign = true;
+ index += negativeSign.Length;
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ }
+ }
+ }
+
+ int answer = 0;
+
+ if (IsDigit(num))
+ {
+ // Skip past leading zeros.
+ if (num == '0')
+ {
+ do
+ {
+ index++;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ num = source[index];
+ } while (num == '0');
+ if (!IsDigit(num)) goto HasTrailingChars;
+ }
+
+ // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
+ answer = num - '0'; // first digit
+ 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 (!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 (!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;
+
+ // At this point, we're either overflowing or hitting a formatting error.
+ // Format errors take precedence for compatibility.
+ num = source[index];
+ while (IsDigit(num))
+ {
+ overflow = true;
+ index++;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ num = source[index];
+ }
+ goto HasTrailingChars;
+ }
+
+ FalseExit: // parsing failed
+ result = 0;
+ return false;
+
+ DoneAtEndButPotentialOverflow:
+ if (overflow || (hasNegativeSign && answer != 0))
+ {
+ failureIsOverflow = true;
+ goto FalseExit;
+ }
+ 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 ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+ for (index++; index < source.Length; index++)
+ {
+ if (!IsWhite(source[index])) break;
+ }
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ }
+
+ if (!TrailingZeros(source, index)) goto FalseExit;
+
+ goto DoneAtEndButPotentialOverflow;
+ }
+
internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out ulong result)
{
NumberBuffer number = default;
result = 0;
+ 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 (!TryStringToNumber(s, style, ref number, info, false))
{
return false;
return true;
}
+ /// <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)
+ {
+ 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;
+
+ bool overflow = false;
+ bool hasNegativeSign = false;
+ int index = 0;
+ int num = source[0];
+
+ // Skip past any whitespace at the beginning.
+ if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
+ {
+ index++;
+ while (true)
+ {
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ if (!IsWhite(num)) break;
+ index++;
+ }
+ }
+
+ // Parse leading sign.
+ if ((styles & NumberStyles.AllowLeadingSign) != 0)
+ {
+ string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
+
+ if (positiveSign == "+" && negativeSign == "-")
+ {
+ if (num == '+')
+ {
+ index++;
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ }
+ else if (num == '-')
+ {
+ hasNegativeSign = true;
+ index++;
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ }
+ }
+ else
+ {
+ source = source.Slice(index);
+ index = 0;
+ if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+ {
+ index += positiveSign.Length;
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ }
+ else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+ {
+ hasNegativeSign = true;
+ index += negativeSign.Length;
+ if ((uint)index >= (uint)source.Length) goto FalseExit;
+ num = source[index];
+ }
+ }
+ }
+
+ long answer = 0;
+
+ if (IsDigit(num))
+ {
+ // Skip past leading zeros.
+ if (num == '0')
+ {
+ do
+ {
+ index++;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ num = source[index];
+ } while (num == '0');
+ if (!IsDigit(num)) goto HasTrailingChars;
+ }
+
+ // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
+ answer = num - '0'; // first digit
+ 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 (!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 (!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;
+
+ // At this point, we're either overflowing or hitting a formatting error.
+ // Format errors take precedence for compatibility.
+ num = source[index];
+ while (IsDigit(num))
+ {
+ overflow = true;
+ index++;
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ num = source[index];
+ }
+ goto HasTrailingChars;
+ }
+
+ FalseExit: // parsing failed
+ result = 0;
+ return false;
+
+ DoneAtEndButPotentialOverflow:
+ if (overflow || (hasNegativeSign && answer != 0))
+ {
+ failureIsOverflow = true;
+ goto FalseExit;
+ }
+ 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 < source.Length; index++)
+ {
+ if (!IsWhite(source[index])) break;
+ }
+ if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+ }
+
+ if (!TrailingZeros(source, index)) goto FalseExit;
+
+ goto DoneAtEndButPotentialOverflow;
+ }
+
internal static unsafe decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
{
NumberBuffer number = default;
return null;
}
- private static bool IsWhite(int ch) => ch == 0x20 || (ch >= 0x09 && ch <= 0x0D);
+ private static bool IsWhite(int ch) => ch == 0x20 || ((uint)(ch - 0x09) <= (0x0D - 0x09));
private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;