switch (Type.GetTypeCode(rt))
{
case TypeCode.SByte:
- parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_SByte), out intResult);
+ parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, TypeCode.SByte, out intResult);
result = parsed ? InternalBoxEnum(rt, intResult) : null;
return parsed;
case TypeCode.Int16:
- parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Int16), out intResult);
+ parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int16, out intResult);
result = parsed ? InternalBoxEnum(rt, intResult) : null;
return parsed;
case TypeCode.Int32:
- parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Int32), out intResult);
+ parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int32, out intResult);
result = parsed ? InternalBoxEnum(rt, intResult) : null;
return parsed;
case TypeCode.Byte:
- parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Byte), out uintResult);
+ parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, TypeCode.Byte, out uintResult);
result = parsed ? InternalBoxEnum(rt, uintResult) : null;
return parsed;
case TypeCode.UInt16:
- parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_UInt16), out uintResult);
+ parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt16, out uintResult);
result = parsed ? InternalBoxEnum(rt, uintResult) : null;
return parsed;
case TypeCode.UInt32:
- parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_UInt32), out uintResult);
+ parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt32, out uintResult);
result = parsed ? InternalBoxEnum(rt, uintResult) : null;
return parsed;
switch (Type.GetTypeCode(typeof(TEnum)))
{
case TypeCode.SByte:
- parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_SByte), out intResult);
+ parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, TypeCode.SByte, out intResult);
sbyte sbyteResult = (sbyte)intResult;
result = Unsafe.As<sbyte, TEnum>(ref sbyteResult);
return parsed;
case TypeCode.Int16:
- parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Int16), out intResult);
+ parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int16, out intResult);
short shortResult = (short)intResult;
result = Unsafe.As<short, TEnum>(ref shortResult);
return parsed;
case TypeCode.Int32:
- parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Int32), out intResult);
+ parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int32, out intResult);
result = Unsafe.As<int, TEnum>(ref intResult);
return parsed;
case TypeCode.Byte:
- parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Byte), out uintResult);
+ parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, TypeCode.Byte, out uintResult);
byte byteResult = (byte)uintResult;
result = Unsafe.As<byte, TEnum>(ref byteResult);
return parsed;
case TypeCode.UInt16:
- parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_UInt16), out uintResult);
+ parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt16, out uintResult);
ushort ushortResult = (ushort)uintResult;
result = Unsafe.As<ushort, TEnum>(ref ushortResult);
return parsed;
case TypeCode.UInt32:
- parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_UInt32), out uintResult);
+ parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt32, out uintResult);
result = Unsafe.As<uint, TEnum>(ref uintResult);
return parsed;
/// <summary>Tries to parse the value of an enum with known underlying types that fit in an Int32 (Int32, Int16, and SByte).</summary>
private static bool TryParseInt32Enum(
- RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, int minInclusive, int maxInclusive, bool ignoreCase, bool throwOnFailure, string overflowFailureResourceName, out int result)
+ RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, int minInclusive, int maxInclusive, bool ignoreCase, bool throwOnFailure, TypeCode type, out int result)
{
Debug.Assert(
enumType.GetEnumUnderlyingType() == typeof(sbyte) ||
enumType.GetEnumUnderlyingType() == typeof(short) ||
enumType.GetEnumUnderlyingType() == typeof(int));
- bool failureIsOverflow = false;
- if (StartsNumber(value[0]) && Number.TryParseInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result, ref failureIsOverflow))
+ Number.ParsingStatus status = default;
+ if (StartsNumber(value[0]))
{
- if ((uint)(result - minInclusive) <= (uint)(maxInclusive - minInclusive))
+ status = Number.TryParseInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
+ if (status == Number.ParsingStatus.OK)
{
- return true;
- }
+ if ((uint)(result - minInclusive) <= (uint)(maxInclusive - minInclusive))
+ {
+ return true;
+ }
- failureIsOverflow = true;
+ status = Number.ParsingStatus.Overflow;
+ }
}
- if (failureIsOverflow)
+ if (status == Number.ParsingStatus.Overflow)
{
if (throwOnFailure)
{
- throw new OverflowException(SR.GetResourceString(overflowFailureResourceName));
+ Number.ThrowOverflowException(type);
}
}
else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
}
/// <summary>Tries to parse the value of an enum with known underlying types that fit in a UInt32 (UInt32, UInt16, and Byte).</summary>
- private static bool TryParseUInt32Enum(RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, uint maxInclusive, bool ignoreCase, bool throwOnFailure, string overflowFailureResourceName, out uint result)
+ private static bool TryParseUInt32Enum(RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, uint maxInclusive, bool ignoreCase, bool throwOnFailure, TypeCode type, out uint result)
{
Debug.Assert(
enumType.GetEnumUnderlyingType() == typeof(byte) ||
enumType.GetEnumUnderlyingType() == typeof(ushort) ||
enumType.GetEnumUnderlyingType() == typeof(uint));
- bool failureIsOverflow = false;
- if (StartsNumber(value[0]) && Number.TryParseUInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result, ref failureIsOverflow))
+ Number.ParsingStatus status = default;
+ if (StartsNumber(value[0]))
{
- if (result <= maxInclusive)
+ status = Number.TryParseUInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
+ if (status == Number.ParsingStatus.OK)
{
- return true;
- }
+ if (result <= maxInclusive)
+ {
+ return true;
+ }
- failureIsOverflow = true;
+ status = Number.ParsingStatus.Overflow;
+ }
}
- if (failureIsOverflow)
+ if (status == Number.ParsingStatus.Overflow)
{
if (throwOnFailure)
{
- throw new OverflowException(SR.GetResourceString(overflowFailureResourceName));
+ Number.ThrowOverflowException(type);
}
}
else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
{
Debug.Assert(enumType.GetEnumUnderlyingType() == typeof(long));
- bool failureIsOverflow = false;
- if (StartsNumber(value[0]) && Number.TryParseInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result, ref failureIsOverflow))
+ Number.ParsingStatus status = default;
+ if (StartsNumber(value[0]))
{
- return true;
+ status = Number.TryParseInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
+ if (status == Number.ParsingStatus.OK)
+ {
+ return true;
+ }
}
- if (failureIsOverflow)
+ if (status == Number.ParsingStatus.Overflow)
{
if (throwOnFailure)
{
- throw new OverflowException(SR.Overflow_Int64);
+ Number.ThrowOverflowException(TypeCode.Int64);
}
}
else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
{
Debug.Assert(enumType.GetEnumUnderlyingType() == typeof(ulong));
- bool failureIsOverflow = false;
- if (StartsNumber(value[0]) && Number.TryParseUInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result, ref failureIsOverflow))
+ Number.ParsingStatus status = default;
+ if (StartsNumber(value[0]))
{
- return true;
+ status = Number.TryParseUInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
+ if (status == Number.ParsingStatus.OK)
+ {
+ return true;
+ }
}
- if (failureIsOverflow)
+ if (status == Number.ParsingStatus.Overflow)
{
if (throwOnFailure)
{
- throw new OverflowException(SR.Overflow_UInt64);
+ Number.ThrowOverflowException(TypeCode.UInt64);
}
}
else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out result))
using System.Diagnostics;
using System.Globalization;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Internal.Runtime.CompilerServices;
private const int Int64Precision = 19;
private const int UInt64Precision = 20;
- /// <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>
- internal static readonly int[] s_charToHexLookup =
+ /// <summary>Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
+ internal static ReadOnlySpan<byte> CharToHexLookup => new byte[]
{
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
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
+ 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf // 102
};
private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- if (!TryParseInt32(value, styles, info, out int result, out bool failureIsOverflow))
+ ParsingStatus status = TryParseInt32(value, styles, info, out int result);
+ if (status != ParsingStatus.OK)
{
- ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int32));
+ ThrowOverflowOrFormatException(status, TypeCode.Int32);
}
return result;
internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- if (!TryParseInt64(value, styles, info, out long result, out bool failureIsOverflow))
+ ParsingStatus status = TryParseInt64(value, styles, info, out long result);
+ if (status != ParsingStatus.OK)
{
- ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int64));
+ ThrowOverflowOrFormatException(status, TypeCode.Int64);
}
return result;
internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- if (!TryParseUInt32(value, styles, info, out uint result, out bool failureIsOverflow))
+ ParsingStatus status = TryParseUInt32(value, styles, info, out uint result);
+ if (status != ParsingStatus.OK)
{
- ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt32));
+ ThrowOverflowOrFormatException(status, TypeCode.UInt32);
}
return result;
internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- if (!TryParseUInt64(value, styles, info, out ulong result, out bool failureIsOverflow))
+ ParsingStatus status = TryParseUInt64(value, styles, info, out ulong result);
+ if (status != ParsingStatus.OK)
{
- ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt64));
+ ThrowOverflowOrFormatException(status, TypeCode.UInt64);
}
return result;
return false;
}
- internal static unsafe bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, out bool failureIsOverflow)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static ParsingStatus TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
{
- result = 0;
- failureIsOverflow = false;
-
if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- return TryParseInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
+ return TryParseInt32IntegerStyle(value, styles, info, out result);
}
if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result), ref failureIsOverflow);
+ result = 0;
+ return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result));
}
+ return TryParseInt32Number(value, styles, info, out result);
+ }
+
+ private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
+ {
+ result = 0;
byte* pDigits = stackalloc byte[Int32NumberBufferLength];
NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
if (!TryStringToNumber(value, styles, ref number, info))
{
- return false;
+ return ParsingStatus.Failed;
}
if (!TryNumberToInt32(ref number, ref result))
{
- failureIsOverflow = true;
- return false;
+ return ParsingStatus.Overflow;
}
- return true;
+ return ParsingStatus.OK;
}
/// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
- internal static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
+ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
{
Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
- Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((uint)value.Length < 1)
+ if (value.IsEmpty)
goto FalseExit;
- bool overflow = false;
- int sign = 1;
int index = 0;
int num = value[0];
}
// Parse leading sign.
+ int sign = 1;
if ((styles & NumberStyles.AllowLeadingSign) != 0)
{
- string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-
- if (positiveSign == "+" && negativeSign == "-")
+ if (info.HasInvariantNumberSigns)
{
if (num == '-')
{
{
value = value.Slice(index);
index = 0;
+ string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
}
}
+ bool overflow = false;
int answer = 0;
if (IsDigit(num))
{
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto DoneAtEnd;
num = value[index];
} while (num == '0');
if (!IsDigit(num))
for (int i = 0; i < 8; i++) // next 8 digits can't overflow
{
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto DoneAtEnd;
num = value[index];
if (!IsDigit(num))
goto HasTrailingChars;
answer = 10 * answer + num - '0';
}
- // Potential overflow now processing the 10th digit.
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto DoneAtEnd;
num = value[index];
if (!IsDigit(num))
goto HasTrailingChars;
index++;
- if (answer > int.MaxValue / 10)
- {
- overflow = true;
- }
+ // Potential overflow now processing the 10th digit.
+ overflow = answer > int.MaxValue / 10;
answer = answer * 10 + num - '0';
- if ((uint)answer > (uint)int.MaxValue + (-1 * sign + 1) / 2)
- {
- overflow = true;
- }
+ overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31);
if ((uint)index >= (uint)value.Length)
goto DoneAtEndButPotentialOverflow;
overflow = true;
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto OverflowExit;
num = value[index];
}
goto HasTrailingChars;
}
-
- FalseExit: // parsing failed
- result = 0;
- return false;
+ goto FalseExit;
DoneAtEndButPotentialOverflow:
if (overflow)
{
- failureIsOverflow = true;
- goto FalseExit;
+ goto OverflowExit;
}
+ DoneAtEnd:
result = answer * sign;
- return true;
+ ParsingStatus status = ParsingStatus.OK;
+ Exit:
+ return status;
+
+ FalseExit: // parsing failed
+ result = 0;
+ status = ParsingStatus.Failed;
+ goto Exit;
+ OverflowExit:
+ result = 0;
+ status = ParsingStatus.Overflow;
+ goto Exit;
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.
}
/// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
- internal static bool TryParseInt64IntegerStyle(
- ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
+ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
{
Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
- Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((uint)value.Length < 1)
+ if (value.IsEmpty)
goto FalseExit;
- bool overflow = false;
- int sign = 1;
int index = 0;
int num = value[0];
}
// Parse leading sign.
+ int sign = 1;
if ((styles & NumberStyles.AllowLeadingSign) != 0)
{
- string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-
- if (positiveSign == "+" && negativeSign == "-")
+ if (info.HasInvariantNumberSigns)
{
if (num == '-')
{
{
value = value.Slice(index);
index = 0;
+ string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
}
}
+ bool overflow = false;
long answer = 0;
if (IsDigit(num))
{
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto DoneAtEnd;
num = value[index];
} while (num == '0');
if (!IsDigit(num))
for (int i = 0; i < 17; i++) // next 17 digits can't overflow
{
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto DoneAtEnd;
num = value[index];
if (!IsDigit(num))
goto HasTrailingChars;
answer = 10 * answer + num - '0';
}
- // Potential overflow now processing the 19th digit.
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto DoneAtEnd;
num = value[index];
if (!IsDigit(num))
goto HasTrailingChars;
index++;
- if (answer > long.MaxValue / 10)
- {
- overflow = true;
- }
+ // Potential overflow now processing the 19th digit.
+ overflow = answer > long.MaxValue / 10;
answer = answer * 10 + num - '0';
- if ((ulong)answer > (ulong)long.MaxValue + (ulong)((-1 * sign + 1) / 2)) // + sign => 0, - sign => 1
- {
- overflow = true;
- }
+ overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31);
if ((uint)index >= (uint)value.Length)
goto DoneAtEndButPotentialOverflow;
overflow = true;
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto OverflowExit;
num = value[index];
}
goto HasTrailingChars;
}
-
- FalseExit: // parsing failed
- result = 0;
- return false;
+ goto FalseExit;
DoneAtEndButPotentialOverflow:
if (overflow)
{
- failureIsOverflow = true;
- goto FalseExit;
+ goto OverflowExit;
}
+ DoneAtEnd:
result = answer * sign;
- return true;
+ ParsingStatus status = ParsingStatus.OK;
+ Exit:
+ return status;
+
+ FalseExit: // parsing failed
+ result = 0;
+ status = ParsingStatus.Failed;
+ goto Exit;
+ OverflowExit:
+ result = 0;
+ status = ParsingStatus.Overflow;
+ goto Exit;
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.
goto DoneAtEndButPotentialOverflow;
}
- internal static unsafe bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, out bool failureIsOverflow)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static ParsingStatus TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
{
- result = 0;
- failureIsOverflow = false;
-
if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- return TryParseInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
+ return TryParseInt64IntegerStyle(value, styles, info, out result);
}
if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result), ref failureIsOverflow);
+ result = 0;
+ return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result));
}
+ return TryParseInt64Number(value, styles, info, out result);
+ }
+
+ private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
+ {
+ result = 0;
byte* pDigits = stackalloc byte[Int64NumberBufferLength];
NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
if (!TryStringToNumber(value, styles, ref number, info))
{
- return false;
+ return ParsingStatus.Failed;
}
if (!TryNumberToInt64(ref number, ref result))
{
- failureIsOverflow = true;
- return false;
+ return ParsingStatus.Overflow;
}
- return true;
+ return ParsingStatus.OK;
}
- internal static unsafe bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, out bool failureIsOverflow)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static ParsingStatus TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
{
- result = 0;
- failureIsOverflow = false;
-
if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- return TryParseUInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
+ return TryParseUInt32IntegerStyle(value, styles, info, out result);
}
if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- return TryParseUInt32HexNumberStyle(value, styles, out result, ref failureIsOverflow);
+ return TryParseUInt32HexNumberStyle(value, styles, out result);
}
+ return TryParseUInt32Number(value, styles, info, out result);
+ }
+
+ private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+ {
+ result = 0;
byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
if (!TryStringToNumber(value, styles, ref number, info))
{
- return false;
+ return ParsingStatus.Failed;
}
if (!TryNumberToUInt32(ref number, ref result))
{
- failureIsOverflow = true;
- return false;
+ return ParsingStatus.Overflow;
}
- return true;
+ return ParsingStatus.OK;
}
/// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
- internal static bool TryParseUInt32IntegerStyle(
- ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
+ internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
{
Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
- Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((uint)value.Length < 1)
+ if (value.IsEmpty)
goto FalseExit;
- bool overflow = false;
- bool hasNegativeSign = false;
int index = 0;
int num = value[0];
}
// Parse leading sign.
+ bool overflow = false;
if ((styles & NumberStyles.AllowLeadingSign) != 0)
{
- string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-
- if (positiveSign == "+" && negativeSign == "-")
+ if (info.HasInvariantNumberSigns)
{
if (num == '+')
{
}
else if (num == '-')
{
- hasNegativeSign = true;
+ overflow = true;
index++;
if ((uint)index >= (uint)value.Length)
goto FalseExit;
{
value = value.Slice(index);
index = 0;
+ string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
}
else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
- hasNegativeSign = true;
+ overflow = true;
index += negativeSign.Length;
if ((uint)index >= (uint)value.Length)
goto FalseExit;
{
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto DoneAtEnd;
num = value[index];
} while (num == '0');
if (!IsDigit(num))
- goto HasTrailingChars;
+ goto HasTrailingCharsZero;
}
// Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
answer = 10 * answer + num - '0';
}
- // Potential overflow now processing the 10th digit.
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;
- }
+ // Potential overflow now processing the 10th digit.
+ overflow |= (uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5');
answer = answer * 10 + num - '0';
if ((uint)index >= (uint)value.Length)
goto DoneAtEndButPotentialOverflow;
overflow = true;
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto OverflowExit;
num = value[index];
}
goto HasTrailingChars;
}
-
- FalseExit: // parsing failed
- result = 0;
- return false;
+ goto FalseExit;
DoneAtEndButPotentialOverflow:
- if (overflow || (hasNegativeSign && answer != 0))
+ if (overflow)
{
- failureIsOverflow = true;
- goto FalseExit;
+ goto OverflowExit;
}
+ DoneAtEnd:
result = (uint)answer;
- return true;
+ ParsingStatus status = ParsingStatus.OK;
+ Exit:
+ return status;
+
+ FalseExit: // parsing failed
+ result = 0;
+ status = ParsingStatus.Failed;
+ goto Exit;
+ OverflowExit:
+ result = 0;
+ status = ParsingStatus.Overflow;
+ goto Exit;
+ HasTrailingCharsZero:
+ overflow = false;
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))
}
/// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
- private static bool TryParseUInt32HexNumberStyle(
- ReadOnlySpan<char> value, NumberStyles styles, out uint result, ref bool failureIsOverflow)
+ private static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out uint result)
{
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)
+ if (value.IsEmpty)
goto FalseExit;
- bool overflow = false;
int index = 0;
int num = value[0];
- int numValue = 0;
+ uint numValue;
// Skip past any whitespace at the beginning.
if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
while (IsWhite(num));
}
- int answer = 0;
- int[] charToHexLookup = s_charToHexLookup;
+ bool overflow = false;
+ uint answer = 0;
+ ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
{
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)
+ do
{
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto OverflowExit;
num = value[index];
- }
+ } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
+ overflow = true;
goto HasTrailingChars;
}
-
- FalseExit: // parsing failed
- result = 0;
- return false;
+ goto FalseExit;
DoneAtEndButPotentialOverflow:
if (overflow)
{
- failureIsOverflow = true;
- goto FalseExit;
+ goto OverflowExit;
}
DoneAtEnd:
- result = (uint)answer;
- return true;
+ result = answer;
+ ParsingStatus status = ParsingStatus.OK;
+ Exit:
+ return status;
+
+ FalseExit: // parsing failed
+ result = 0;
+ status = ParsingStatus.Failed;
+ goto Exit;
+ OverflowExit:
+ result = 0;
+ status = ParsingStatus.Overflow;
+ goto Exit;
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.
goto DoneAtEndButPotentialOverflow;
}
- internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, out bool failureIsOverflow)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static ParsingStatus TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
{
- result = 0;
- failureIsOverflow = false;
-
if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- return TryParseUInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
+ return TryParseUInt64IntegerStyle(value, styles, info, out result);
}
if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- return TryParseUInt64HexNumberStyle(value, styles, out result, ref failureIsOverflow);
+ return TryParseUInt64HexNumberStyle(value, styles, out result);
}
+ return TryParseUInt64Number(value, styles, info, out result);
+ }
+
+ private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+ {
+ result = 0;
byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
if (!TryStringToNumber(value, styles, ref number, info))
{
- return false;
+ return ParsingStatus.Failed;
}
if (!TryNumberToUInt64(ref number, ref result))
{
- failureIsOverflow = true;
- return false;
+ return ParsingStatus.Overflow;
}
- return true;
+ return ParsingStatus.OK;
}
/// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
- internal static bool TryParseUInt64IntegerStyle(
- ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
+ internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
{
Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
- Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
- if ((uint)value.Length < 1)
+ if (value.IsEmpty)
goto FalseExit;
- bool overflow = false;
- bool hasNegativeSign = false;
int index = 0;
int num = value[0];
}
// Parse leading sign.
+ bool overflow = false;
if ((styles & NumberStyles.AllowLeadingSign) != 0)
{
- string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-
- if (positiveSign == "+" && negativeSign == "-")
+ if (info.HasInvariantNumberSigns)
{
if (num == '+')
{
}
else if (num == '-')
{
- hasNegativeSign = true;
+ overflow = true;
index++;
if ((uint)index >= (uint)value.Length)
goto FalseExit;
{
value = value.Slice(index);
index = 0;
+ string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
}
else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
- hasNegativeSign = true;
+ overflow = true;
index += negativeSign.Length;
if ((uint)index >= (uint)value.Length)
goto FalseExit;
{
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto DoneAtEnd;
num = value[index];
} while (num == '0');
if (!IsDigit(num))
- goto HasTrailingChars;
+ goto HasTrailingCharsZero;
}
// Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
answer = 10 * answer + num - '0';
}
- // Potential overflow now processing the 20th digit.
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;
- }
+ // Potential overflow now processing the 20th digit.
+ overflow |= (ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5');
answer = answer * 10 + num - '0';
if ((uint)index >= (uint)value.Length)
goto DoneAtEndButPotentialOverflow;
overflow = true;
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto OverflowExit;
num = value[index];
}
goto HasTrailingChars;
}
-
- FalseExit: // parsing failed
- result = 0;
- return false;
+ goto FalseExit;
DoneAtEndButPotentialOverflow:
- if (overflow || (hasNegativeSign && answer != 0))
+ if (overflow)
{
- failureIsOverflow = true;
- goto FalseExit;
+ goto OverflowExit;
}
+ DoneAtEnd:
result = (ulong)answer;
- return true;
+ ParsingStatus status = ParsingStatus.OK;
+ Exit:
+ return status;
+ FalseExit: // parsing failed
+ result = 0;
+ status = ParsingStatus.Failed;
+ goto Exit;
+ OverflowExit:
+ result = 0;
+ status = ParsingStatus.Overflow;
+ goto Exit;
+
+ HasTrailingCharsZero:
+ overflow = false;
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))
}
/// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
- private static bool TryParseUInt64HexNumberStyle(
- ReadOnlySpan<char> value, NumberStyles styles, out ulong result, ref bool failureIsOverflow)
+ private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out ulong result)
{
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)
+ if (value.IsEmpty)
goto FalseExit;
- bool overflow = false;
int index = 0;
int num = value[0];
- int numValue = 0;
+ uint numValue;
// Skip past any whitespace at the beginning.
if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
while (IsWhite(num));
}
- long answer = 0;
- int[] charToHexLookup = s_charToHexLookup;
+ bool overflow = false;
+ ulong answer = 0;
+ ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
{
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)
+ do
{
index++;
if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
+ goto OverflowExit;
num = value[index];
- }
+ } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
+ overflow = true;
goto HasTrailingChars;
}
-
- FalseExit: // parsing failed
- result = 0;
- return false;
+ goto FalseExit;
DoneAtEndButPotentialOverflow:
if (overflow)
{
- failureIsOverflow = true;
- goto FalseExit;
+ goto OverflowExit;
}
DoneAtEnd:
- result = (ulong)answer;
- return true;
+ result = answer;
+ ParsingStatus status = ParsingStatus.OK;
+ Exit:
+ return status;
+
+ FalseExit: // parsing failed
+ result = 0;
+ status = ParsingStatus.Failed;
+ goto Exit;
+ OverflowExit:
+ result = 0;
+ status = ParsingStatus.Overflow;
+ goto Exit;
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.
internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- if (!TryParseDecimal(value, styles, info, out decimal result, out bool failureIsOverflow))
+ ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result);
+ if (status != ParsingStatus.OK)
{
- ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Decimal));
+ ThrowOverflowOrFormatException(status, TypeCode.Decimal);
}
return result;
{
if (!TryParseDouble(value, styles, info, out double result))
{
- ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
+ ThrowOverflowOrFormatException(ParsingStatus.Failed);
}
return result;
{
if (!TryParseSingle(value, styles, info, out float result))
{
- ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
+ ThrowOverflowOrFormatException(ParsingStatus.Failed);
}
return result;
}
- internal static unsafe bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result, out bool failureIsOverflow)
+ internal static unsafe ParsingStatus TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
{
byte* pDigits = stackalloc byte[DecimalNumberBufferLength];
NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
result = 0;
- failureIsOverflow = false;
if (!TryStringToNumber(value, styles, ref number, info))
{
- return false;
+ return ParsingStatus.Failed;
}
if (!TryNumberToDecimal(ref number, ref result))
{
- failureIsOverflow = true;
- return false;
+ return ParsingStatus.Overflow;
}
- return true;
+ return ParsingStatus.OK;
}
internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
return true;
}
- private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
- {
- if (!TryStringToNumber(value, styles, ref number, info))
- {
- ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
- }
- }
-
internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
{
Debug.Assert(info != null);
{
char* p = stringPointer;
if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
- || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
+ || ((int)(p - stringPointer) < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
{
number.CheckConsistency();
return false;
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 < value.Length; i++)
+ for (int i = index; (uint)i < (uint)value.Length; i++)
{
if (value[i] != '\0')
{
return null;
}
- private static bool IsWhite(int ch) => ch == 0x20 || ((uint)(ch - 0x09) <= (0x0D - 0x09));
+ // Ternary op is a workaround for https://github.com/dotnet/coreclr/issues/914
+ private static bool IsWhite(int ch) => ch == 0x20 || (uint)(ch - 0x09) <= (0x0D - 0x09) ? true : false;
private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
- private static void ThrowOverflowOrFormatException(bool overflow, string overflowResourceKey)
+ internal enum ParsingStatus
+ {
+ OK,
+ Failed,
+ Overflow
+ }
+
+ internal static void ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type = 0) => throw GetException(status, type);
+
+ internal static void ThrowOverflowException(TypeCode type) => throw GetException(ParsingStatus.Overflow, type);
+
+ private static Exception GetException(ParsingStatus status, TypeCode type)
{
- throw overflow ?
- new OverflowException(SR.GetResourceString(overflowResourceKey)) :
- (Exception)new FormatException(SR.Format_InvalidString);
+ if (status == ParsingStatus.Failed)
+ return new FormatException(SR.Format_InvalidString);
+
+ string s;
+ switch (type)
+ {
+ case TypeCode.SByte:
+ s = SR.Overflow_SByte;
+ break;
+ case TypeCode.Byte:
+ s = SR.Overflow_Byte;
+ break;
+ case TypeCode.Int16:
+ s = SR.Overflow_Int16;
+ break;
+ case TypeCode.UInt16:
+ s = SR.Overflow_UInt16;
+ break;
+ case TypeCode.Int32:
+ s = SR.Overflow_Int32;
+ break;
+ case TypeCode.UInt32:
+ s = SR.Overflow_UInt32;
+ break;
+ case TypeCode.Int64:
+ s = SR.Overflow_Int64;
+ break;
+ case TypeCode.UInt64:
+ s = SR.Overflow_UInt64;
+ break;
+ default:
+ Debug.Assert(type == TypeCode.Decimal);
+ s = SR.Overflow_Decimal;
+ break;
+ }
+ return new OverflowException(s);
}
internal static double NumberToDouble(ref NumberBuffer number)