Skip to content

Commit

Permalink
Expose IUtf8SpanParsable and implement it on the primitive numeric ty…
Browse files Browse the repository at this point in the history
…pes (#86875)

* Expose IUtf8SpanParsable

* Have INumberBase implement IUtf8SpanParsable

* Deduplicate some floating-point parsing logic

* Refactoring the primitive parsing logic to support UTF-8

* Updating the primitive numeric types to include UTF-8 parsing support

* Adding tests covering the new UTF-8 parsing support

* Ensure that tests don't try to capture a span in lambda

* Ensure that MatchChars does the right thing

* Account for the switch from string to ROSpan<TChar> for currSymbol

* Ensure EqualsIgnoreCaseUtf8_Scalar handles the remaining elements correctly

* Allow alloc-free conversion for the UTF8 parsing fallback paths

* Remove unnecessary attributes and ensure trailing elements are correctly handled

* Fix the handling of UTF8 as per the feedback

* Fix a slightly broken merge

* Ensure the lengths are properly checked on the ASCII path

* Ensure that length is defined for 32-bit

* Responding to PR feedback and allow invalid sequences to self match

* Fixing a copy/paste error

* Fixing a build failure due to not assigning the out parameter
  • Loading branch information
tannergooding committed Jul 12, 2023
1 parent 5464296 commit 1dd0fa2
Show file tree
Hide file tree
Showing 54 changed files with 3,446 additions and 948 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Icu.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Invariant.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Nls.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Utf8.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.WebAssembly.cs" Condition="'$(TargetsBrowser)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareOptions.cs" />
Expand Down Expand Up @@ -375,6 +376,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\NumberFormatInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\NumberStyles.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Ordinal.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Ordinal.Utf8.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\OrdinalCasing.Icu.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\PersianCalendar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\RegionInfo.cs" />
Expand Down Expand Up @@ -510,6 +512,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\ISpanFormattable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IUtfChar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IUtf8SpanFormattable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IUtf8SpanParsable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Lazy.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LazyOfTTMetadata.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimization.cs" />
Expand All @@ -526,7 +529,9 @@
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryDebugView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Globalization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Globalization.Utf8.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Trim.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Trim.Utf8.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MethodAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MidpointRounding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MissingFieldException.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static unsafe bool TryParse(ReadOnlySpan<byte> source, out float value, o

if (TryParseNormalAsFloatingPoint(source, ref number, out bytesConsumed, standardFormat))
{
value = Number.NumberToSingle(ref number);
value = Number.NumberToFloat<float>(ref number);
return true;
}

Expand Down Expand Up @@ -66,7 +66,7 @@ public static unsafe bool TryParse(ReadOnlySpan<byte> source, out double value,

if (TryParseNormalAsFloatingPoint(source, ref number, out bytesConsumed, standardFormat))
{
value = Number.NumberToDouble(ref number);
value = Number.NumberToFloat<double>(ref number);
return true;
}

Expand Down
36 changes: 34 additions & 2 deletions src/libraries/System.Private.CoreLib/src/System/Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,19 @@ public static byte Parse(string s, NumberStyles style, IFormatProvider? provider
public static byte Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.ParseBinaryInteger<byte>(s, style, NumberFormatInfo.GetInstance(provider));
return Number.ParseBinaryInteger<char, byte>(s, style, NumberFormatInfo.GetInstance(provider));
}

public static bool TryParse([NotNullWhen(true)] string? s, out byte result) => TryParse(s, NumberStyles.Integer, provider: null, out result);

public static bool TryParse(ReadOnlySpan<char> s, out byte result) => TryParse(s, NumberStyles.Integer, provider: null, out result);

/// <summary>Tries to convert a UTF-8 character span containing the string representation of a number to its 8-bit unsigned integer equivalent.</summary>
/// <param name="utf8Text">A span containing the UTF-8 characters representing the number to convert.</param>
/// <param name="result">When this method returns, contains the 8-bit unsigned integer value equivalent to the number contained in <paramref name="utf8Text" /> if the conversion succeeded, or zero if the conversion failed. This parameter is passed uninitialized; any value originally supplied in result will be overwritten.</param>
/// <returns><c>true</c> if <paramref name="utf8Text" /> was converted successfully; otherwise, false.</returns>
public static bool TryParse(ReadOnlySpan<byte> utf8Text, out byte result) => TryParse(utf8Text, NumberStyles.Integer, provider: null, out result);

public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
Expand All @@ -124,7 +130,7 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I
result = 0;
return false;
}
return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
}

public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out byte result)
Expand Down Expand Up @@ -1151,6 +1157,30 @@ static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [MaybeN
/// <inheritdoc cref="IUnaryPlusOperators{TSelf, TResult}.op_UnaryPlus(TSelf)" />
static byte IUnaryPlusOperators<byte, byte>.operator +(byte value) => (byte)(+value);

//
// IUtf8SpanParsable
//

/// <inheritdoc cref="INumberBase{TSelf}.Parse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?)" />
public static byte Parse(ReadOnlySpan<byte> utf8Text, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.ParseBinaryInteger<byte, byte>(utf8Text, style, NumberFormatInfo.GetInstance(provider));
}

/// <inheritdoc cref="INumberBase{TSelf}.TryParse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf)" />
public static bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out byte result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
}

