// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; using System.Text; using System.Text.Unicode; namespace System.Numerics { /// <summary>Defines the base of other number types.</summary> /// <typeparam name="TSelf">The type that implements the interface.</typeparam> public interface INumberBase<TSelf> : IAdditionOperators<TSelf, TSelf, TSelf>, IAdditiveIdentity<TSelf, TSelf>, IDecrementOperators<TSelf>, IDivisionOperators<TSelf, TSelf, TSelf>, IEquatable<TSelf>, IEqualityOperators<TSelf, TSelf, bool>, IIncrementOperators<TSelf>, IMultiplicativeIdentity<TSelf, TSelf>, IMultiplyOperators<TSelf, TSelf, TSelf>, ISpanFormattable, ISpanParsable<TSelf>, ISubtractionOperators<TSelf, TSelf, TSelf>, IUnaryPlusOperators<TSelf, TSelf>, IUnaryNegationOperators<TSelf, TSelf>, IUtf8SpanFormattable, IUtf8SpanParsable<TSelf> where TSelf : INumberBase<TSelf>? { /// <summary>Gets the value <c>1</c> for the type.</summary> static abstract TSelf One { get; } /// <summary>Gets the radix, or base, for the type.</summary> static abstract int Radix { get; } /// <summary>Gets the value <c>0</c> for the type.</summary> static abstract TSelf Zero { get; } /// <summary>Computes the absolute of a value.</summary> /// <param name="value">The value for which to get its absolute.</param> /// <returns>The absolute of <paramref name="value" />.</returns> /// <exception cref="OverflowException">The absolute of <paramref name="value" /> is not representable by <typeparamref name="TSelf" />.</exception> static abstract TSelf Abs(TSelf value); /// <summary>Creates an instance of the current type from a value, throwing an overflow exception for any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type of <paramref name="value" />.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TSelf" />.</param> /// <returns>An instance of <typeparamref name="TSelf" /> created from <paramref name="value" />.</returns> /// <exception cref="NotSupportedException"><typeparamref name="TOther" /> is not supported.</exception> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <typeparamref name="TSelf" />.</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] static virtual TSelf CreateChecked<TOther>(TOther value) #nullable disable where TOther : INumberBase<TOther> #nullable restore { TSelf? result; if (typeof(TOther) == typeof(TSelf)) { result = (TSelf)(object)value; } else if (!TSelf.TryConvertFromChecked(value, out result) && !TOther.TryConvertToChecked<TSelf>(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <summary>Creates an instance of the current type from a value, saturating any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type of <paramref name="value" />.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TSelf" />.</param> /// <returns>An instance of <typeparamref name="TSelf" /> created from <paramref name="value" />, saturating if <paramref name="value" /> falls outside the representable range of <typeparamref name="TSelf" />.</returns> /// <exception cref="NotSupportedException"><typeparamref name="TOther" /> is not supported.</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] static virtual TSelf CreateSaturating<TOther>(TOther value) #nullable disable where TOther : INumberBase<TOther> #nullable restore { TSelf? result; if (typeof(TOther) == typeof(TSelf)) { result = (TSelf)(object)value; } else if (!TSelf.TryConvertFromSaturating(value, out result) && !TOther.TryConvertToSaturating<TSelf>(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <summary>Creates an instance of the current type from a value, truncating any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type of <paramref name="value" />.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TSelf" />.</param> /// <returns>An instance of <typeparamref name="TSelf" /> created from <paramref name="value" />, truncating if <paramref name="value" /> falls outside the representable range of <typeparamref name="TSelf" />.</returns> /// <exception cref="NotSupportedException"><typeparamref name="TOther" /> is not supported.</exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] static virtual TSelf CreateTruncating<TOther>(TOther value) #nullable disable where TOther : INumberBase<TOther> #nullable restore { TSelf? result; if (typeof(TOther) == typeof(TSelf)) { result = (TSelf)(object)value; } else if (!TSelf.TryConvertFromTruncating(value, out result) && !TOther.TryConvertToTruncating<TSelf>(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <summary>Determines if a value is in its canonical representation.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is in its canonical representation; otherwise, <c>false</c>.</returns> static abstract bool IsCanonical(TSelf value); /// <summary>Determines if a value represents a complex value.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is a complex number; otherwise, <c>false</c>.</returns> /// <remarks>This function returns <c>false</c> for a complex number <c>a + bi</c> where either <c>a</c> or <c>b</c> is zero. In other words, it excludes real numbers and pure imaginary numbers.</remarks> static abstract bool IsComplexNumber(TSelf value); /// <summary>Determines if a value represents an even integral value.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is an even integer; otherwise, <c>false</c>.</returns> /// <remarks> /// <para>This correctly handles floating-point values and so <c>2.0</c> will return <c>true</c> while <c>2.2</c> will return <c>false</c>.</para> /// <para>This functioning returning <c>false</c> does not imply that <see cref="IsOddInteger(TSelf)" /> will return <c>true</c>. A number with a fractional portion, <c>3.3</c>, is not even nor odd.</para> /// </remarks> static abstract bool IsEvenInteger(TSelf value); /// <summary>Determines if a value is finite.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is finite; otherwise, <c>false</c>.</returns> /// <remarks>This function returning <c>false</c> does not imply that <see cref="IsInfinity(TSelf)" /> will return <c>true</c>. <c>NaN</c> is not finite nor infinite.</remarks> static abstract bool IsFinite(TSelf value); /// <summary>Determines if a value represents a pure imaginary value.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is a pure imaginary number; otherwise, <c>false</c>.</returns> /// <remarks>This function returns <c>false</c> for a complex number <c>a + bi</c> where <c>a</c> is non-zero, as that number is not purely imaginary.</remarks> static abstract bool IsImaginaryNumber(TSelf value); /// <summary>Determines if a value is infinite.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is infinite; otherwise, <c>false</c>.</returns> /// <remarks>This function returning <c>false</c> does not imply that <see cref="IsFinite(TSelf)" /> will return <c>true</c>. <c>NaN</c> is not finite nor infinite.</remarks> static abstract bool IsInfinity(TSelf value); /// <summary>Determines if a value represents an integral value.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is an integer; otherwise, <c>false</c>.</returns> /// <remarks>This correctly handles floating-point values and so <c>2.0</c> and <c>3.0</c> will return <c>true</c> while <c>2.2</c> and <c>3.3</c> will return <c>false</c>.</remarks> static abstract bool IsInteger(TSelf value); /// <summary>Determines if a value is NaN.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is NaN; otherwise, <c>false</c>.</returns> static abstract bool IsNaN(TSelf value); /// <summary>Determines if a value represents a negative real number.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> represents negative zero or a negative real number; otherwise, <c>false</c>.</returns> /// <remarks> /// <para>If this type has signed zero, then <c>-0</c> is also considered negative.</para> /// <para>This function returning <c>false</c> does not imply that <see cref="IsPositive(TSelf)" /> will return <c>true</c>. A complex number, <c>a + bi</c> for non-zero <c>b</c>, is not positive nor negative</para> /// </remarks> static abstract bool IsNegative(TSelf value); /// <summary>Determines if a value is negative infinity.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is negative infinity; otherwise, <c>false</c>.</returns> static abstract bool IsNegativeInfinity(TSelf value); /// <summary>Determines if a value is normal.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is normal; otherwise, <c>false</c>.</returns> static abstract bool IsNormal(TSelf value); /// <summary>Determines if a value represents an odd integral value.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is an odd integer; otherwise, <c>false</c>.</returns> /// <remarks> /// <para>This correctly handles floating-point values and so <c>3.0</c> will return <c>true</c> while <c>3.3</c> will return <c>false</c>.</para> /// <para>This functioning returning <c>false</c> does not imply that <see cref="IsEvenInteger(TSelf)" /> will return <c>true</c>. A number with a fractional portion, <c>3.3</c>, is neither even nor odd.</para> /// </remarks> static abstract bool IsOddInteger(TSelf value); /// <summary>Determines if a value represents zero or a positive real number.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> represents (positive) zero or a positive real number; otherwise, <c>false</c>.</returns> /// <remarks> /// <para>If this type has signed zero, then <c>-0</c> is not considered positive, but <c>+0</c> is.</para> /// <para>This function returning <c>false</c> does not imply that <see cref="IsNegative(TSelf)" /> will return <c>true</c>. A complex number, <c>a + bi</c> for non-zero <c>b</c>, is not positive nor negative</para> /// </remarks> static abstract bool IsPositive(TSelf value); /// <summary>Determines if a value is positive infinity.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is positive infinity; otherwise, <c>false</c>.</returns> static abstract bool IsPositiveInfinity(TSelf value); /// <summary>Determines if a value represents a real value.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is a real number; otherwise, <c>false</c>.</returns> /// <remarks> /// <para>This function returns <c>true</c> for a complex number <c>a + bi</c> where <c>b</c> is zero.</para> /// <para>This function checks values against the extended real number line, thus returns <c>true</c> for positive and negative infinity.</para> /// </remarks> static abstract bool IsRealNumber(TSelf value); /// <summary>Determines if a value is subnormal.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is subnormal; otherwise, <c>false</c>.</returns> static abstract bool IsSubnormal(TSelf value); /// <summary>Determines if a value is zero.</summary> /// <param name="value">The value to be checked.</param> /// <returns><c>true</c> if <paramref name="value" /> is zero; otherwise, <c>false</c>.</returns> /// <remarks>This function treats both positive and negative zero as zero and so will return <c>true</c> for <c>+0.0</c> and <c>-0.0</c>.</remarks> static abstract bool IsZero(TSelf value); /// <summary>Compares two values to compute which has the greater magnitude.</summary> /// <param name="x">The value to compare with <paramref name="y" />.</param> /// <param name="y">The value to compare with <paramref name="x" />.</param> /// <returns><paramref name="x" /> if it has a greater magnitude than <paramref name="y" />; otherwise, <paramref name="y" />.</returns> /// <remarks>For <see cref="IFloatingPointIeee754{TSelf}" /> this method matches the IEEE 754:2019 <c>maximumMagnitude</c> function. This requires NaN inputs to be propagated back to the caller and for <c>-0.0</c> to be treated as less than <c>+0.0</c>.</remarks> static abstract TSelf MaxMagnitude(TSelf x, TSelf y); /// <summary>Compares two values to compute which has the greater magnitude and returning the other value if an input is <c>NaN</c>.</summary> /// <param name="x">The value to compare with <paramref name="y" />.</param> /// <param name="y">The value to compare with <paramref name="x" />.</param> /// <returns><paramref name="x" /> if it has a greater magnitude than <paramref name="y" />; otherwise, <paramref name="y" />.</returns> /// <remarks>For <see cref="IFloatingPointIeee754{TSelf}" /> this method matches the IEEE 754:2019 <c>maximumMagnitudeNumber</c> function. This requires NaN inputs to not be propagated back to the caller and for <c>-0.0</c> to be treated as less than <c>+0.0</c>.</remarks> static abstract TSelf MaxMagnitudeNumber(TSelf x, TSelf y); /// <summary>Compares two values to compute which has the lesser magnitude.</summary> /// <param name="x">The value to compare with <paramref name="y" />.</param> /// <param name="y">The value to compare with <paramref name="x" />.</param> /// <returns><paramref name="x" /> if it has a lesser magnitude than <paramref name="y" />; otherwise, <paramref name="y" />.</returns> /// <remarks>For <see cref="IFloatingPointIeee754{TSelf}" /> this method matches the IEEE 754:2019 <c>minimumMagnitude</c> function. This requires NaN inputs to be propagated back to the caller and for <c>-0.0</c> to be treated as less than <c>+0.0</c>.</remarks> static abstract TSelf MinMagnitude(TSelf x, TSelf y); /// <summary>Compares two values to compute which has the lesser magnitude and returning the other value if an input is <c>NaN</c>.</summary> /// <param name="x">The value to compare with <paramref name="y" />.</param> /// <param name="y">The value to compare with <paramref name="x" />.</param> /// <returns><paramref name="x" /> if it has a lesser magnitude than <paramref name="y" />; otherwise, <paramref name="y" />.</returns> /// <remarks>For <see cref="IFloatingPointIeee754{TSelf}" /> this method matches the IEEE 754:2019 <c>minimumMagnitudeNumber</c> function. This requires NaN inputs to not be propagated back to the caller and for <c>-0.0</c> to be treated as less than <c>+0.0</c>.</remarks> static abstract TSelf MinMagnitudeNumber(TSelf x, TSelf y); /// <summary>Computes an estimate of (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>.</summary> /// <param name="left">The value to be multiplied with <paramref name="right" />.</param> /// <param name="right">The value to be multiplied with <paramref name="left" />.</param> /// <param name="addend">The value to be added to the result of <paramref name="left" /> multiplied by <paramref name="right" />.</param> /// <returns>An estimate of (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>.</returns> /// <remarks> /// <para>On hardware that natively supports <see cref="IFloatingPointIeee754{TSelf}.FusedMultiplyAdd" />, this may return a result that was rounded as one ternary operation.</para> /// <para>On hardware without specialized support, this may just return (<paramref name="left"/> * <paramref name="right"/>) + <paramref name="addend"/>.</para> /// </remarks> static virtual TSelf MultiplyAddEstimate(TSelf left, TSelf right, TSelf addend) => (left * right) + addend; /// <summary>Parses a string into a value.</summary> /// <param name="s">The string to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="s" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="s" />.</param> /// <returns>The result of parsing <paramref name="s" />.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> /// <exception cref="ArgumentNullException"><paramref name="s" /> is <c>null</c>.</exception> /// <exception cref="FormatException"><paramref name="s" /> is not in the correct format.</exception> /// <exception cref="OverflowException"><paramref name="s" /> is not representable by <typeparamref name="TSelf" />.</exception> static abstract TSelf Parse(string s, NumberStyles style, IFormatProvider? provider); /// <summary>Parses a span of characters into a value.</summary> /// <param name="s">The span of characters to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="s" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="s" />.</param> /// <returns>The result of parsing <paramref name="s" />.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> /// <exception cref="FormatException"><paramref name="s" /> is not in the correct format.</exception> /// <exception cref="OverflowException"><paramref name="s" /> is not representable by <typeparamref name="TSelf" />.</exception> static abstract TSelf Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider); /// <summary>Parses a span of UTF-8 characters into a value.</summary> /// <param name="utf8Text">The span of UTF-8 characters to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="utf8Text" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="utf8Text" />.</param> /// <returns>The result of parsing <paramref name="utf8Text" />.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> /// <exception cref="FormatException"><paramref name="utf8Text" /> is not in the correct format.</exception> /// <exception cref="OverflowException"><paramref name="utf8Text" /> is not representable by <typeparamref name="TSelf" />.</exception> static virtual unsafe TSelf Parse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider) { // Convert text using stackalloc for <= 256 characters and ArrayPool otherwise char[]? utf16TextArray; scoped Span<char> utf16Text; int textMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Text.Length); if (textMaxCharCount <= 256) { utf16TextArray = null; utf16Text = stackalloc char[256]; } else { utf16TextArray = ArrayPool<char>.Shared.Rent(textMaxCharCount); utf16Text = utf16TextArray; } OperationStatus utf8TextStatus = Utf8.ToUtf16(utf8Text, utf16Text, out _, out int utf16TextLength, replaceInvalidSequences: false); if (utf8TextStatus != OperationStatus.Done) { if (utf16TextArray != null) { // Return rented buffers if necessary ArrayPool<char>.Shared.Return(utf16TextArray); } ThrowHelper.ThrowFormatInvalidString(); } utf16Text = utf16Text.Slice(0, utf16TextLength); // Actual operation TSelf result = TSelf.Parse(utf16Text, style, provider); // Return rented buffers if necessary if (utf16TextArray != null) { ArrayPool<char>.Shared.Return(utf16TextArray); } return result; } /// <summary>Tries to convert a value to an instance of the current type, throwing an overflow exception for any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type of <paramref name="value" />.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TSelf" />.</param> /// <param name="result">On return, contains an instance of <typeparamref name="TSelf" /> converted from <paramref name="value" />.</param> /// <returns><c>false</c> if <typeparamref name="TOther" /> is not supported; otherwise, <c>true</c>.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <typeparamref name="TSelf" />.</exception> protected static abstract bool TryConvertFromChecked<TOther>(TOther value, [MaybeNullWhen(false)] out TSelf result) #nullable disable where TOther : INumberBase<TOther>; #nullable restore /// <summary>Tries to convert a value to an instance of the current type, saturating any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type of <paramref name="value" />.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TSelf" />.</param> /// <param name="result">On return, contains an instance of <typeparamref name="TSelf" /> converted from <paramref name="value" />.</param> /// <returns><c>false</c> if <typeparamref name="TOther" /> is not supported; otherwise, <c>true</c>.</returns> protected static abstract bool TryConvertFromSaturating<TOther>(TOther value, [MaybeNullWhen(false)] out TSelf result) #nullable disable where TOther : INumberBase<TOther>; #nullable restore /// <summary>Tries to convert a value to an instance of the current type, truncating any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type of <paramref name="value" />.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TSelf" />.</param> /// <param name="result">On return, contains an instance of <typeparamref name="TSelf" /> converted from <paramref name="value" />.</param> /// <returns><c>false</c> if <typeparamref name="TOther" /> is not supported; otherwise, <c>true</c>.</returns> protected static abstract bool TryConvertFromTruncating<TOther>(TOther value, [MaybeNullWhen(false)] out TSelf result) #nullable disable where TOther : INumberBase<TOther>; #nullable restore /// <summary>Tries to convert an instance of the current type to another type, throwing an overflow exception for any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type to which <paramref name="value" /> should be converted.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TOther" />.</param> /// <param name="result">On return, contains an instance of <typeparamref name="TOther" /> converted from <paramref name="value" />.</param> /// <returns><c>false</c> if <typeparamref name="TOther" /> is not supported; otherwise, <c>true</c>.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <typeparamref name="TOther" />.</exception> protected static abstract bool TryConvertToChecked<TOther>(TSelf value, [MaybeNullWhen(false)] out TOther result) #nullable disable where TOther : INumberBase<TOther>; #nullable restore /// <summary>Tries to convert an instance of the current type to another type, saturating any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type to which <paramref name="value" /> should be converted.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TOther" />.</param> /// <param name="result">On return, contains an instance of <typeparamref name="TOther" /> converted from <paramref name="value" />.</param> /// <returns><c>false</c> if <typeparamref name="TOther" /> is not supported; otherwise, <c>true</c>.</returns> protected static abstract bool TryConvertToSaturating<TOther>(TSelf value, [MaybeNullWhen(false)] out TOther result) #nullable disable where TOther : INumberBase<TOther>; #nullable restore /// <summary>Tries to convert an instance of the current type to another type, truncating any values that fall outside the representable range of the current type.</summary> /// <typeparam name="TOther">The type to which <paramref name="value" /> should be converted.</typeparam> /// <param name="value">The value which is used to create the instance of <typeparamref name="TOther" />.</param> /// <param name="result">On return, contains an instance of <typeparamref name="TOther" /> converted from <paramref name="value" />.</param> /// <returns><c>false</c> if <typeparamref name="TOther" /> is not supported; otherwise, <c>true</c>.</returns> protected static abstract bool TryConvertToTruncating<TOther>(TSelf value, [MaybeNullWhen(false)] out TOther result) #nullable disable where TOther : INumberBase<TOther>; #nullable restore /// <summary>Tries to parse a string into a value.</summary> /// <param name="s">The string to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="s" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="s" />.</param> /// <param name="result">On return, contains the result of successfully parsing <paramref name="s" /> or an undefined value on failure.</param> /// <returns><c>true</c> if <paramref name="s" /> was successfully parsed; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> static abstract bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result); /// <summary>Tries to parse the leading portion of a string into a value, stopping at the first character that is not part of the value.</summary> /// <param name="s">The string to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="s" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="s" />.</param> /// <param name="result">On return, contains the result of successfully parsing <paramref name="s" /> or an undefined value on failure.</param> /// <param name="charsConsumed">On return, contains the number of characters consumed during parsing.</param> /// <returns><c>true</c> if the leading portion of <paramref name="s" /> was successfully parsed; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> static virtual bool TryParsePartial([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result, out int charsConsumed) { // This default implementation is a fallback for types that do not provide a true partial-parsing // implementation. It defers to the regular TryParse and, on success, reports the entire input as // consumed. Types that support stopping at the first invalid character override this method. if (TSelf.TryParse(s, style, provider, out result)) { charsConsumed = s.Length; return true; } charsConsumed = 0; return false; } /// <summary>Tries to parse a span of characters into a value.</summary> /// <param name="s">The span of characters to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="s" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="s" />.</param> /// <param name="result">On return, contains the result of successfully parsing <paramref name="s" /> or an undefined value on failure.</param> /// <returns><c>true</c> if <paramref name="s" /> was successfully parsed; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> static abstract bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result); /// <summary>Tries to parse the leading portion of a span of characters into a value, stopping at the first character that is not part of the value.</summary> /// <param name="s">The span of characters to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="s" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="s" />.</param> /// <param name="result">On return, contains the result of successfully parsing <paramref name="s" /> or an undefined value on failure.</param> /// <param name="charsConsumed">On return, contains the number of characters consumed during parsing.</param> /// <returns><c>true</c> if the leading portion of <paramref name="s" /> was successfully parsed; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> static virtual bool TryParsePartial(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result, out int charsConsumed) { // This default implementation is a fallback for types that do not provide a true partial-parsing // implementation. It defers to the regular TryParse and, on success, reports the entire input as // consumed. Types that support stopping at the first invalid character override this method. if (TSelf.TryParse(s, style, provider, out result)) { charsConsumed = s.Length; return true; } charsConsumed = 0; return false; } /// <summary>Tries to parse a span of UTF-8 characters into a value.</summary> /// <param name="utf8Text">The span of UTF-8 characters to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="utf8Text" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="utf8Text" />.</param> /// <param name="result">On return, contains the result of successfully parsing <paramref name="utf8Text" /> or an undefined value on failure.</param> /// <returns><c>true</c> if <paramref name="utf8Text" /> was successfully parsed; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> static virtual unsafe bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result) { // Convert text using stackalloc for <= 256 characters and ArrayPool otherwise char[]? utf16TextArray; scoped Span<char> utf16Text; int textMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Text.Length); if (textMaxCharCount <= 256) { utf16TextArray = null; utf16Text = stackalloc char[256]; } else { utf16TextArray = ArrayPool<char>.Shared.Rent(textMaxCharCount); utf16Text = utf16TextArray; } OperationStatus utf8TextStatus = Utf8.ToUtf16(utf8Text, utf16Text, out _, out int utf16TextLength, replaceInvalidSequences: false); if (utf8TextStatus != OperationStatus.Done) { if (utf16TextArray != null) { // Return rented buffers if necessary ArrayPool<char>.Shared.Return(utf16TextArray); } result = default; return false; } utf16Text = utf16Text.Slice(0, utf16TextLength); // Actual operation bool succeeded = TSelf.TryParse(utf16Text, style, provider, out result); // Return rented buffers if necessary if (utf16TextArray != null) { ArrayPool<char>.Shared.Return(utf16TextArray); } return succeeded; } /// <summary>Tries to parse the leading portion of a span of UTF-8 characters into a value, stopping at the first character that is not part of the value.</summary> /// <param name="utf8Text">The span of UTF-8 characters to parse.</param> /// <param name="style">A bitwise combination of number styles that can be present in <paramref name="utf8Text" />.</param> /// <param name="provider">An object that provides culture-specific formatting information about <paramref name="utf8Text" />.</param> /// <param name="result">On return, contains the result of successfully parsing <paramref name="utf8Text" /> or an undefined value on failure.</param> /// <param name="bytesConsumed">On return, contains the number of bytes consumed during parsing.</param> /// <returns><c>true</c> if the leading portion of <paramref name="utf8Text" /> was successfully parsed; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentException"><paramref name="style" /> is not a supported <see cref="NumberStyles" /> value.</exception> static virtual bool TryParsePartial(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result, out int bytesConsumed) { // This default implementation is a fallback for types that do not provide a true partial-parsing // implementation. It defers to the regular TryParse and, on success, reports the entire input as // consumed. Types that support stopping at the first invalid character override this method. if (TSelf.TryParse(utf8Text, style, provider, out result)) { bytesConsumed = utf8Text.Length; return true; } bytesConsumed = 0; return false; } unsafe bool IUtf8SpanFormattable.TryFormat(Span<byte> utf8Destination, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider? provider) { char[]? utf16DestinationArray; scoped Span<char> utf16Destination; int destinationMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Destination.Length); if (destinationMaxCharCount <= 256) { utf16DestinationArray = null; utf16Destination = stackalloc char[256]; } else { utf16DestinationArray = ArrayPool<char>.Shared.Rent(destinationMaxCharCount); utf16Destination = utf16DestinationArray; } if (!TryFormat(utf16Destination, out int charsWritten, format, provider)) { if (utf16DestinationArray != null) { // Return rented buffers if necessary ArrayPool<char>.Shared.Return(utf16DestinationArray); } bytesWritten = 0; return false; } // Make sure we slice the buffer to just the characters written utf16Destination = utf16Destination.Slice(0, charsWritten); OperationStatus utf8DestinationStatus = Utf8.FromUtf16(utf16Destination, utf8Destination, out _, out bytesWritten, replaceInvalidSequences: false); if (utf16DestinationArray != null) { // Return rented buffers if necessary ArrayPool<char>.Shared.Return(utf16DestinationArray); } if (utf8DestinationStatus == OperationStatus.Done) { return true; } if (utf8DestinationStatus != OperationStatus.DestinationTooSmall) { ThrowHelper.ThrowInvalidOperationException_InvalidUtf8(); } bytesWritten = 0; return false; } static unsafe TSelf IUtf8SpanParsable<TSelf>.Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) { // Convert text using stackalloc for <= 256 characters and ArrayPool otherwise char[]? utf16TextArray; scoped Span<char> utf16Text; int textMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Text.Length); if (textMaxCharCount <= 256) { utf16TextArray = null; utf16Text = stackalloc char[256]; } else { utf16TextArray = ArrayPool<char>.Shared.Rent(textMaxCharCount); utf16Text = utf16TextArray; } OperationStatus utf8TextStatus = Utf8.ToUtf16(utf8Text, utf16Text, out _, out int utf16TextLength, replaceInvalidSequences: false); if (utf8TextStatus != OperationStatus.Done) { if (utf16TextArray != null) { // Return rented buffers if necessary ArrayPool<char>.Shared.Return(utf16TextArray); } ThrowHelper.ThrowFormatInvalidString(); } utf16Text = utf16Text.Slice(0, utf16TextLength); // Actual operation TSelf result = TSelf.Parse(utf16Text, provider); // Return rented buffers if necessary if (utf16TextArray != null) { ArrayPool<char>.Shared.Return(utf16TextArray); } return result; } static unsafe bool IUtf8SpanParsable<TSelf>.TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, [MaybeNullWhen(returnValue: false)] out TSelf result) { // Convert text using stackalloc for <= 256 characters and ArrayPool otherwise char[]? utf16TextArray; scoped Span<char> utf16Text; int textMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Text.Length); if (textMaxCharCount <= 256) { utf16TextArray = null; utf16Text = stackalloc char[256]; } else { utf16TextArray = ArrayPool<char>.Shared.Rent(textMaxCharCount); utf16Text = utf16TextArray; } OperationStatus utf8TextStatus = Utf8.ToUtf16(utf8Text, utf16Text, out _, out int utf16TextLength, replaceInvalidSequences: false); if (utf8TextStatus != OperationStatus.Done) { if (utf16TextArray != null) { // Return rented buffers if necessary ArrayPool<char>.Shared.Return(utf16TextArray); } result = default; return false; } utf16Text = utf16Text.Slice(0, utf16TextLength); // Actual operation bool succeeded = TSelf.TryParse(utf16Text, provider, out result); // Return rented buffers if necessary if (utf16TextArray != null) { ArrayPool<char>.Shared.Return(utf16TextArray); } return succeeded; } } }