diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index ab924f1e4b55a..b34610f2a2f41 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -23,7 +23,8 @@ public readonly struct Byte IMinMaxValue, IUnsignedNumber, IUtf8SpanFormattable, - IUtfChar + IUtfChar, + IBinaryIntegerParseAndFormatInfo { private readonly byte m_value; // Do not rename (binary serialization) @@ -92,98 +93,44 @@ public override int GetHashCode() return m_value; } - public static byte Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static byte Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static byte Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); - } + public static byte Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static byte Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static byte Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); - // Parses an unsigned byte from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. public static byte Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static byte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Parse(s, style, NumberFormatInfo.GetInstance(provider)); - } - - private static byte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) - { - Number.ParsingStatus status = Number.TryParseUInt32(s, style, info, out uint i); - if (status != Number.ParsingStatus.OK) - { - Number.ThrowOverflowOrFormatException(status, s, TypeCode.Byte); - } - - if (i > MaxValue) Number.ThrowOverflowException(TypeCode.Byte); - return (byte)i; + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out byte result) - { - if (s == null) - { - result = 0; - return false; - } + public static bool TryParse([NotNullWhen(true)] string? s, out byte result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); - } - - public static bool TryParse(ReadOnlySpan s, out byte result) - { - return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); - } + public static bool TryParse(ReadOnlySpan s, out byte result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out byte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out byte result) - { - if (Number.TryParseUInt32(s, style, info, out uint i) != Number.ParsingStatus.OK - || i > MaxValue) - { - result = 0; - return false; - } - result = (byte)i; - return true; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public override string ToString() @@ -1213,5 +1160,25 @@ static bool INumberBase.TryConvertToTruncating(byte value, [MaybeN static byte IUtfChar.CastFrom(int value) => (byte)value; static byte IUtfChar.CastFrom(uint value) => (byte)value; static byte IUtfChar.CastFrom(ulong value) => (byte)value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 3; // 255 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 2; // 0xFF + + static byte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Byte; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(byte left, byte right) => left > right; + + static byte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(byte value) => (byte)(value * 10); + + static byte IBinaryIntegerParseAndFormatInfo.MultiplyBy16(byte value) => (byte)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 643574b8fbfb1..b83d7d76f7ddb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -36,7 +36,8 @@ public readonly struct Char IMinMaxValue, IUnsignedNumber, IUtf8SpanFormattable, - IUtfChar + IUtfChar, + IBinaryIntegerParseAndFormatInfo { // // Member Variables @@ -200,29 +201,37 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static char Parse(string s) { - if (s == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - } + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan()); + } + internal static char Parse(ReadOnlySpan s) + { if (s.Length != 1) { - throw new FormatException(SR.Format_NeedSingleChar); + ThrowHelper.ThrowFormatException_NeedSingleChar(); } return s[0]; } public static bool TryParse([NotNullWhen(true)] string? s, out char result) { - result = '\0'; - if (s == null) + if (s is null) { + result = '\0'; return false; } + return TryParse(s.AsSpan(), out result); + } + + internal static bool TryParse(ReadOnlySpan s, out char result) + { if (s.Length != 1) { + result = '\0'; return false; } + result = s[0]; return true; } @@ -1508,14 +1517,7 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b static char INumberBase.Parse(string s, NumberStyles style, IFormatProvider? provider) => Parse(s); - static char INumberBase.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) - { - if (s.Length != 1) - { - throw new FormatException(SR.Format_NeedSingleChar); - } - return s[0]; - } + static char INumberBase.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) => Parse(s); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1929,16 +1931,7 @@ static bool INumberBase.TryConvertToTruncating(char value, [MaybeN static bool INumberBase.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out char result) => TryParse(s, out result); - static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out char result) - { - if (s.Length != 1) - { - result = default; - return false; - } - result = s[0]; - return true; - } + static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out char result) => TryParse(s, out result); // // IParsable @@ -1965,25 +1958,9 @@ static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, // ISpanParsable // - static char ISpanParsable.Parse(ReadOnlySpan s, IFormatProvider? provider) - { - if (s.Length != 1) - { - throw new FormatException(SR.Format_NeedSingleChar); - } - return s[0]; - } + static char ISpanParsable.Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s); - static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out char result) - { - if (s.Length != 1) - { - result = default; - return false; - } - result = s[0]; - return true; - } + static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out char result) => TryParse(s, out result); // // ISubtractionOperators @@ -2021,5 +1998,25 @@ static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? static char IUtfChar.CastFrom(int value) => (char)value; static char IUtfChar.CastFrom(uint value) => (char)value; static char IUtfChar.CastFrom(ulong value) => (char)value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 65_535 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0xFFFF + + static char IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => (char)(MaxValue / 10); + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Char; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(char left, char right) => left > right; + + static char IBinaryIntegerParseAndFormatInfo.MultiplyBy10(char value) => (char)(value * 10); + + static char IBinaryIntegerParseAndFormatInfo.MultiplyBy16(char value) => (char)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index 33a5beae402de..bb7fbaa73fffa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -781,35 +781,35 @@ static bool TryParseRareTypes(RuntimeType rt, ReadOnlySpan value, bool ign { case CorElementType.ELEMENT_TYPE_R4: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out float localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out float localResult); result = BitConverter.SingleToInt32Bits(localResult); } break; case CorElementType.ELEMENT_TYPE_R8: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out double localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out double localResult); result = BitConverter.DoubleToInt64Bits(localResult); } break; case CorElementType.ELEMENT_TYPE_I: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out nint localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out nint localResult); result = localResult; } break; case CorElementType.ELEMENT_TYPE_U: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out nuint localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out nuint localResult); result = (long)localResult; } break; case CorElementType.ELEMENT_TYPE_CHAR: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out char localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out char localResult); result = localResult; } break; @@ -901,11 +901,11 @@ private static bool TryParse(ReadOnlySpan value, bool ignoreCase, b if (underlyingType == typeof(long)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); if (underlyingType == typeof(ulong)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(nuint)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(float)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(double)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(char)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(nint)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(nuint)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(float)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(double)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(char)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); #endif throw CreateUnknownEnumTypeException(); @@ -914,8 +914,8 @@ private static bool TryParse(ReadOnlySpan value, bool ignoreCase, b /// Core implementation for all {Try}Parse methods, both generic and non-generic, parsing either by value or by name. private static unsafe bool TryParseByValueOrName( RuntimeType enumType, ReadOnlySpan value, bool ignoreCase, bool throwOnFailure, out TUnderlying result) - where TUnderlying : struct, INumber, IBitwiseOperators, IMinMaxValue - where TStorage : struct, INumber, IBitwiseOperators, IMinMaxValue + where TUnderlying : unmanaged, IBinaryIntegerParseAndFormatInfo + where TStorage : unmanaged, IBinaryIntegerParseAndFormatInfo { AssertValidGenerics(); @@ -942,89 +942,77 @@ private static unsafe bool TryParseByValueOrName( NumberFormatInfo numberFormat = CultureInfo.InvariantCulture.NumberFormat; const NumberStyles NumberStyle = NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite; - Number.ParsingStatus status; - if (typeof(TUnderlying) == typeof(int)) + Number.ParsingStatus status = Number.TryParseBinaryIntegerStyle(value, NumberStyle, numberFormat, out result); + if (status == Number.ParsingStatus.OK) { - Unsafe.SkipInit(out result); - status = Number.TryParseInt32IntegerStyle(value, NumberStyle, numberFormat, out Unsafe.As(ref result)); - if (status == Number.ParsingStatus.OK) - { - return true; - } + return true; } - else if (typeof(TUnderlying) == typeof(uint)) + + if (status != Number.ParsingStatus.Overflow) { Unsafe.SkipInit(out result); - status = Number.TryParseUInt32IntegerStyle(value, NumberStyle, numberFormat, out Unsafe.As(ref result)); - if (status == Number.ParsingStatus.OK) - { - return true; - } + return TryParseByName(enumType, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); } - else if (typeof(TUnderlying) == typeof(long)) + + if (throwOnFailure) { - Unsafe.SkipInit(out result); - status = Number.TryParseInt64IntegerStyle(value, NumberStyle, numberFormat, out Unsafe.As(ref result)); - if (status == Number.ParsingStatus.OK) + Number.ThrowOverflowException(Type.GetTypeCode(typeof(TUnderlying))); + } + } + + ParseFailure: + if (throwOnFailure) + { + ThrowInvalidEmptyParseArgument(); + } + + result = default; + return false; + } + + private static unsafe bool TryParseRareTypeByValueOrName( + RuntimeType enumType, ReadOnlySpan value, bool ignoreCase, bool throwOnFailure, out TUnderlying result) + where TUnderlying : struct, INumber, IBitwiseOperators, IMinMaxValue + where TStorage : struct, INumber, IBitwiseOperators, IMinMaxValue + { + AssertValidGenerics(); + + if (!value.IsEmpty) + { + char c = value[0]; + if (char.IsWhiteSpace(c)) + { + value = value.TrimStart(); + if (value.IsEmpty) { - return true; + goto ParseFailure; } + + c = value[0]; } - else if (typeof(TUnderlying) == typeof(ulong)) + + if (!char.IsAsciiDigit(c) && c != '-' && c != '+') { Unsafe.SkipInit(out result); - status = Number.TryParseUInt64IntegerStyle(value, NumberStyle, numberFormat, out Unsafe.As(ref result)); - if (status == Number.ParsingStatus.OK) - { - return true; - } + return TryParseByName(enumType, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); } - else if (typeof(TUnderlying) == typeof(byte) || typeof(TUnderlying) == typeof(ushort)) + +#if RARE_ENUMS + Number.ParsingStatus status; + Type underlyingType = GetUnderlyingType(enumType); + + try { - status = Number.TryParseUInt32IntegerStyle(value, NumberStyle, numberFormat, out uint uint32result); - if (status == Number.ParsingStatus.OK) - { - if (uint32result <= uint.CreateTruncating(TUnderlying.MaxValue)) - { - result = TUnderlying.CreateTruncating(uint32result); - return true; - } - status = Number.ParsingStatus.Overflow; - } + result = (TUnderlying)ToObject(enumType, Convert.ChangeType(value.ToString(), underlyingType, CultureInfo.InvariantCulture)!); + return true; } - else if (typeof(TUnderlying) == typeof(sbyte) || typeof(TUnderlying) == typeof(short)) + catch (FormatException) { - status = Number.TryParseInt32IntegerStyle(value, NumberStyle, numberFormat, out int int32result); - if (status == Number.ParsingStatus.OK) - { - if (int32result >= int.CreateTruncating(TUnderlying.MinValue) && int32result <= int.CreateTruncating(TUnderlying.MaxValue)) - { - result = TUnderlying.CreateTruncating(int32result); - return true; - } - status = Number.ParsingStatus.Overflow; - } + status = Number.ParsingStatus.Failed; // e.g. tlbimp enums that can have values of the form "3D" } - else + catch when (!throwOnFailure) { -#if RARE_ENUMS - Type underlyingType = GetUnderlyingType(enumType); - try - { - result = (TUnderlying)ToObject(enumType, Convert.ChangeType(value.ToString(), underlyingType, CultureInfo.InvariantCulture)!); - return true; - } - catch (FormatException) - { - status = Number.ParsingStatus.Failed; // e.g. tlbimp enums that can have values of the form "3D" - } - catch when (!throwOnFailure) - { - status = Number.ParsingStatus.Overflow; // fall through to returning failure - } -#else - throw CreateUnknownEnumTypeException(); -#endif + status = Number.ParsingStatus.Overflow; // fall through to returning failure } if (status != Number.ParsingStatus.Overflow) @@ -1037,6 +1025,9 @@ private static unsafe bool TryParseByValueOrName( { Number.ThrowOverflowException(Type.GetTypeCode(typeof(TUnderlying))); } +#else + throw CreateUnknownEnumTypeException(); +#endif } ParseFailure: @@ -1049,7 +1040,7 @@ private static unsafe bool TryParseByValueOrName( return false; } - /// Handles just the name parsing portion of . + /// Handles just the name parsing portion of . private static bool TryParseByName(RuntimeType enumType, ReadOnlySpan value, bool ignoreCase, bool throwOnFailure, out TStorage result) where TStorage : struct, INumber, IBitwiseOperators { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs index 5af77c998154a..03a4ba17f78c6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs @@ -830,6 +830,7 @@ public static NumberFormatInfo ReadOnly(NumberFormatInfo nfi) | NumberStyles.AllowThousands | NumberStyles.AllowExponent | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier); + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ValidateParseStyleInteger(NumberStyles style) { // Check for undefined flags or invalid hex number flags diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 4a1ede42b511a..a9d1f77364aa7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -490,7 +490,7 @@ static bool TryCompatParsing(ReadOnlySpan guidString, ref GuidResult resul result._fg = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)uintTmp) : (ushort)uintTmp; // Unlike the other components, this one never allowed 0x or +, so we can parse it as straight hex. - if (Number.TryParseUInt32HexNumberStyle(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _h, _i, _j, _k + if (Number.TryParseBinaryIntegerHexNumberStyle(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _h, _i, _j, _k { result._hijk = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(uintTmp) : uintTmp; return true; diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 8247f5a600eac..3c253f434fa70 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -18,7 +18,8 @@ public readonly struct Int128 : IBinaryInteger, IMinMaxValue, ISignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { internal const int Size = 16; @@ -125,75 +126,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatInt128(this, format, provider, utf8Destination, out bytesWritten); } - public static Int128 Parse(string s) - { - ArgumentNullException.ThrowIfNull(s); - return Number.ParseInt128(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static Int128 Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static Int128 Parse(string s, NumberStyles style) - { - ArgumentNullException.ThrowIfNull(s); - NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt128(s, style, NumberFormatInfo.CurrentInfo); - } + public static Int128 Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static Int128 Parse(string s, IFormatProvider? provider) - { - ArgumentNullException.ThrowIfNull(s); - return Number.ParseInt128(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static Int128 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static Int128 Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); - NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt128(s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static Int128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt128(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out Int128 result) - { - if (s is not null) - { - return Number.TryParseInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } - else - { - result = default; - return false; - } - } + public static bool TryParse([NotNullWhen(true)] string? s, out Int128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out Int128 result) - { - return Number.TryParseInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out Int128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Int128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is not null) - { - return Number.TryParseInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; - } - else + if (s is null) { - result = default; + result = 0; return false; } + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Int128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -2199,5 +2169,25 @@ static bool INumberBase.TryConvertToTruncating(Int128 value, [Ma /// public static Int128 operator +(Int128 value) => value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 170_141_183_460_469_231_731_687_303_715_884_105_727 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 32; // 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF + + static Int128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new Int128(0x0CCC_CCCC_CCCC_CCCC, 0xCCCC_CCCC_CCCC_CCCC); + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int128; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(Int128 left, Int128 right) => (UInt128)(left) > (UInt128)(right); + + static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(Int128 value) => value * 10; + + static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy16(Int128 value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 7286d104def07..c6e775daf5c2e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -24,7 +24,8 @@ public readonly struct Int16 IBinaryInteger, IMinMaxValue, ISignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { private readonly short m_value; // Do not rename (binary serialization) @@ -125,102 +126,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatInt32(m_value, 0x0000FFFF, format, provider, utf8Destination, out bytesWritten); } - public static short Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static short Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static short Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); - } + public static short Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static short Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static short Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static short Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static short Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - private static short Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) - { - Number.ParsingStatus status = Number.TryParseInt32(s, style, info, out int i); - if (status != Number.ParsingStatus.OK) - { - Number.ThrowOverflowOrFormatException(status, s, TypeCode.Int16); - } + public static bool TryParse([NotNullWhen(true)] string? s, out short result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - // For hex number styles AllowHexSpecifier << 6 == 0x8000 and cancels out MinValue so the check is effectively: (uint)i > ushort.MaxValue - // For integer styles it's zero and the effective check is (uint)(i - MinValue) > ushort.MaxValue - if ((uint)(i - MinValue - ((int)(style & NumberStyles.AllowHexSpecifier) << 6)) > ushort.MaxValue) - { - Number.ThrowOverflowException(TypeCode.Int16); - } - return (short)i; - } - - public static bool TryParse([NotNullWhen(true)] string? s, out short result) - { - if (s == null) - { - result = 0; - return false; - } - - return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); - } - - public static bool TryParse(ReadOnlySpan s, out short result) - { - return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); - } + public static bool TryParse(ReadOnlySpan s, out short result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out short result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out short result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out short result) - { - // For hex number styles AllowHexSpecifier << 6 == 0x8000 and cancels out MinValue so the check is effectively: (uint)i > ushort.MaxValue - // For integer styles it's zero and the effective check is (uint)(i - MinValue) > ushort.MaxValue - if (Number.TryParseInt32(s, style, info, out int i) != Number.ParsingStatus.OK - || (uint)(i - MinValue - ((int)(style & NumberStyles.AllowHexSpecifier) << 6)) > ushort.MaxValue) - { - result = 0; - return false; - } - result = (short)i; - return true; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -1421,5 +1364,25 @@ static bool INumberBase.TryConvertToTruncating(short value, [Mayb /// static short IUnaryPlusOperators.operator +(short value) => (short)(+value); + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 32_767 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0x7FFF + + static short IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int16; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(short left, short right) => (ushort)(left) > (ushort)(right); + + static short IBinaryIntegerParseAndFormatInfo.MultiplyBy10(short value) => (short)(value * 10); + + static short IBinaryIntegerParseAndFormatInfo.MultiplyBy16(short value) => (short)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index cff29460616af..2349b48793d38 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -24,7 +24,8 @@ public readonly struct Int32 IBinaryInteger, IMinMaxValue, ISignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { private readonly int m_value; // Do not rename (binary serialization) @@ -135,85 +136,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatInt32(m_value, ~0, format, provider, utf8Destination, out bytesWritten); } - public static int Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static int Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static int Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt32(s, style, NumberFormatInfo.CurrentInfo); - } + public static int Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - // Parses an integer from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // - public static int Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static int Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); - // Parses an integer from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // public static int Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static int Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - // Parses an integer from a String. Returns false rather - // than throwing an exception if input is invalid. - // - public static bool TryParse([NotNullWhen(true)] string? s, out int result) - { - if (s == null) - { - result = 0; - return false; - } + public static bool TryParse([NotNullWhen(true)] string? s, out int result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - return Number.TryParseInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out int result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out int result) - { - return Number.TryParseInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } - - // Parses an integer from a String in the given style. Returns false rather - // than throwing an exception if input is invalid. - // public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out int result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out int result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -1440,5 +1400,25 @@ static bool INumberBase.TryConvertToTruncating(int value, [MaybeNul /// static int IUnaryPlusOperators.operator +(int value) => +value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 10; // 2_147_483_647 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 8; // 0x7FFF_FFFF + + static int IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int32; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(int left, int right) => (uint)(left) > (uint)(right); + + static int IBinaryIntegerParseAndFormatInfo.MultiplyBy10(int value) => value * 10; + + static int IBinaryIntegerParseAndFormatInfo.MultiplyBy16(int value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index 4d7143d4a8ebd..0fdbd57db4d2c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -24,7 +24,8 @@ public readonly struct Int64 IBinaryInteger, IMinMaxValue, ISignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { private readonly long m_value; // Do not rename (binary serialization) @@ -132,75 +133,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatInt64(m_value, format, provider, utf8Destination, out bytesWritten); } - public static long Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static long Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static long Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt64(s, style, NumberFormatInfo.CurrentInfo); - } + public static long Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static long Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static long Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); - // Parses a long from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // public static long Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static long Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out long result) - { - if (s == null) - { - result = 0; - return false; - } - - return Number.TryParseInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out long result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out long result) - { - return Number.TryParseInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out long result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out long result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out long result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -1433,5 +1403,25 @@ static bool INumberBase.TryConvertToTruncating(long value, [MaybeN /// static long IUnaryPlusOperators.operator +(long value) => +value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 19; // 9_223_372_036_854_775_807 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 16; // 0x7FFF_FFFF_FFFF_FFFF + + static long IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int64; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(long left, long right) => (ulong)(left) > (ulong)(right); + + static long IBinaryIntegerParseAndFormatInfo.MultiplyBy10(long value) => value * 10; + + static long IBinaryIntegerParseAndFormatInfo.MultiplyBy16(long value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index b738f5058d08a..fd89703d9d96f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -24,6 +25,26 @@ namespace System // specified. Note, however, that the Parse methods do not accept // NaNs or Infinities. + internal interface IBinaryIntegerParseAndFormatInfo : IBinaryInteger, IMinMaxValue + where TSelf : unmanaged, IBinaryIntegerParseAndFormatInfo + { + static abstract bool IsSigned { get; } + + static abstract int MaxDigitCount { get; } + + static abstract int MaxHexDigitCount { get; } + + static abstract TSelf MaxValueDiv10 { get; } + + static abstract string OverflowMessage { get; } + + static abstract bool IsGreaterThanAsUnsigned(TSelf left, TSelf right); + + static abstract TSelf MultiplyBy10(TSelf value); + + static abstract TSelf MultiplyBy16(TSelf value); + } + internal static partial class Number { private const int Int32Precision = 10; @@ -45,300 +66,75 @@ internal static partial class Number private const int HalfMaxExponent = 5; private const int HalfMinExponent = -8; - private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value) + private static unsafe bool TryNumberBufferToBinaryInteger(ref NumberBuffer number, ref TInteger value) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { number.CheckConsistency(); int i = number.Scale; - if (i > Int32Precision || i < number.DigitsCount) - { - return false; - } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - int n = 0; - while (--i >= 0) - { - if ((uint)n > (0x7FFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') - { - n += (*p++ - '0'); - } - } - if (number.IsNegative) - { - n = -n; - if (n > 0) - { - return false; - } - } - else - { - if (n < 0) - { - return false; - } - } - value = n; - return true; - } - - private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value) - { - number.CheckConsistency(); - int i = number.Scale; - if (i > Int64Precision || i < number.DigitsCount) + if ((i > TInteger.MaxDigitCount) || (i < number.DigitsCount) || (!TInteger.IsSigned && number.IsNegative)) { return false; } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - long n = 0; - while (--i >= 0) - { - if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') - { - n += (*p++ - '0'); - } - } - if (number.IsNegative) - { - n = -n; - if (n > 0) - { - return false; - } - } - else - { - if (n < 0) - { - return false; - } - } - value = n; - return true; - } - - private static unsafe bool TryNumberToInt128(ref NumberBuffer number, ref Int128 value) - { - number.CheckConsistency(); - int i = number.Scale; - if ((i > Int128Precision) || (i < number.DigitsCount)) - { - return false; - } byte* p = number.GetDigitsPointer(); + Debug.Assert(p != null); - Int128 n = 0; + TInteger n = TInteger.Zero; + while (--i >= 0) { - if ((UInt128)n > new UInt128(0x0CCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC)) // Int128.MaxValue / 10 - { - return false; - } - n *= 10; - if (*p != '\0') - { - n += (*p++ - '0'); - } - } - if (number.IsNegative) - { - n = -n; - if (n > 0) - { - return false; - } - } - else - { - if (n < 0) + if (TInteger.IsGreaterThanAsUnsigned(n, TInteger.MaxValueDiv10)) { return false; } - } - value = n; - return true; - } - private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value) - { - number.CheckConsistency(); + n = TInteger.MultiplyBy10(n); - int i = number.Scale; - if (i > UInt32Precision || i < number.DigitsCount || number.IsNegative) - { - return false; - } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - uint n = 0; - while (--i >= 0) - { - if (n > (0xFFFFFFFF / 10)) - { - return false; - } - n *= 10; if (*p != '\0') { - uint newN = n + (uint)(*p++ - '0'); - // Detect an overflow here... - if (newN < n) + TInteger newN = n + TInteger.CreateTruncating(*p++ - '0'); + + if (!TInteger.IsSigned && (newN < n)) { return false; } + n = newN; } } - value = n; - return true; - } - private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value) - { - number.CheckConsistency(); - - int i = number.Scale; - if (i > UInt64Precision || i < number.DigitsCount || number.IsNegative) - { - return false; - } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - ulong n = 0; - while (--i >= 0) + if (TInteger.IsSigned) { - if (n > (0xFFFFFFFFFFFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') + if (number.IsNegative) { - ulong newN = n + (ulong)(*p++ - '0'); - // Detect an overflow here... - if (newN < n) + n = -n; + + if (n > TInteger.Zero) { return false; } - n = newN; } - } - value = n; - return true; - } - - private static unsafe bool TryNumberToUInt128(ref NumberBuffer number, ref UInt128 value) - { - number.CheckConsistency(); - - int i = number.Scale; - if (i > UInt128Precision || (i < number.DigitsCount) || number.IsNegative) - { - return false; - } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - UInt128 n = 0U; - while (--i >= 0) - { - if (n > new UInt128(0x1999999999999999, 0x9999999999999999)) // UInt128.MaxValue / 10 + else if (n < TInteger.Zero) { return false; } - n *= 10U; - if (*p != '\0') - { - UInt128 newN = n + (UInt128)(*p++ - '0'); - // Detect an overflow here... - if (newN < n) - { - return false; - } - n = newN; - } } + value = n; return true; } - internal static int ParseInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseInt32(value, styles, info, out int result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatException(status, value, TypeCode.Int32); - } - - return result; - } - - internal static long ParseInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) + internal static TInteger ParseBinaryInteger(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { - ParsingStatus status = TryParseInt64(value, styles, info, out long result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatException(status, value, TypeCode.Int64); - } - - return result; - } - - internal static Int128 ParseInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseInt128(value, styles, info, out Int128 result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatExceptionInt128(status); - } - - return result; - } - - internal static uint ParseUInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseUInt32(value, styles, info, out uint result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatException(status, value, TypeCode.UInt32); - } - - return result; - } - - internal static ulong ParseUInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseUInt64(value, styles, info, out ulong result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatException(status, value, TypeCode.UInt64); - } - - return result; - } + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out TInteger result); - internal static UInt128 ParseUInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseUInt128(value, styles, info, out UInt128 result); if (status != ParsingStatus.OK) { - ThrowOverflowOrFormatExceptionUInt128(status); + ThrowOverflowOrFormatException(status, value); } - return result; } @@ -478,1473 +274,159 @@ private static unsafe bool TryParseNumber(scoped ref char* str, char* strEnd, Nu { 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)) - { - state |= StateDecimal; - p = next - 1; - } - else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || (parsingCurrency && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null)) - { - p = next - 1; - } - else - { - break; - } - ch = ++p < strEnd ? *p : '\0'; - } - - bool negExp = false; - number.DigitsCount = digEnd; - number.Digits[digEnd] = (byte)('\0'); - if ((state & StateDigits) != 0) - { - if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0)) - { - char* temp = p; - ch = ++p < strEnd ? *p : '\0'; - if ((next = MatchChars(p, strEnd, info._positiveSign)) != null) - { - ch = (p = next) < strEnd ? *p : '\0'; - } - else if ((next = MatchNegativeSignChars(p, strEnd, info)) != null) - { - ch = (p = next) < strEnd ? *p : '\0'; - negExp = true; - } - if (IsDigit(ch)) - { - int exp = 0; - do - { - exp = exp * 10 + (ch - '0'); - ch = ++p < strEnd ? *p : '\0'; - if (exp > 1000) - { - exp = 9999; - while (IsDigit(ch)) - { - ch = ++p < strEnd ? *p : '\0'; - } - } - } while (IsDigit(ch)); - if (negExp) - { - exp = -exp; - } - number.Scale += exp; - } - else - { - p = temp; - ch = p < strEnd ? *p : '\0'; - } - } - - if (number.Kind == NumberBufferKind.FloatingPoint && !number.HasNonZeroTail) - { - // Adjust the number buffer for trailing zeros - int numberOfFractionalDigits = digEnd - number.Scale; - if (numberOfFractionalDigits > 0) - { - numberOfTrailingZeros = Math.Min(numberOfTrailingZeros, numberOfFractionalDigits); - Debug.Assert(numberOfTrailingZeros >= 0); - number.DigitsCount = digEnd - numberOfTrailingZeros; - number.Digits[number.DigitsCount] = (byte)('\0'); - } - } - - while (true) - { - if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0) - { - if ((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchNegativeSignChars(p, strEnd, info)) != null) && (number.IsNegative = true)))) - { - state |= StateSign; - p = next - 1; - } - else if (ch == ')' && ((state & StateParens) != 0)) - { - state &= ~StateParens; - } - else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null) - { - currSymbol = null; - p = next - 1; - } - else - { - break; - } - } - ch = ++p < strEnd ? *p : '\0'; - } - if ((state & StateParens) == 0) - { - if ((state & StateNonZero) == 0) - { - if (number.Kind != NumberBufferKind.Decimal) - { - number.Scale = 0; - } - if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0) - { - number.IsNegative = false; - } - } - str = p; - return true; - } - } - str = p; - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out int result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseInt32IntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - result = 0; - return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As(ref result)); - } - - return TryParseInt32Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out int result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int32NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberToInt32(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; - } - - /// Parses int limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out int result) - { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - // Parse leading sign. - int sign = 1; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - 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; - num = value[index]; - } - } - } - - bool overflow = false; - int answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!IsDigit(num)) - goto HasTrailingChars; - } - - // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits. - answer = num - '0'; // first digit - index++; - for (int i = 0; i < 8; i++) // next 8 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // Potential overflow now processing the 10th digit. - overflow = answer > int.MaxValue / 10; - answer = answer * 10 + num - '0'; - overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31); - 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. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = answer * sign; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0; - status = ParsingStatus.Overflow; - goto Exit; - - HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span - // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - /// Parses long inputs limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) - { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - // Parse leading sign. - int sign = 1; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - 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; - num = value[index]; - } - } - } - - bool overflow = false; - long answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - 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 DoneAtEnd; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // Potential overflow now processing the 19th digit. - overflow = answer > long.MaxValue / 10; - answer = answer * 10 + num - '0'; - overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31); - 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. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = answer * sign; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0; - status = ParsingStatus.Overflow; - goto Exit; - - HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span - // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseInt64IntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - result = 0; - return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As(ref result)); - } - - return TryParseInt64Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int64NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberToInt64(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; - } - - /// Parses Int128 inputs limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseInt128IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) - { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - // Parse leading sign. - int sign = 1; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - 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; - num = value[index]; - } - } - } - - bool overflow = false; - Int128 answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - 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 < 37; i++) // next 37 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // Potential overflow now processing the 39th digit. - overflow = answer > new Int128(0x0CCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC); // Int128.MaxValue / 10 - answer = answer * 10 + num - '0'; - overflow |= (UInt128)answer > new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF) + (((uint)sign) >> 31); - 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. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = answer * sign; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0; - status = ParsingStatus.Overflow; - goto Exit; - - HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span - // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseInt128IntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - ParsingStatus status = TryParseUInt128HexNumberStyle(value, styles, out UInt128 unsignedResult); - result = new Int128(unsignedResult.Upper, unsignedResult.Lower); - return status; - } - - return TryParseInt128Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseInt128Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int128NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberToInt128(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseUInt32IntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - return TryParseUInt32HexNumberStyle(value, styles, out result); - } - - return TryParseUInt32Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt32NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberToUInt32(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; - } - - /// Parses uint limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) - { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - // Parse leading sign. - bool overflow = false; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) - { - overflow = true; - index += negativeSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - } - - int answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!IsDigit(num)) - goto HasTrailingCharsZero; - } - - // 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; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // Potential overflow now processing the 10th digit. - overflow |= (uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5'); - answer = answer * 10 + num - '0'; - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = (uint)answer; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0; - status = ParsingStatus.Overflow; - goto Exit; - - HasTrailingCharsZero: - overflow = false; - HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span - // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - /// Parses uint limited to styles that make up NumberStyles.HexNumber. - internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out uint result) - { - Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - bool overflow = false; - uint answer = 0; - - if (HexConverter.IsHexChar(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - } - - // Parse up through 8 digits, as no overflow is possible - answer = (uint)HexConverter.FromChar(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; - num = value[index]; - - uint numValue = (uint)HexConverter.FromChar(num); - if (numValue == 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; - num = value[index]; - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. Read through any remaining digits. - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } while (HexConverter.IsHexChar(num)); - overflow = true; - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = answer; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0; - status = ParsingStatus.Overflow; - goto Exit; - - HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span - // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseUInt64IntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - return TryParseUInt64HexNumberStyle(value, styles, out result); - } - - return TryParseUInt64Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt64NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberToUInt64(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; - } - - /// Parses ulong limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) - { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - // Parse leading sign. - bool overflow = false; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) - { - overflow = true; - index += negativeSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - } - - long answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!IsDigit(num)) - goto HasTrailingCharsZero; - } - - // 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; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // Potential overflow now processing the 20th digit. - overflow |= (ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5'); - answer = answer * 10 + num - '0'; - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = (ulong)answer; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0; - status = ParsingStatus.Overflow; - goto Exit; - - HasTrailingCharsZero: - overflow = false; - HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span - // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) + } + 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)) { - if (!IsWhite(value[index])) - break; + state |= StateDecimal; + p = next - 1; } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - /// Parses ulong limited to styles that make up NumberStyles.HexNumber. - private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out ulong result) - { - Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do + else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || (parsingCurrency && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null)) { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; + p = next - 1; } - while (IsWhite(num)); + else + { + break; + } + ch = ++p < strEnd ? *p : '\0'; } - bool overflow = false; - ulong answer = 0; - - if (HexConverter.IsHexChar(num)) + bool negExp = false; + number.DigitsCount = digEnd; + number.Digits[digEnd] = (byte)('\0'); + if ((state & StateDigits) != 0) { - // Skip past leading zeros. - if (num == '0') + if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0)) { - do + char* temp = p; + ch = ++p < strEnd ? *p : '\0'; + if ((next = MatchChars(p, strEnd, info._positiveSign)) != null) { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; + ch = (p = next) < strEnd ? *p : '\0'; + } + else if ((next = MatchNegativeSignChars(p, strEnd, info)) != null) + { + ch = (p = next) < strEnd ? *p : '\0'; + negExp = true; + } + if (IsDigit(ch)) + { + int exp = 0; + do + { + exp = exp * 10 + (ch - '0'); + ch = ++p < strEnd ? *p : '\0'; + if (exp > 1000) + { + exp = 9999; + while (IsDigit(ch)) + { + ch = ++p < strEnd ? *p : '\0'; + } + } + } while (IsDigit(ch)); + if (negExp) + { + exp = -exp; + } + number.Scale += exp; + } + else + { + p = temp; + ch = p < strEnd ? *p : '\0'; + } } - // Parse up through 16 digits, as no overflow is possible - answer = (uint)HexConverter.FromChar(num); // first digit - index++; - for (int i = 0; i < 15; i++) // next 15 digits can't overflow + if (number.Kind == NumberBufferKind.FloatingPoint && !number.HasNonZeroTail) { - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - - uint numValue = (uint)HexConverter.FromChar(num); - if (numValue == 0xFF) - goto HasTrailingChars; - index++; - answer = 16 * answer + numValue; + // Adjust the number buffer for trailing zeros + int numberOfFractionalDigits = digEnd - number.Scale; + if (numberOfFractionalDigits > 0) + { + numberOfTrailingZeros = Math.Min(numberOfTrailingZeros, numberOfFractionalDigits); + Debug.Assert(numberOfTrailingZeros >= 0); + number.DigitsCount = digEnd - numberOfTrailingZeros; + number.Digits[number.DigitsCount] = (byte)('\0'); + } } - // If there's another digit, it's an overflow. - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. Read through any remaining digits. - do + while (true) { - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } while (HexConverter.IsHexChar(num)); - overflow = true; - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = answer; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0; - status = ParsingStatus.Overflow; - goto Exit; - - HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span - // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) + if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0) + { + if ((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchNegativeSignChars(p, strEnd, info)) != null) && (number.IsNegative = true)))) + { + state |= StateSign; + p = next - 1; + } + else if (ch == ')' && ((state & StateParens) != 0)) + { + state &= ~StateParens; + } + else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null) + { + currSymbol = null; + p = next - 1; + } + else + { + break; + } + } + ch = ++p < strEnd ? *p : '\0'; + } + if ((state & StateParens) == 0) { - if (!IsWhite(value[index])) - break; + if ((state & StateNonZero) == 0) + { + if (number.Kind != NumberBufferKind.Decimal) + { + number.Scale = 0; + } + if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0) + { + number.IsNegative = false; + } + } + str = p; + return true; } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; + str = p; + return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out UInt128 result) + internal static ParsingStatus TryParseBinaryInteger(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { if ((styles & ~NumberStyles.Integer) == 0) { // Optimized path for the common case of anything that's allowed for integer style. - return TryParseUInt128IntegerStyle(value, styles, info, out result); + return TryParseBinaryIntegerStyle(value, styles, info, out result); } if ((styles & NumberStyles.AllowHexSpecifier) != 0) { - return TryParseUInt128HexNumberStyle(value, styles, out result); + return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); } - return TryParseUInt128Number(value, styles, info, out result); + return TryParseBinaryIntegerNumber(value, styles, info, out result); } - private static unsafe ParsingStatus TryParseUInt128Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out UInt128 result) + private static unsafe ParsingStatus TryParseBinaryIntegerNumber(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { - result = 0U; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt128NumberBufferLength]); + result = TInteger.Zero; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[TInteger.MaxDigitCount + 1]); if (!TryStringToNumber(value, styles, ref number, info)) { return ParsingStatus.Failed; } - if (!TryNumberToUInt128(ref number, ref result)) + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) { return ParsingStatus.Overflow; } @@ -1952,8 +434,9 @@ private static unsafe ParsingStatus TryParseUInt128Number(ReadOnlySpan val return ParsingStatus.OK; } - /// Parses UInt128 limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out UInt128 result) + /// Parses int limited to styles that make up NumberStyles.Integer. + internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); @@ -1977,21 +460,21 @@ internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan val } // Parse leading sign. - bool overflow = false; + bool isNegative = false; if ((styles & NumberStyles.AllowLeadingSign) != 0) { if (info.HasInvariantNumberSigns) { - if (num == '+') + if (num == '-') { + isNegative = true; index++; if ((uint)index >= (uint)value.Length) goto FalseExit; num = value[index]; } - else if (num == '-') + else if (num == '+') { - overflow = true; index++; if ((uint)index >= (uint)value.Length) goto FalseExit; @@ -2000,7 +483,7 @@ internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan val } else if (info.AllowHyphenDuringParsing && num == '-') { - overflow = true; + isNegative = true; index++; if ((uint)index >= (uint)value.Length) goto FalseExit; @@ -2020,7 +503,7 @@ internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan val } else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) { - overflow = true; + isNegative = true; index += negativeSign.Length; if ((uint)index >= (uint)value.Length) goto FalseExit; @@ -2029,7 +512,8 @@ internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan val } } - Int128 answer = 0; + bool overflow = !TInteger.IsSigned && isNegative; + TInteger answer = TInteger.Zero; if (IsDigit(num)) { @@ -2043,35 +527,63 @@ internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan val goto DoneAtEnd; num = value[index]; } while (num == '0'); + if (!IsDigit(num)) - goto HasTrailingCharsZero; + { + if (!TInteger.IsSigned) + { + overflow = false; + } + goto HasTrailingChars; + } } - // Parse most digits, up to the potential for overflow, which can't happen until after 38 digits. - answer = num - '0'; // first digit + // Parse most digits, up to the potential for overflow, which can't happen until after MaxDigitCount - 1 digits. + answer = TInteger.CreateTruncating(num - '0'); // first digit index++; - for (int i = 0; i < 37; i++) // next 37 digits can't overflow + for (int i = 0; i < TInteger.MaxDigitCount - 2; i++) // next MaxDigitCount - 2 digits can't overflow { if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; + { + if (!TInteger.IsSigned) + goto DoneAtEndButPotentialOverflow; + else + goto DoneAtEnd; + } num = value[index]; if (!IsDigit(num)) goto HasTrailingChars; index++; - answer = 10 * answer + num - '0'; + answer = TInteger.MultiplyBy10(answer); + answer += TInteger.CreateTruncating(num - '0'); } if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; + { + if (!TInteger.IsSigned) + goto DoneAtEndButPotentialOverflow; + else + goto DoneAtEnd; + } num = value[index]; if (!IsDigit(num)) goto HasTrailingChars; index++; - // Potential overflow now processing the 39th digit. - UInt128 maxValueDiv10 = new UInt128(0x1999999999999999, 0x9999999999999999); // UInt128.MaxValue / 10 - overflow |= (UInt128)answer > maxValueDiv10; - overflow |= ((UInt128)answer == maxValueDiv10 && num > '5'); - answer = answer * 10 + num - '0'; + // Potential overflow now processing the MaxDigitCount digit. + if (!TInteger.IsSigned) + { + overflow |= (answer > TInteger.MaxValueDiv10) || (answer == TInteger.MaxValueDiv10) && (num > '5'); + } + else + { + overflow = answer > TInteger.MaxValueDiv10; + } + answer = TInteger.MultiplyBy10(answer); + answer += TInteger.CreateTruncating(num - '0'); + if (TInteger.IsSigned) + { + overflow |= TInteger.IsGreaterThanAsUnsigned(answer, TInteger.MaxValue + (isNegative ? TInteger.One : TInteger.Zero)); + } if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow; @@ -2096,22 +608,27 @@ internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan val goto OverflowExit; } DoneAtEnd: - result = (UInt128)answer; + if (!TInteger.IsSigned) + { + result = answer; + } + else + { + result = isNegative ? -answer : answer; + } ParsingStatus status = ParsingStatus.OK; Exit: return status; FalseExit: // parsing failed - result = 0U; + result = TInteger.Zero; status = ParsingStatus.Failed; goto Exit; OverflowExit: - result = 0U; + result = TInteger.Zero; status = ParsingStatus.Overflow; goto Exit; - HasTrailingCharsZero: - overflow = false; HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail. if (IsWhite(num)) @@ -2133,8 +650,9 @@ internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan val goto DoneAtEndButPotentialOverflow; } - /// Parses UInt128 limited to styles that make up NumberStyles.HexNumber. - private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out UInt128 result) + /// Parses uint limited to styles that make up NumberStyles.HexNumber. + internal static ParsingStatus TryParseBinaryIntegerHexNumberStyle(ReadOnlySpan value, NumberStyles styles, out TInteger result) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); @@ -2158,7 +676,7 @@ private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan va } bool overflow = false; - UInt128 answer = 0U; + TInteger answer = TInteger.Zero; if (HexConverter.IsHexChar(num)) { @@ -2176,10 +694,10 @@ private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan va goto HasTrailingChars; } - // Parse up through 32 digits, as no overflow is possible - answer = (uint)HexConverter.FromChar(num); // first digit + // Parse up through MaxHexDigitCount digits, as no overflow is possible + answer = TInteger.CreateTruncating((uint)HexConverter.FromChar(num)); // first digit index++; - for (int i = 0; i < 31; i++) // next 31 digits can't overflow + for (int i = 0; i < TInteger.MaxHexDigitCount - 1; i++) // next MaxHexDigitCount - 1 digits can't overflow { if ((uint)index >= (uint)value.Length) goto DoneAtEnd; @@ -2189,7 +707,8 @@ private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan va if (numValue == 0xFF) goto HasTrailingChars; index++; - answer = 16U * answer + numValue; + answer = TInteger.MultiplyBy16(answer); + answer += TInteger.CreateTruncating(numValue); } // If there's another digit, it's an overflow. @@ -2225,11 +744,11 @@ private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan va return status; FalseExit: // parsing failed - result = 0U; + result = TInteger.Zero; status = ParsingStatus.Failed; goto Exit; OverflowExit: - result = 0U; + result = TInteger.Zero; status = ParsingStatus.Overflow; goto Exit; @@ -2719,6 +1238,13 @@ internal enum ParsingStatus [DoesNotReturn] internal static void ThrowOverflowOrFormatException(ParsingStatus status, ReadOnlySpan value, TypeCode type = 0) => throw GetException(status, value, type); + [DoesNotReturn] + internal static void ThrowOverflowOrFormatException(ParsingStatus status, ReadOnlySpan value) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo + { + throw GetException(status, value); + } + [DoesNotReturn] internal static void ThrowOverflowException(TypeCode type) => throw GetOverflowException(type); @@ -2736,6 +1262,15 @@ private static Exception GetException(ParsingStatus status, ReadOnlySpan v return GetOverflowException(type); } + private static Exception GetException(ParsingStatus status, ReadOnlySpan value) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo + { + if (status == ParsingStatus.Failed) + return new FormatException(SR.Format(SR.Format_InvalidStringWithValue, value.ToString())); + + return new OverflowException(TInteger.OverflowMessage); + } + private static OverflowException GetOverflowException(TypeCode type) { string s; diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 9a95eecef3de4..161c71556f0a0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -24,7 +24,8 @@ public readonly struct SByte IBinaryInteger, IMinMaxValue, ISignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { private readonly sbyte m_value; // Do not rename (binary serialization) @@ -128,106 +129,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatInt32(m_value, 0x000000FF, format, provider, utf8Destination, out bytesWritten); } - public static sbyte Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static sbyte Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static sbyte Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); - } + public static sbyte Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static sbyte Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static sbyte Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); - // Parses a signed byte from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // public static sbyte Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static sbyte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - private static sbyte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) - { - Number.ParsingStatus status = Number.TryParseInt32(s, style, info, out int i); - if (status != Number.ParsingStatus.OK) - { - Number.ThrowOverflowOrFormatException(status, s, TypeCode.SByte); - } - - // For hex number styles AllowHexSpecifier >> 2 == 0x80 and cancels out MinValue so the check is effectively: (uint)i > byte.MaxValue - // For integer styles it's zero and the effective check is (uint)(i - MinValue) > byte.MaxValue - if ((uint)(i - MinValue - ((int)(style & NumberStyles.AllowHexSpecifier) >> 2)) > byte.MaxValue) - { - Number.ThrowOverflowException(TypeCode.SByte); - } - return (sbyte)i; - } - - public static bool TryParse([NotNullWhen(true)] string? s, out sbyte result) - { - if (s == null) - { - result = 0; - return false; - } - - return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); - } + public static bool TryParse([NotNullWhen(true)] string? s, out sbyte result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out sbyte result) - { - return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); - } + public static bool TryParse(ReadOnlySpan s, out sbyte result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out sbyte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out sbyte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out sbyte result) - { - // For hex number styles AllowHexSpecifier >> 2 == 0x80 and cancels out MinValue so the check is effectively: (uint)i > byte.MaxValue - // For integer styles it's zero and the effective check is (uint)(i - MinValue) > byte.MaxValue - if (Number.TryParseInt32(s, style, info, out int i) != Number.ParsingStatus.OK - || (uint)(i - MinValue - ((int)(style & NumberStyles.AllowHexSpecifier) >> 2)) > byte.MaxValue) - { - result = 0; - return false; - } - result = (sbyte)i; - return true; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -1386,5 +1325,25 @@ static bool INumberBase.TryConvertToTruncating(sbyte value, [Mayb /// static sbyte IUnaryPlusOperators.operator +(sbyte value) => (sbyte)(+value); + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 3; // 127 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 2; // 0x7F + + static sbyte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_SByte; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(sbyte left, sbyte right) => (byte)(left) > (byte)(right); + + static sbyte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(sbyte value) => (sbyte)(value * 10); + + static sbyte IBinaryIntegerParseAndFormatInfo.MultiplyBy16(sbyte value) => (sbyte)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index e8b9232e48901..142b94d84a545 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -521,6 +521,12 @@ internal static void ThrowFormatException_BadFormatSpecifier() throw new FormatException(SR.Argument_BadFormatSpecifier); } + [DoesNotReturn] + internal static void ThrowFormatException_NeedSingleChar() + { + throw new FormatException(SR.Format_NeedSingleChar); + } + [DoesNotReturn] internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge() { diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 5a6d33150c3a3..76a4f3ce579dc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -19,7 +19,8 @@ public readonly struct UInt128 : IBinaryInteger, IMinMaxValue, IUnsignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { internal const int Size = 16; @@ -126,75 +127,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatUInt128(this, format, provider, utf8Destination, out bytesWritten); } - public static UInt128 Parse(string s) - { - ArgumentNullException.ThrowIfNull(s); - return Number.ParseUInt128(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static UInt128 Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static UInt128 Parse(string s, NumberStyles style) - { - ArgumentNullException.ThrowIfNull(s); - NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt128(s, style, NumberFormatInfo.CurrentInfo); - } + public static UInt128 Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static UInt128 Parse(string s, IFormatProvider? provider) - { - ArgumentNullException.ThrowIfNull(s); - return Number.ParseUInt128(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static UInt128 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static UInt128 Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); - NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt128(s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static UInt128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt128(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out UInt128 result) - { - if (s is not null) - { - return Number.TryParseUInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } - else - { - result = default; - return false; - } - } + public static bool TryParse([NotNullWhen(true)] string? s, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out UInt128 result) - { - return Number.TryParseUInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out UInt128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is not null) - { - return Number.TryParseUInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; - } - else + if (s is null) { - result = default; + result = 0; return false; } + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out UInt128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseUInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -2162,5 +2132,25 @@ static bool INumberBase.TryConvertToTruncating(UInt128 value, [ /// public static UInt128 operator +(UInt128 value) => value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 340_282_366_920_938_463_463_374_607_431_768_211_455 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 32; // 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF + + static UInt128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new UInt128(0x1999_9999_9999_9999, 0x9999_9999_9999_9999); + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_UInt128; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(UInt128 left, UInt128 right) => left > right; + + static UInt128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(UInt128 value) => value * 10; + + static UInt128 IBinaryIntegerParseAndFormatInfo.MultiplyBy16(UInt128 value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 17a211c51b5ea..4122d64b496cf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -24,7 +24,8 @@ public readonly struct UInt16 IBinaryInteger, IMinMaxValue, IUnsignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { private readonly ushort m_value; // Do not rename (binary serialization) @@ -120,95 +121,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatUInt32(m_value, format, provider, utf8Destination, out bytesWritten); } - public static ushort Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static ushort Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static ushort Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); - } + public static ushort Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static ushort Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static ushort Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static ushort Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static ushort Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Parse(s, style, NumberFormatInfo.GetInstance(provider)); - } - - private static ushort Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) - { - Number.ParsingStatus status = Number.TryParseUInt32(s, style, info, out uint i); - if (status != Number.ParsingStatus.OK) - { - Number.ThrowOverflowOrFormatException(status, s, TypeCode.UInt16); - } - - if (i > MaxValue) Number.ThrowOverflowException(TypeCode.UInt16); - return (ushort)i; + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out ushort result) - { - if (s == null) - { - result = 0; - return false; - } + public static bool TryParse([NotNullWhen(true)] string? s, out ushort result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); - } - - public static bool TryParse(ReadOnlySpan s, out ushort result) - { - return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); - } + public static bool TryParse(ReadOnlySpan s, out ushort result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ushort result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ushort result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out ushort result) - { - if (Number.TryParseUInt32(s, style, info, out uint i) != Number.ParsingStatus.OK - || i > MaxValue) - { - result = 0; - return false; - } - result = (ushort)i; - return true; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -1229,5 +1179,25 @@ static bool INumberBase.TryConvertToTruncating(ushort value, [Ma /// static ushort IUnaryPlusOperators.operator +(ushort value) => (ushort)(+value); + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 65_535 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0xFFFF + + static ushort IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_UInt16; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ushort left, ushort right) => left > right; + + static ushort IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ushort value) => (ushort)(value * 10); + + static ushort IBinaryIntegerParseAndFormatInfo.MultiplyBy16(ushort value) => (ushort)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index 9813330a72499..a8b9b3eb1ecda 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -24,7 +24,8 @@ public readonly struct UInt32 IBinaryInteger, IMinMaxValue, IUnsignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { private readonly uint m_value; // Do not rename (binary serialization) @@ -130,71 +131,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatUInt32(m_value, format, provider, utf8Destination, out bytesWritten); } - public static uint Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static uint Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static uint Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt32(s, style, NumberFormatInfo.CurrentInfo); - } + public static uint Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static uint Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static uint Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static uint Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static uint Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out uint result) - { - if (s == null) - { - result = 0; - return false; - } + public static bool TryParse([NotNullWhen(true)] string? s, out uint result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - return Number.TryParseUInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } - - public static bool TryParse(ReadOnlySpan s, out uint result) - { - return Number.TryParseUInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out uint result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out uint result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out uint result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -1244,5 +1218,25 @@ static bool INumberBase.TryConvertToTruncating(uint value, [MaybeN /// static uint IUnaryPlusOperators.operator +(uint value) => +value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 10; // 4_294_967_295 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 8; // 0xFFFF_FFFF + + static uint IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_UInt32; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(uint left, uint right) => left > right; + + static uint IBinaryIntegerParseAndFormatInfo.MultiplyBy10(uint value) => value * 10; + + static uint IBinaryIntegerParseAndFormatInfo.MultiplyBy16(uint value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index da40613f88f7b..847db94ff33d3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -24,7 +24,8 @@ public readonly struct UInt64 IBinaryInteger, IMinMaxValue, IUnsignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { private readonly ulong m_value; // Do not rename (binary serialization) @@ -129,71 +130,44 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri return Number.TryFormatUInt64(m_value, format, provider, utf8Destination, out bytesWritten); } - public static ulong Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static ulong Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static ulong Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt64(s, style, NumberFormatInfo.CurrentInfo); - } + public static ulong Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static ulong Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static ulong Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static ulong Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } + return Parse(s.AsSpan(), style, provider); } public static ulong Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out ulong result) - { - if (s == null) - { - result = 0; - return false; - } + public static bool TryParse([NotNullWhen(true)] string? s, out ulong result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - return Number.TryParseUInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } - - public static bool TryParse(ReadOnlySpan s, out ulong result) - { - return Number.TryParseUInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out ulong result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ulong result) { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ulong result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // @@ -1237,5 +1211,25 @@ static bool INumberBase.TryConvertToTruncating(ulong value, [Mayb /// static ulong IUnaryPlusOperators.operator +(ulong value) => +value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 20; // 18_446_744_073_709_551_615 + + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 16; // 0xFFFF_FFFF_FFFF_FFFF + + static ulong IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_UInt64; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ulong left, ulong right) => left > right; + + static ulong IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ulong value) => value * 10; + + static ulong IBinaryIntegerParseAndFormatInfo.MultiplyBy16(ulong value) => value * 16; } }