/// <inheritdoc cref="IUtf8SpanParsable{TSelf}.Parse(ReadOnlySpan{byte}, IFormatProvider?)" />
public static byte Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Integer, provider);

/// <inheritdoc cref="IUtf8SpanParsable{TSelf}.TryParse(ReadOnlySpan{byte}, IFormatProvider?, out TSelf)" />
public static bool TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, out byte result) => TryParse(utf8Text, NumberStyles.Integer, provider, out result);

//
// IUtfChar
//
Expand All @@ -1161,6 +1191,8 @@ static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [MaybeN
static byte IUtfChar<byte>.CastFrom(uint value) => (byte)value;
static byte IUtfChar<byte>.CastFrom(ulong value) => (byte)value;

static uint IUtfChar<byte>.CastToUInt32(byte value) => value;

//
// IBinaryIntegerParseAndFormatInfo
//
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1993,6 +1993,8 @@ static bool INumberBase<char>.TryConvertToTruncating<TOther>(char value, [MaybeN
static char IUtfChar<char>.CastFrom(uint value) => (char)value;
static char IUtfChar<char>.CastFrom(ulong value) => (char)value;

static uint IUtfChar<char>.CastToUInt32(char value) => value;

//
// IBinaryIntegerParseAndFormatInfo
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ private static void UInt64x64To128(ulong a, ulong b, ref DecCalc result)
high++;

if (high > uint.MaxValue)
Number.ThrowOverflowException(TypeCode.Decimal);
Number.ThrowOverflowException(SR.Overflow_Decimal);
result.Low64 = low;
result.High = (uint)high;
}
Expand Down Expand Up @@ -681,7 +681,7 @@ private static unsafe int ScaleResult(Buf24* bufRes, uint hiRes, int scale)
return scale;

ThrowOverflow:
Number.ThrowOverflowException(TypeCode.Decimal);
Number.ThrowOverflowException(SR.Overflow_Decimal);
return 0;
}

Expand Down Expand Up @@ -725,7 +725,7 @@ private static unsafe uint DivByConst(uint* result, uint hiRes, out uint quotien
private static int OverflowUnscale(ref Buf12 bufQuo, int scale, bool sticky)
{
if (--scale < 0)
Number.ThrowOverflowException(TypeCode.Decimal);
Number.ThrowOverflowException(SR.Overflow_Decimal);

Debug.Assert(bufQuo.U2 == 0);

Expand Down Expand Up @@ -837,7 +837,7 @@ private static int SearchScale(ref Buf12 bufQuo, int scale)
// positive if it isn't already.
//
if (curScale + scale < 0)
Number.ThrowOverflowException(TypeCode.Decimal);
Number.ThrowOverflowException(SR.Overflow_Decimal);

return curScale;
}
Expand Down Expand Up @@ -1107,7 +1107,7 @@ internal static unsafe void DecAddSub(ref DecCalc d1, ref DecCalc d2, bool sign)
// Divide the value by 10, dropping the scale factor.
//
if ((flags & ScaleMask) == 0)
Number.ThrowOverflowException(TypeCode.Decimal);
Number.ThrowOverflowException(SR.Overflow_Decimal);
flags -= 1 << ScaleShift;

const uint den = 10;
Expand Down Expand Up @@ -1534,7 +1534,7 @@ internal static void VarDecFromR4(float input, out DecCalc result)
return; // result should be zeroed out

if (exp > 96)
Number.ThrowOverflowException(TypeCode.Decimal);
Number.ThrowOverflowException(SR.Overflow_Decimal);

uint flags = 0;
if (input < 0)
Expand Down Expand Up @@ -1701,7 +1701,7 @@ internal static void VarDecFromR8(double input, out DecCalc result)
return; // result should be zeroed out

if (exp > 96)
Number.ThrowOverflowException(TypeCode.Decimal);
Number.ThrowOverflowException(SR.Overflow_Decimal);

uint flags = 0;
if (input < 0)
Expand Down Expand Up @@ -2170,7 +2170,7 @@ internal static unsafe void VarDecDiv(ref DecCalc d1, ref DecCalc d2)
}

ThrowOverflow:
Number.ThrowOverflowException(TypeCode.Decimal);
Number.ThrowOverflowException(SR.Overflow_Decimal);
}

/// <summary>
Expand Down
Loading

0 comments on commit 1dd0fa2

Please sign in to comment.