<data name="Argument_MethodRedefined" xml:space="preserve">
<value>Method has been already defined.</value>
</data>
-</root>
+ <data name="Argument_CannotParsePrecision" xml:space="preserve">
+ <value>Characters following the format symbol must be a number of {0} or less.</value>
+ </data>
+ <data name="Argument_GWithPrecisionNotSupported" xml:space="preserve">
+ <value>The 'G' format combined with a precision is not supported.</value>
+ </data>
+ <data name="Argument_PrecisionTooLarge" xml:space="preserve">
+ <value>Precision cannot be larger than {0}.</value>
+ </data>
+</root>
\ No newline at end of file
// multiple times for different instantiation.
//
+using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
throw GetArraySegmentCtorValidationFailedException(array, offset, count);
}
+ internal static void ThrowFormatException_BadFormatSpecifier()
+ {
+ throw CreateFormatException_BadFormatSpecifier();
+ }
+
+ private static Exception CreateFormatException_BadFormatSpecifier()
+ {
+ return new FormatException(SR.Argument_BadFormatSpecifier);
+ }
+
+ internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge()
+ {
+ throw CreateArgumentOutOfRangeException_PrecisionTooLarge();
+ }
+
+ private static Exception CreateArgumentOutOfRangeException_PrecisionTooLarge()
+ {
+ return new ArgumentOutOfRangeException("precision", SR.Format(SR.Argument_PrecisionTooLarge, StandardFormat.MaxPrecision));
+ }
+
+ internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit()
+ {
+ throw CreateArgumentOutOfRangeException_SymbolDoesNotFit();
+ }
+
+ private static Exception CreateArgumentOutOfRangeException_SymbolDoesNotFit()
+ {
+ return new ArgumentOutOfRangeException("symbol", SR.Argument_BadFormatSpecifier);
+ }
+
private static Exception GetArraySegmentCtorValidationFailedException(Array array, int offset, int count)
{
if (array == null)
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\IPinnable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryHandle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryManager.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\StandardFormat.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\TlsOverPerCoreLockedStacksArrayPool.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Utilities.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\Reader.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\WriterBigEndian.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Binary\WriterLittleEndian.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\FormattingHelpers.CountDigits.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Constants.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\FormattingHelpers.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Boolean.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.G.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.L.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.O.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.R.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Decimal.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Decimal.E.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Decimal.F.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Decimal.G.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Float.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Guid.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Signed.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Signed.D.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Signed.Default.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Signed.N.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Unsigned.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Unsigned.D.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Unsigned.Default.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Unsigned.N.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Integer.Unsigned.X.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.TimeSpan.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\ParserHelpers.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Boolean.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Date.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Date.Default.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Date.G.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Date.Helpers.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Date.O.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Date.R.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Decimal.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Float.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Guid.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Integer.Signed.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Integer.Signed.D.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Integer.Signed.N.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Integer.Unsigned.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Integer.Unsigned.D.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Integer.Unsigned.N.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Integer.Unsigned.X.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.Number.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.TimeSpan.BigG.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.TimeSpan.C.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.TimeSpan.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.TimeSpan.LittleG.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Parser\Utf8Parser.TimeSpanSplitter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Byte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Char.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\CharEnumerator.cs" />
}
#endregion Math Helper methods
+
+ //
+ // Enable use of ThrowHelper from TryFormat() routines without introducing dozens of non-code-coveraged "bytesWritten = 0; return false" boilerplate.
+ //
+ public static bool TryFormatThrowFormatException(out int bytesWritten)
+ {
+ bytesWritten = 0;
+ ThrowHelper.ThrowFormatException_BadFormatSpecifier();
+ return false;
+ }
}
}
return false;
BadFormat:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
}
}
return TryFormatDateTimeG(value.DateTime, offset, destination, out bytesWritten);
default:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
}
return TryFormatDateTimeG(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten);
default:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
}
}
{
public static partial class Utf8Formatter
{
- private static bool TryFormatDecimalE(ref NumberBuffer number, Span<byte> destination, out int bytesWritten, byte precision, byte exponentSymbol)
+ private static bool TryFormatDecimalE(ref Number.NumberBuffer number, Span<byte> destination, out int bytesWritten, byte precision, byte exponentSymbol)
{
const int NumExponentDigits = 3;
exponent = -exponent;
}
- Debug.Assert(exponent < Number.DECIMAL_PRECISION, "If you're trying to reuse this routine for double/float, you'll need to review the code carefully for Decimal-specific assumptions.");
+ Debug.Assert(exponent < Number.DecimalPrecision, "If you're trying to reuse this routine for double/float, you'll need to review the code carefully for Decimal-specific assumptions.");
// Emit exactly three digits for the exponent.
destination[dstIndex++] = (byte)'0'; // The exponent for Decimal can never exceed 28 (let alone 99)
{
public static partial class Utf8Formatter
{
- private static bool TryFormatDecimalF(ref NumberBuffer number, Span<byte> destination, out int bytesWritten, byte precision)
+ private static bool TryFormatDecimalF(ref Number.NumberBuffer number, Span<byte> destination, out int bytesWritten, byte precision)
{
int scale = number.Scale;
ReadOnlySpan<byte> digits = number.Digits;
{
public static partial class Utf8Formatter
{
- private static bool TryFormatDecimalG(ref NumberBuffer number, Span<byte> destination, out int bytesWritten)
+ private static bool TryFormatDecimalG(ref Number.NumberBuffer number, Span<byte> destination, out int bytesWritten)
{
int scale = number.Scale;
ReadOnlySpan<byte> digits = number.Digits;
- int numDigits = number.NumDigits;
+ int numDigits = number.DigitsCount;
bool isFraction = scale < numDigits;
int numBytesNeeded;
/// <exceptions>
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
- public static bool TryFormat(decimal value, Span<byte> destination, out int bytesWritten, StandardFormat format = default)
+ public static unsafe bool TryFormat(decimal value, Span<byte> destination, out int bytesWritten, StandardFormat format = default)
{
if (format.IsDefault)
{
{
if (format.Precision != StandardFormat.NoPrecision)
throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported);
- NumberBuffer number = default;
- Number.DecimalToNumber(value, ref number);
+
+ byte* pDigits = stackalloc byte[Number.DecimalNumberBufferLength];
+ Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength);
+
+ Number.DecimalToNumber(ref value, ref number);
bool success = TryFormatDecimalG(ref number, destination, out bytesWritten);
#if DEBUG
// This DEBUG segment exists to close a code coverage hole inside TryFormatDecimalG(). Because we don't call RoundNumber() on this path, we have no way to feed
if (success)
{
Span<byte> digits = number.Digits;
- int numDigits = number.NumDigits;
+ int numDigits = number.DigitsCount;
if (numDigits != 0 && number.Scale == numDigits && digits[numDigits - 1] == '0')
{
while (numDigits != 0 && digits[numDigits - 1] == '0')
numDigits--;
}
+ number.DigitsCount = numDigits;
number.CheckConsistency();
byte[] buffer2 = new byte[destination.Length];
case 'f':
case 'F':
{
- NumberBuffer number = default;
- Number.DecimalToNumber(value, ref number);
+ byte* pDigits = stackalloc byte[Number.DecimalNumberBufferLength];
+ Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength);
+
+ Number.DecimalToNumber(ref value, ref number);
byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)2 : format.Precision;
Number.RoundNumber(ref number, number.Scale + precision);
return TryFormatDecimalF(ref number, destination, out bytesWritten, precision);
case 'e':
case 'E':
{
- NumberBuffer number = default;
- Number.DecimalToNumber(value, ref number);
+ byte* pDigits = stackalloc byte[Number.DecimalNumberBufferLength];
+ Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength);
+
+ Number.DecimalToNumber(ref value, ref number);
byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)6 : format.Precision;
Number.RoundNumber(ref number, precision + 1);
return TryFormatDecimalE(ref number, destination, out bytesWritten, precision, exponentSymbol: (byte)format.Symbol);
}
default:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
}
}
break;
default:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
string formatString = format.ToString();
break;
default:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
// At this point, the low byte of flags contains the minimum required length
return TryFormatUInt64X((ulong)value & mask, format.Precision, false, destination, out bytesWritten);
default:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
}
}
return TryFormatUInt64X(value, format.Precision, false /* useLower */, destination, out bytesWritten);
default:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
}
}
break;
default:
- return ThrowHelper.TryFormatThrowFormatException(out bytesWritten);
+ return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten);
}
// First, calculate how large an output buffer is needed to hold the entire output.
{
return (uint)(i - '0') <= ('9' - '0');
}
+
+ //
+ // Enable use of ThrowHelper from TryParse() routines without introducing dozens of non-code-coveraged "value= default; bytesConsumed = 0; return false" boilerplate.
+ //
+ public static bool TryParseThrowFormatException(out int bytesConsumed)
+ {
+ bytesConsumed = 0;
+ ThrowHelper.ThrowFormatException_BadFormatSpecifier();
+ return false;
+ }
+
+ //
+ // Enable use of ThrowHelper from TryParse() routines without introducing dozens of non-code-coveraged "value= default; bytesConsumed = 0; return false" boilerplate.
+ //
+ public static bool TryParseThrowFormatException<T>(out T value, out int bytesConsumed)
+ {
+ value = default;
+ return TryParseThrowFormatException(out bytesConsumed);
+ }
}
}
public static bool TryParse(ReadOnlySpan<byte> source, out bool value, out int bytesConsumed, char standardFormat = default)
{
if (!(standardFormat == default(char) || standardFormat == 'G' || standardFormat == 'l'))
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
if (source.Length >= 4)
{
return TryParseDateTimeG(source, out value, out _, out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return TryParseDateTimeG(source, out DateTime _, out value, out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
/// <exceptions>
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
- public static bool TryParse(ReadOnlySpan<byte> source, out decimal value, out int bytesConsumed, char standardFormat = default)
+ public static unsafe bool TryParse(ReadOnlySpan<byte> source, out decimal value, out int bytesConsumed, char standardFormat = default)
{
ParseNumberOptions options;
switch (standardFormat)
break;
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
- NumberBuffer number = default;
+ byte* pDigits = stackalloc byte[Number.DecimalNumberBufferLength];
+ Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength);
+
if (!TryParseNumber(source, ref number, out bytesConsumed, options, out bool textUsedExponentNotation))
{
value = default;
}
value = default;
- if (!Number.NumberBufferToDecimal(ref number, ref value))
+ if (!Number.TryNumberToDecimal(ref number, ref value))
{
value = default;
bytesConsumed = 0;
/// <exceptions>
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
- public static bool TryParse(ReadOnlySpan<byte> source, out float value, out int bytesConsumed, char standardFormat = default)
+ public static unsafe bool TryParse(ReadOnlySpan<byte> source, out float value, out int bytesConsumed, char standardFormat = default)
{
- if (TryParseNormalAsFloatingPoint(source, out double d, out bytesConsumed, standardFormat))
+ byte* pDigits = stackalloc byte[Number.SingleNumberBufferLength];
+ Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.FloatingPoint, pDigits, Number.SingleNumberBufferLength);
+
+ if (TryParseNormalAsFloatingPoint(source, ref number, out bytesConsumed, standardFormat))
{
- value = (float)(d);
+ value = Number.NumberToSingle(ref number);
return true;
}
/// <exceptions>
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
- public static bool TryParse(ReadOnlySpan<byte> source, out double value, out int bytesConsumed, char standardFormat = default)
+ public static unsafe bool TryParse(ReadOnlySpan<byte> source, out double value, out int bytesConsumed, char standardFormat = default)
{
- if (TryParseNormalAsFloatingPoint(source, out value, out bytesConsumed, standardFormat))
+ byte* pDigits = stackalloc byte[Number.DoubleNumberBufferLength];
+ Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.FloatingPoint, pDigits, Number.DoubleNumberBufferLength);
+
+ if (TryParseNormalAsFloatingPoint(source, ref number, out bytesConsumed, standardFormat))
+ {
+ value = Number.NumberToDouble(ref number);
return true;
+ }
return TryParseAsSpecialFloatingPoint(source, double.PositiveInfinity, double.NegativeInfinity, double.NaN, out value, out bytesConsumed);
}
//
// Attempt to parse the regular floating points (the ones without names like "Infinity" and "NaN")
//
- private static bool TryParseNormalAsFloatingPoint(ReadOnlySpan<byte> source, out double value, out int bytesConsumed, char standardFormat)
+ private static bool TryParseNormalAsFloatingPoint(ReadOnlySpan<byte> source, ref Number.NumberBuffer number, out int bytesConsumed, char standardFormat)
{
ParseNumberOptions options;
switch (standardFormat)
case 'e':
options = ParseNumberOptions.AllowExponent;
break;
-
case 'F':
case 'f':
options = default;
break;
-
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out bytesConsumed);
}
-
- NumberBuffer number = default;
if (!TryParseNumber(source, ref number, out bytesConsumed, options, out bool textUsedExponentNotation))
{
- value = default;
return false;
}
-
if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e'))
{
- value = default;
bytesConsumed = 0;
return false;
}
-
- value = Number.NumberBufferToDouble(ref number);
return true;
}
case 'N':
return TryParseGuidN(source, out value, out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return TryParseByteX(source, out Unsafe.As<sbyte, byte>(ref value), out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return TryParseUInt16X(source, out Unsafe.As<short, ushort>(ref value), out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return TryParseUInt32X(source, out Unsafe.As<int, uint>(ref value), out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return TryParseUInt64X(source, out Unsafe.As<long, ulong>(ref value), out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
}
return TryParseByteX(source, out value, out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return TryParseUInt16X(source, out value, out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return TryParseUInt32X(source, out value, out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return TryParseUInt64X(source, out value, out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
}
AllowExponent = 0x00000001,
}
- private static bool TryParseNumber(ReadOnlySpan<byte> source, ref NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation)
+ private static bool TryParseNumber(ReadOnlySpan<byte> source, ref Number.NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation)
{
- Debug.Assert(number.Digits[0] == 0 && number.Scale == 0 && !number.IsNegative, "Number not initialized to default(NumberBuffer)");
+ Debug.Assert(number.DigitsCount == 0);
+ Debug.Assert(number.Scale == 0);
+ Debug.Assert(number.IsNegative == false);
+ Debug.Assert(number.HasNonZeroTail == false);
+ number.CheckConsistency();
textUsedExponentNotation = false;
if (source.Length == 0)
}
int startIndexDigitsBeforeDecimal = srcIndex;
+ int digitCount = 0;
+ int maxDigitCount = digits.Length - 1;
// Throw away any leading zeroes
while (srcIndex != source.Length)
if (srcIndex == source.Length)
{
- digits[0] = 0;
- number.Scale = 0;
number.IsNegative = false;
bytesConsumed = srcIndex;
number.CheckConsistency();
}
int startIndexNonLeadingDigitsBeforeDecimal = srcIndex;
+
+ int hasNonZeroTail = 0;
while (srcIndex != source.Length)
{
c = source[srcIndex];
- if ((c - 48u) > 9)
+
+ if ((c -= (byte)('0')) > 9)
+ {
break;
+ }
+
srcIndex++;
+ digitCount++;
+
+ if (digitCount >= maxDigitCount)
+ {
+ // For decimal and binary floating-point numbers, we only
+ // need to store digits up to maxDigCount. However, we still
+ // need to keep track of whether any additional digits past
+ // maxDigCount were non-zero, as that can impact rounding
+ // for an input that falls evenly between two representable
+ // results.
+
+ hasNonZeroTail |= c;
+ }
}
+ number.HasNonZeroTail = (hasNonZeroTail != 0);
int numDigitsBeforeDecimal = srcIndex - startIndexDigitsBeforeDecimal;
int numNonLeadingDigitsBeforeDecimal = srcIndex - startIndexNonLeadingDigitsBeforeDecimal;
Debug.Assert(dstIndex == 0);
- int numNonLeadingDigitsBeforeDecimalToCopy = Math.Min(numNonLeadingDigitsBeforeDecimal, NumberBuffer.BufferSize - 1);
+ int numNonLeadingDigitsBeforeDecimalToCopy = Math.Min(numNonLeadingDigitsBeforeDecimal, maxDigitCount);
source.Slice(startIndexNonLeadingDigitsBeforeDecimal, numNonLeadingDigitsBeforeDecimalToCopy).CopyTo(digits);
dstIndex = numNonLeadingDigitsBeforeDecimalToCopy;
number.Scale = numNonLeadingDigitsBeforeDecimal;
if (srcIndex == source.Length)
{
+ digits[dstIndex] = 0;
+ number.DigitsCount = dstIndex;
bytesConsumed = srcIndex;
number.CheckConsistency();
return true;
srcIndex++;
int startIndexDigitsAfterDecimal = srcIndex;
+
while (srcIndex != source.Length)
{
c = source[srcIndex];
- if ((c - 48u) > 9)
+
+ if ((c -= (byte)('0')) > 9)
+ {
break;
+ }
+
srcIndex++;
+ digitCount++;
+
+ if (digitCount >= maxDigitCount)
+ {
+ // For decimal and binary floating-point numbers, we only
+ // need to store digits up to maxDigCount. However, we still
+ // need to keep track of whether any additional digits past
+ // maxDigCount were non-zero, as that can impact rounding
+ // for an input that falls evenly between two representable
+ // results.
+
+ hasNonZeroTail |= c;
+ }
}
+ number.HasNonZeroTail = (hasNonZeroTail != 0);
+
numDigitsAfterDecimal = srcIndex - startIndexDigitsAfterDecimal;
int startIndexOfDigitsAfterDecimalToCopy = startIndexDigitsAfterDecimal;
}
}
- int numDigitsAfterDecimalToCopy = Math.Min(srcIndex - startIndexOfDigitsAfterDecimalToCopy, NumberBuffer.BufferSize - dstIndex - 1);
+ int numDigitsAfterDecimalToCopy = Math.Min(srcIndex - startIndexOfDigitsAfterDecimalToCopy, maxDigitCount - dstIndex);
source.Slice(startIndexOfDigitsAfterDecimalToCopy, numDigitsAfterDecimalToCopy).CopyTo(digits.Slice(dstIndex));
dstIndex += numDigitsAfterDecimalToCopy;
// We "should" really NUL terminate, but there are multiple places we'd have to do this and it is a precondition that the caller pass in a fully zero=initialized Number.
return false;
}
+ digits[dstIndex] = 0;
+ number.DigitsCount = dstIndex;
bytesConsumed = srcIndex;
number.CheckConsistency();
return true;
number.IsNegative = false;
}
+ digits[dstIndex] = 0;
+ number.DigitsCount = dstIndex;
bytesConsumed = srcIndex;
number.CheckConsistency();
return true;
number.Scale += (int)absoluteExponent;
}
+ digits[dstIndex] = 0;
+ number.DigitsCount = dstIndex;
bytesConsumed = srcIndex;
number.CheckConsistency();
return true;
return TryParseTimeSpanLittleG(source, out value, out bytesConsumed);
default:
- return ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed);
+ return ParserHelpers.TryParseThrowFormatException(out value, out bytesConsumed);
}
}
return sb.TryCopyTo(destination, out charsWritten);
}
- private static unsafe void DecimalToNumber(ref decimal d, ref NumberBuffer number)
+ internal static unsafe void DecimalToNumber(ref decimal d, ref NumberBuffer number)
{
byte* buffer = number.GetDigitsPointer();
number.DigitsCount = DecimalPrecision;
}
}
- private static unsafe void RoundNumber(ref NumberBuffer number, int pos)
+ internal static unsafe void RoundNumber(ref NumberBuffer number, int pos)
{
byte* dig = number.GetDigitsPointer();
internal static partial class Number
{
// We need 1 additional byte, per length, for the terminating null
- private const int DecimalNumberBufferLength = 29 + 1 + 1; // 29 for the longest input + 1 for rounding
- private const int DoubleNumberBufferLength = 767 + 1 + 1; // 767 for the longest input + 1 for rounding: 4.9406564584124654E-324
- private const int Int32NumberBufferLength = 10 + 1; // 10 for the longest input: 2,147,483,647
- private const int Int64NumberBufferLength = 19 + 1; // 19 for the longest input: 9,223,372,036,854,775,807
- private const int SingleNumberBufferLength = 112 + 1 + 1; // 112 for the longest input + 1 for rounding: 1.40129846E-45
- private const int UInt32NumberBufferLength = 10 + 1; // 10 for the longest input: 4,294,967,295
- private const int UInt64NumberBufferLength = 20 + 1; // 20 for the longest input: 18,446,744,073,709,551,615
+ internal const int DecimalNumberBufferLength = 29 + 1 + 1; // 29 for the longest input + 1 for rounding
+ internal const int DoubleNumberBufferLength = 767 + 1 + 1; // 767 for the longest input + 1 for rounding: 4.9406564584124654E-324
+ internal const int Int32NumberBufferLength = 10 + 1; // 10 for the longest input: 2,147,483,647
+ internal const int Int64NumberBufferLength = 19 + 1; // 19 for the longest input: 9,223,372,036,854,775,807
+ internal const int SingleNumberBufferLength = 112 + 1 + 1; // 112 for the longest input + 1 for rounding: 1.40129846E-45
+ internal const int UInt32NumberBufferLength = 10 + 1; // 10 for the longest input: 4,294,967,295
+ internal const int UInt64NumberBufferLength = 20 + 1; // 20 for the longest input: 18,446,744,073,709,551,615
internal unsafe ref struct NumberBuffer
{
return result;
}
- private static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
+ internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
{
number.CheckConsistency();
(Exception)new FormatException(SR.Format_InvalidString);
}
- private static double NumberToDouble(ref NumberBuffer number)
+ internal static double NumberToDouble(ref NumberBuffer number)
{
number.CheckConsistency();
return number.IsNegative ? -result : result;
}
- private static float NumberToSingle(ref NumberBuffer number)
+ internal static float NumberToSingle(ref NumberBuffer number)
{
number.CheckConsistency();