internal static partial class Number
{
internal const int DecimalPrecision = 29; // Decimal.DecCalc also uses this value
- private const int FloatPrecision = 7;
+ private const int SinglePrecision = 7;
private const int DoublePrecision = 15;
private const int ScaleNAN = unchecked((int)0x80000000);
private const int ScaleINF = 0x7FFFFFFF;
"(#)", "-#", "- #", "#-", "# -",
};
- public static string FormatDecimal(decimal value, ReadOnlySpan<char> format, NumberFormatInfo info)
+ public static unsafe string FormatDecimal(decimal value, ReadOnlySpan<char> format, NumberFormatInfo info)
{
char fmt = ParseFormatSpecifier(format, out int digits);
- NumberBuffer number = default;
+ char* pDigits = stackalloc char[DecimalNumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
+
DecimalToNumber(ref value, ref number);
ValueStringBuilder sb;
return sb.ToString();
}
- public static bool TryFormatDecimal(decimal value, ReadOnlySpan<char> format, NumberFormatInfo info, Span<char> destination, out int charsWritten)
+ public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan<char> format, NumberFormatInfo info, Span<char> destination, out int charsWritten)
{
char fmt = ParseFormatSpecifier(format, out int digits);
- NumberBuffer number = default;
+ char* pDigits = stackalloc char[DecimalNumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
+
DecimalToNumber(ref value, ref number);
ValueStringBuilder sb;
private static unsafe void DecimalToNumber(ref decimal d, ref NumberBuffer number)
{
char* buffer = number.GetDigitsPointer();
- number.precision = DecimalPrecision;
- number.sign = d.IsNegative;
- number.kind = NumberBufferKind.Decimal;
+ number.Precision = DecimalPrecision;
+ number.Sign = d.IsNegative;
char* p = buffer + DecimalPrecision;
while ((d.Mid | d.High) != 0)
p = UInt32ToDecChars(p, d.Low, 0);
int i = (int)((byte*)(buffer + DecimalPrecision) - (byte*)p) >> 1;
- number.scale = i - d.Scale;
+ number.Scale = i - d.Scale;
char* dst = number.GetDigitsPointer();
while (--i >= 0)
/// Non-null if an existing string can be returned, in which case the builder will be unmodified.
/// Null if no existing string was returned, in which case the formatted output is in the builder.
/// </returns>
- private static string FormatDouble(ref ValueStringBuilder sb, double value, ReadOnlySpan<char> format, NumberFormatInfo info)
+ private static unsafe string FormatDouble(ref ValueStringBuilder sb, double value, ReadOnlySpan<char> format, NumberFormatInfo info)
{
char fmt = ParseFormatSpecifier(format, out int digits);
int precision = DoublePrecision;
- NumberBuffer number = default;
- number.kind = NumberBufferKind.Double;
+
+ char* pDigits = stackalloc char[DoubleNumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Double, pDigits, DoubleNumberBufferLength);
switch (fmt)
{
case 'R':
case 'r':
+ {
+ // In order to give numbers that are both friendly to display and round-trippable, we parse the
+ // number using 15 digits and then determine if it round trips to the same value. If it does, we
+ // convert that NUMBER to a string, otherwise we reparse using 17 digits and display that.
+ DoubleToNumber(value, DoublePrecision, ref number);
+ if (number.Scale == ScaleNAN)
{
- // In order to give numbers that are both friendly to display and round-trippable, we parse the
- // number using 15 digits and then determine if it round trips to the same value. If it does, we
- // convert that NUMBER to a string, otherwise we reparse using 17 digits and display that.
- DoubleToNumber(value, DoublePrecision, ref number);
- if (number.scale == ScaleNAN)
- {
- return info.NaNSymbol;
- }
- else if (number.scale == ScaleINF)
- {
- return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
- }
-
- if (NumberToDouble(ref number) == value)
- {
- NumberToString(ref sb, ref number, 'G', DoublePrecision, info);
- }
- else
- {
- DoubleToNumber(value, 17, ref number);
- NumberToString(ref sb, ref number, 'G', 17, info);
- }
+ return info.NaNSymbol;
+ }
+ else if (number.Scale == ScaleINF)
+ {
+ return number.Sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
+ }
- return null;
+ if (NumberToDouble(ref number) == value)
+ {
+ NumberToString(ref sb, ref number, 'G', DoublePrecision, info);
+ }
+ else
+ {
+ DoubleToNumber(value, 17, ref number);
+ NumberToString(ref sb, ref number, 'G', 17, info);
}
+ return null;
+ }
+
case 'E':
case 'e':
// Round values less than E14 to 15 digits
}
DoubleToNumber(value, precision, ref number);
- if (number.scale == ScaleNAN)
+ if (number.Scale == ScaleNAN)
{
return info.NaNSymbol;
}
- else if (number.scale == ScaleINF)
+ else if (number.Scale == ScaleINF)
{
- return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
+ return number.Sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
}
if (fmt != 0)
/// Non-null if an existing string can be returned, in which case the builder will be unmodified.
/// Null if no existing string was returned, in which case the formatted output is in the builder.
/// </returns>
- private static string FormatSingle(ref ValueStringBuilder sb, float value, ReadOnlySpan<char> format, NumberFormatInfo info)
+ private static unsafe string FormatSingle(ref ValueStringBuilder sb, float value, ReadOnlySpan<char> format, NumberFormatInfo info)
{
char fmt = ParseFormatSpecifier(format, out int digits);
- int precision = FloatPrecision;
- NumberBuffer number = default;
- number.kind = NumberBufferKind.Double;
+ int precision = SinglePrecision;
+
+ char* pDigits = stackalloc char[SingleNumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Double, pDigits, SingleNumberBufferLength);
switch (fmt)
{
case 'R':
case 'r':
+ {
+ // In order to give numbers that are both friendly to display and round-trippable, we parse the
+ // number using 7 digits and then determine if it round trips to the same value. If it does, we
+ // convert that NUMBER to a string, otherwise we reparse using 9 digits and display that.
+ DoubleToNumber(value, SinglePrecision, ref number);
+ if (number.Scale == ScaleNAN)
{
- // In order to give numbers that are both friendly to display and round-trippable, we parse the
- // number using 7 digits and then determine if it round trips to the same value. If it does, we
- // convert that NUMBER to a string, otherwise we reparse using 9 digits and display that.
- DoubleToNumber(value, FloatPrecision, ref number);
- if (number.scale == ScaleNAN)
- {
- return info.NaNSymbol;
- }
- else if (number.scale == ScaleINF)
- {
- return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
- }
+ return info.NaNSymbol;
+ }
+ else if (number.Scale == ScaleINF)
+ {
+ return number.Sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
+ }
- if ((float)NumberToDouble(ref number) == value)
- {
- NumberToString(ref sb, ref number, 'G', FloatPrecision, info);
- }
- else
- {
- DoubleToNumber(value, 9, ref number);
- NumberToString(ref sb, ref number, 'G', 9, info);
- }
- return null;
+ if ((float)NumberToDouble(ref number) == value)
+ {
+ NumberToString(ref sb, ref number, 'G', SinglePrecision, info);
}
+ else
+ {
+ DoubleToNumber(value, 9, ref number);
+ NumberToString(ref sb, ref number, 'G', 9, info);
+ }
+ return null;
+ }
case 'E':
case 'e':
}
DoubleToNumber(value, precision, ref number);
- if (number.scale == ScaleNAN)
+ if (number.Scale == ScaleNAN)
{
return info.NaNSymbol;
}
- else if (number.scale == ScaleINF)
+ else if (number.Scale == ScaleINF)
{
- return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
+ return number.Sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
}
if (fmt != 0)
return false;
}
- public static string FormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider)
+ public static unsafe string FormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider)
{
// Fast path for default format with a non-negative value
if (value >= 0 && format.Length == 0)
else
{
NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
- NumberBuffer number = default;
+
+ char* pDigits = stackalloc char[Int32NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
Int32ToNumber(value, ref number);
ValueStringBuilder sb;
unsafe
}
}
- public static bool TryFormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
+ public static unsafe bool TryFormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
{
// Fast path for default format with a non-negative value
if (value >= 0 && format.Length == 0)
else
{
NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
- NumberBuffer number = default;
+
+ char* pDigits = stackalloc char[Int32NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
Int32ToNumber(value, ref number);
ValueStringBuilder sb;
unsafe
}
}
- public static string FormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider)
+ public static unsafe string FormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider)
{
// Fast path for default format
if (format.Length == 0)
else
{
NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
- NumberBuffer number = default;
+
+ char* pDigits = stackalloc char[UInt32NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
UInt32ToNumber(value, ref number);
ValueStringBuilder sb;
unsafe
}
}
- public static bool TryFormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
+ public static unsafe bool TryFormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
{
// Fast path for default format
if (format.Length == 0)
else
{
NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
- NumberBuffer number = default;
+
+ char* pDigits = stackalloc char[UInt32NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
UInt32ToNumber(value, ref number);
ValueStringBuilder sb;
unsafe
}
}
- public static string FormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider)
+ public static unsafe string FormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider)
{
// Fast path for default format with a non-negative value
if (value >= 0 && format.Length == 0)
else
{
NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
- NumberBuffer number = default;
+
+ char* pDigits = stackalloc char[Int64NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
Int64ToNumber(value, ref number);
ValueStringBuilder sb;
unsafe
}
}
- public static bool TryFormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
+ public static unsafe bool TryFormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
{
// Fast path for default format with a non-negative value
if (value >= 0 && format.Length == 0)
else
{
NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
- NumberBuffer number = default;
+
+ char* pDigits = stackalloc char[Int64NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
Int64ToNumber(value, ref number);
ValueStringBuilder sb;
unsafe
}
}
- public static string FormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider)
+ public static unsafe string FormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider)
{
// Fast path for default format
if (format.Length == 0)
else
{
NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
- NumberBuffer number = default;
+
+ char* pDigits = stackalloc char[UInt64NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+
UInt64ToNumber(value, ref number);
ValueStringBuilder sb;
unsafe
}
}
- public static bool TryFormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
+ public static unsafe bool TryFormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
{
// Fast path for default format
if (format.Length == 0)
else
{
NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
- NumberBuffer number = default;
+
+ char* pDigits = stackalloc char[UInt64NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+
UInt64ToNumber(value, ref number);
ValueStringBuilder sb;
unsafe
[MethodImpl(MethodImplOptions.AggressiveInlining)] // called from only one location
private static unsafe void Int32ToNumber(int value, ref NumberBuffer number)
{
- number.precision = Int32Precision;
+ number.Precision = Int32Precision;
if (value >= 0)
{
- number.sign = false;
+ number.Sign = false;
}
else
{
- number.sign = true;
+ number.Sign = true;
value = -value;
}
char* p = UInt32ToDecChars(buffer + Int32Precision, (uint)value, 0);
int i = (int)(buffer + Int32Precision - p);
- number.scale = i;
- number.kind = NumberBufferKind.Integer;
+ number.Scale = i;
char* dst = number.GetDigitsPointer();
while (--i >= 0)
[MethodImpl(MethodImplOptions.AggressiveInlining)] // called from only one location
private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number)
{
- number.precision = UInt32Precision;
- number.sign = false;
+ number.Precision = UInt32Precision;
+ number.Sign = false;
char* buffer = number.GetDigitsPointer();
char* p = UInt32ToDecChars(buffer + UInt32Precision, value, 0);
int i = (int)(buffer + UInt32Precision - p);
- number.scale = i;
- number.kind = NumberBufferKind.Integer;
+ number.Scale = i;
char* dst = number.GetDigitsPointer();
while (--i >= 0)
private static unsafe void Int64ToNumber(long input, ref NumberBuffer number)
{
ulong value = (ulong)input;
- number.sign = input < 0;
- number.precision = Int64Precision;
- if (number.sign)
+ number.Sign = input < 0;
+ number.Precision = Int64Precision;
+ if (number.Sign)
{
value = (ulong)(-input);
}
p = UInt32ToDecChars(p, Low32(value), 0);
int i = (int)(buffer + Int64Precision - p);
- number.scale = i;
- number.kind = NumberBufferKind.Integer;
+ number.Scale = i;
char* dst = number.GetDigitsPointer();
while (--i >= 0)
private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number)
{
- number.precision = UInt64Precision;
- number.sign = false;
+ number.Precision = UInt64Precision;
+ number.Sign = false;
char* buffer = number.GetDigitsPointer();
char* p = buffer + UInt64Precision;
p = UInt32ToDecChars(p, Low32(value), 0);
int i = (int)(buffer + UInt64Precision - p);
- number.scale = i;
- number.kind = NumberBufferKind.Integer;
+ number.Scale = i;
char* dst = number.GetDigitsPointer();
while (--i >= 0)
// Default empty format to be "G"; custom format is signified with '\0'.
digits = -1;
return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it.
- 'G' :
+ 'G' :
'\0';
}
internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info)
{
- Debug.Assert(number.kind != NumberBufferKind.Unknown);
+ Debug.Assert(number.Kind != NumberBufferKind.Unknown);
switch (format)
{
case 'C':
case 'c':
- {
- if (nMaxDigits < 0)
- nMaxDigits = info.CurrencyDecimalDigits;
+ {
+ if (nMaxDigits < 0)
+ nMaxDigits = info.CurrencyDecimalDigits;
- RoundNumber(ref number, number.scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed.
+ RoundNumber(ref number, number.Scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed.
- FormatCurrency(ref sb, ref number, nMaxDigits, info);
+ FormatCurrency(ref sb, ref number, nMaxDigits, info);
- break;
- }
+ break;
+ }
case 'F':
case 'f':
- {
- if (nMaxDigits < 0)
- nMaxDigits = info.NumberDecimalDigits;
+ {
+ if (nMaxDigits < 0)
+ nMaxDigits = info.NumberDecimalDigits;
- RoundNumber(ref number, number.scale + nMaxDigits);
+ RoundNumber(ref number, number.Scale + nMaxDigits);
- if (number.sign)
- sb.Append(info.NegativeSign);
+ if (number.Sign)
+ sb.Append(info.NegativeSign);
- FormatFixed(ref sb, ref number, nMaxDigits, info, null, info.NumberDecimalSeparator, null);
+ FormatFixed(ref sb, ref number, nMaxDigits, info, null, info.NumberDecimalSeparator, null);
- break;
- }
+ break;
+ }
case 'N':
case 'n':
- {
- if (nMaxDigits < 0)
- nMaxDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation
+ {
+ if (nMaxDigits < 0)
+ nMaxDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation
- RoundNumber(ref number, number.scale + nMaxDigits);
+ RoundNumber(ref number, number.Scale + nMaxDigits);
- FormatNumber(ref sb, ref number, nMaxDigits, info);
+ FormatNumber(ref sb, ref number, nMaxDigits, info);
- break;
- }
+ break;
+ }
case 'E':
case 'e':
- {
- if (nMaxDigits < 0)
- nMaxDigits = 6;
- nMaxDigits++;
+ {
+ if (nMaxDigits < 0)
+ nMaxDigits = 6;
+ nMaxDigits++;
- RoundNumber(ref number, nMaxDigits);
+ RoundNumber(ref number, nMaxDigits);
- if (number.sign)
- sb.Append(info.NegativeSign);
+ if (number.Sign)
+ sb.Append(info.NegativeSign);
- FormatScientific(ref sb, ref number, nMaxDigits, info, format);
+ FormatScientific(ref sb, ref number, nMaxDigits, info, format);
- break;
- }
+ break;
+ }
case 'G':
case 'g':
+ {
+ bool noRounding = false;
+ if (nMaxDigits < 1)
{
- bool noRounding = false;
- if (nMaxDigits < 1)
+ if ((number.Kind == NumberBufferKind.Decimal) && (nMaxDigits == -1))
{
- if ((number.kind == NumberBufferKind.Decimal) && (nMaxDigits == -1))
- {
- noRounding = true; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
- goto SkipRounding;
- }
- else
- {
- // This ensures that the PAL code pads out to the correct place even when we use the default precision
- nMaxDigits = number.precision;
- }
+ noRounding = true; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
+ goto SkipRounding;
}
+ else
+ {
+ // This ensures that the PAL code pads out to the correct place even when we use the default precision
+ nMaxDigits = number.Precision;
+ }
+ }
- RoundNumber(ref number, nMaxDigits);
+ RoundNumber(ref number, nMaxDigits);
-SkipRounding:
- if (number.sign)
- sb.Append(info.NegativeSign);
+ SkipRounding:
+ if (number.Sign)
+ sb.Append(info.NegativeSign);
- FormatGeneral(ref sb, ref number, nMaxDigits, info, (char)(format - ('G' - 'E')), noRounding);
+ FormatGeneral(ref sb, ref number, nMaxDigits, info, (char)(format - ('G' - 'E')), noRounding);
- break;
- }
+ break;
+ }
case 'P':
case 'p':
- {
- if (nMaxDigits < 0)
- nMaxDigits = info.PercentDecimalDigits;
- number.scale += 2;
+ {
+ if (nMaxDigits < 0)
+ nMaxDigits = info.PercentDecimalDigits;
+ number.Scale += 2;
- RoundNumber(ref number, number.scale + nMaxDigits);
+ RoundNumber(ref number, number.Scale + nMaxDigits);
- FormatPercent(ref sb, ref number, nMaxDigits, info);
+ FormatPercent(ref sb, ref number, nMaxDigits, info);
- break;
- }
+ break;
+ }
default:
throw new FormatException(SR.Argument_BadFormatSpecifier);
internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref NumberBuffer number, ReadOnlySpan<char> format, NumberFormatInfo info)
{
- Debug.Assert(number.kind != NumberBufferKind.Unknown);
+ Debug.Assert(number.Kind != NumberBufferKind.Unknown);
int digitCount;
int decimalPos;
char* dig = number.GetDigitsPointer();
char ch;
- section = FindSection(format, dig[0] == 0 ? 2 : number.sign ? 1 : 0);
+ section = FindSection(format, dig[0] == 0 ? 2 : number.Sign ? 1 : 0);
while (true)
{
if ((src < format.Length && pFormat[src] == '0') ||
(src + 1 < format.Length && (pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0'))
{
- while (++src < format.Length && pFormat[src] == '0');
+ while (++src < format.Length && pFormat[src] == '0')
+ ;
scientific = true;
}
break;
if (dig[0] != 0)
{
- number.scale += scaleAdjust;
- int pos = scientific ? digitCount : number.scale + digitCount - decimalPos;
+ number.Scale += scaleAdjust;
+ int pos = scientific ? digitCount : number.Scale + digitCount - decimalPos;
RoundNumber(ref number, pos);
if (dig[0] == 0)
{
}
else
{
- number.scale = 0; // Decimals with scale ('0.00') should be rounded.
+ number.Scale = 0; // Decimals with scale ('0.00') should be rounded.
}
break;
}
else
{
- digPos = number.scale > decimalPos ? number.scale : decimalPos;
- adjust = number.scale - decimalPos;
+ digPos = number.Scale > decimalPos ? number.Scale : decimalPos;
+ adjust = number.Scale - decimalPos;
}
src = section;
}
}
- if (number.sign && (section == 0) && (number.scale != 0))
+ if (number.Sign && (section == 0) && (number.Scale != 0))
sb.Append(info.NegativeSign);
bool decimalWritten = false;
{
case '#':
case '0':
+ {
+ if (adjust < 0)
{
- if (adjust < 0)
- {
- adjust++;
- ch = digPos <= firstDigit ? '0' : '\0';
- }
- else
- {
- ch = *cur != 0 ? *cur++ : digPos > lastDigit ? '0' : '\0';
- }
- if (ch != 0)
+ adjust++;
+ ch = digPos <= firstDigit ? '0' : '\0';
+ }
+ else
+ {
+ ch = *cur != 0 ? *cur++ : digPos > lastDigit ? '0' : '\0';
+ }
+ if (ch != 0)
+ {
+ sb.Append(ch);
+ if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0)
{
- sb.Append(ch);
- if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0)
+ if (digPos == thousandsSepPos[thousandsSepCtr] + 1)
{
- if (digPos == thousandsSepPos[thousandsSepCtr] + 1)
- {
- sb.Append(info.NumberGroupSeparator);
- thousandsSepCtr--;
- }
+ sb.Append(info.NumberGroupSeparator);
+ thousandsSepCtr--;
}
}
-
- digPos--;
- break;
}
+
+ digPos--;
+ break;
+ }
case '.':
+ {
+ if (digPos != 0 || decimalWritten)
{
- if (digPos != 0 || decimalWritten)
- {
- // For compatibility, don't echo repeated decimals
- break;
- }
- // If the format has trailing zeros or the format has a decimal and digits remain
- if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0))
- {
- sb.Append(info.NumberDecimalSeparator);
- decimalWritten = true;
- }
+ // For compatibility, don't echo repeated decimals
break;
}
+ // If the format has trailing zeros or the format has a decimal and digits remain
+ if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0))
+ {
+ sb.Append(info.NumberDecimalSeparator);
+ decimalWritten = true;
+ }
+ break;
+ }
case '\x2030':
sb.Append(info.PerMilleSymbol);
break;
break;
case 'E':
case 'e':
+ {
+ bool positiveSign = false;
+ int i = 0;
+ if (scientific)
{
- bool positiveSign = false;
- int i = 0;
- if (scientific)
+ if (src < format.Length && pFormat[src] == '0')
{
- if (src < format.Length && pFormat[src] == '0')
- {
- // Handles E0, which should format the same as E-0
- i++;
- }
- else if (src+1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0')
- {
- // Handles E+0
- positiveSign = true;
- }
- else if (src+1 < format.Length && pFormat[src] == '-' && pFormat[src + 1] == '0')
- {
- // Handles E-0
- // Do nothing, this is just a place holder s.t. we don't break out of the loop.
- }
- else
- {
- sb.Append(ch);
- break;
- }
-
- while (++src < format.Length && pFormat[src] == '0')
- i++;
- if (i > 10)
- i = 10;
-
- int exp = dig[0] == 0 ? 0 : number.scale - decimalPos;
- FormatExponent(ref sb, info, exp, ch, i, positiveSign);
- scientific = false;
+ // Handles E0, which should format the same as E-0
+ i++;
+ }
+ else if (src + 1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0')
+ {
+ // Handles E+0
+ positiveSign = true;
+ }
+ else if (src + 1 < format.Length && pFormat[src] == '-' && pFormat[src + 1] == '0')
+ {
+ // Handles E-0
+ // Do nothing, this is just a place holder s.t. we don't break out of the loop.
}
else
{
- sb.Append(ch); // Copy E or e to output
- if (src < format.Length)
- {
- if (pFormat[src] == '+' || pFormat[src] == '-')
- sb.Append(pFormat[src++]);
- while (src < format.Length && pFormat[src] == '0')
- sb.Append(pFormat[src++]);
- }
+ sb.Append(ch);
+ break;
+ }
+
+ while (++src < format.Length && pFormat[src] == '0')
+ i++;
+ if (i > 10)
+ i = 10;
+
+ int exp = dig[0] == 0 ? 0 : number.Scale - decimalPos;
+ FormatExponent(ref sb, info, exp, ch, i, positiveSign);
+ scientific = false;
+ }
+ else
+ {
+ sb.Append(ch); // Copy E or e to output
+ if (src < format.Length)
+ {
+ if (pFormat[src] == '+' || pFormat[src] == '-')
+ sb.Append(pFormat[src++]);
+ while (src < format.Length && pFormat[src] == '0')
+ sb.Append(pFormat[src++]);
}
- break;
}
+ break;
+ }
default:
sb.Append(ch);
break;
}
}
- if (number.sign && (section == 0) && (number.scale == 0) && (sb.Length > 0))
+ if (number.Sign && (section == 0) && (number.Scale == 0) && (sb.Length > 0))
sb.Insert(0, info.NegativeSign);
}
private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
{
- string fmt = number.sign ?
+ string fmt = number.Sign ?
s_negCurrencyFormats[info.CurrencyNegativePattern] :
s_posCurrencyFormats[info.CurrencyPositivePattern];
private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup)
{
- int digPos = number.scale;
+ int digPos = number.Scale;
char* dig = number.GetDigitsPointer();
if (digPos > 0)
private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
{
- string fmt = number.sign ?
+ string fmt = number.Sign ?
s_negNumberFormats[info.NumberNegativePattern] :
PosNumberFormat;
while (--nMaxDigits > 0)
sb.Append((*dig != 0) ? *dig++ : '0');
- int e = number.digits[0] == 0 ? 0 : number.scale - 1;
+ int e = number.Digits[0] == 0 ? 0 : number.Scale - 1;
FormatExponent(ref sb, info, e, expChar, 3, true);
}
private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific)
{
- int digPos = number.scale;
+ int digPos = number.Scale;
bool scientific = false;
if (!bSuppressScientific)
}
if (scientific)
- FormatExponent(ref sb, info, number.scale - 1, expChar, 2, true);
+ FormatExponent(ref sb, info, number.Scale - 1, expChar, 2, true);
}
private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
{
- string fmt = number.sign ?
+ string fmt = number.Sign ?
s_negPercentFormats[info.PercentNegativePattern] :
s_posPercentFormats[info.PercentPositivePattern];
}
else
{
- number.scale++;
+ number.Scale++;
dig[0] = '1';
i = 1;
}
}
if (i == 0)
{
- number.scale = 0;
+ number.Scale = 0;
- if (number.kind == NumberBufferKind.Integer)
+ if (number.Kind == NumberBufferKind.Integer)
{
- number.sign = false;
+ number.Sign = false;
}
}
dig[i] = '\0';
fixed (char* pFormat = &MemoryMarshal.GetReference(format))
{
src = 0;
- for (;;)
+ for (; ; )
{
if (src >= format.Length)
{
private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number)
{
- number.precision = precision;
+ number.Precision = precision;
if (!double.IsFinite(value))
{
- number.scale = double.IsNaN(value) ? ScaleNAN : ScaleINF;
- number.sign = double.IsNegative(value);
- number.digits[0] = '\0';
+ number.Scale = double.IsNaN(value) ? ScaleNAN : ScaleINF;
+ number.Sign = double.IsNegative(value);
+ number.Digits[0] = '\0';
}
else if (value == 0.0)
{
- number.scale = 0;
- number.sign = double.IsNegative(value);
- number.digits[0] = '\0';
+ number.Scale = 0;
+ number.Sign = double.IsNegative(value);
+ number.Digits[0] = '\0';
}
else if (!Grisu3.Run(value, precision, ref number))
{
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)
+ private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
{
- int i = number.scale;
- if (i > Int32Precision || i < number.precision)
+ int i = number.Scale;
+ if (i > Int32Precision || i < number.Precision)
{
return false;
}
n += (*p++ - '0');
}
}
- if (number.sign)
+ if (number.Sign)
{
n = -n;
if (n > 0)
return true;
}
- private static unsafe bool NumberToInt64(ref NumberBuffer number, ref long value)
+ private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value)
{
- int i = number.scale;
- if (i > Int64Precision || i < number.precision)
+ int i = number.Scale;
+ if (i > Int64Precision || i < number.Precision)
{
return false;
}
n += (*p++ - '0');
}
}
- if (number.sign)
+ if (number.Sign)
{
n = -n;
if (n > 0)
return true;
}
- private static unsafe bool NumberToUInt32(ref NumberBuffer number, ref uint value)
+ private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value)
{
- int i = number.scale;
- if (i > UInt32Precision || i < number.precision || number.sign)
+ int i = number.Scale;
+ if (i > UInt32Precision || i < number.Precision || number.Sign)
{
return false;
}
return true;
}
- private static unsafe bool NumberToUInt64(ref NumberBuffer number, ref ulong value)
+ private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value)
{
- int i = number.scale;
- if (i > UInt64Precision || i < number.precision || number.sign)
+ int i = number.Scale;
+ if (i > UInt64Precision || i < number.Precision || number.Sign)
{
return false;
}
internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- if ((styles & ~NumberStyles.Integer) == 0)
+ if (!TryParseInt32(value, styles, info, out int result, out bool failureIsOverflow))
{
- // Optimized path for the common case of anything that's allowed for integer style.
- bool overflow = false;
- if (!TryParseInt32IntegerStyle(value, styles, info, out int intResult, ref overflow))
- {
- ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int32));
- }
- return intResult;
+ ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int32));
}
- if ((styles & NumberStyles.AllowHexSpecifier) != 0)
- {
- bool overflow = false;
- if (!TryParseUInt32HexNumberStyle(value, styles, out uint hexResult, ref overflow))
- {
- ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int32));
- }
- return (int)hexResult;
- }
-
- NumberBuffer number = default;
- int result = 0;
- StringToNumber(value, styles, ref number, info, false);
- if (!NumberToInt32(ref number, ref result))
- {
- ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Int32));
- }
return result;
}
internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- 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, styles, info, out long intResult, ref overflow))
- {
- ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int64));
- }
- return intResult;
- }
-
- if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+ if (!TryParseInt64(value, styles, info, out long result, out bool failureIsOverflow))
{
- bool overflow = false;
- if (!TryParseUInt64HexNumberStyle(value, styles, out ulong hexResult, ref overflow))
- {
- ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int64));
- }
- return (long)hexResult;
+ ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int64));
}
- NumberBuffer number = default;
- long result = 0;
- StringToNumber(value, styles, ref number, info, false);
- if (!NumberToInt64(ref number, ref result))
- {
- ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Int64));
- }
return result;
}
internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- uint result = 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, styles, info, out result, ref overflow))
- {
- ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt32));
- }
- return result;
- }
-
- if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+ if (!TryParseUInt32(value, styles, info, out uint result, out bool failureIsOverflow))
{
- bool overflow = false;
- if (!TryParseUInt32HexNumberStyle(value, styles, out result, ref overflow))
- {
- ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt32));
- }
- return result;
+ ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt32));
}
- NumberBuffer number = default;
- StringToNumber(value, styles, ref number, info, false);
- if (!NumberToUInt32(ref number, ref result))
- {
- ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_UInt32));
- }
return result;
}
internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- ulong result = 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, styles, info, out result, ref overflow))
- {
- ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt64));
- }
- return result;
- }
-
- if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+ if (!TryParseUInt64(value, styles, info, out ulong result, out bool failureIsOverflow))
{
- bool overflow = false;
- if (!TryParseUInt64HexNumberStyle(value, styles, out result, ref overflow))
- {
- ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt64));
- }
- return result;
+ ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt64));
}
- NumberBuffer number = default;
- StringToNumber(value, styles, ref number, info, false);
- if (!NumberToUInt64(ref number, ref result))
- {
- ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_UInt64));
- }
return result;
}
- private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
+ private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
{
Debug.Assert(str != null);
Debug.Assert(strEnd != null);
const int StateDecimal = 0x0010;
const int StateCurrency = 0x0020;
- number.scale = 0;
- number.sign = false;
+ number.Scale = 0;
+ number.Sign = false;
string decSep; // decimal separator from NumberFormatInfo.
string groupSep; // group separator from NumberFormatInfo.
string currSymbol = null; // currency symbol from NumberFormatInfo.
// "-Kr 1231.47" is legal but "- 1231.47" is not.
if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
{
- 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))))
+ 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 == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
{
state |= StateSign | StateParens;
- number.sign = true;
+ number.Sign = true;
}
else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
{
}
ch = ++p < strEnd ? *p : '\0';
}
+
int digCount = 0;
int digEnd = 0;
+ int maxDigCount = number.Digits.Length - 1;
+
while (true)
{
if (IsDigit(ch))
if (ch != '0' || (state & StateNonZero) != 0)
{
- if (digCount < NumberMaxDigits)
+ if (digCount < maxDigCount)
{
- number.digits[digCount++] = ch;
- if (ch != '0' || parseDecimal)
+ number.Digits[digCount++] = ch;
+ if (ch != '0' || number.Kind == NumberBufferKind.Decimal)
{
digEnd = digCount;
}
}
if ((state & StateDecimal) == 0)
{
- number.scale++;
+ number.Scale++;
}
state |= StateNonZero;
}
else if ((state & StateDecimal) != 0)
{
- number.scale--;
+ number.Scale--;
}
}
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))
}
bool negExp = false;
- number.precision = digEnd;
- number.digits[digEnd] = '\0';
+ number.Precision = digEnd;
+ number.Digits[digEnd] = '\0';
if ((state & StateDigits) != 0)
{
if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
{
exp = -exp;
}
- number.scale += exp;
+ number.Scale += exp;
}
else
{
{
if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
{
- 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))))
+ 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;
{
if ((state & StateNonZero) == 0)
{
- if (!parseDecimal)
+ if (number.Kind != NumberBufferKind.Decimal)
{
- number.scale = 0;
+ number.Scale = 0;
}
if ((state & StateDecimal) == 0)
{
- number.sign = false;
+ number.Sign = false;
}
}
str = p;
return false;
}
- internal static bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
+ internal static unsafe bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, out bool failureIsOverflow)
{
+ result = 0;
+ failureIsOverflow = false;
+
if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- bool overflow = false;
- return TryParseInt32IntegerStyle(value, styles, info, out result, ref overflow);
+ return TryParseInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
}
- result = 0;
-
if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- bool overflow = false;
- return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result), ref overflow);
+ return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result), ref failureIsOverflow);
+ }
+
+ char* pDigits = stackalloc char[Int32NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
+ if (!TryStringToNumber(value, styles, ref number, info))
+ {
+ return false;
+ }
+
+ if (!TryNumberToInt32(ref number, ref result))
+ {
+ failureIsOverflow = true;
+ return false;
}
- NumberBuffer number = default;
- return
- TryStringToNumber(value, styles, ref number, info, false) &&
- NumberToInt32(ref number, ref result);
+ return true;
}
/// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
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) goto FalseExit;
+ if ((uint)value.Length < 1)
+ goto FalseExit;
bool overflow = false;
int sign = 1;
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
while (IsWhite(num));
{
sign = -1;
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
else if (num == '+')
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
}
if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
sign = -1;
index += negativeSign.Length;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
}
}
-
+
int answer = 0;
if (IsDigit(num))
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
} while (num == '0');
- if (!IsDigit(num)) goto HasTrailingChars;
+ 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)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
- if (!IsDigit(num)) goto HasTrailingChars;
+ if (!IsDigit(num))
+ goto HasTrailingChars;
index++;
answer = 10 * answer + num - '0';
}
// Potential overflow now processing the 10th digit.
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
- if (!IsDigit(num)) goto HasTrailingChars;
+ if (!IsDigit(num))
+ goto HasTrailingChars;
index++;
if (answer > int.MaxValue / 10)
{
{
overflow = true;
}
- if ((uint)index >= (uint)value.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.
}
goto HasTrailingChars;
}
-
+
FalseExit: // parsing failed
result = 0;
return false;
// 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 ((styles & NumberStyles.AllowTrailingWhite) == 0)
+ goto FalseExit;
for (index++; index < value.Length; index++)
{
- if (!IsWhite(value[index])) break;
+ if (!IsWhite(value[index]))
+ break;
}
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(value, index)) goto FalseExit;
-
+ if (!TrailingZeros(value, index))
+ goto FalseExit;
+
goto DoneAtEndButPotentialOverflow;
}
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) goto FalseExit;
+ if ((uint)value.Length < 1)
+ goto FalseExit;
bool overflow = false;
int sign = 1;
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
while (IsWhite(num));
{
sign = -1;
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
else if (num == '+')
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
}
if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
sign = -1;
index += negativeSign.Length;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
}
}
-
+
long answer = 0;
if (IsDigit(num))
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
} while (num == '0');
- if (!IsDigit(num)) goto HasTrailingChars;
+ if (!IsDigit(num))
+ goto HasTrailingChars;
}
-
+
// 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)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
- if (!IsDigit(num)) goto HasTrailingChars;
+ if (!IsDigit(num))
+ goto HasTrailingChars;
index++;
answer = 10 * answer + num - '0';
}
// Potential overflow now processing the 19th digit.
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
- if (!IsDigit(num)) goto HasTrailingChars;
+ if (!IsDigit(num))
+ goto HasTrailingChars;
index++;
if (answer > long.MaxValue / 10)
{
{
overflow = true;
}
- if ((uint)index >= (uint)value.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.
{
overflow = true;
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
}
goto HasTrailingChars;
}
-
+
FalseExit: // parsing failed
result = 0;
return false;
// 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 ((styles & NumberStyles.AllowTrailingWhite) == 0)
+ goto FalseExit;
for (index++; index < value.Length; index++)
{
- if (!IsWhite(value[index])) break;
+ if (!IsWhite(value[index]))
+ break;
}
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(value, index)) goto FalseExit;
-
+ if (!TrailingZeros(value, index))
+ goto FalseExit;
+
goto DoneAtEndButPotentialOverflow;
}
- internal static bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
+ internal static unsafe bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, out bool failureIsOverflow)
{
+ result = 0;
+ failureIsOverflow = false;
+
if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- bool overflow = false;
- return TryParseInt64IntegerStyle(value, styles, info, out result, ref overflow);
+ return TryParseInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
}
- result = 0;
-
if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- bool overflow = false;
- return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result), ref overflow);
+ return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result), ref failureIsOverflow);
+ }
+
+ char* pDigits = stackalloc char[Int64NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
+ if (!TryStringToNumber(value, styles, ref number, info))
+ {
+ return false;
}
- NumberBuffer number = default;
- return
- TryStringToNumber(value, styles, ref number, info, false) &&
- NumberToInt64(ref number, ref result);
+ if (!TryNumberToInt64(ref number, ref result))
+ {
+ failureIsOverflow = true;
+ return false;
+ }
+
+ return true;
}
- internal static bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+ internal static unsafe bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, out bool failureIsOverflow)
{
+ result = 0;
+ failureIsOverflow = false;
+
if ((styles & ~NumberStyles.Integer) == 0)
{
// Optimized path for the common case of anything that's allowed for integer style.
- bool overflow = false;
- return TryParseUInt32IntegerStyle(value, styles, info, out result, ref overflow);
+ return TryParseUInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
}
if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- bool overflow = false;
- return TryParseUInt32HexNumberStyle(value, styles, out result, ref overflow);
+ return TryParseUInt32HexNumberStyle(value, styles, out result, ref failureIsOverflow);
}
- NumberBuffer number = default;
- result = 0;
- return
- TryStringToNumber(value, styles, ref number, info, false) &&
- NumberToUInt32(ref number, ref result);
+ char* pDigits = stackalloc char[UInt32NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
+ if (!TryStringToNumber(value, styles, ref number, info))
+ {
+ return false;
+ }
+
+
+ if (!TryNumberToUInt32(ref number, ref result))
+ {
+ failureIsOverflow = true;
+ return false;
+ }
+
+ return true;
}
/// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
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) goto FalseExit;
+ if ((uint)value.Length < 1)
+ goto FalseExit;
bool overflow = false;
bool hasNegativeSign = false;
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
while (IsWhite(num));
if (num == '+')
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
else if (num == '-')
{
hasNegativeSign = true;
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
}
if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
hasNegativeSign = true;
index += negativeSign.Length;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
}
}
-
+
int answer = 0;
if (IsDigit(num))
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
} while (num == '0');
- if (!IsDigit(num)) goto HasTrailingChars;
+ 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)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
- if (!IsDigit(num)) goto HasTrailingChars;
+ if (!IsDigit(num))
+ goto HasTrailingChars;
index++;
answer = 10 * answer + num - '0';
}
// Potential overflow now processing the 10th digit.
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
- if (!IsDigit(num)) goto HasTrailingChars;
+ 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)value.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.
{
overflow = true;
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
}
goto HasTrailingChars;
}
-
+
FalseExit: // parsing failed
result = 0;
return false;
// 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 ((styles & NumberStyles.AllowTrailingWhite) == 0)
+ goto FalseExit;
for (index++; index < value.Length; index++)
{
- if (!IsWhite(value[index])) break;
+ if (!IsWhite(value[index]))
+ break;
}
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(value, index)) goto FalseExit;
-
+ if (!TrailingZeros(value, index))
+ goto FalseExit;
+
goto DoneAtEndButPotentialOverflow;
}
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;
+ if ((uint)value.Length < 1)
+ goto FalseExit;
bool overflow = false;
int index = 0;
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
while (IsWhite(num));
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+ 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;
+ 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;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEnd;
num = value[index];
- if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+ 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;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEnd;
num = value[index];
- if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+ if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
+ goto HasTrailingChars;
index++;
overflow = true;
- if ((uint)index >= (uint)value.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. Read through any remaining digits.
while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
{
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
}
goto HasTrailingChars;
}
-
+
FalseExit: // parsing failed
result = 0;
return false;
// 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 ((styles & NumberStyles.AllowTrailingWhite) == 0)
+ goto FalseExit;
for (index++; index < value.Length; index++)
{
- if (!IsWhite(value[index])) break;
+ if (!IsWhite(value[index]))
+ break;
}
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(value, index)) goto FalseExit;
-
+ if (!TrailingZeros(value, index))
+ goto FalseExit;
+
goto DoneAtEndButPotentialOverflow;
}
-
- internal static bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+
+ internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, out bool failureIsOverflow)
{
+ result = 0;
+ failureIsOverflow = false;
+
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);
+ return TryParseUInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
}
if ((styles & NumberStyles.AllowHexSpecifier) != 0)
{
- bool overflow = false;
- return TryParseUInt64HexNumberStyle(value, styles, out result, ref overflow);
+ return TryParseUInt64HexNumberStyle(value, styles, out result, ref failureIsOverflow);
}
- NumberBuffer number = default;
- result = 0;
- return
- TryStringToNumber(value, styles, ref number, info, false) &&
- NumberToUInt64(ref number, ref result);
+ char* pDigits = stackalloc char[UInt64NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+
+ if (!TryStringToNumber(value, styles, ref number, info))
+ {
+ return false;
+ }
+
+
+ if (!TryNumberToUInt64(ref number, ref result))
+ {
+ failureIsOverflow = true;
+ return false;
+ }
+
+ return true;
}
/// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
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) goto FalseExit;
+ if ((uint)value.Length < 1)
+ goto FalseExit;
bool overflow = false;
bool hasNegativeSign = false;
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
while (IsWhite(num));
if (num == '+')
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
else if (num == '-')
{
hasNegativeSign = true;
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
}
if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
{
index += positiveSign.Length;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
{
hasNegativeSign = true;
index += negativeSign.Length;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
}
}
-
+
long answer = 0;
if (IsDigit(num))
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
} while (num == '0');
- if (!IsDigit(num)) goto HasTrailingChars;
+ 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)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
- if (!IsDigit(num)) goto HasTrailingChars;
+ if (!IsDigit(num))
+ goto HasTrailingChars;
index++;
answer = 10 * answer + num - '0';
}
// Potential overflow now processing the 20th digit.
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
- if (!IsDigit(num)) goto HasTrailingChars;
+ 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)value.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.
{
overflow = true;
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
}
goto HasTrailingChars;
}
-
+
FalseExit: // parsing failed
result = 0;
return false;
// 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 ((styles & NumberStyles.AllowTrailingWhite) == 0)
+ goto FalseExit;
for (index++; index < value.Length; index++)
{
- if (!IsWhite(value[index])) break;
+ if (!IsWhite(value[index]))
+ break;
}
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(value, index)) goto FalseExit;
-
+ if (!TrailingZeros(value, index))
+ goto FalseExit;
+
goto DoneAtEndButPotentialOverflow;
}
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;
+ if ((uint)value.Length < 1)
+ goto FalseExit;
bool overflow = false;
int index = 0;
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto FalseExit;
+ if ((uint)index >= (uint)value.Length)
+ goto FalseExit;
num = value[index];
}
while (IsWhite(num));
do
{
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+ 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;
+ 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;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEnd;
num = value[index];
- if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+ 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;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEnd;
num = value[index];
- if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+ if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
+ goto HasTrailingChars;
index++;
overflow = true;
- if ((uint)index >= (uint)value.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. Read through any remaining digits.
while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
{
index++;
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
num = value[index];
}
goto HasTrailingChars;
}
-
+
FalseExit: // parsing failed
result = 0;
return false;
// 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 ((styles & NumberStyles.AllowTrailingWhite) == 0)
+ goto FalseExit;
for (index++; index < value.Length; index++)
{
- if (!IsWhite(value[index])) break;
+ if (!IsWhite(value[index]))
+ break;
}
- if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+ if ((uint)index >= (uint)value.Length)
+ goto DoneAtEndButPotentialOverflow;
}
- if (!TrailingZeros(value, index)) goto FalseExit;
-
+ 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, styles, ref number, info, true);
-
- if (!NumberBufferToDecimal(ref number, ref result))
+ if (!TryParseDecimal(value, styles, info, out decimal result, out bool failureIsOverflow))
{
- ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Decimal));
+ ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Decimal));
}
+
return result;
}
- private static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value)
+ private static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
{
char* p = number.GetDigitsPointer();
- int e = number.scale;
- bool sign = number.sign;
+ int e = number.Scale;
+ bool sign = number.Sign;
uint c = *p;
if (c == 0)
{
internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- NumberBuffer number = default;
- double d = 0;
-
- if (!TryStringToNumber(value, styles, ref number, info, false))
+ if (!TryParseDouble(value, styles, info, out double result, out bool failureIsOverflow))
{
- //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(info.PositiveInfinitySymbol))
- {
- return double.PositiveInfinity;
- }
- if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
- {
- return double.NegativeInfinity;
- }
- if (sTrim.EqualsOrdinal(info.NaNSymbol))
- {
- return double.NaN;
- }
- ThrowOverflowOrFormatException(overflow: false, null);
+ ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Double));
}
- if (!NumberBufferToDouble(ref number, ref d))
- {
- ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Double));
- }
-
- return d;
+ return result;
}
internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
{
- NumberBuffer number = default;
- double d = 0;
-
- if (!TryStringToNumber(value, styles, ref number, info, false))
+ if (!TryParseSingle(value, styles, info, out float result, out bool failureIsOverflow))
{
- //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(info.PositiveInfinitySymbol))
- {
- return float.PositiveInfinity;
- }
- if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
- {
- return float.NegativeInfinity;
- }
- if (sTrim.EqualsOrdinal(info.NaNSymbol))
- {
- return float.NaN;
- }
- ThrowOverflowOrFormatException(overflow: false, null);
+ ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Single));
}
- if (!NumberBufferToDouble(ref number, ref d))
- {
- ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Single));
- }
- float castSingle = (float)d;
- if (float.IsInfinity(castSingle))
- {
- ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Single));
- }
- return castSingle;
+ return result;
}
- internal static bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
+ internal static unsafe bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result, out bool failureIsOverflow)
{
- NumberBuffer number = default;
+ char* pDigits = stackalloc char[DecimalNumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
+
result = 0;
+ failureIsOverflow = false;
- if (!TryStringToNumber(value, styles, ref number, info, true))
+ if (!TryStringToNumber(value, styles, ref number, info))
{
return false;
}
- if (!NumberBufferToDecimal(ref number, ref result))
+ if (!TryNumberToDecimal(ref number, ref result))
{
+ failureIsOverflow = true;
return false;
}
+
return true;
}
- internal static bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
+ internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result, out bool failureIsOverflow)
{
- NumberBuffer number = default;
+ char* pDigits = stackalloc char[DoubleNumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Double, pDigits, DoubleNumberBufferLength);
+
result = 0;
+ failureIsOverflow = false;
- if (!TryStringToNumber(value, styles, ref number, info, false))
+ if (!TryStringToNumber(value, styles, ref number, info))
{
- return false;
+ ReadOnlySpan<char> valueTrim = value.Trim();
+
+ if (valueTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
+ {
+ result = double.PositiveInfinity;
+ }
+ else if (valueTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
+ {
+ result = double.NegativeInfinity;
+ }
+ else if (valueTrim.EqualsOrdinal(info.NaNSymbol))
+ {
+ result = double.NaN;
+ }
+ else
+ {
+ return false; // We really failed
+ }
+
+ return true;
}
- if (!NumberBufferToDouble(ref number, ref result))
+
+ if (!TryNumberToDouble(ref number, ref result))
{
+ failureIsOverflow = true;
return false;
}
+
return true;
}
- internal static bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
+ internal static bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result, out bool failureIsOverflow)
{
- NumberBuffer number = default;
result = 0;
- double d = 0;
- if (!TryStringToNumber(value, styles, ref number, info, false))
- {
- return false;
- }
- if (!NumberBufferToDouble(ref number, ref d))
+ if (!TryParseDouble(value, styles, info, out double doubleResult, out failureIsOverflow))
{
return false;
}
- float castSingle = (float)d;
- if (float.IsInfinity(castSingle))
+
+ float singleResult = (float)(doubleResult);
+
+ if (float.IsInfinity(singleResult) && double.IsFinite(doubleResult))
{
+ failureIsOverflow = true;
return false;
}
- result = castSingle;
+ result = singleResult;
return true;
}
- private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
+ private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
{
- Debug.Assert(info != null);
- fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
+ if (!TryStringToNumber(value, styles, ref number, info))
{
- char* p = stringPointer;
- if (!ParseNumber(ref p, p + value.Length, styles, ref number, info, parseDecimal)
- || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
- {
- ThrowOverflowOrFormatException(overflow: false, null);
- }
+ ThrowOverflowOrFormatException(overflow: false, null);
}
}
- internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
+ internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
{
Debug.Assert(info != null);
fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
{
char* p = stringPointer;
- if (!ParseNumber(ref p, p + value.Length, styles, ref number, info, parseDecimal)
+ if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
|| (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
{
return false;
}
p++;
str++;
- if (*str == '\0') return p;
+ if (*str == '\0')
+ return p;
}
}
}
(Exception)new FormatException(SR.Format_InvalidString);
}
- private static bool NumberBufferToDouble(ref NumberBuffer number, ref double value)
+ private static bool TryNumberToDouble(ref NumberBuffer number, ref double value)
{
double d = NumberToDouble(ref number);
if (!double.IsFinite(d))