diff --git a/src/libraries/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.Negative.cs b/src/libraries/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.Negative.cs deleted file mode 100644 index 9abf2fb650ebc..0000000000000 --- a/src/libraries/System.Memory/tests/ParsersAndFormatters/Formatter/FormatterTests.Negative.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Xunit; - -namespace System.Buffers.Text.Tests -{ - public static partial class FormatterTests - { - [Theory] - [MemberData(nameof(TestData.TypesThatCanBeFormatted), MemberType = typeof(TestData))] - public static void TestBadFormat(Type integerType) - { - if ((integerType == typeof(double)) || (integerType == typeof(float))) - { - // double and float support all the same formats as the UTF16 formatter - return; - } - - object value = Activator.CreateInstance(integerType); - Assert.Throws(() => TryFormatUtf8(value, Array.Empty(), out int bytesWritten, new StandardFormat('$', 1))); - } - - [Theory] - [MemberData(nameof(TestData.IntegerTypesTheoryData), MemberType = typeof(TestData))] - [InlineData(typeof(decimal))] - public static void TestGFormatWithPrecisionNotSupported(Type type) - { - object value = Activator.CreateInstance(type); - Assert.Throws(() => TryFormatUtf8(value, Array.Empty(), out int bytesWritten, new StandardFormat('G', 1))); - Assert.Throws(() => TryFormatUtf8(value, Array.Empty(), out int bytesWritten, new StandardFormat('g', 1))); - } - } -} diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index e35c06dfd3708..e7748a30e2488 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -248,7 +248,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index eaae7a28b9c46..16ba6ff21026e 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -131,21 +131,9 @@ - - - - - - - - - - - - @@ -516,6 +504,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs index 784b08c5ec555..91dca61b2415a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs @@ -43,7 +43,7 @@ namespace System.Buffers /// /// true if the StandardFormat == default(StandardFormat) /// - public bool IsDefault => _format == 0 && _precision == 0; + public bool IsDefault => (_format | _precision) == 0; /// /// Create a StandardFormat. @@ -144,12 +144,7 @@ private static bool ParseHelper(ReadOnlySpan format, out StandardFormat st /// /// Returns the format in classic .NET format. /// - public override string ToString() - { - Span buffer = stackalloc char[FormatStringLength]; - int charsWritten = Format(buffer); - return new string(buffer.Slice(0, charsWritten)); - } + public override string ToString() => new string(Format(stackalloc char[FormatStringLength])); /// The exact buffer length required by . internal const int FormatStringLength = 3; @@ -157,17 +152,15 @@ public override string ToString() /// /// Formats the format in classic .NET format. /// - internal int Format(Span destination) + internal Span Format(Span destination) { Debug.Assert(destination.Length == FormatStringLength); - int count = 0; char symbol = Symbol; if (symbol != default && destination.Length == FormatStringLength) { destination[0] = symbol; - count = 1; uint precision = Precision; if (precision != NoPrecision) @@ -185,15 +178,18 @@ internal int Format(Span destination) uint div; (div, precision) = Math.DivRem(precision, 10); destination[1] = (char)('0' + div % 10); - count = 2; + destination[2] = (char)('0' + precision); + return destination; } - destination[count] = (char)('0' + precision); - count++; + destination[1] = (char)('0' + precision); + return destination.Slice(0, 2); } + + return destination.Slice(0, 1); } - return count; + return default; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Constants.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Constants.cs index 704d0d91e0b33..e4f226cd4d0b5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Constants.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Constants.cs @@ -14,8 +14,6 @@ internal static partial class Utf8Constants public const byte Space = (byte)' '; public const byte Hyphen = (byte)'-'; - public const byte Separator = (byte)','; - // Invariant formatting uses groups of 3 for each number group separated by commas. // ex. 1,234,567,890 public const int GroupSize = 3; @@ -26,8 +24,5 @@ internal static partial class Utf8Constants public const int DateTimeNumFractionDigits = 7; // TimeSpan and DateTime formats allow exactly up to many digits for specifying the fraction after the seconds. public const int MaxDateTimeFraction = 9999999; // ... and hence, the largest fraction expressible is this. - - public const ulong BillionMaxUIntValue = (ulong)uint.MaxValue * Billion; // maximum value that can be split into two uint32 {1-10 digits}{9 digits} - public const uint Billion = 1000000000; // 10^9, used to split int64/uint64 into three uint32 {1-2 digits}{9 digits}{9 digits} } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/FormattingHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/FormattingHelpers.cs index a3022370d88bd..7a86f1943ec46 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/FormattingHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/FormattingHelpers.cs @@ -1,18 +1,24 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Numerics; +using System.Globalization; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System.Buffers.Text { - // All the helper methods in this class assume that the by-ref is valid and that there is - // enough space to fit the items that will be written into the underlying memory. The calling - // code must have already done all the necessary validation. internal static partial class FormattingHelpers { + public static bool TryFormat(T value, Span utf8Destination, out int bytesWritten, StandardFormat format) where T : IUtf8SpanFormattable + { + scoped Span formatText = default; + if (!format.IsDefault) + { + formatText = format.Format(stackalloc char[StandardFormat.FormatStringLength]); + } + + return value.TryFormat(utf8Destination, out bytesWritten, formatText, CultureInfo.InvariantCulture); + } + /// /// Returns the symbol contained within the standard format. If the standard format /// has not been initialized, returns the provided fallback symbol. @@ -32,138 +38,5 @@ public static char GetSymbolOrDefault(in StandardFormat format, char defaultSymb } return symbol; } - - /// - /// Fills a buffer with the ASCII character '0' (0x30). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void FillWithAsciiZeros(Span buffer) - { - // This is a faster implementation of Span.Fill() for very short buffers. - for (int i = 0; i < buffer.Length; i++) - { - buffer[i] = (byte)'0'; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteDigits(ulong value, Span buffer) where TChar : unmanaged, IBinaryInteger - { - // We can mutate the 'value' parameter since it's a copy-by-value local. - // It'll be used to represent the value left over after each division by 10. - - for (int i = buffer.Length - 1; i >= 1; i--) - { - ulong temp = '0' + value; - value /= 10; - buffer[i] = TChar.CreateTruncating(temp - (value * 10)); - } - - Debug.Assert(value < 10); - buffer[0] = TChar.CreateTruncating('0' + value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteDigitsWithGroupSeparator(ulong value, Span buffer) - { - // We can mutate the 'value' parameter since it's a copy-by-value local. - // It'll be used to represent the value left over after each division by 10. - - int digitsWritten = 0; - for (int i = buffer.Length - 1; i >= 1; i--) - { - ulong temp = '0' + value; - value /= 10; - buffer[i] = (byte)(temp - (value * 10)); - if (digitsWritten == Utf8Constants.GroupSize - 1) - { - buffer[--i] = Utf8Constants.Comma; - digitsWritten = 0; - } - else - { - digitsWritten++; - } - } - - Debug.Assert(value < 10); - buffer[0] = (byte)('0' + value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteDigits(uint value, Span buffer) where TChar : unmanaged, IBinaryInteger - { - Debug.Assert(buffer.Length > 0); - - for (int i = buffer.Length - 1; i >= 1; i--) - { - uint temp = '0' + value; - value /= 10; - buffer[i] = TChar.CreateTruncating(temp - (value * 10)); - } - - Debug.Assert(value < 10); - buffer[0] = TChar.CreateTruncating('0' + value); - } - - /// - /// Writes a value [ 00 .. 99 ] to the buffer starting at the specified offset. - /// This method performs best when the starting index is a constant literal. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteTwoDigits(uint value, Span buffer, int startingIndex = 0) where TChar : unmanaged, IBinaryInteger - { - Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); - Debug.Assert(value <= 99); - Debug.Assert(startingIndex <= buffer.Length - 2); - - fixed (TChar* bufferPtr = &MemoryMarshal.GetReference(buffer)) - { - Number.WriteTwoDigits(bufferPtr + startingIndex, value); - } - } - - /// - /// Writes a value [ 0000 .. 9999 ] to the buffer starting at the specified offset. - /// This method performs best when the starting index is a constant literal. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteFourDigits(uint value, Span buffer, int startingIndex = 0) where TChar : unmanaged, IBinaryInteger - { - Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); - Debug.Assert(value <= 9999); - Debug.Assert(startingIndex <= buffer.Length - 4); - - (value, uint remainder) = Math.DivRem(value, 100); - fixed (TChar* bufferPtr = &MemoryMarshal.GetReference(buffer)) - { - Number.WriteTwoDigits(bufferPtr + startingIndex, value); - Number.WriteTwoDigits(bufferPtr + startingIndex + 2, remainder); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CopyFour(ReadOnlySpan source, Span destination) where TChar : unmanaged, IBinaryInteger - { - if (typeof(TChar) == typeof(byte)) - { - Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), - Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(source)))); - } - else - { - Debug.Assert(typeof(TChar) == typeof(char)); - Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), - Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(source)))); - } - } - - /// Enable use of ThrowHelper from TryFormat() routines without introducing dozens of non-code-coveraged "bytesWritten = 0; return false" boilerplate. - public static bool TryFormatThrowFormatException(out int bytesWritten) - { - bytesWritten = 0; - ThrowHelper.ThrowFormatException_BadFormatSpecifier(); - return false; - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Boolean.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Boolean.cs index 220a49308ff4e..70c0ba85887c2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Boolean.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Boolean.cs @@ -1,8 +1,6 @@ // 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.Binary; - namespace System.Buffers.Text { public static partial class Utf8Formatter @@ -34,19 +32,14 @@ public static bool TryFormat(bool value, Span destination, out int bytesWr { if (symbol == 'G') { - // By having each branch perform its own call to TryWriteUInt32BigEndian, we ensure that a - // constant value is passed to this routine, which means the compiler can reverse endianness - // at compile time instead of runtime if necessary. - const uint TrueValueUppercase = ('T' << 24) + ('r' << 16) + ('u' << 8) + ('e' << 0); - if (!BinaryPrimitives.TryWriteUInt32BigEndian(destination, TrueValueUppercase)) + if (!"True"u8.TryCopyTo(destination)) { goto BufferTooSmall; } } else if (symbol == 'l') { - const uint TrueValueLowercase = ('t' << 24) + ('r' << 16) + ('u' << 8) + ('e' << 0); - if (!BinaryPrimitives.TryWriteUInt32BigEndian(destination, TrueValueLowercase)) + if (!"true"u8.TryCopyTo(destination)) { goto BufferTooSmall; } @@ -63,42 +56,33 @@ public static bool TryFormat(bool value, Span destination, out int bytesWr { if (symbol == 'G') { - // This check can't be performed earlier because we need to throw if an invalid symbol is - // provided, even if the buffer is too small. - if (destination.Length <= 4) + if (!"False"u8.TryCopyTo(destination)) { goto BufferTooSmall; } - - const uint FalsValueUppercase = ('F' << 24) + ('a' << 16) + ('l' << 8) + ('s' << 0); - BinaryPrimitives.WriteUInt32BigEndian(destination, FalsValueUppercase); } else if (symbol == 'l') { - if (destination.Length <= 4) + if (!"false"u8.TryCopyTo(destination)) { goto BufferTooSmall; } - - const uint FalsValueLowercase = ('f' << 24) + ('a' << 16) + ('l' << 8) + ('s' << 0); - BinaryPrimitives.WriteUInt32BigEndian(destination, FalsValueLowercase); } else { goto BadFormat; } - destination[4] = (byte)'e'; bytesWritten = 5; return true; } + BadFormat: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + BufferTooSmall: bytesWritten = 0; return false; - - BadFormat: - return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.G.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.G.cs index 9c38cb08399c4..2a5d463e05316 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.G.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.G.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.InteropServices; + namespace System.Buffers.Text { public static partial class Utf8Formatter @@ -18,7 +20,7 @@ public static partial class Utf8Formatter // -------------------------- // 05/25/2017 10:30:15 -08:00 // - private static bool TryFormatDateTimeG(DateTime value, TimeSpan offset, Span destination, out int bytesWritten) + private static unsafe bool TryFormatDateTimeG(DateTime value, TimeSpan offset, Span destination, out int bytesWritten) { const int MinimumBytesNeeded = 19; @@ -37,54 +39,51 @@ private static bool TryFormatDateTimeG(DateTime value, TimeSpan offset, Span destination, out int bytesWritten) { - if (destination.Length <= 28) + if (((IUtf8SpanFormattable)value).TryFormat(destination, out bytesWritten, "r", CultureInfo.InvariantCulture)) { - bytesWritten = 0; - return false; + Debug.Assert(bytesWritten == 29); + Ascii.ToLowerInPlace(destination.Slice(0, bytesWritten), out bytesWritten); + return true; } - value.GetDate(out int year, out int month, out int day); - value.GetTime(out int hour, out int minute, out int second); - - FormattingHelpers.CopyFour("sun,mon,tue,wed,thu,fri,sat,"u8.Slice(4 * (int)value.DayOfWeek), destination); - destination[4] = Utf8Constants.Space; - - FormattingHelpers.WriteTwoDigits((uint)day, destination, 5); - destination[7] = Utf8Constants.Space; - - FormattingHelpers.CopyFour("jan feb mar apr may jun jul aug sep oct nov dec "u8.Slice(4 * (month - 1)), destination.Slice(8)); - - FormattingHelpers.WriteFourDigits((uint)year, destination, 12); - destination[16] = Utf8Constants.Space; - - FormattingHelpers.WriteTwoDigits((uint)hour, destination, 17); - destination[19] = Utf8Constants.Colon; - - FormattingHelpers.WriteTwoDigits((uint)minute, destination, 20); - destination[22] = Utf8Constants.Colon; - - FormattingHelpers.WriteTwoDigits((uint)second, destination, 23); - - FormattingHelpers.CopyFour(" gmt"u8, destination.Slice(25)); - - bytesWritten = 29; - return true; + return false; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.cs index 7c4885eb432e0..3d8e68b9c4660 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Date.cs @@ -37,13 +37,23 @@ public static bool TryFormat(DateTimeOffset value, Span destination, out i offset = value.Offset; } - return symbol switch + switch (symbol) { - 'R' => DateTimeFormat.TryFormatR(value.UtcDateTime, new TimeSpan(DateTimeFormat.NullOffset), destination, out bytesWritten), - 'l' => TryFormatDateTimeL(value.UtcDateTime, destination, out bytesWritten), - 'O' => DateTimeFormat.TryFormatO(value.DateTime, value.Offset, destination, out bytesWritten), - 'G' => TryFormatDateTimeG(value.DateTime, offset, destination, out bytesWritten), - _ => FormattingHelpers.TryFormatThrowFormatException(out bytesWritten), + case 'R': + return DateTimeFormat.TryFormatR(value.UtcDateTime, new TimeSpan(DateTimeFormat.NullOffset), destination, out bytesWritten); + + case 'O': + return DateTimeFormat.TryFormatO(value.DateTime, value.Offset, destination, out bytesWritten); + + case 'l': + return TryFormatDateTimeL(value.UtcDateTime, destination, out bytesWritten); + + case 'G': + return TryFormatDateTimeG(value.DateTime, offset, destination, out bytesWritten); + + default: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + goto case 'R'; }; } @@ -70,16 +80,24 @@ public static bool TryFormat(DateTimeOffset value, Span destination, out i /// public static bool TryFormat(DateTime value, Span destination, out int bytesWritten, StandardFormat format = default) { - char symbol = FormattingHelpers.GetSymbolOrDefault(format, 'G'); - - return symbol switch + switch (FormattingHelpers.GetSymbolOrDefault(format, 'G')) { - 'R' => DateTimeFormat.TryFormatR(value, new TimeSpan(DateTimeFormat.NullOffset), destination, out bytesWritten), - 'l' => TryFormatDateTimeL(value, destination, out bytesWritten), - 'O' => DateTimeFormat.TryFormatO(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten), - 'G' => TryFormatDateTimeG(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten), - _ => FormattingHelpers.TryFormatThrowFormatException(out bytesWritten), - }; + case 'R': + return DateTimeFormat.TryFormatR(value, new TimeSpan(DateTimeFormat.NullOffset), destination, out bytesWritten); + + case 'O': + return DateTimeFormat.TryFormatO(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten); + + case 'l': + return TryFormatDateTimeL(value, destination, out bytesWritten); + + case 'G': + return TryFormatDateTimeG(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten); + + default: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + goto case 'R'; // unreachable + } } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.E.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.E.cs deleted file mode 100644 index b9cb146fe4819..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.E.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace System.Buffers.Text -{ - public static partial class Utf8Formatter - { - private static bool TryFormatDecimalE(ref Number.NumberBuffer number, Span destination, out int bytesWritten, byte precision, byte exponentSymbol) - { - const int NumExponentDigits = 3; - - int scale = number.Scale; - ReadOnlySpan digits = number.Digits; - - int numBytesNeeded = - ((number.IsNegative) ? 1 : 0) // minus sign - + 1 // digits before the decimal point (exactly 1) - + ((precision == 0) ? 0 : (precision + 1)) // period and the digits after the decimal point - + 2 // 'E' or 'e' followed by '+' or '-' - + NumExponentDigits; // exponent digits - - if (destination.Length < numBytesNeeded) - { - bytesWritten = 0; - return false; - } - - int dstIndex = 0; - int srcIndex = 0; - if (number.IsNegative) - { - destination[dstIndex++] = Utf8Constants.Minus; - } - - // - // Emit exactly one digit before the decimal point. - // - int exponent; - byte firstDigit = digits[srcIndex]; - if (firstDigit == 0) - { - destination[dstIndex++] = (byte)'0'; // Special case: number before the decimal point is exactly 0: Number does not store the zero in this case. - exponent = 0; - } - else - { - destination[dstIndex++] = firstDigit; - srcIndex++; - exponent = scale - 1; - } - - if (precision > 0) - { - destination[dstIndex++] = Utf8Constants.Period; - - // - // Emit digits after the decimal point. - // - int numDigitsEmitted = 0; - while (numDigitsEmitted < precision) - { - byte digit = digits[srcIndex]; - if (digit == 0) - { - while (numDigitsEmitted++ < precision) - { - destination[dstIndex++] = (byte)'0'; - } - break; - } - destination[dstIndex++] = digit; - srcIndex++; - numDigitsEmitted++; - } - } - - // Emit the exponent symbol - destination[dstIndex++] = exponentSymbol; - if (exponent >= 0) - { - destination[dstIndex++] = Utf8Constants.Plus; - } - else - { - destination[dstIndex++] = Utf8Constants.Minus; - exponent = -exponent; - } - - Debug.Assert(exponent < Number.DecimalPrecision, "If you're trying to reuse this routine for double/float, you'll need to review the code carefully for Decimal-specific assumptions."); - - // Emit exactly three digits for the exponent. - destination[dstIndex++] = (byte)'0'; // The exponent for Decimal can never exceed 28 (let alone 99) - destination[dstIndex++] = (byte)((exponent / 10) + '0'); - destination[dstIndex++] = (byte)((exponent % 10) + '0'); - - Debug.Assert(dstIndex == numBytesNeeded); - bytesWritten = numBytesNeeded; - return true; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.F.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.F.cs deleted file mode 100644 index b569dffce00b7..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.F.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace System.Buffers.Text -{ - public static partial class Utf8Formatter - { - private static bool TryFormatDecimalF(ref Number.NumberBuffer number, Span destination, out int bytesWritten, byte precision) - { - int scale = number.Scale; - ReadOnlySpan digits = number.Digits; - - int numBytesNeeded = - ((number.IsNegative) ? 1 : 0) // minus sign - + ((scale <= 0) ? 1 : scale) // digits before the decimal point (minimum 1) - + ((precision == 0) ? 0 : (precision + 1)); // if specified precision != 0, the decimal point and the digits after the decimal point (padded with zeroes if needed) - - if (destination.Length < numBytesNeeded) - { - bytesWritten = 0; - return false; - } - - int srcIndex = 0; - int dstIndex = 0; - if (number.IsNegative) - { - destination[dstIndex++] = Utf8Constants.Minus; - } - - // - // Emit digits before the decimal point. - // - if (scale <= 0) - { - destination[dstIndex++] = (byte)'0'; // The integer portion is 0 and not stored. The formatter, however, needs to emit it. - } - else - { - while (srcIndex < scale) - { - byte digit = digits[srcIndex]; - if (digit == 0) - { - int numTrailingZeroes = scale - srcIndex; - for (int i = 0; i < numTrailingZeroes; i++) - { - destination[dstIndex++] = (byte)'0'; - } - break; - } - - destination[dstIndex++] = digit; - srcIndex++; - } - } - - if (precision > 0) - { - destination[dstIndex++] = Utf8Constants.Period; - - // - // Emit digits after the decimal point. - // - int numDigitsEmitted = 0; - if (scale < 0) - { - int numLeadingZeroesToEmit = Math.Min((int)precision, -scale); - for (int i = 0; i < numLeadingZeroesToEmit; i++) - { - destination[dstIndex++] = (byte)'0'; - } - numDigitsEmitted += numLeadingZeroesToEmit; - } - - while (numDigitsEmitted < precision) - { - byte digit = digits[srcIndex]; - if (digit == 0) - { - while (numDigitsEmitted++ < precision) - { - destination[dstIndex++] = (byte)'0'; - } - break; - } - destination[dstIndex++] = digit; - srcIndex++; - numDigitsEmitted++; - } - } - - Debug.Assert(dstIndex == numBytesNeeded); - bytesWritten = numBytesNeeded; - return true; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.G.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.G.cs deleted file mode 100644 index ea14ef852e4c4..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.G.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace System.Buffers.Text -{ - public static partial class Utf8Formatter - { - private static bool TryFormatDecimalG(ref Number.NumberBuffer number, Span destination, out int bytesWritten) - { - int scale = number.Scale; - ReadOnlySpan digits = number.Digits; - int numDigits = number.DigitsCount; - - bool isFraction = scale < numDigits; - int numBytesNeeded; - if (isFraction) - { - numBytesNeeded = numDigits + 1; // A fraction. Must include one for the decimal point. - if (scale <= 0) - { - numBytesNeeded += 1 + (-scale); // A fraction of the form 0.ddd. Need to emit the non-stored 0 before the decimal point plus (-scale) leading 0's after the decimal point. - } - } - else - { - numBytesNeeded = ((scale <= 0) ? 1 : scale); // An integral. Just emit the digits before the decimal point (minimum 1) and no decimal point. - } - - if (number.IsNegative) - { - numBytesNeeded++; // And the minus sign. - } - - if (destination.Length < numBytesNeeded) - { - bytesWritten = 0; - return false; - } - - int srcIndex = 0; - int dstIndex = 0; - - if (number.IsNegative) - { - destination[dstIndex++] = Utf8Constants.Minus; - } - - // - // Emit digits before the decimal point. - // - if (scale <= 0) - { - destination[dstIndex++] = (byte)'0'; // The integer portion is 0 and not stored. The formatter, however, needs to emit it. - } - else - { - while (srcIndex < scale) - { - byte digit = digits[srcIndex]; - if (digit == 0) - { - int numTrailingZeroes = scale - srcIndex; - for (int i = 0; i < numTrailingZeroes; i++) - { - destination[dstIndex++] = (byte)'0'; - } - break; - } - - destination[dstIndex++] = digit; - srcIndex++; - } - } - - if (isFraction) - { - destination[dstIndex++] = Utf8Constants.Period; - - // - // Emit digits after the decimal point. - // - if (scale < 0) - { - int numLeadingZeroesToEmit = -scale; - for (int i = 0; i < numLeadingZeroesToEmit; i++) - { - destination[dstIndex++] = (byte)'0'; - } - } - - byte digit; - while ((digit = digits[srcIndex++]) != 0) - { - destination[dstIndex++] = digit; - } - } - - Debug.Assert(dstIndex == numBytesNeeded); - - bytesWritten = numBytesNeeded; - return true; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.cs index 16c102b89887e..1f619009c0594 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Decimal.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; - namespace System.Buffers.Text { public static partial class Utf8Formatter @@ -27,89 +25,7 @@ public static partial class Utf8Formatter /// /// System.FormatException if the format is not valid for this data type. /// - public static unsafe bool TryFormat(decimal value, Span destination, out int bytesWritten, StandardFormat format = default) - { - if (format.IsDefault) - { - format = 'G'; - } - - switch (format.Symbol) - { - case 'g': - case 'G': - { - if (format.Precision != StandardFormat.NoPrecision) - throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); - - Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, stackalloc byte[Number.DecimalNumberBufferLength]); - - Number.DecimalToNumber(ref value, ref number); - if (number.Digits[0] == 0) - { - number.IsNegative = false; // For Decimals, -0 must print as normal 0. - } - bool success = TryFormatDecimalG(ref number, destination, out bytesWritten); -#if DEBUG - // This DEBUG segment exists to close a code coverage hole inside TryFormatDecimalG(). Because we don't call RoundNumber() on this path, we have no way to feed - // TryFormatDecimalG() a number where trailing zeros before the decimal point have been cropped. So if the chance comes up, we'll crop the zeroes - // ourselves and make a second call to ensure we get the same outcome. - if (success) - { - Span digits = number.Digits; - int numDigits = number.DigitsCount; - if (numDigits != 0 && number.Scale == numDigits && digits[numDigits - 1] == '0') - { - while (numDigits != 0 && digits[numDigits - 1] == '0') - { - digits[numDigits - 1] = 0; - numDigits--; - } - - number.DigitsCount = numDigits; - number.CheckConsistency(); - - byte[] buffer2 = new byte[destination.Length]; - bool success2 = TryFormatDecimalG(ref number, buffer2, out int bytesWritten2); - Debug.Assert(success2); - Debug.Assert(bytesWritten2 == bytesWritten); - for (int i = 0; i < bytesWritten; i++) - { - Debug.Assert(destination[i] == buffer2[i]); - } - } - } -#endif // DEBUG - return success; - } - - case 'f': - case 'F': - { - Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, stackalloc byte[Number.DecimalNumberBufferLength]); - - Number.DecimalToNumber(ref value, ref number); - byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)2 : format.Precision; - Number.RoundNumber(ref number, number.Scale + precision, isCorrectlyRounded: false); - Debug.Assert((number.Digits[0] != 0) || !number.IsNegative); // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant. - return TryFormatDecimalF(ref number, destination, out bytesWritten, precision); - } - - case 'e': - case 'E': - { - Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, stackalloc byte[Number.DecimalNumberBufferLength]); - - Number.DecimalToNumber(ref value, ref number); - byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)6 : format.Precision; - Number.RoundNumber(ref number, precision + 1, isCorrectlyRounded: false); - Debug.Assert((number.Digits[0] != 0) || !number.IsNegative); // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant. - return TryFormatDecimalE(ref number, destination, out bytesWritten, precision, exponentSymbol: (byte)format.Symbol); - } - - default: - return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten); - } - } + public static bool TryFormat(decimal value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Float.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Float.cs index bbbcee7d0b1bf..3827e79a948a6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Float.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Float.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Globalization; -using System.Text; - namespace System.Buffers.Text { public static partial class Utf8Formatter @@ -28,10 +25,8 @@ public static partial class Utf8Formatter /// /// System.FormatException if the format is not valid for this data type. /// - public static bool TryFormat(double value, Span destination, out int bytesWritten, StandardFormat format = default) - { - return TryFormatFloatingPoint(value, destination, out bytesWritten, format); - } + public static bool TryFormat(double value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); /// /// Formats a Single as a UTF8 string. @@ -53,69 +48,7 @@ public static bool TryFormat(double value, Span destination, out int bytes /// /// System.FormatException if the format is not valid for this data type. /// - public static bool TryFormat(float value, Span destination, out int bytesWritten, StandardFormat format = default) - { - return TryFormatFloatingPoint(value, destination, out bytesWritten, format); - } - - private static bool TryFormatFloatingPoint(T value, Span destination, out int bytesWritten, StandardFormat format) where T : ISpanFormattable - { - scoped Span formatText = default; - - if (!format.IsDefault) - { - formatText = stackalloc char[StandardFormat.FormatStringLength]; - int formatTextLength = format.Format(formatText); - formatText = formatText.Slice(0, formatTextLength); - } - - // We first try to format into a stack-allocated buffer, and if it succeeds, we can avoid - // all allocation. If that fails, we fall back to allocating strings. If it proves impactful, - // that allocation (as well as roundtripping from byte to char and back to byte) could be avoided by - // calling into a refactored Number.FormatSingle/Double directly. - - const int StackBufferLength = 128; // large enough to handle the majority cases - Span stackBuffer = stackalloc char[StackBufferLength]; - scoped ReadOnlySpan utf16Text; - - // Try to format into the stack buffer. If we're successful, we can avoid all allocations. - if (value.TryFormat(stackBuffer, out int formattedLength, formatText, CultureInfo.InvariantCulture)) - { - utf16Text = stackBuffer.Slice(0, formattedLength); - } - else - { - // The stack buffer wasn't large enough. If the destination buffer isn't at least as - // big as the stack buffer, we know the whole operation will eventually fail, so we - // can just fail now. - if (destination.Length <= StackBufferLength) - { - bytesWritten = 0; - return false; - } - - // Fall back to using a string format and allocating a string for the resulting formatted value. - utf16Text = value.ToString(new string(formatText), CultureInfo.InvariantCulture); - } - - // Copy the value to the destination, if it's large enough. - - if (utf16Text.Length > destination.Length) - { - bytesWritten = 0; - return false; - } - - try - { - bytesWritten = Encoding.UTF8.GetBytes(utf16Text, destination); - return true; - } - catch - { - bytesWritten = 0; - return false; - } - } + public static bool TryFormat(float value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs index db604c2bc3ef8..8effbc24857b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs @@ -139,7 +139,8 @@ public static bool TryFormat(Guid value, Span destination, out int bytesWr break; default: - return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten); + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + goto case 'D'; // unreachable } // At this point, the low byte of flags contains the minimum required length diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.D.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.D.cs deleted file mode 100644 index 0e43b0537165b..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.D.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatInt64D(long value, byte precision, Span destination, out int bytesWritten) - { - bool insertNegationSign = false; - if (value < 0) - { - insertNegationSign = true; - value = -value; - } - - return TryFormatUInt64D((ulong)value, precision, destination, insertNegationSign, out bytesWritten); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.Default.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.Default.cs deleted file mode 100644 index 634f69e48fb85..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.Default.cs +++ /dev/null @@ -1,147 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatInt64Default(long value, Span destination, out int bytesWritten) - { - if ((ulong)value < 10) - { - return TryFormatUInt32SingleDigit((uint)value, destination, out bytesWritten); - } - - if (IntPtr.Size == 8) // x64 - { - return TryFormatInt64MultipleDigits(value, destination, out bytesWritten); - } - else // x86 - { - if (value <= int.MaxValue && value >= int.MinValue) - { - return TryFormatInt32MultipleDigits((int)value, destination, out bytesWritten); - } - else - { - if (value <= (long)Utf8Constants.BillionMaxUIntValue && value >= -(long)Utf8Constants.BillionMaxUIntValue) - { - return value < 0 ? - TryFormatInt64MoreThanNegativeBillionMaxUInt(-value, destination, out bytesWritten) : - TryFormatUInt64LessThanBillionMaxUInt((ulong)value, destination, out bytesWritten); - } - else - { - return value < 0 ? - TryFormatInt64LessThanNegativeBillionMaxUInt(-value, destination, out bytesWritten) : - TryFormatUInt64MoreThanBillionMaxUInt((ulong)value, destination, out bytesWritten); - } - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatInt32MultipleDigits(int value, Span destination, out int bytesWritten) - { - if (value < 0) - { - value = -value; - int digitCount = FormattingHelpers.CountDigits((uint)value); - // WriteDigits does not do bounds checks - if (digitCount >= destination.Length) - { - bytesWritten = 0; - return false; - } - destination[0] = Utf8Constants.Minus; - bytesWritten = digitCount + 1; - FormattingHelpers.WriteDigits((uint)value, destination.Slice(1, digitCount)); - return true; - } - else - { - return TryFormatUInt32MultipleDigits((uint)value, destination, out bytesWritten); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatInt64MultipleDigits(long value, Span destination, out int bytesWritten) - { - if (value < 0) - { - value = -value; - int digitCount = FormattingHelpers.CountDigits((ulong)value); - // WriteDigits does not do bounds checks - if (digitCount >= destination.Length) - { - bytesWritten = 0; - return false; - } - destination[0] = Utf8Constants.Minus; - bytesWritten = digitCount + 1; - FormattingHelpers.WriteDigits((ulong)value, destination.Slice(1, digitCount)); - return true; - } - else - { - return TryFormatUInt64MultipleDigits((ulong)value, destination, out bytesWritten); - } - } - - // Split long into two parts that can each fit in a uint - {1-10 digits}{9 digits} - private static bool TryFormatInt64MoreThanNegativeBillionMaxUInt(long value, Span destination, out int bytesWritten) - { - uint overNineDigits = (uint)(value / Utf8Constants.Billion); - uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); - - int digitCountOverNineDigits = FormattingHelpers.CountDigits(overNineDigits); - Debug.Assert(digitCountOverNineDigits >= 1 && digitCountOverNineDigits <= 10); - int digitCount = digitCountOverNineDigits + 9; - // WriteDigits does not do bounds checks - if (digitCount >= destination.Length) - { - bytesWritten = 0; - return false; - } - destination[0] = Utf8Constants.Minus; - bytesWritten = digitCount + 1; - FormattingHelpers.WriteDigits(overNineDigits, destination.Slice(1, digitCountOverNineDigits)); - FormattingHelpers.WriteDigits(lastNineDigits, destination.Slice(digitCountOverNineDigits + 1, 9)); - return true; - } - - // Split long into three parts that can each fit in a uint - {1 digit}{9 digits}{9 digits} - private static bool TryFormatInt64LessThanNegativeBillionMaxUInt(long value, Span destination, out int bytesWritten) - { - // value can still be negative if value == long.MinValue - // Therefore, cast to ulong, since (ulong)value actually equals abs(long.MinValue) - ulong overNineDigits = (ulong)value / Utf8Constants.Billion; - uint lastNineDigits = (uint)((ulong)value - (overNineDigits * Utf8Constants.Billion)); - uint overEighteenDigits = (uint)(overNineDigits / Utf8Constants.Billion); - uint middleNineDigits = (uint)(overNineDigits - (overEighteenDigits * Utf8Constants.Billion)); - - int digitCountOverEighteenDigits = FormattingHelpers.CountDigits(overEighteenDigits); - Debug.Assert(digitCountOverEighteenDigits == 1); - int digitCount = digitCountOverEighteenDigits + 18; - // WriteDigits does not do bounds checks - if (digitCount >= destination.Length) - { - bytesWritten = 0; - return false; - } - destination[0] = Utf8Constants.Minus; - bytesWritten = digitCount + 1; - FormattingHelpers.WriteDigits(overEighteenDigits, destination.Slice(1, digitCountOverEighteenDigits)); - FormattingHelpers.WriteDigits(middleNineDigits, destination.Slice(digitCountOverEighteenDigits + 1, 9)); - FormattingHelpers.WriteDigits(lastNineDigits, destination.Slice(digitCountOverEighteenDigits + 1 + 9, 9)); - return true; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.N.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.N.cs deleted file mode 100644 index dcf8812c278c3..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.N.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatInt64N(long value, byte precision, Span destination, out int bytesWritten) - { - bool insertNegationSign = false; - if (value < 0) - { - insertNegationSign = true; - value = -value; - } - - return TryFormatUInt64N((ulong)value, precision, destination, insertNegationSign, out bytesWritten); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.cs deleted file mode 100644 index 3bb65faebde2e..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Signed.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - // - // Common worker for all signed integer TryFormat overloads - // - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatInt64(long value, ulong mask, Span destination, out int bytesWritten, StandardFormat format) - { - if (format.IsDefault) - { - return TryFormatInt64Default(value, destination, out bytesWritten); - } - - switch (format.Symbol) - { - case 'G': - case 'g': - case 'R': - case 'r': // treat 'r' (which historically was only for floating-point) as being equivalent to 'g', rather than throwing - if (format.HasPrecision) - { - throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); // With a precision, 'G' can produce exponential format, even for integers. - } - return TryFormatInt64D(value, format.Precision, destination, out bytesWritten); - - case 'd': - case 'D': - return TryFormatInt64D(value, format.Precision, destination, out bytesWritten); - - case 'n': - case 'N': - return TryFormatInt64N(value, format.Precision, destination, out bytesWritten); - - case 'x': - return TryFormatUInt64X((ulong)value & mask, format.Precision, true, destination, out bytesWritten); - - case 'X': - return TryFormatUInt64X((ulong)value & mask, format.Precision, false, destination, out bytesWritten); - - default: - return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten); - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.D.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.D.cs deleted file mode 100644 index 2a2059674f916..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.D.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - private static bool TryFormatUInt64D(ulong value, byte precision, Span destination, bool insertNegationSign, out int bytesWritten) - { - // Calculate the actual digit count and the number of padding zeroes requested. - // From all of this we can get the required buffer length. - - int digitCount = FormattingHelpers.CountDigits(value); - int leadingZeroCount = ((precision == StandardFormat.NoPrecision) ? 0 : (int)precision) - digitCount; - if (leadingZeroCount < 0) - { - leadingZeroCount = 0; - } - - int requiredBufferLength = digitCount + leadingZeroCount; - - if (insertNegationSign) - { - requiredBufferLength++; - } - - if (requiredBufferLength > destination.Length) - { - bytesWritten = 0; - return false; - } - - bytesWritten = requiredBufferLength; - - if (insertNegationSign) - { - destination[0] = Utf8Constants.Minus; - destination = destination.Slice(1); - } - - if (leadingZeroCount > 0) - { - FormattingHelpers.FillWithAsciiZeros(destination.Slice(0, leadingZeroCount)); - } - FormattingHelpers.WriteDigits(value, destination.Slice(leadingZeroCount, digitCount)); - - return true; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.Default.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.Default.cs deleted file mode 100644 index 505e818bd515f..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.Default.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatUInt64Default(ulong value, Span destination, out int bytesWritten) - { - if (value < 10) - { - return TryFormatUInt32SingleDigit((uint)value, destination, out bytesWritten); - } - - if (IntPtr.Size == 8) // x64 - { - return TryFormatUInt64MultipleDigits(value, destination, out bytesWritten); - } - else // x86 - { - if (value <= uint.MaxValue) - { - return TryFormatUInt32MultipleDigits((uint)value, destination, out bytesWritten); - } - else - { - if (value <= Utf8Constants.BillionMaxUIntValue) - { - return TryFormatUInt64LessThanBillionMaxUInt(value, destination, out bytesWritten); - } - else - { - return TryFormatUInt64MoreThanBillionMaxUInt(value, destination, out bytesWritten); - } - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatUInt32SingleDigit(uint value, Span destination, out int bytesWritten) - { - if (destination.Length == 0) - { - bytesWritten = 0; - return false; - } - destination[0] = (byte)('0' + value); - bytesWritten = 1; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatUInt32MultipleDigits(uint value, Span destination, out int bytesWritten) - { - int digitCount = FormattingHelpers.CountDigits(value); - // WriteDigits does not do bounds checks - if (digitCount > destination.Length) - { - bytesWritten = 0; - return false; - } - bytesWritten = digitCount; - FormattingHelpers.WriteDigits(value, destination.Slice(0, digitCount)); - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatUInt64MultipleDigits(ulong value, Span destination, out int bytesWritten) - { - int digitCount = FormattingHelpers.CountDigits(value); - // WriteDigits does not do bounds checks - if (digitCount > destination.Length) - { - bytesWritten = 0; - return false; - } - bytesWritten = digitCount; - FormattingHelpers.WriteDigits(value, destination.Slice(0, digitCount)); - return true; - } - - // Split ulong into two parts that can each fit in a uint - {1-10 digits}{9 digits} - private static bool TryFormatUInt64LessThanBillionMaxUInt(ulong value, Span destination, out int bytesWritten) - { - uint overNineDigits = (uint)(value / Utf8Constants.Billion); - uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); - - int digitCountOverNineDigits = FormattingHelpers.CountDigits(overNineDigits); - Debug.Assert(digitCountOverNineDigits >= 1 && digitCountOverNineDigits <= 10); - int digitCount = digitCountOverNineDigits + 9; - // WriteDigits does not do bounds checks - if (digitCount > destination.Length) - { - bytesWritten = 0; - return false; - } - bytesWritten = digitCount; - FormattingHelpers.WriteDigits(overNineDigits, destination.Slice(0, digitCountOverNineDigits)); - FormattingHelpers.WriteDigits(lastNineDigits, destination.Slice(digitCountOverNineDigits, 9)); - return true; - } - - // Split ulong into three parts that can each fit in a uint - {1-2 digits}{9 digits}{9 digits} - private static bool TryFormatUInt64MoreThanBillionMaxUInt(ulong value, Span destination, out int bytesWritten) - { - ulong overNineDigits = value / Utf8Constants.Billion; - uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); - uint overEighteenDigits = (uint)(overNineDigits / Utf8Constants.Billion); - uint middleNineDigits = (uint)(overNineDigits - (overEighteenDigits * Utf8Constants.Billion)); - - int digitCountOverEighteenDigits = FormattingHelpers.CountDigits(overEighteenDigits); - Debug.Assert(digitCountOverEighteenDigits >= 1 && digitCountOverEighteenDigits <= 2); - int digitCount = digitCountOverEighteenDigits + 18; - // WriteDigits does not do bounds checks - if (digitCount > destination.Length) - { - bytesWritten = 0; - return false; - } - bytesWritten = digitCount; - FormattingHelpers.WriteDigits(overEighteenDigits, destination.Slice(0, digitCountOverEighteenDigits)); - FormattingHelpers.WriteDigits(middleNineDigits, destination.Slice(digitCountOverEighteenDigits, 9)); - FormattingHelpers.WriteDigits(lastNineDigits, destination.Slice(digitCountOverEighteenDigits + 9, 9)); - return true; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.N.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.N.cs deleted file mode 100644 index c7f564ae6c9f6..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.N.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - private static bool TryFormatUInt64N(ulong value, byte precision, Span destination, bool insertNegationSign, out int bytesWritten) - { - // Calculate the actual digit count, number of group separators required, and the - // number of trailing zeros requested. From all of this we can get the required - // buffer length. - - int digitCount = FormattingHelpers.CountDigits(value); - int commaCount = (digitCount - 1) / 3; - int trailingZeroCount = (precision == StandardFormat.NoPrecision) ? 2 /* default for 'N' */ : precision; - - int requiredBufferLength = digitCount + commaCount; - if (trailingZeroCount > 0) - { - requiredBufferLength += trailingZeroCount + 1; - } - - if (insertNegationSign) - { - requiredBufferLength++; - } - - if (requiredBufferLength > destination.Length) - { - bytesWritten = 0; - return false; - } - - bytesWritten = requiredBufferLength; - - if (insertNegationSign) - { - destination[0] = Utf8Constants.Minus; - destination = destination.Slice(1); - } - - FormattingHelpers.WriteDigitsWithGroupSeparator(value, destination.Slice(0, digitCount + commaCount)); - - if (trailingZeroCount > 0) - { - destination[digitCount + commaCount] = Utf8Constants.Period; - FormattingHelpers.FillWithAsciiZeros(destination.Slice(digitCount + commaCount + 1, trailingZeroCount)); - } - - return true; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.X.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.X.cs deleted file mode 100644 index 00ea1d2e29b5d..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.X.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - private static bool TryFormatUInt64X(ulong value, byte precision, bool useLower, Span destination, out int bytesWritten) - { - int actualDigitCount = FormattingHelpers.CountHexDigits(value); - int computedOutputLength = (precision == StandardFormat.NoPrecision) - ? actualDigitCount - : Math.Max(precision, actualDigitCount); - - if (destination.Length < computedOutputLength) - { - bytesWritten = 0; - return false; - } - - bytesWritten = computedOutputLength; - - // Writing the output backward in this manner allows the JIT to elide - // bounds checking on the output buffer. The JIT won't elide the bounds - // check on the hex table lookup, but we can live with that for now. - - // It doesn't quite make sense to use the fast hex conversion functionality - // for this method since that routine works on bytes, and here we're working - // directly with nibbles. There may be opportunity for improvement by special- - // casing output lengths of 2, 4, 8, and 16 and running them down optimized - // code paths. - - if (useLower) - { - while ((uint)(--computedOutputLength) < (uint)destination.Length) - { - destination[computedOutputLength] = (byte)HexConverter.ToCharLower((int)value); - value >>= 4; - } - } - else - { - while ((uint)(--computedOutputLength) < (uint)destination.Length) - { - destination[computedOutputLength] = (byte)HexConverter.ToCharUpper((int)value); - value >>= 4; - } - } - - return true; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.cs deleted file mode 100644 index c2414eb1e00c9..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.Unsigned.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Buffers.Text -{ - /// - /// Methods to format common data types as Utf8 strings. - /// - public static partial class Utf8Formatter - { - // - // Common worker for all unsigned integer TryFormat overloads - // - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryFormatUInt64(ulong value, Span destination, out int bytesWritten, StandardFormat format) - { - if (format.IsDefault) - { - return TryFormatUInt64Default(value, destination, out bytesWritten); - } - - switch (format.Symbol) - { - case 'G': - case 'g': - case 'R': - case 'r': // treat 'R'/'r' (which historically was only for floating-point) as being equivalent to 'g', rather than throwing - if (format.HasPrecision) - { - throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); // With a precision, 'G' can produce exponential format, even for integers. - } - return TryFormatUInt64D(value, format.Precision, destination, insertNegationSign: false, out bytesWritten); - - case 'd': - case 'D': - return TryFormatUInt64D(value, format.Precision, destination, insertNegationSign: false, out bytesWritten); - - case 'n': - case 'N': - return TryFormatUInt64N(value, format.Precision, destination, insertNegationSign: false, out bytesWritten); - - case 'x': - return TryFormatUInt64X(value, format.Precision, true /* useLower */, destination, out bytesWritten); - - case 'X': - return TryFormatUInt64X(value, format.Precision, false /* useLower */, destination, out bytesWritten); - - default: - return FormattingHelpers.TryFormatThrowFormatException(out bytesWritten); - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs index d22e06e0a5cc1..303383a097e33 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs @@ -29,8 +29,8 @@ public static partial class Utf8Formatter /// /// System.FormatException if the format is not valid for this data type. /// - public static bool TryFormat(byte value, Span destination, out int bytesWritten, StandardFormat format = default) - => TryFormatUInt64(value, destination, out bytesWritten, format); + public static bool TryFormat(byte value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); /// /// Formats an SByte as a UTF8 string. @@ -54,8 +54,8 @@ public static bool TryFormat(byte value, Span destination, out int bytesWr /// System.FormatException if the format is not valid for this data type. /// [CLSCompliant(false)] - public static bool TryFormat(sbyte value, Span destination, out int bytesWritten, StandardFormat format = default) - => TryFormatInt64(value, 0xff, destination, out bytesWritten, format); + public static bool TryFormat(sbyte value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); /// /// Formats a Unt16 as a UTF8 string. @@ -79,8 +79,8 @@ public static bool TryFormat(sbyte value, Span destination, out int bytesW /// System.FormatException if the format is not valid for this data type. /// [CLSCompliant(false)] - public static bool TryFormat(ushort value, Span destination, out int bytesWritten, StandardFormat format = default) - => TryFormatUInt64(value, destination, out bytesWritten, format); + public static bool TryFormat(ushort value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); /// /// Formats an Int16 as a UTF8 string. @@ -103,8 +103,8 @@ public static bool TryFormat(ushort value, Span destination, out int bytes /// /// System.FormatException if the format is not valid for this data type. /// - public static bool TryFormat(short value, Span destination, out int bytesWritten, StandardFormat format = default) - => TryFormatInt64(value, 0xffff, destination, out bytesWritten, format); + public static bool TryFormat(short value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); /// /// Formats a UInt32 as a UTF8 string. @@ -128,8 +128,8 @@ public static bool TryFormat(short value, Span destination, out int bytesW /// System.FormatException if the format is not valid for this data type. /// [CLSCompliant(false)] - public static bool TryFormat(uint value, Span destination, out int bytesWritten, StandardFormat format = default) - => TryFormatUInt64(value, destination, out bytesWritten, format); + public static bool TryFormat(uint value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); /// /// Formats an Int32 as a UTF8 string. @@ -152,8 +152,8 @@ public static bool TryFormat(uint value, Span destination, out int bytesWr /// /// System.FormatException if the format is not valid for this data type. /// - public static bool TryFormat(int value, Span destination, out int bytesWritten, StandardFormat format = default) - => TryFormatInt64(value, 0xffffffff, destination, out bytesWritten, format); + public static bool TryFormat(int value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); /// /// Formats a UInt64 as a UTF8 string. @@ -177,8 +177,8 @@ public static bool TryFormat(int value, Span destination, out int bytesWri /// System.FormatException if the format is not valid for this data type. /// [CLSCompliant(false)] - public static bool TryFormat(ulong value, Span destination, out int bytesWritten, StandardFormat format = default) - => TryFormatUInt64(value, destination, out bytesWritten, format); + public static bool TryFormat(ulong value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); /// /// Formats an Int64 as a UTF8 string. @@ -201,7 +201,7 @@ public static bool TryFormat(ulong value, Span destination, out int bytesW /// /// System.FormatException if the format is not valid for this data type. /// - public static bool TryFormat(long value, Span destination, out int bytesWritten, StandardFormat format = default) - => TryFormatInt64(value, 0xffffffffffffffff, destination, out bytesWritten, format); + public static bool TryFormat(long value, Span destination, out int bytesWritten, StandardFormat format = default) => + FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.TimeSpan.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.TimeSpan.cs index aebcc9a154e99..c1d8476209ae0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.TimeSpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.TimeSpan.cs @@ -30,12 +30,12 @@ public static partial class Utf8Formatter public static bool TryFormat(TimeSpan value, Span destination, out int bytesWritten, StandardFormat format = default) { TimeSpanFormat.StandardFormat sf = TimeSpanFormat.StandardFormat.C; - string? decimalSeparator = null; + ReadOnlySpan decimalSeparator = default; char symbol = FormattingHelpers.GetSymbolOrDefault(format, 'c'); if (symbol != 'c' && (symbol | 0x20) != 't') { - decimalSeparator = DateTimeFormatInfo.InvariantInfo.DecimalSeparator; + decimalSeparator = DateTimeFormatInfo.InvariantInfo.DecimalSeparatorTChar(); if (symbol == 'g') { sf = TimeSpanFormat.StandardFormat.g; diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 2a45415301196..ab924f1e4b55a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -1,7 +1,6 @@ // 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.Binary; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; @@ -22,7 +21,9 @@ public readonly struct Byte IEquatable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IUtf8SpanFormattable, + IUtfChar { private readonly byte m_value; // Do not rename (binary serialization) @@ -210,6 +211,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatUInt32(m_value, format, provider, utf8Destination, out bytesWritten); + } + // // IConvertible // @@ -1196,5 +1203,15 @@ static bool INumberBase.TryConvertToTruncating(byte value, [MaybeN /// static byte IUnaryPlusOperators.operator +(byte value) => (byte)(+value); + + // + // IUtfChar + // + + static byte IUtfChar.CastFrom(byte value) => value; + static byte IUtfChar.CastFrom(char value) => (byte)value; + static byte IUtfChar.CastFrom(int value) => (byte)value; + static byte IUtfChar.CastFrom(uint value) => (byte)value; + static byte IUtfChar.CastFrom(ulong value) => (byte)value; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 5678c119e0b96..643574b8fbfb1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -35,7 +35,8 @@ public readonly struct Char IBinaryInteger, IMinMaxValue, IUnsignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IUtfChar { // // Member Variables @@ -2010,5 +2011,15 @@ static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? /// static char IUnaryPlusOperators.operator +(char value) => (char)(+value); + + // + // IUtfChar + // + + static char IUtfChar.CastFrom(byte value) => (char)value; + static char IUtfChar.CastFrom(char value) => value; + static char IUtfChar.CastFrom(int value) => (char)value; + static char IUtfChar.CastFrom(uint value) => (char)value; + static char IUtfChar.CastFrom(ulong value) => (char)value; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs index 01c19a05b9c58..85c86d3dcd29c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs @@ -58,27 +58,71 @@ public void Append(T item) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Append(scoped ReadOnlySpan source) + { + int pos = _pos; + Span span = _span; + if (source.Length == 1 && (uint)pos < (uint)span.Length) + { + span[pos] = source[0]; + _pos = pos + 1; + } + else + { + AppendMultiChar(source); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendMultiChar(scoped ReadOnlySpan source) { if ((uint)(_pos + source.Length) > (uint)_span.Length) { - Grow(source.Length); + Grow(_span.Length - _pos + source.Length); } source.CopyTo(_span.Slice(_pos)); _pos += source.Length; } + public void Insert(int index, scoped ReadOnlySpan source) + { + Debug.Assert(index >= 0 && index <= _pos); + + if ((uint)(_pos + source.Length) > (uint)_span.Length) + { + Grow(source.Length); + } + + _span.Slice(0, _pos).CopyTo(_span.Slice(source.Length)); + source.CopyTo(_span); + _pos += source.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span AppendSpan(int length) { Debug.Assert(length >= 0); int pos = _pos; - if ((uint)(pos + length) > (uint)_span.Length) + Span span = _span; + if ((ulong)(uint)pos + (ulong)(uint)length <= (ulong)(uint)span.Length) // same guard condition as in Span.Slice on 64-bit + { + _pos = pos + length; + return span.Slice(pos, length); + } + else { - Grow(length); + return AppendSpanWithGrow(length); } + } + [MethodImpl(MethodImplOptions.NoInlining)] + private Span AppendSpanWithGrow(int length) + { + int pos = _pos; + Grow(_span.Length - pos + length); _pos += length; return _span.Slice(pos, length); } diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs index 13985c540633b..a515a66f8ae97 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs @@ -491,16 +491,14 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, Read if (format.Length == 1) { - switch (format[0]) + switch (format[0] | 0x20) { case 'o': - case 'O': format = OFormat; provider = CultureInfo.InvariantCulture.DateTimeFormat; break; case 'r': - case 'R': format = RFormat; provider = CultureInfo.InvariantCulture.DateTimeFormat; break; @@ -570,16 +568,14 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri if (format.Length == 1) { - switch (format[0]) + switch (format[0] | 0x20) { case 'o': - case 'O': format = OFormat; dtfiToUse = CultureInfo.InvariantCulture.DateTimeFormat; break; case 'r': - case 'R': format = RFormat; dtfiToUse = CultureInfo.InvariantCulture.DateTimeFormat; break; @@ -750,30 +746,25 @@ public string ToString([StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] stri if (format.Length == 1) { - switch (format[0]) + switch (format[0] | 0x20) { case 'o': - case 'O': return string.Create(10, this, (destination, value) => { - bool b = DateTimeFormat.TryFormatDateOnlyO(value.Year, value.Month, value.Day, destination); - Debug.Assert(b); + DateTimeFormat.TryFormatDateOnlyO(value.Year, value.Month, value.Day, destination, out int charsWritten); + Debug.Assert(charsWritten == destination.Length); }); case 'r': - case 'R': return string.Create(16, this, (destination, value) => { - bool b = DateTimeFormat.TryFormatDateOnlyR(value.DayOfWeek, value.Year, value.Month, value.Day, destination); - Debug.Assert(b); + DateTimeFormat.TryFormatDateOnlyR(value.DayOfWeek, value.Year, value.Month, value.Day, destination, out int charsWritten); + Debug.Assert(charsWritten == destination.Length); }); case 'm': - case 'M': case 'd': - case 'D': case 'y': - case 'Y': return DateTimeFormat.Format(GetEquivalentDateTime(), format, provider); default: @@ -800,7 +791,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri TryFormatCore(utf8Destination, out bytesWritten, format, provider); private bool TryFormatCore(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider = null) - where TChar : unmanaged, IBinaryInteger + where TChar : unmanaged, IUtfChar { if (format.Length == 0) { @@ -809,39 +800,22 @@ private bool TryFormatCore(Span destination, out int charsWritten, if (format.Length == 1) { - switch (format[0]) + switch (format[0] | 0x20) { case 'o': - case 'O': - if (!DateTimeFormat.TryFormatDateOnlyO(Year, Month, Day, destination)) - { - charsWritten = 0; - return false; - } - charsWritten = 10; - return true; + return DateTimeFormat.TryFormatDateOnlyO(Year, Month, Day, destination, out charsWritten); case 'r': - case 'R': - - if (!DateTimeFormat.TryFormatDateOnlyR(DayOfWeek, Year, Month, Day, destination)) - { - charsWritten = 0; - return false; - } - charsWritten = 16; - return true; + return DateTimeFormat.TryFormatDateOnlyR(DayOfWeek, Year, Month, Day, destination, out charsWritten); case 'm': - case 'M': case 'd': - case 'D': case 'y': - case 'Y': return DateTimeFormat.TryFormat(GetEquivalentDateTime(), destination, out charsWritten, format, provider); default: - throw new FormatException(SR.Argument_BadFormatSpecifier); + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + break; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs index 71895a2427352..a0d257376ca6a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs @@ -68,7 +68,8 @@ public readonly partial struct Decimal ISerializable, IDeserializationCallback, IFloatingPoint, - IMinMaxValue + IMinMaxValue, + IUtf8SpanFormattable { // Sign mask for the flags field. A value of zero in this bit indicates a // positive Decimal value, and a value of one in this bit indicates a @@ -503,6 +504,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatDecimal(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatDecimal(this, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); + } + // Converts a string to a Decimal. The string must consist of an optional // minus sign ("-") followed by a sequence of digits ("0" - "9"). The // sequence of digits may optionally contain a single decimal point (".") diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 627e6e0cd9dfc..8af4921f6a7b2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -32,7 +32,8 @@ public readonly struct Double IComparable, IEquatable, IBinaryFloatingPointIeee754, - IMinMaxValue + IMinMaxValue, + IUtf8SpanFormattable { private readonly double m_value; // Do not rename (binary serialization) @@ -365,6 +366,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatDouble(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); + } + public static double Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index 5a58418c9d624..b38edce0cf21a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -135,6 +135,8 @@ internal static class DateTimeFormat private const int DEFAULT_ALL_DATETIMES_SIZE = 132; internal static readonly DateTimeFormatInfo InvariantFormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; + private static readonly string[] s_invariantAbbreviatedMonthNames = InvariantFormatInfo.AbbreviatedMonthNames; + private static readonly string[] s_invariantAbbreviatedDayNames = InvariantFormatInfo.AbbreviatedDayNames; internal static string[] fixedNumberFormats = new string[] { "0", @@ -146,57 +148,44 @@ internal static class DateTimeFormat "0000000", }; - //////////////////////////////////////////////////////////////////////////// - // - // Format the positive integer value to a string and prefix with assigned - // length of leading zero. - // - // Parameters: - // value: The value to format - // len: The maximum length for leading zero. - // If the digits of the value is greater than len, no leading zero is added. - // - // Notes: - // The function can format to int.MaxValue. - // - //////////////////////////////////////////////////////////////////////////// - internal static void FormatDigits(ref ValueListBuilder outputBuffer, int value, int len) where TChar : unmanaged, IBinaryInteger - { - Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0"); - FormatDigits(ref outputBuffer, value, len, false); - } - - internal static unsafe void FormatDigits(ref ValueListBuilder outputBuffer, int value, int len, bool overrideLengthLimit) where TChar : unmanaged, IBinaryInteger + /// Format the positive integer value to a string and prefix with assigned length of leading zero. + /// The type of the character. + /// The buffer into which to write the digits. + /// The value to format + /// + /// The minimum length for formatted number. If the number of digits in the value is less than this length, it will be padded with leading zeros. + /// + internal static unsafe void FormatDigits(ref ValueListBuilder outputBuffer, int value, int minimumLength) where TChar : unmanaged, IUtfChar { Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0"); + Debug.Assert(minimumLength <= 16); - // Limit the use of this function to be two-digits, so that we have the same behavior - // as RTM bits. - if (!overrideLengthLimit && len > 2) + switch (minimumLength) { - len = 2; - } + case 1 when value < 10: + outputBuffer.Append(TChar.CreateTruncating(value + '0')); + break; - TChar* buffer = stackalloc TChar[16]; - TChar* p = buffer + 16; - int n = value; - do - { - *--p = TChar.CreateTruncating(n % 10 + '0'); - n /= 10; - } while ((n != 0) && (p > buffer)); + case 2 when value < 100: + fixed (TChar* ptr = &MemoryMarshal.GetReference(outputBuffer.AppendSpan(2))) + { + Number.WriteTwoDigits((uint)value, ptr); + } + break; - int digits = (int)(buffer + 16 - p); + case 4 when value < 10000: + fixed (TChar* ptr = &MemoryMarshal.GetReference(outputBuffer.AppendSpan(4))) + { + Number.WriteFourDigits((uint)value, ptr); + } + break; - // If the repeat count is greater than 0, we're trying - // to emulate the "00" format, so we have to prepend - // a zero if the string only has one character. - while ((digits < len) && (p > buffer)) - { - *--p = TChar.CreateTruncating('0'); - digits++; + default: + TChar* buffer = stackalloc TChar[16]; + TChar* p = Number.UInt32ToDecChars(buffer + 16, (uint)value, minimumLength); + outputBuffer.Append(new ReadOnlySpan(p, (int)(buffer + 16 - p))); + break; } - new ReadOnlySpan(p, digits).CopyTo(outputBuffer.AppendSpan(digits)); } internal static int ParseRepeatPattern(ReadOnlySpan format, int pos, char patternChar) @@ -287,7 +276,7 @@ private static string FormatHebrewMonthName(DateTime time, int month, int repeat // The pos should point to a quote character. This method will // append to the result StringBuilder the string enclosed by the quote character. // - internal static int ParseQuoteString(scoped ReadOnlySpan format, int pos, ref ValueListBuilder result) where TChar : unmanaged, IBinaryInteger + internal static int ParseQuoteString(scoped ReadOnlySpan format, int pos, ref ValueListBuilder result) where TChar : unmanaged, IUtfChar { // // NOTE : pos will be the index of the quote character in the 'format' string. @@ -314,7 +303,7 @@ internal static int ParseQuoteString(scoped ReadOnlySpan format, in // because the second double quote is escaped. if (pos < formatLen) { - result.Append(TChar.CreateTruncating(format[pos++])); + result.Append(TChar.CastFrom(format[pos++])); } else { @@ -326,7 +315,7 @@ internal static int ParseQuoteString(scoped ReadOnlySpan format, in } else { - result.Append(TChar.CreateTruncating(ch)); + result.Append(TChar.CastFrom(ch)); } } @@ -432,7 +421,7 @@ private static bool IsUseGenitiveForm(ReadOnlySpan format, int index, int // Actions: Format the DateTime instance using the specified format. // private static void FormatCustomized( - DateTime dateTime, scoped ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset, ref ValueListBuilder result) where TChar : unmanaged, IBinaryInteger + DateTime dateTime, scoped ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset, ref ValueListBuilder result) where TChar : unmanaged, IUtfChar { Calendar cal = dtfi.Calendar; @@ -455,6 +444,7 @@ private static void FormatCustomized( tokenLen = ParseRepeatPattern(format, i, ch); AppendString(ref result, dtfi.GetEraName(cal.GetEra(dateTime))); break; + case 'h': tokenLen = ParseRepeatPattern(format, i, ch); hour12 = dateTime.Hour % 12; @@ -462,20 +452,24 @@ private static void FormatCustomized( { hour12 = 12; } - FormatDigits(ref result, hour12, tokenLen); + FormatDigits(ref result, hour12, Math.Min(tokenLen, 2)); break; + case 'H': tokenLen = ParseRepeatPattern(format, i, ch); - FormatDigits(ref result, dateTime.Hour, tokenLen); + FormatDigits(ref result, dateTime.Hour, Math.Min(tokenLen, 2)); break; + case 'm': tokenLen = ParseRepeatPattern(format, i, ch); - FormatDigits(ref result, dateTime.Minute, tokenLen); + FormatDigits(ref result, dateTime.Minute, Math.Min(tokenLen, 2)); break; + case 's': tokenLen = ParseRepeatPattern(format, i, ch); - FormatDigits(ref result, dateTime.Second, tokenLen); + FormatDigits(ref result, dateTime.Second, Math.Min(tokenLen, 2)); break; + case 'f': case 'F': tokenLen = ParseRepeatPattern(format, i, ch); @@ -509,7 +503,7 @@ private static void FormatCustomized( else { // No fraction to emit, so see if we should remove decimal also. - if (result.Length > 0 && result[result.Length - 1] == TChar.CreateTruncating('.')) + if (result.Length > 0 && result[result.Length - 1] == TChar.CastFrom('.')) { result.Length--; } @@ -521,6 +515,7 @@ private static void FormatCustomized( throw new FormatException(SR.Format_InvalidString); } break; + case 't': tokenLen = ParseRepeatPattern(format, i, ch); if (tokenLen == 1) @@ -533,9 +528,10 @@ private static void FormatCustomized( } else { - AppendString(ref result, dateTime.Hour < 12 ? dtfi.AMDesignator : dtfi.PMDesignator); + result.Append(dateTime.Hour < 12 ? dtfi.AMDesignatorTChar() : dtfi.PMDesignatorTChar()); } break; + case 'd': // // tokenLen == 1 : Day of month as digits with no leading zero. @@ -564,13 +560,12 @@ private static void FormatCustomized( } bTimeOnly = false; break; + case 'M': - // // tokenLen == 1 : Month as digits with no leading zero. // tokenLen == 2 : Month as digits with leading zero for single-digit months. // tokenLen == 3 : Month as a three-letter abbreviation. // tokenLen >= 4 : Month as its full name. - // tokenLen = ParseRepeatPattern(format, i, ch); int month = cal.GetMonth(dateTime); if (tokenLen <= 2) @@ -609,6 +604,7 @@ private static void FormatCustomized( } bTimeOnly = false; break; + case 'y': // Notes about OS behavior: // y: Always print (year % 100). No leading zero. @@ -630,7 +626,7 @@ private static void FormatCustomized( } else if (dtfi.HasForceTwoDigitYears) { - FormatDigits(ref result, year, tokenLen <= 2 ? tokenLen : 2); + FormatDigits(ref result, year, Math.Min(tokenLen, 2)); } else if (cal.ID == CalendarId.HEBREW) { @@ -644,7 +640,7 @@ private static void FormatCustomized( } else if (tokenLen <= 16) // FormatDigits has an implicit 16-digit limit { - FormatDigits(ref result, year, tokenLen, overrideLengthLimit: true); + FormatDigits(ref result, year, tokenLen); } else { @@ -653,26 +649,32 @@ private static void FormatCustomized( } bTimeOnly = false; break; + case 'z': tokenLen = ParseRepeatPattern(format, i, ch); FormatCustomizedTimeZone(dateTime, offset, tokenLen, bTimeOnly, ref result); break; + case 'K': tokenLen = 1; FormatCustomizedRoundripTimeZone(dateTime, offset, ref result); break; + case ':': - AppendString(ref result, dtfi.TimeSeparator); + result.Append(dtfi.TimeSeparatorTChar()); tokenLen = 1; break; + case '/': - AppendString(ref result, dtfi.DateSeparator); + result.Append(dtfi.DateSeparatorTChar()); tokenLen = 1; break; + case '\'': case '\"': tokenLen = ParseQuoteString(format, i, ref result); break; + case '%': // Optional format character. // For example, format string "%d" will print day of month @@ -695,6 +697,7 @@ private static void FormatCustomized( throw new FormatException(SR.Format_InvalidString); } break; + case '\\': // Escaped character. Can be used to insert a character into the format string. // For example, "\d" will insert the character 'd' into the string. @@ -707,7 +710,7 @@ private static void FormatCustomized( nextChar = ParseNextChar(format, i); if (nextChar >= 0) { - result.Append(TChar.CreateTruncating(nextChar)); + result.Append(TChar.CastFrom(nextChar)); tokenLen = 2; } else @@ -718,12 +721,13 @@ private static void FormatCustomized( throw new FormatException(SR.Format_InvalidString); } break; + default: // NOTENOTE : we can remove this rule if we enforce the enforced quote // character rule. // That is, if we ask everyone to use single quote or double quote to insert characters, // then we can remove this default block. - result.Append(TChar.CreateTruncating(ch)); + result.Append(TChar.CastFrom(ch)); tokenLen = 1; break; } @@ -732,11 +736,11 @@ private static void FormatCustomized( } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void AppendChar(ref ValueListBuilder result, char ch) where TChar : unmanaged, IBinaryInteger + internal static void AppendChar(ref ValueListBuilder result, char ch) where TChar : unmanaged, IUtfChar { if (typeof(TChar) == typeof(char) || char.IsAscii(ch)) { - result.Append(TChar.CreateTruncating(ch)); + result.Append(TChar.CastFrom(ch)); } else { @@ -747,7 +751,7 @@ internal static void AppendChar(ref ValueListBuilder result, char } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AppendString(ref ValueListBuilder result, scoped ReadOnlySpan s) where TChar : unmanaged, IBinaryInteger + private static void AppendString(ref ValueListBuilder result, scoped ReadOnlySpan s) where TChar : unmanaged, IUtfChar { if (typeof(TChar) == typeof(char)) { @@ -760,20 +764,22 @@ private static void AppendString(ref ValueListBuilder result, scop } } - internal static void FormatFraction(ref ValueListBuilder result, int fraction, ReadOnlySpan fractionFormat) where TChar : unmanaged, IBinaryInteger + internal static void FormatFraction(ref ValueListBuilder result, int fraction, ReadOnlySpan fractionFormat) where TChar : unmanaged, IUtfChar { - // TODO https://github.com/dotnet/runtime/issues/84527: Update when Int32 implements IUtf8SpanFormattable - Span chars = stackalloc char[11]; - fraction.TryFormat(chars, out int charsWritten, fractionFormat, CultureInfo.InvariantCulture); - Debug.Assert(charsWritten != 0); - AppendString(ref result, chars.Slice(0, charsWritten)); + Span chars = stackalloc TChar[11]; + int charCount; + bool formatted = typeof(TChar) == typeof(char) ? + fraction.TryFormat(MemoryMarshal.Cast(chars), out charCount, fractionFormat, CultureInfo.InvariantCulture) : + ((IUtf8SpanFormattable)fraction).TryFormat(MemoryMarshal.Cast(chars), out charCount, fractionFormat, CultureInfo.InvariantCulture); + Debug.Assert(charCount != 0); + result.Append(chars.Slice(0, charCount)); } // output the 'z' family of formats, which output a the offset from UTC, e.g. "-07:30" - private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset, int tokenLen, bool timeOnly, ref ValueListBuilder result) where TChar : unmanaged, IBinaryInteger + private static unsafe void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset, int tokenLen, bool timeOnly, ref ValueListBuilder result) where TChar : unmanaged, IUtfChar { // See if the instance already has an offset - bool dateTimeFormat = (offset.Ticks == NullOffset); + bool dateTimeFormat = offset.Ticks == NullOffset; if (dateTimeFormat) { // No offset. The instance is a DateTime and the output should be the local time zone @@ -793,15 +799,15 @@ private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); } } + if (offset.Ticks >= 0) { - result.Append(TChar.CreateTruncating('+')); + result.Append(TChar.CastFrom('+')); } else { - result.Append(TChar.CreateTruncating('-')); - // get a positive offset, so that you don't need a separate code path for the negative numbers. - offset = offset.Negate(); + result.Append(TChar.CastFrom('-')); + offset = offset.Negate(); // get a positive offset, so that you don't need a separate code path for the negative numbers. } if (tokenLen <= 1) @@ -810,24 +816,32 @@ private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan (int tens, int ones) = Math.DivRem(offset.Hours, 10); if (tens != 0) { - result.Append(TChar.CreateTruncating('0' + tens)); + result.Append(TChar.CastFrom('0' + tens)); + } + result.Append(TChar.CastFrom('0' + ones)); + } + else if (tokenLen == 2) + { + // 'zz' format e.g "-07" + fixed (TChar* p = &MemoryMarshal.GetReference(result.AppendSpan(2))) + { + Number.WriteTwoDigits((uint)offset.Hours, p); } - result.Append(TChar.CreateTruncating('0' + ones)); } else { - // 'zz' or longer format e.g "-07" - FormattingHelpers.WriteTwoDigits((uint)offset.Hours, result.AppendSpan(2), 0); - if (tokenLen >= 3) + Debug.Assert(tokenLen >= 3); + fixed (TChar* p = &MemoryMarshal.GetReference(result.AppendSpan(5))) { - result.Append(TChar.CreateTruncating(':')); - FormattingHelpers.WriteTwoDigits((uint)offset.Minutes, result.AppendSpan(2), 0); + Number.WriteTwoDigits((uint)offset.Hours, p); + p[2] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)offset.Minutes, p + 3); } } } // output the 'K' format, which is for round-tripping the data - private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, TimeSpan offset, ref ValueListBuilder result) where TChar : unmanaged, IBinaryInteger + private static unsafe void FormatCustomizedRoundripTimeZone(DateTime dateTime, TimeSpan offset, ref ValueListBuilder result) where TChar : unmanaged, IUtfChar { // The objective of this format is to round trip the data in the type // For DateTime it should round-trip the Kind value and preserve the time zone. @@ -845,7 +859,7 @@ private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, T break; case DateTimeKind.Utc: // The 'Z' constant is a marker for a UTC date - result.Append(TChar.CreateTruncating('Z')); + result.Append(TChar.CastFrom('Z')); return; default: // If the kind is unspecified, we output nothing here @@ -854,19 +868,21 @@ private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, T } if (offset.Ticks >= 0) { - result.Append(TChar.CreateTruncating('+')); + result.Append(TChar.CastFrom('+')); } else { - result.Append(TChar.CreateTruncating('-')); + result.Append(TChar.CastFrom('-')); // get a positive offset, so that you don't need a separate code path for the negative numbers. offset = offset.Negate(); } - Span hoursMinutes = result.AppendSpan(5); - FormattingHelpers.WriteTwoDigits((uint)offset.Hours, hoursMinutes, 0); - hoursMinutes[2] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)offset.Minutes, hoursMinutes, 3); + fixed (TChar* hoursMinutes = &MemoryMarshal.GetReference(result.AppendSpan(5))) + { + Number.WriteTwoDigits((uint)offset.Hours, hoursMinutes); + hoursMinutes[2] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)offset.Minutes, hoursMinutes + 3); + } } internal static string GetRealFormat(ReadOnlySpan format, DateTimeFormatInfo dtfi) @@ -1014,10 +1030,10 @@ internal static string Format(DateTime dateTime, string? format, IFormatProvider return resultString; } - internal static bool TryFormat(DateTime dateTime, Span destination, out int written, ReadOnlySpan format, IFormatProvider? provider) where TChar : unmanaged, IBinaryInteger => + internal static bool TryFormat(DateTime dateTime, Span destination, out int written, ReadOnlySpan format, IFormatProvider? provider) where TChar : unmanaged, IUtfChar => TryFormat(dateTime, destination, out written, format, provider, new TimeSpan(NullOffset)); - internal static bool TryFormat(DateTime dateTime, Span destination, out int written, ReadOnlySpan format, IFormatProvider? provider, TimeSpan offset) where TChar : unmanaged, IBinaryInteger + internal static bool TryFormat(DateTime dateTime, Span destination, out int written, ReadOnlySpan format, IFormatProvider? provider, TimeSpan offset) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -1043,7 +1059,7 @@ internal static bool TryFormat(DateTime dateTime, Span destination return copied; } - private static void FormatIntoBuilder(DateTime dateTime, ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset, ref ValueListBuilder result) where TChar : unmanaged, IBinaryInteger + private static void FormatIntoBuilder(DateTime dateTime, ReadOnlySpan format, DateTimeFormatInfo dtfi, TimeSpan offset, ref ValueListBuilder result) where TChar : unmanaged, IUtfChar { Debug.Assert(dtfi != null); if (format.Length == 0) @@ -1238,20 +1254,26 @@ internal static bool IsValidCustomTimeFormat(ReadOnlySpan format, bool thr // 012345678901234567890123456789012 // --------------------------------- // 05:30:45.7680000 - internal static bool TryFormatTimeOnlyO(int hour, int minute, int second, long fraction, Span destination) where TChar : unmanaged, IBinaryInteger + internal static unsafe bool TryFormatTimeOnlyO(int hour, int minute, int second, long fraction, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { if (destination.Length < 16) { + charsWritten = 0; return false; } - FormattingHelpers.WriteTwoDigits((uint)hour, destination, 0); - destination[2] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)minute, destination, 3); - destination[5] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)second, destination, 6); - destination[8] = TChar.CreateTruncating('.'); - FormattingHelpers.WriteDigits((uint)fraction, destination.Slice(9, 7)); + charsWritten = 16; + + fixed (TChar* dest = &MemoryMarshal.GetReference(destination)) + { + Number.WriteTwoDigits((uint)hour, dest); + dest[2] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)minute, dest + 3); + dest[5] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)second, dest + 6); + dest[8] = TChar.CastFrom('.'); + Number.WriteDigits((uint)fraction, dest + 9, 7); + } return true; } @@ -1259,18 +1281,24 @@ internal static bool TryFormatTimeOnlyO(int hour, int minute, int second, // 012345678901234567890123456789012 // --------------------------------- // 05:30:45 - internal static bool TryFormatTimeOnlyR(int hour, int minute, int second, Span destination) where TChar : unmanaged, IBinaryInteger + internal static unsafe bool TryFormatTimeOnlyR(int hour, int minute, int second, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { if (destination.Length < 8) { + charsWritten = 0; return false; } - FormattingHelpers.WriteTwoDigits((uint)hour, destination, 0); - destination[2] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)minute, destination, 3); - destination[5] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)second, destination, 6); + charsWritten = 8; + + fixed (TChar* dest = &MemoryMarshal.GetReference(destination)) + { + Number.WriteTwoDigits((uint)hour, dest); + dest[2] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)minute, dest + 3); + dest[5] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)second, dest +6); + } return true; } @@ -1279,18 +1307,25 @@ internal static bool TryFormatTimeOnlyR(int hour, int minute, int second, // 012345678901234567890123456789012 // --------------------------------- // 2017-06-12 - internal static bool TryFormatDateOnlyO(int year, int month, int day, Span destination) where TChar : unmanaged, IBinaryInteger + internal static unsafe bool TryFormatDateOnlyO(int year, int month, int day, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { if (destination.Length < 10) { + charsWritten = 0; return false; } - FormattingHelpers.WriteFourDigits((uint)year, destination, 0); - destination[4] = TChar.CreateTruncating('-'); - FormattingHelpers.WriteTwoDigits((uint)month, destination, 5); - destination[7] = TChar.CreateTruncating('-'); - FormattingHelpers.WriteTwoDigits((uint)day, destination, 8); + charsWritten = 10; + + fixed (TChar* dest = &MemoryMarshal.GetReference(destination)) + { + Number.WriteFourDigits((uint)year, dest); + dest[4] = TChar.CastFrom('-'); + Number.WriteTwoDigits((uint)month, dest + 5); + dest[7] = TChar.CastFrom('-'); + Number.WriteTwoDigits((uint)day, dest + 8); + } + return true; } @@ -1298,37 +1333,39 @@ internal static bool TryFormatDateOnlyO(int year, int month, int day, Spa // 01234567890123456789012345678 // ----------------------------- // Tue, 03 Jan 2017 - internal static bool TryFormatDateOnlyR(DayOfWeek dayOfWeek, int year, int month, int day, Span destination) where TChar : unmanaged, IBinaryInteger + internal static unsafe bool TryFormatDateOnlyR(DayOfWeek dayOfWeek, int year, int month, int day, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { - Debug.Assert((uint)dayOfWeek < 7); - if (destination.Length < 16) { + charsWritten = 0; return false; } - if (typeof(TChar) == typeof(char)) + charsWritten = 16; + + Debug.Assert((uint)dayOfWeek < 7); + string dayAbbrev = s_invariantAbbreviatedDayNames[(int)dayOfWeek]; + Debug.Assert(dayAbbrev.Length == 3); + + string monthAbbrev = s_invariantAbbreviatedMonthNames[month - 1]; + Debug.Assert(monthAbbrev.Length == 3); + + fixed (TChar* dest = &MemoryMarshal.GetReference(destination)) { - Span dest = MemoryMarshal.Cast(destination); - - FormattingHelpers.CopyFour("Sun,Mon,Tue,Wed,Thu,Fri,Sat,".AsSpan(4 * (int)dayOfWeek), dest); - dest[4] = ' '; - FormattingHelpers.WriteTwoDigits((uint)day, dest, 5); - dest[7] = ' '; - FormattingHelpers.CopyFour("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ".AsSpan(4 * (month - 1)), dest.Slice(8)); - FormattingHelpers.WriteFourDigits((uint)year, dest, 12); - } - else - { - Debug.Assert(typeof(TChar) == typeof(byte)); - Span dest = MemoryMarshal.Cast(destination); - - FormattingHelpers.CopyFour("Sun,Mon,Tue,Wed,Thu,Fri,Sat,"u8.Slice(4 * (int)dayOfWeek), dest); - dest[4] = (byte)' '; - FormattingHelpers.WriteTwoDigits((uint)day, dest, 5); - dest[7] = (byte)' '; - FormattingHelpers.CopyFour("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec "u8.Slice(4 * (month - 1)), dest.Slice(8)); - FormattingHelpers.WriteFourDigits((uint)year, dest, 12); + char c = dayAbbrev[2]; // remove bounds checks on remaining dayAbbrev accesses + dest[0] = TChar.CastFrom(dayAbbrev[0]); + dest[1] = TChar.CastFrom(dayAbbrev[1]); + dest[2] = TChar.CastFrom(c); + dest[3] = TChar.CastFrom(','); + dest[4] = TChar.CastFrom(' '); + Number.WriteTwoDigits((uint)day, dest + 5); + dest[7] = TChar.CastFrom(' '); + c = monthAbbrev[2]; // remove bounds checks on remaining monthAbbrev accesses + dest[8] = TChar.CastFrom(monthAbbrev[0]); + dest[9] = TChar.CastFrom(monthAbbrev[1]); + dest[10] = TChar.CastFrom(c); + dest[11] = TChar.CastFrom(' '); + Number.WriteFourDigits((uint)year, dest + 12); } return true; @@ -1340,7 +1377,7 @@ internal static bool TryFormatDateOnlyR(DayOfWeek dayOfWeek, int year, in // 2017-06-12T05:30:45.7680000-07:00 // 2017-06-12T05:30:45.7680000Z (Z is short for "+00:00" but also distinguishes DateTimeKind.Utc from DateTimeKind.Local) // 2017-06-12T05:30:45.7680000 (interpreted as local time wrt to current time zone) - internal static bool TryFormatO(DateTime dateTime, TimeSpan offset, Span destination, out int charsWritten) where TChar : unmanaged, IBinaryInteger + internal static unsafe bool TryFormatO(DateTime dateTime, TimeSpan offset, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { const int MinimumBytesNeeded = 27; @@ -1372,49 +1409,47 @@ internal static bool TryFormatO(DateTime dateTime, TimeSpan offset, Span< } charsWritten = charsRequired; - // Hoist most of the bounds checks on destination. - { _ = destination[MinimumBytesNeeded - 1]; } - dateTime.GetDate(out int year, out int month, out int day); dateTime.GetTimePrecise(out int hour, out int minute, out int second, out int tick); - FormattingHelpers.WriteFourDigits((uint)year, destination, 0); - destination[4] = TChar.CreateTruncating('-'); - FormattingHelpers.WriteTwoDigits((uint)month, destination, 5); - destination[7] = TChar.CreateTruncating('-'); - FormattingHelpers.WriteTwoDigits((uint)day, destination, 8); - destination[10] = TChar.CreateTruncating('T'); - FormattingHelpers.WriteTwoDigits((uint)hour, destination, 11); - destination[13] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)minute, destination, 14); - destination[16] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)second, destination, 17); - destination[19] = TChar.CreateTruncating('.'); - FormattingHelpers.WriteDigits((uint)tick, destination.Slice(20, 7)); - - if (kind == DateTimeKind.Local) + fixed (TChar* dest = &MemoryMarshal.GetReference(destination)) { - int offsetTotalMinutes = (int)(offset.Ticks / TimeSpan.TicksPerMinute); + Number.WriteFourDigits((uint)year, dest); + dest[4] = TChar.CastFrom('-'); + Number.WriteTwoDigits((uint)month, dest + 5); + dest[7] = TChar.CastFrom('-'); + Number.WriteTwoDigits((uint)day, dest + 8); + dest[10] = TChar.CastFrom('T'); + Number.WriteTwoDigits((uint)hour, dest + 11); + dest[13] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)minute, dest + 14); + dest[16] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)second, dest + 17); + dest[19] = TChar.CastFrom('.'); + Number.WriteDigits((uint)tick, dest + 20, 7); - char sign = '+'; - if (offsetTotalMinutes < 0) + if (kind == DateTimeKind.Local) { - sign = '-'; - offsetTotalMinutes = -offsetTotalMinutes; - } + int offsetTotalMinutes = (int)(offset.Ticks / TimeSpan.TicksPerMinute); + + char sign = '+'; + if (offsetTotalMinutes < 0) + { + sign = '-'; + offsetTotalMinutes = -offsetTotalMinutes; + } - int offsetHours = Math.DivRem(offsetTotalMinutes, 60, out int offsetMinutes); + (int offsetHours, int offsetMinutes) = Math.DivRem(offsetTotalMinutes, 60); - // Writing the value backward allows the JIT to optimize by - // performing a single bounds check against buffer. - FormattingHelpers.WriteTwoDigits((uint)offsetMinutes, destination, 31); - destination[30] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)offsetHours, destination, 28); - destination[27] = TChar.CreateTruncating(sign); - } - else if (kind == DateTimeKind.Utc) - { - destination[27] = TChar.CreateTruncating('Z'); + dest[27] = TChar.CastFrom(sign); + Number.WriteTwoDigits((uint)offsetHours, dest + 28); + dest[30] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)offsetMinutes, dest + 31); + } + else if (kind == DateTimeKind.Utc) + { + dest[27] = TChar.CastFrom('Z'); + } } return true; @@ -1424,7 +1459,7 @@ internal static bool TryFormatO(DateTime dateTime, TimeSpan offset, Span< // 01234567890123456789012345678 // ----------------------------- // Tue, 03 Jan 2017 08:08:05 GMT - internal static bool TryFormatR(DateTime dateTime, TimeSpan offset, Span destination, out int charsWritten) where TChar : unmanaged, IBinaryInteger + internal static unsafe bool TryFormatR(DateTime dateTime, TimeSpan offset, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { if (destination.Length <= 28) { @@ -1432,6 +1467,8 @@ internal static bool TryFormatR(DateTime dateTime, TimeSpan offset, Span< return false; } + charsWritten = 29; + if (offset.Ticks != NullOffset) { // Convert to UTC invariants. @@ -1441,45 +1478,40 @@ internal static bool TryFormatR(DateTime dateTime, TimeSpan offset, Span< dateTime.GetDate(out int year, out int month, out int day); dateTime.GetTime(out int hour, out int minute, out int second); - if (typeof(TChar) == typeof(char)) - { - Span dest = MemoryMarshal.Cast(destination); - - FormattingHelpers.CopyFour("Sun,Mon,Tue,Wed,Thu,Fri,Sat,".AsSpan(4 * (int)dateTime.DayOfWeek), dest); - dest[4] = ' '; - FormattingHelpers.WriteTwoDigits((uint)day, dest, 5); - dest[7] = ' '; - FormattingHelpers.CopyFour("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ".AsSpan(4 * (month - 1)), dest.Slice(8)); - FormattingHelpers.WriteFourDigits((uint)year, dest, 12); - dest[16] = ' '; - FormattingHelpers.WriteTwoDigits((uint)hour, dest, 17); - dest[19] = ':'; - FormattingHelpers.WriteTwoDigits((uint)minute, dest, 20); - dest[22] = ':'; - FormattingHelpers.WriteTwoDigits((uint)second, dest, 23); - FormattingHelpers.CopyFour(" GMT", dest.Slice(25)); - } - else + string dayAbbrev = s_invariantAbbreviatedDayNames[(int)dateTime.DayOfWeek]; + Debug.Assert(dayAbbrev.Length == 3); + + string monthAbbrev = s_invariantAbbreviatedMonthNames[month - 1]; + Debug.Assert(monthAbbrev.Length == 3); + + fixed (TChar* dest = &MemoryMarshal.GetReference(destination)) { - Debug.Assert(typeof(TChar) == typeof(byte)); - Span dest = MemoryMarshal.Cast(destination); - - FormattingHelpers.CopyFour("Sun,Mon,Tue,Wed,Thu,Fri,Sat,"u8.Slice(4 * (int)dateTime.DayOfWeek), dest); - dest[4] = (byte)' '; - FormattingHelpers.WriteTwoDigits((uint)day, dest, 5); - dest[7] = (byte)' '; - FormattingHelpers.CopyFour("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec "u8.Slice(4 * (month - 1)), dest.Slice(8)); - FormattingHelpers.WriteFourDigits((uint)year, dest, 12); - dest[16] = (byte)' '; - FormattingHelpers.WriteTwoDigits((uint)hour, dest, 17); - dest[19] = (byte)':'; - FormattingHelpers.WriteTwoDigits((uint)minute, dest, 20); - dest[22] = (byte)':'; - FormattingHelpers.WriteTwoDigits((uint)second, dest, 23); - FormattingHelpers.CopyFour(" GMT"u8, dest.Slice(25)); + char c = dayAbbrev[2]; // remove bounds checks on remaining dayAbbrev accesses + dest[0] = TChar.CastFrom(dayAbbrev[0]); + dest[1] = TChar.CastFrom(dayAbbrev[1]); + dest[2] = TChar.CastFrom(c); + dest[3] = TChar.CastFrom(','); + dest[4] = TChar.CastFrom(' '); + Number.WriteTwoDigits((uint)day, dest + 5); + dest[7] = TChar.CastFrom(' '); + c = monthAbbrev[2]; // remove bounds checks on remaining monthAbbrev accesses + dest[8] = TChar.CastFrom(monthAbbrev[0]); + dest[9] = TChar.CastFrom(monthAbbrev[1]); + dest[10] = TChar.CastFrom(c); + dest[11] = TChar.CastFrom(' '); + Number.WriteFourDigits((uint)year, dest + 12); + dest[16] = TChar.CastFrom(' '); + Number.WriteTwoDigits((uint)hour, dest + 17); + dest[19] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)minute, dest + 20); + dest[22] = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)second, dest + 23); + dest[25] = TChar.CastFrom(' '); + dest[26] = TChar.CastFrom('G'); + dest[27] = TChar.CastFrom('M'); + dest[28] = TChar.CastFrom('T'); } - charsWritten = 29; return true; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs index 26a182843ac8b..6d07fa3ff9cb1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs @@ -5,6 +5,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; namespace System.Globalization { @@ -81,6 +83,11 @@ public sealed class DateTimeFormatInfo : IFormatProvider, ICloneable private string? monthDayPattern; private string? dateTimeOffsetPattern; + private byte[]? amDesignatorUtf8; + private byte[]? pmDesignatorUtf8; + private byte[]? timeSeparatorUtf8; + private byte[]? dateSeparatorUtf8; + private const string rfc1123Pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"; // The sortable pattern is based on ISO 8601. @@ -360,9 +367,18 @@ public string AMDesignator ClearTokenHashTable(); amDesignator = value; + amDesignatorUtf8 = null; } } + internal ReadOnlySpan AMDesignatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(AMDesignator) : + MemoryMarshal.Cast(amDesignatorUtf8 ??= Encoding.UTF8.GetBytes(AMDesignator)); + } + public Calendar Calendar { get @@ -597,9 +613,18 @@ public string DateSeparator ClearTokenHashTable(); dateSeparator = value; + dateSeparatorUtf8 = null; } } + internal ReadOnlySpan DateSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(DateSeparator) : + MemoryMarshal.Cast(dateSeparatorUtf8 ??= Encoding.UTF8.GetBytes(DateSeparator)); + } + public DayOfWeek FirstDayOfWeek { get @@ -791,9 +816,18 @@ public string PMDesignator ClearTokenHashTable(); pmDesignator = value; + pmDesignatorUtf8 = null; } } + internal ReadOnlySpan PMDesignatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(PMDesignator) : + MemoryMarshal.Cast(pmDesignatorUtf8 ??= Encoding.UTF8.GetBytes(PMDesignator)); + } + public string RFC1123Pattern => rfc1123Pattern; /// @@ -966,9 +1000,18 @@ public string TimeSeparator ClearTokenHashTable(); timeSeparator = value; + timeSeparatorUtf8 = null; } } + internal ReadOnlySpan TimeSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(TimeSeparator) : + MemoryMarshal.Cast(timeSeparatorUtf8 ??= Encoding.UTF8.GetBytes(TimeSeparator)); + } + public string UniversalSortableDateTimePattern => universalSortableDateTimePattern; /// @@ -1713,6 +1756,15 @@ public string[] MonthGenitiveNames _decimalSeparator ??= new NumberFormatInfo(_cultureData.UseUserOverride ? CultureData.GetCultureData(_cultureData.CultureName, false) : _cultureData).NumberDecimalSeparator; + private byte[]? _decimalSeparatorUtf8; + internal ReadOnlySpan DecimalSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(DecimalSeparator) : + MemoryMarshal.Cast(_decimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(DecimalSeparator)); + } + // Positive TimeSpan Pattern private string? _fullTimeSpanPositivePattern; internal string FullTimeSpanPositivePattern => diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewNumber.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewNumber.cs index dddbaba644777..94652898a521c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewNumber.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewNumber.cs @@ -83,7 +83,7 @@ internal static class HebrewNumber // //////////////////////////////////////////////////////////////////////////// - internal static void Append(ref ValueListBuilder outputBuffer, int Number) where TChar : unmanaged, IBinaryInteger + internal static void Append(ref ValueListBuilder outputBuffer, int Number) where TChar : unmanaged, IUtfChar { int outputBufferStartingLength = outputBuffer.Length; @@ -205,7 +205,7 @@ internal static void Append(ref ValueListBuilder outputBuffer, int { TChar last = outputBuffer[outputBuffer.Length - 1]; outputBuffer.Length--; - outputBuffer.Append(TChar.CreateTruncating('"')); + outputBuffer.Append(TChar.CastFrom('"')); outputBuffer.Append(last); } else @@ -213,7 +213,7 @@ internal static void Append(ref ValueListBuilder outputBuffer, int Debug.Assert(typeof(TChar) == typeof(byte)); Rune.DecodeLastFromUtf8(MemoryMarshal.AsBytes(outputBuffer.AsSpan()), out Rune value, out int bytesConsumed); outputBuffer.Length -= bytesConsumed; - outputBuffer.Append(TChar.CreateTruncating('"')); + outputBuffer.Append(TChar.CastFrom('"')); DateTimeFormat.AppendChar(ref outputBuffer, (char)value.Value); } } 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 3d2c60fd01550..5af77c998154a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs @@ -1,6 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + namespace System.Globalization { /// @@ -55,6 +61,21 @@ public sealed class NumberFormatInfo : IFormatProvider, ICloneable internal string _percentSymbol = "%"; internal string _perMilleSymbol = "\u2030"; + internal byte[]? _positiveSignUtf8; + internal byte[]? _negativeSignUtf8; + internal byte[]? _currencySymbolUtf8; + internal byte[]? _numberDecimalSeparatorUtf8; + internal byte[]? _currencyDecimalSeparatorUtf8; + internal byte[]? _currencyGroupSeparatorUtf8; + internal byte[]? _numberGroupSeparatorUtf8; + internal byte[]? _percentSymbolUtf8; + internal byte[]? _percentDecimalSeparatorUtf8; + internal byte[]? _percentGroupSeparatorUtf8; + internal byte[]? _perMilleSymbolUtf8; + internal byte[]? _nanSymbolUtf8; + internal byte[]? _positiveInfinitySymbolUtf8; + internal byte[]? _negativeInfinitySymbolUtf8; + internal string[] _nativeDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; internal int _numberDecimalDigits = 2; @@ -242,9 +263,19 @@ public string CurrencyDecimalSeparator VerifyWritable(); ArgumentException.ThrowIfNullOrEmpty(value); _currencyDecimalSeparator = value; + _currencyDecimalSeparatorUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan CurrencyDecimalSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_currencyDecimalSeparator) : + MemoryMarshal.Cast(_currencyDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_currencyDecimalSeparator)); + } + public bool IsReadOnly => _isReadOnly; /// @@ -324,9 +355,19 @@ public string CurrencyGroupSeparator VerifyWritable(); ArgumentNullException.ThrowIfNull(value); _currencyGroupSeparator = value; + _currencyGroupSeparatorUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan CurrencyGroupSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_currencyGroupSeparator) : + MemoryMarshal.Cast(_currencyGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_currencyGroupSeparator)); + } + public string CurrencySymbol { get => _currencySymbol; @@ -336,9 +377,21 @@ public string CurrencySymbol VerifyWritable(); _currencySymbol = value; + _currencySymbolUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan CurrencySymbolTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_currencySymbol) : + MemoryMarshal.Cast(_currencySymbolUtf8 ??= Encoding.UTF8.GetBytes(_currencySymbol)); + } + + internal byte[]? CurrencySymbolUtf8 => _currencySymbolUtf8 ??= Encoding.UTF8.GetBytes(_currencySymbol); + /// /// Returns the current culture's NumberFormatInfo. Used by Parse methods. /// @@ -370,9 +423,19 @@ public string NaNSymbol VerifyWritable(); _nanSymbol = value; + _nanSymbolUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan NaNSymbolTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_nanSymbol) : + MemoryMarshal.Cast(_nanSymbolUtf8 ??= Encoding.UTF8.GetBytes(_nanSymbol)); + } + public int CurrencyNegativePattern { get => _currencyNegativePattern; @@ -457,9 +520,19 @@ public string NegativeInfinitySymbol VerifyWritable(); _negativeInfinitySymbol = value; + _negativeInfinitySymbolUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan NegativeInfinitySymbolTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_negativeInfinitySymbol) : + MemoryMarshal.Cast(_negativeInfinitySymbolUtf8 ??= Encoding.UTF8.GetBytes(_negativeInfinitySymbol)); + } + public string NegativeSign { get => _negativeSign; @@ -469,10 +542,20 @@ public string NegativeSign VerifyWritable(); _negativeSign = value; + _negativeSignUtf8 = null; InitializeInvariantAndNegativeSignFlags(); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan NegativeSignTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_negativeSign) : + MemoryMarshal.Cast(_negativeSignUtf8 ??= Encoding.UTF8.GetBytes(_negativeSign)); + } + public int NumberDecimalDigits { get => _numberDecimalDigits; @@ -499,9 +582,19 @@ public string NumberDecimalSeparator VerifyWritable(); ArgumentException.ThrowIfNullOrEmpty(value); _numberDecimalSeparator = value; + _numberDecimalSeparatorUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan NumberDecimalSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_numberDecimalSeparator) : + MemoryMarshal.Cast(_numberDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_numberDecimalSeparator)); + } + public string NumberGroupSeparator { get => _numberGroupSeparator; @@ -510,9 +603,19 @@ public string NumberGroupSeparator VerifyWritable(); ArgumentNullException.ThrowIfNull(value); _numberGroupSeparator = value; + _numberGroupSeparatorUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan NumberGroupSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_numberGroupSeparator) : + MemoryMarshal.Cast(_numberGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_numberGroupSeparator)); + } + public int CurrencyPositivePattern { get => _currencyPositivePattern; @@ -540,9 +643,19 @@ public string PositiveInfinitySymbol VerifyWritable(); _positiveInfinitySymbol = value; + _positiveInfinitySymbolUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan PositiveInfinitySymbolTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_positiveInfinitySymbol) : + MemoryMarshal.Cast(_positiveInfinitySymbolUtf8 ??= Encoding.UTF8.GetBytes(_positiveInfinitySymbol)); + } + public string PositiveSign { get => _positiveSign; @@ -552,10 +665,20 @@ public string PositiveSign VerifyWritable(); _positiveSign = value; + _positiveSignUtf8 = null; InitializeInvariantAndNegativeSignFlags(); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan PositiveSignTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_positiveSign) : + MemoryMarshal.Cast(_positiveSignUtf8 ??= Encoding.UTF8.GetBytes(_positiveSign)); + } + public int PercentDecimalDigits { get => _percentDecimalDigits; @@ -582,9 +705,19 @@ public string PercentDecimalSeparator VerifyWritable(); ArgumentException.ThrowIfNullOrEmpty(value); _percentDecimalSeparator = value; + _percentDecimalSeparatorUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan PercentDecimalSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_percentDecimalSeparator) : + MemoryMarshal.Cast(_percentDecimalSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_percentDecimalSeparator)); + } + public string PercentGroupSeparator { get => _percentGroupSeparator; @@ -593,9 +726,19 @@ public string PercentGroupSeparator VerifyWritable(); ArgumentNullException.ThrowIfNull(value); _percentGroupSeparator = value; + _percentGroupSeparatorUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan PercentGroupSeparatorTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_percentGroupSeparator) : + MemoryMarshal.Cast(_percentGroupSeparatorUtf8 ??= Encoding.UTF8.GetBytes(_percentGroupSeparator)); + } + public string PercentSymbol { get => _percentSymbol; @@ -604,9 +747,19 @@ public string PercentSymbol ArgumentNullException.ThrowIfNull(value); VerifyWritable(); _percentSymbol = value; + _percentSymbolUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan PercentSymbolTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_percentSymbol) : + MemoryMarshal.Cast(_percentSymbolUtf8 ??= Encoding.UTF8.GetBytes(_percentSymbol)); + } + public string PerMilleSymbol { get => _perMilleSymbol; @@ -616,9 +769,19 @@ public string PerMilleSymbol VerifyWritable(); _perMilleSymbol = value; + _perMilleSymbolUtf8 = null; } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan PerMilleSymbolTChar() where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + return typeof(TChar) == typeof(char) ? + MemoryMarshal.Cast(_perMilleSymbol) : + MemoryMarshal.Cast(_perMilleSymbolUtf8 ??= Encoding.UTF8.GetBytes(_perMilleSymbol)); + } + public string[] NativeDigits { get => (string[])_nativeDigits.Clone(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs index aa891eb81846a..5b35c0d6ede5c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs @@ -48,7 +48,7 @@ internal static string Format(TimeSpan value, string? format, IFormatProvider? f } /// Main method called from TimeSpan.TryFormat. - internal static bool TryFormat(TimeSpan value, Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? formatProvider) where TChar : unmanaged, IBinaryInteger + internal static bool TryFormat(TimeSpan value, Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? formatProvider) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -70,7 +70,7 @@ internal static bool TryFormat(TimeSpan value, Span destination, o c == 'g' ? StandardFormat.g : c == 'G' ? StandardFormat.G : throw new FormatException(SR.Format_InvalidString); - return TryFormatStandard(value, sf, DateTimeFormatInfo.GetInstance(formatProvider).DecimalSeparator, destination, out charsWritten); + return TryFormatStandard(value, sf, DateTimeFormatInfo.GetInstance(formatProvider).DecimalSeparatorTChar(), destination, out charsWritten); } } @@ -101,7 +101,7 @@ private static string FormatG(TimeSpan value, DateTimeFormatInfo dtfi, StandardF internal enum StandardFormat { C, G, g } - internal static bool TryFormatStandard(TimeSpan value, StandardFormat format, string? decimalSeparator, Span destination, out int written) where TChar : unmanaged, IBinaryInteger + internal static unsafe bool TryFormatStandard(TimeSpan value, StandardFormat format, ReadOnlySpan decimalSeparator, Span destination, out int written) where TChar : unmanaged, IUtfChar { Debug.Assert(format == StandardFormat.C || format == StandardFormat.G || format == StandardFormat.g); @@ -144,7 +144,7 @@ internal static bool TryFormatStandard(TimeSpan value, StandardFormat for // "c": Write out a fraction only if it's non-zero, and write out all 7 digits of it. if (fraction != 0) { - Debug.Assert(decimalSeparator is null); + Debug.Assert(decimalSeparator.IsEmpty); fractionDigits = DateTimeFormat.MaxSecondsFractionDigits; requiredOutputLength += fractionDigits + 1; // digits plus leading decimal separator } @@ -152,25 +152,19 @@ internal static bool TryFormatStandard(TimeSpan value, StandardFormat for case StandardFormat.G: // "G": Write out a fraction regardless of whether it's 0, and write out all 7 digits of it. - Debug.Assert(decimalSeparator is not null); fractionDigits = DateTimeFormat.MaxSecondsFractionDigits; requiredOutputLength += fractionDigits; - requiredOutputLength += typeof(TChar) == typeof(char) || (decimalSeparator.Length == 1 && char.IsAscii(decimalSeparator[0])) ? - decimalSeparator.Length : - Encoding.UTF8.GetByteCount(decimalSeparator); + requiredOutputLength += decimalSeparator.Length; break; default: // "g": Write out a fraction only if it's non-zero, and write out only the most significant digits. Debug.Assert(format == StandardFormat.g); - Debug.Assert(decimalSeparator is not null); if (fraction != 0) { fractionDigits = DateTimeFormat.MaxSecondsFractionDigits - FormattingHelpers.CountDecimalTrailingZeros(fraction, out fraction); requiredOutputLength += fractionDigits; - requiredOutputLength += typeof(TChar) == typeof(char) || (decimalSeparator.Length == 1 && char.IsAscii(decimalSeparator[0])) ? - decimalSeparator.Length : - Encoding.UTF8.GetByteCount(decimalSeparator); + requiredOutputLength += decimalSeparator.Length; } break; } @@ -230,82 +224,73 @@ internal static bool TryFormatStandard(TimeSpan value, StandardFormat for return false; } - // Write leading '-' if necessary - int idx = 0; - if (value.Ticks < 0) + fixed (TChar* dest = &MemoryMarshal.GetReference(destination)) { - destination[idx++] = TChar.CreateTruncating('-'); - } + TChar* p = dest; - // Write day and separator, if necessary - if (dayDigits != 0) - { - FormattingHelpers.WriteDigits(days, destination.Slice(idx, dayDigits)); - idx += dayDigits; - destination[idx++] = TChar.CreateTruncating(format == StandardFormat.C ? '.' : ':'); - } + // Write leading '-' if necessary + if (value.Ticks < 0) + { + *p++ = TChar.CastFrom('-'); + } - // Write "[h]h:mm:ss - Debug.Assert(hourDigits == 1 || hourDigits == 2); - if (hourDigits == 2) - { - FormattingHelpers.WriteTwoDigits(hours, destination, idx); - idx += 2; - } - else - { - destination[idx++] = TChar.CreateTruncating('0' + hours); - } - destination[idx++] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)minutes, destination, idx); - idx += 2; - destination[idx++] = TChar.CreateTruncating(':'); - FormattingHelpers.WriteTwoDigits((uint)seconds, destination, idx); - idx += 2; - - // Write fraction and separator, if necessary - if (fractionDigits != 0) - { - Debug.Assert(format == StandardFormat.C || decimalSeparator != null); - if (format == StandardFormat.C) + // Write day and separator, if necessary + if (dayDigits != 0) { - destination[idx++] = TChar.CreateTruncating('.'); + Number.WriteDigits(days, p, dayDigits); + p += dayDigits; + *p++ = TChar.CastFrom(format == StandardFormat.C ? '.' : ':'); } - else if (typeof(TChar) == typeof(char)) + + // Write "[h]h:mm:ss + Debug.Assert(hourDigits == 1 || hourDigits == 2); + if (hourDigits == 2) { - if (decimalSeparator!.Length == 1) - { - destination[idx++] = TChar.CreateTruncating(decimalSeparator[0]); - } - else - { - decimalSeparator.CopyTo(MemoryMarshal.Cast(destination).Slice(idx)); - idx += decimalSeparator.Length; - } + Number.WriteTwoDigits(hours, p); + p += 2; } else { - Debug.Assert(typeof(TChar) == typeof(byte)); - if (decimalSeparator!.Length == 1 && char.IsAscii(decimalSeparator[0])) + *p++ = TChar.CastFrom('0' + hours); + } + *p++ = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)minutes, p); + p += 2; + *p++ = TChar.CastFrom(':'); + Number.WriteTwoDigits((uint)seconds, p); + p += 2; + + // Write fraction and separator, if necessary + if (fractionDigits != 0) + { + Debug.Assert(format == StandardFormat.C || decimalSeparator != null); + if (format == StandardFormat.C) + { + *p++ = TChar.CastFrom('.'); + } + else if (decimalSeparator!.Length == 1) { - destination[idx++] = TChar.CreateTruncating(decimalSeparator[0]); + *p++ = decimalSeparator[0]; } else { - idx += Encoding.UTF8.GetBytes(decimalSeparator, MemoryMarshal.Cast(destination).Slice(idx)); + decimalSeparator.CopyTo(new Span(p, decimalSeparator.Length)); + p += decimalSeparator.Length; } + + Number.WriteDigits(fraction, p, fractionDigits); + p += fractionDigits; } - FormattingHelpers.WriteDigits(fraction, destination.Slice(idx, fractionDigits)); - idx += fractionDigits; + + Debug.Assert(p - dest == requiredOutputLength); } - Debug.Assert(idx == requiredOutputLength); written = requiredOutputLength; return true; } /// Format the TimeSpan instance using the specified format. - private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan format, DateTimeFormatInfo dtfi, ref ValueListBuilder result) where TChar : unmanaged, IBinaryInteger + private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan format, DateTimeFormatInfo dtfi, ref ValueListBuilder result) where TChar : unmanaged, IUtfChar { Debug.Assert(dtfi != null); @@ -411,7 +396,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< goto default; // to release the builder and throw } - DateTimeFormat.FormatDigits(ref result, day, tokenLen, true); + DateTimeFormat.FormatDigits(ref result, day, tokenLen); break; case '\'': case '\"': @@ -446,7 +431,7 @@ private static void FormatCustomized(TimeSpan value, scoped ReadOnlySpan< nextChar = DateTimeFormat.ParseNextChar(format, i); if (nextChar >= 0) { - result.Append(TChar.CreateTruncating(nextChar)); + result.Append(TChar.CastFrom(nextChar)); tokenLen = 2; } else diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index 34173dd54768e..3a7a2048e45a2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -23,7 +23,8 @@ public readonly struct Half IComparable, IEquatable, IBinaryFloatingPointIeee754, - IMinMaxValue + IMinMaxValue, + IUtf8SpanFormattable { private const NumberStyles DefaultParseStyle = NumberStyles.Float | NumberStyles.AllowThousands; @@ -539,6 +540,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatHalf(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatHalf(this, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); + } + // // Explicit Convert To Half // diff --git a/src/libraries/System.Private.CoreLib/src/System/IUtfChar.cs b/src/libraries/System.Private.CoreLib/src/System/IUtfChar.cs new file mode 100644 index 0000000000000..09d8ba184436f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IUtfChar.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; + +namespace System +{ + // NOTE: This is a workaround for current inlining limitations of some backend code generators. + // We would prefer to not have this interface at all and instead just use TChar.CreateTruncuating. + // Once inlining is improved on these hot code paths in formatting, we can remove this interface. + + /// Internal interface used to unify char and byte in formatting operations. + internal interface IUtfChar : + IBinaryInteger + where TSelf : unmanaged, IUtfChar + { + /// Casts the specified value to this type. + public static abstract TSelf CastFrom(byte value); + + /// Casts the specified value to this type. + public static abstract TSelf CastFrom(char value); + + /// Casts the specified value to this type. + public static abstract TSelf CastFrom(int value); + + /// Casts the specified value to this type. + public static abstract TSelf CastFrom(uint value); + + /// Casts the specified value to this type. + public static abstract TSelf CastFrom(ulong value); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index cbfeeb9b8ba6e..8247f5a600eac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -17,7 +17,8 @@ namespace System public readonly struct Int128 : IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IUtf8SpanFormattable { internal const int Size = 16; @@ -118,6 +119,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt128(this, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatInt128(this, format, provider, utf8Destination, out bytesWritten); + } + public static Int128 Parse(string s) { ArgumentNullException.ThrowIfNull(s); diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 67183c0b1b5f7..7286d104def07 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -23,7 +23,8 @@ public readonly struct Int16 IEquatable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IUtf8SpanFormattable { private readonly short m_value; // Do not rename (binary serialization) @@ -118,6 +119,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt32(m_value, 0x0000FFFF, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatInt32(m_value, 0x0000FFFF, format, provider, utf8Destination, out bytesWritten); + } + public static short Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 56dc9a7c8ab05..cff29460616af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -23,7 +23,8 @@ public readonly struct Int32 IEquatable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IUtf8SpanFormattable { private readonly int m_value; // Do not rename (binary serialization) @@ -128,6 +129,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt32(m_value, ~0, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatInt32(m_value, ~0, format, provider, utf8Destination, out bytesWritten); + } + public static int Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index de499239ebfd3..4d7143d4a8ebd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -23,7 +23,8 @@ public readonly struct Int64 IEquatable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IUtf8SpanFormattable { private readonly long m_value; // Do not rename (binary serialization) @@ -125,6 +126,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt64(m_value, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatInt64(m_value, format, provider, utf8Destination, out bytesWritten); + } + public static long Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index cac171836c749..f44bca5e46955 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -32,7 +32,8 @@ public readonly struct IntPtr ISerializable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IUtf8SpanFormattable { private readonly nint _value; @@ -210,6 +211,10 @@ public int CompareTo(nint value) public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) => ((nint_t)_value).TryFormat(destination, out charsWritten, format, provider); + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) => + ((IUtf8SpanFormattable)(nint_t)_value).TryFormat(utf8Destination, out bytesWritten, format, provider); + public static nint Parse(string s) => (nint)nint_t.Parse(s); public static nint Parse(string s, NumberStyles style) => (nint)nint_t.Parse(s, style); public static nint Parse(string s, IFormatProvider? provider) => (nint)nint_t.Parse(s, provider); diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index 5b106f5063147..41db3a79f707c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Text; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Numerics; @@ -271,8 +272,8 @@ internal static partial class Number /// /// This is a semi-arbitrary bound. For mono, which is often used for more size-constrained workloads, /// we keep the size really small, supporting only single digit values. For coreclr, we use a larger - /// value, still relatively small but large enough to accomodate common sources of numbers to strings, e.g. HTTP success status codes. - /// By being >= 255, it also accomodates all byte.ToString()s. If no small numbers are ever formatted, we incur + /// value, still relatively small but large enough to accommodate common sources of numbers to strings, e.g. HTTP success status codes. + /// By being >= 255, it also accommodates all byte.ToString()s. If no small numbers are ever formatted, we incur /// the ~2400 bytes on 64-bit for the array itself. If all small numbers are formatted, we incur ~11,500 bytes /// on 64-bit for the array and all the strings. /// @@ -320,28 +321,28 @@ internal static partial class Number // Optimizations using "TwoDigits" inspired by: // https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c/ - private const string TwoDigitsChars = - "00010203040506070809" + - "10111213141516171819" + - "20212223242526272829" + - "30313233343536373839" + - "40414243444546474849" + - "50515253545556575859" + - "60616263646566676869" + - "70717273747576777879" + - "80818283848586878889" + - "90919293949596979899"; - private static ReadOnlySpan TwoDigitsBytes => - "00010203040506070809"u8 + - "10111213141516171819"u8 + - "20212223242526272829"u8 + - "30313233343536373839"u8 + - "40414243444546474849"u8 + - "50515253545556575859"u8 + - "60616263646566676869"u8 + - "70717273747576777879"u8 + - "80818283848586878889"u8 + - "90919293949596979899"u8; + private static readonly byte[] TwoDigitsCharsAsBytes = + MemoryMarshal.AsBytes("00010203040506070809" + + "10111213141516171819" + + "20212223242526272829" + + "30313233343536373839" + + "40414243444546474849" + + "50515253545556575859" + + "60616263646566676869" + + "70717273747576777879" + + "80818283848586878889" + + "90919293949596979899").ToArray(); + private static readonly byte[] TwoDigitsBytes = + ("00010203040506070809"u8 + + "10111213141516171819"u8 + + "20212223242526272829"u8 + + "30313233343536373839"u8 + + "40414243444546474849"u8 + + "50515253545556575859"u8 + + "60616263646566676869"u8 + + "70717273747576777879"u8 + + "80818283848586878889"u8 + + "90919293949596979899"u8).ToArray(); public static unsafe string FormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info) { @@ -353,22 +354,26 @@ public static unsafe string FormatDecimal(decimal value, ReadOnlySpan form DecimalToNumber(ref value, ref number); char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } - return sb.ToString(); + string result = vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } - public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + char fmt = ParseFormatSpecifier(format, out int digits); byte* pDigits = stackalloc byte[DecimalNumberBufferLength]; @@ -376,19 +381,21 @@ public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan for DecimalToNumber(ref value, ref number); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + TChar* stackPtr = stackalloc TChar[CharStackBufferSize]; + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } - return sb.TryCopyTo(destination, out charsWritten); + bool success = vlb.TryCopyTo(destination, out charsWritten); + vlb.Dispose(); + return success; } internal static unsafe void DecimalToNumber(scoped ref decimal d, ref NumberBuffer number) @@ -414,24 +421,31 @@ internal static unsafe void DecimalToNumber(scoped ref decimal d, ref NumberBuff { *dst++ = *p++; } - *dst = (byte)('\0'); + *dst = (byte)'\0'; number.CheckConsistency(); } public static string FormatDouble(double value, string? format, NumberFormatInfo info) { - var sb = new ValueStringBuilder(stackalloc char[CharStackBufferSize]); - return FormatDouble(ref sb, value, format, info) ?? sb.ToString(); + var vlb = new ValueListBuilder(stackalloc char[CharStackBufferSize]); + string result = FormatDouble(ref vlb, value, format, info) ?? vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } - public static bool TryFormatDouble(double value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + public static bool TryFormatDouble(double value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { - var sb = new ValueStringBuilder(stackalloc char[CharStackBufferSize]); - string? s = FormatDouble(ref sb, value, format, info); - return s != null ? + var vlb = new ValueListBuilder(stackalloc TChar[CharStackBufferSize]); + string? s = FormatDouble(ref vlb, value, format, info); + + Debug.Assert(s is null || typeof(TChar) == typeof(char)); + bool success = s != null ? TryCopyTo(s, destination, out charsWritten) : - sb.TryCopyTo(destination, out charsWritten); + vlb.TryCopyTo(destination, out charsWritten); + + vlb.Dispose(); + return success; } private static int GetFloatingPointMaxDigitsAndPrecision(char fmt, ref int precision, NumberFormatInfo info, out bool isSignificantDigits) @@ -448,107 +462,108 @@ private static int GetFloatingPointMaxDigitsAndPrecision(char fmt, ref int preci { case 'C': case 'c': - { - // The currency format uses the precision specifier to indicate the number of - // decimal digits to format. This defaults to NumberFormatInfo.CurrencyDecimalDigits. - - if (precision == -1) { - precision = info.CurrencyDecimalDigits; - } - isSignificantDigits = false; + // The currency format uses the precision specifier to indicate the number of + // decimal digits to format. This defaults to NumberFormatInfo.CurrencyDecimalDigits. - break; - } + if (precision == -1) + { + precision = info.CurrencyDecimalDigits; + } + isSignificantDigits = false; + + break; + } case 'E': case 'e': - { - // The exponential format uses the precision specifier to indicate the number of - // decimal digits to format. This defaults to 6. However, the exponential format - // also always formats a single integral digit, so we need to increase the precision - // specifier and treat it as the number of significant digits to account for this. - - if (precision == -1) { - precision = DefaultPrecisionExponentialFormat; - } + // The exponential format uses the precision specifier to indicate the number of + // decimal digits to format. This defaults to 6. However, the exponential format + // also always formats a single integral digit, so we need to increase the precision + // specifier and treat it as the number of significant digits to account for this. - precision++; - isSignificantDigits = true; + if (precision == -1) + { + precision = DefaultPrecisionExponentialFormat; + } - break; - } + precision++; + isSignificantDigits = true; + + break; + } case 'F': case 'f': case 'N': case 'n': - { - // The fixed-point and number formats use the precision specifier to indicate the number - // of decimal digits to format. This defaults to NumberFormatInfo.NumberDecimalDigits. - - if (precision == -1) { - precision = info.NumberDecimalDigits; - } - isSignificantDigits = false; + // The fixed-point and number formats use the precision specifier to indicate the number + // of decimal digits to format. This defaults to NumberFormatInfo.NumberDecimalDigits. - break; - } + if (precision == -1) + { + precision = info.NumberDecimalDigits; + } + isSignificantDigits = false; + + break; + } case 'G': case 'g': - { - // The general format uses the precision specifier to indicate the number of significant - // digits to format. This defaults to the shortest roundtrippable string. Additionally, - // given that we can't return zero significant digits, we treat 0 as returning the shortest - // roundtrippable string as well. - - if (precision == 0) { - precision = -1; - } - isSignificantDigits = true; + // The general format uses the precision specifier to indicate the number of significant + // digits to format. This defaults to the shortest roundtrippable string. Additionally, + // given that we can't return zero significant digits, we treat 0 as returning the shortest + // roundtrippable string as well. - break; - } + if (precision == 0) + { + precision = -1; + } + isSignificantDigits = true; + + break; + } case 'P': case 'p': - { - // The percent format uses the precision specifier to indicate the number of - // decimal digits to format. This defaults to NumberFormatInfo.PercentDecimalDigits. - // However, the percent format also always multiplies the number by 100, so we need - // to increase the precision specifier to ensure we get the appropriate number of digits. - - if (precision == -1) { - precision = info.PercentDecimalDigits; - } + // The percent format uses the precision specifier to indicate the number of + // decimal digits to format. This defaults to NumberFormatInfo.PercentDecimalDigits. + // However, the percent format also always multiplies the number by 100, so we need + // to increase the precision specifier to ensure we get the appropriate number of digits. + + if (precision == -1) + { + precision = info.PercentDecimalDigits; + } - precision += 2; - isSignificantDigits = false; + precision += 2; + isSignificantDigits = false; - break; - } + break; + } case 'R': case 'r': - { - // The roundtrip format ignores the precision specifier and always returns the shortest - // roundtrippable string. + { + // The roundtrip format ignores the precision specifier and always returns the shortest + // roundtrippable string. - precision = -1; - isSignificantDigits = true; + precision = -1; + isSignificantDigits = true; - break; - } + break; + } default: - { - throw new FormatException(SR.Argument_BadFormatSpecifier); - } + { + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + goto case 'r'; // unreachable + } } return maxDigits; @@ -559,16 +574,32 @@ private static int GetFloatingPointMaxDigitsAndPrecision(char fmt, ref int preci /// Non-null if an existing string can be returned, in which case the builder will be unmodified. /// Null if no existing string was returned, in which case the formatted output is in the builder. /// - private static unsafe string? FormatDouble(ref ValueStringBuilder sb, double value, ReadOnlySpan format, NumberFormatInfo info) + private static unsafe string? FormatDouble(ref ValueListBuilder vlb, double value, ReadOnlySpan format, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { if (!double.IsFinite(value)) { if (double.IsNaN(value)) { - return info.NaNSymbol; + if (typeof(TChar) == typeof(char)) + { + return info.NaNSymbol; + } + else + { + vlb.Append(info.NaNSymbolTChar()); + return null; + } } - return double.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + if (typeof(TChar) == typeof(char)) + { + return double.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + else + { + vlb.Append(double.IsNegative(value) ? info.NegativeInfinitySymbolTChar() : info.PositiveInfinitySymbolTChar()); + return null; + } } char fmt = ParseFormatSpecifier(format, out int precision); @@ -615,29 +646,36 @@ private static int GetFloatingPointMaxDigitsAndPrecision(char fmt, ref int preci nMaxDigits = Math.Max(number.DigitsCount, DoublePrecision); } - NumberToString(ref sb, ref number, fmt, nMaxDigits, info); + NumberToString(ref vlb, ref number, fmt, nMaxDigits, info); } else { Debug.Assert(precision == DoublePrecisionCustomFormat); - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } return null; } public static string FormatSingle(float value, string? format, NumberFormatInfo info) { - var sb = new ValueStringBuilder(stackalloc char[CharStackBufferSize]); - return FormatSingle(ref sb, value, format, info) ?? sb.ToString(); + var vlb = new ValueListBuilder(stackalloc char[CharStackBufferSize]); + string result = FormatSingle(ref vlb, value, format, info) ?? vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } - public static bool TryFormatSingle(float value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + public static bool TryFormatSingle(float value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { - var sb = new ValueStringBuilder(stackalloc char[CharStackBufferSize]); - string? s = FormatSingle(ref sb, value, format, info); - return s != null ? + var vlb = new ValueListBuilder(stackalloc TChar[CharStackBufferSize]); + string? s = FormatSingle(ref vlb, value, format, info); + + Debug.Assert(s is null || typeof(TChar) == typeof(char)); + bool success = s != null ? TryCopyTo(s, destination, out charsWritten) : - sb.TryCopyTo(destination, out charsWritten); + vlb.TryCopyTo(destination, out charsWritten); + + vlb.Dispose(); + return success; } /// Formats the specified value according to the specified format and info. @@ -645,16 +683,34 @@ public static bool TryFormatSingle(float value, ReadOnlySpan format, Numbe /// Non-null if an existing string can be returned, in which case the builder will be unmodified. /// Null if no existing string was returned, in which case the formatted output is in the builder. /// - private static unsafe string? FormatSingle(ref ValueStringBuilder sb, float value, ReadOnlySpan format, NumberFormatInfo info) + private static unsafe string? FormatSingle(ref ValueListBuilder vlb, float value, ReadOnlySpan format, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + if (!float.IsFinite(value)) { if (float.IsNaN(value)) { - return info.NaNSymbol; + if (typeof(TChar) == typeof(char)) + { + return info.NaNSymbol; + } + else + { + vlb.Append(info.NaNSymbolTChar()); + return null; + } } - return float.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + if (typeof(TChar) == typeof(char)) + { + return float.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + else + { + vlb.Append(float.IsNegative(value) ? info.NegativeInfinitySymbolTChar() : info.PositiveInfinitySymbolTChar()); + return null; + } } char fmt = ParseFormatSpecifier(format, out int precision); @@ -701,20 +757,22 @@ public static bool TryFormatSingle(float value, ReadOnlySpan format, Numbe nMaxDigits = Math.Max(number.DigitsCount, SinglePrecision); } - NumberToString(ref sb, ref number, fmt, nMaxDigits, info); + NumberToString(ref vlb, ref number, fmt, nMaxDigits, info); } else { Debug.Assert(precision == SinglePrecisionCustomFormat); - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } return null; } public static string FormatHalf(Half value, string? format, NumberFormatInfo info) { - var sb = new ValueStringBuilder(stackalloc char[CharStackBufferSize]); - return FormatHalf(ref sb, value, format, info) ?? sb.ToString(); + var vlb = new ValueListBuilder(stackalloc char[CharStackBufferSize]); + string result = FormatHalf(ref vlb, value, format, info) ?? vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } /// Formats the specified value according to the specified format and info. @@ -722,16 +780,34 @@ public static string FormatHalf(Half value, string? format, NumberFormatInfo inf /// Non-null if an existing string can be returned, in which case the builder will be unmodified. /// Null if no existing string was returned, in which case the formatted output is in the builder. /// - private static unsafe string? FormatHalf(ref ValueStringBuilder sb, Half value, ReadOnlySpan format, NumberFormatInfo info) + private static unsafe string? FormatHalf(ref ValueListBuilder vlb, Half value, ReadOnlySpan format, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + if (!Half.IsFinite(value)) { if (Half.IsNaN(value)) { - return info.NaNSymbol; + if (typeof(TChar) == typeof(char)) + { + return info.NaNSymbol; + } + else + { + vlb.Append(info.NaNSymbolTChar()); + return null; + } } - return Half.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + if (typeof(TChar) == typeof(char)) + { + return Half.IsNegative(value) ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol; + } + else + { + vlb.Append(Half.IsNegative(value) ? info.NegativeInfinitySymbolTChar() : info.PositiveInfinitySymbolTChar()); + return null; + } } char fmt = ParseFormatSpecifier(format, out int precision); @@ -776,38 +852,52 @@ public static string FormatHalf(Half value, string? format, NumberFormatInfo inf nMaxDigits = Math.Max(number.DigitsCount, HalfPrecision); } - NumberToString(ref sb, ref number, fmt, nMaxDigits, info); + NumberToString(ref vlb, ref number, fmt, nMaxDigits, info); } else { Debug.Assert(precision == HalfPrecisionCustomFormat); - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } return null; } - public static bool TryFormatHalf(Half value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) + public static bool TryFormatHalf(Half value, ReadOnlySpan format, NumberFormatInfo info, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { - var sb = new ValueStringBuilder(stackalloc char[CharStackBufferSize]); - string? s = FormatHalf(ref sb, value, format, info); - return s != null ? + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + + var vlb = new ValueListBuilder(stackalloc TChar[CharStackBufferSize]); + string? s = FormatHalf(ref vlb, value, format, info); + + Debug.Assert(s is null || typeof(TChar) == typeof(char)); + bool success = s != null ? TryCopyTo(s, destination, out charsWritten) : - sb.TryCopyTo(destination, out charsWritten); - } + vlb.TryCopyTo(destination, out charsWritten); + vlb.Dispose(); + return success; + } - private static bool TryCopyTo(string source, Span destination, out int charsWritten) + private static bool TryCopyTo(string source, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); Debug.Assert(source != null); - if (source.TryCopyTo(destination)) + if (typeof(TChar) == typeof(char)) { - charsWritten = source.Length; - return true; - } + if (source.TryCopyTo(MemoryMarshal.Cast(destination))) + { + charsWritten = source.Length; + return true; + } - charsWritten = 0; - return false; + charsWritten = 0; + return false; + } + else + { + return Encoding.UTF8.TryGetBytes(source, MemoryMarshal.Cast(destination), out charsWritten); + } } private static char GetHexBase(char fmt) @@ -854,34 +944,38 @@ static unsafe string FormatInt32Slow(int value, int hexMask, string? format, IFo Int32ToNumber(value, ref number); char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, formatSpan, info); + NumberToStringFormat(ref vlb, ref number, formatSpan, info); } - return sb.ToString(); + + string result = vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } } } - public static bool TryFormatInt32(int value, int hexMask, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + [MethodImpl(MethodImplOptions.AggressiveInlining)] // expose to caller's likely-const format to trim away slow path + public static bool TryFormatInt32(int value, int hexMask, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { // Fast path for default format if (format.Length == 0) { return value >= 0 ? TryUInt32ToDecStr((uint)value, destination, out charsWritten) : - TryNegativeInt32ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + TryNegativeInt32ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSignTChar(), destination, out charsWritten); } return TryFormatInt32Slow(value, hexMask, format, provider, destination, out charsWritten); - static unsafe bool TryFormatInt32Slow(int value, int hexMask, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + static unsafe bool TryFormatInt32Slow(int value, int hexMask, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { char fmt = ParseFormatSpecifier(format, out int digits); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison @@ -889,7 +983,7 @@ static unsafe bool TryFormatInt32Slow(int value, int hexMask, ReadOnlySpan { return value >= 0 ? TryUInt32ToDecStr((uint)value, digits, destination, out charsWritten) : - TryNegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + TryNegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSignTChar(), destination, out charsWritten); } else if (fmtUpper == 'X') { @@ -904,18 +998,21 @@ static unsafe bool TryFormatInt32Slow(int value, int hexMask, ReadOnlySpan Int32ToNumber(value, ref number); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + TChar* stackPtr = stackalloc TChar[CharStackBufferSize]; + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } - return sb.TryCopyTo(destination, out charsWritten); + + bool success = vlb.TryCopyTo(destination, out charsWritten); + vlb.Dispose(); + return success; } } } @@ -953,23 +1050,29 @@ static unsafe string FormatUInt32Slow(uint value, string? format, IFormatProvide UInt32ToNumber(value, ref number); char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, formatSpan, info); + NumberToStringFormat(ref vlb, ref number, formatSpan, info); } - return sb.ToString(); + + string result = vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } } } - public static bool TryFormatUInt32(uint value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + [MethodImpl(MethodImplOptions.AggressiveInlining)] // expose to caller's likely-const format to trim away slow path + public static bool TryFormatUInt32(uint value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + // Fast path for default format if (format.Length == 0) { @@ -978,7 +1081,7 @@ public static bool TryFormatUInt32(uint value, ReadOnlySpan format, IForma return TryFormatUInt32Slow(value, format, provider, destination, out charsWritten); - static unsafe bool TryFormatUInt32Slow(uint value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + static unsafe bool TryFormatUInt32Slow(uint value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { char fmt = ParseFormatSpecifier(format, out int digits); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison @@ -999,18 +1102,21 @@ static unsafe bool TryFormatUInt32Slow(uint value, ReadOnlySpan format, IF UInt32ToNumber(value, ref number); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + TChar* stackPtr = stackalloc TChar[CharStackBufferSize]; + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } - return sb.TryCopyTo(destination, out charsWritten); + + bool success = vlb.TryCopyTo(destination, out charsWritten); + vlb.Dispose(); + return success; } } } @@ -1052,34 +1158,40 @@ static unsafe string FormatInt64Slow(long value, string? format, IFormatProvider Int64ToNumber(value, ref number); char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, formatSpan, info); + NumberToStringFormat(ref vlb, ref number, formatSpan, info); } - return sb.ToString(); + + string result = vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } } } - public static bool TryFormatInt64(long value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + [MethodImpl(MethodImplOptions.AggressiveInlining)] // expose to caller's likely-const format to trim away slow path + public static bool TryFormatInt64(long value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + // Fast path for default format if (format.Length == 0) { return value >= 0 ? TryUInt64ToDecStr((ulong)value, destination, out charsWritten) : - TryNegativeInt64ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + TryNegativeInt64ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSignTChar(), destination, out charsWritten); } return TryFormatInt64Slow(value, format, provider, destination, out charsWritten); - static unsafe bool TryFormatInt64Slow(long value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + static unsafe bool TryFormatInt64Slow(long value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { char fmt = ParseFormatSpecifier(format, out int digits); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison @@ -1087,7 +1199,7 @@ static unsafe bool TryFormatInt64Slow(long value, ReadOnlySpan format, IFo { return value >= 0 ? TryUInt64ToDecStr((ulong)value, digits, destination, out charsWritten) : - TryNegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + TryNegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSignTChar(), destination, out charsWritten); } else if (fmtUpper == 'X') { @@ -1103,17 +1215,20 @@ static unsafe bool TryFormatInt64Slow(long value, ReadOnlySpan format, IFo Int64ToNumber(value, ref number); char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } - return sb.TryCopyTo(destination, out charsWritten); + + bool success = vlb.TryCopyTo(destination, out charsWritten); + vlb.Dispose(); + return success; } } } @@ -1151,23 +1266,29 @@ static unsafe string FormatUInt64Slow(ulong value, string? format, IFormatProvid UInt64ToNumber(value, ref number); char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, formatSpan, info); + NumberToStringFormat(ref vlb, ref number, formatSpan, info); } - return sb.ToString(); + + string result = vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } } } - public static bool TryFormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + [MethodImpl(MethodImplOptions.AggressiveInlining)] // expose to caller's likely-const format to trim away slow path + public static bool TryFormatUInt64(ulong value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + // Fast path for default format if (format.Length == 0) { @@ -1176,7 +1297,7 @@ public static bool TryFormatUInt64(ulong value, ReadOnlySpan format, IForm return TryFormatUInt64Slow(value, format, provider, destination, out charsWritten); - static unsafe bool TryFormatUInt64Slow(ulong value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + static unsafe bool TryFormatUInt64Slow(ulong value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { char fmt = ParseFormatSpecifier(format, out int digits); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison @@ -1197,18 +1318,21 @@ static unsafe bool TryFormatUInt64Slow(ulong value, ReadOnlySpan format, I UInt64ToNumber(value, ref number); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + TChar* stackPtr = stackalloc TChar[CharStackBufferSize]; + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } - return sb.TryCopyTo(destination, out charsWritten); + + bool success = vlb.TryCopyTo(destination, out charsWritten); + vlb.Dispose(); + return success; } } } @@ -1252,35 +1376,39 @@ static unsafe string FormatInt128Slow(Int128 value, string? format, IFormatProvi Int128ToNumber(value, ref number); char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, formatSpan, info); + NumberToStringFormat(ref vlb, ref number, formatSpan, info); } - return sb.ToString(); + string result = vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } } } - public static bool TryFormatInt128(Int128 value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + public static bool TryFormatInt128(Int128 value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + // Fast path for default format if (format.Length == 0) { return Int128.IsPositive(value) ? TryUInt128ToDecStr((UInt128)value, digits: -1, destination, out charsWritten) - : TryNegativeInt128ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + : TryNegativeInt128ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSignTChar(), destination, out charsWritten); } return TryFormatInt128Slow(value, format, provider, destination, out charsWritten); - static unsafe bool TryFormatInt128Slow(Int128 value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + static unsafe bool TryFormatInt128Slow(Int128 value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { char fmt = ParseFormatSpecifier(format, out int digits); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison @@ -1289,7 +1417,7 @@ static unsafe bool TryFormatInt128Slow(Int128 value, ReadOnlySpan format, { return Int128.IsPositive(value) ? TryUInt128ToDecStr((UInt128)value, digits, destination, out charsWritten) - : TryNegativeInt128ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten); + : TryNegativeInt128ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSignTChar(), destination, out charsWritten); } else if (fmtUpper == 'X') { @@ -1304,19 +1432,21 @@ static unsafe bool TryFormatInt128Slow(Int128 value, ReadOnlySpan format, Int128ToNumber(value, ref number); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + TChar* stackPtr = stackalloc TChar[CharStackBufferSize]; + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } - return sb.TryCopyTo(destination, out charsWritten); + bool success = vlb.TryCopyTo(destination, out charsWritten); + vlb.Dispose(); + return success; } } } @@ -1356,24 +1486,28 @@ static unsafe string FormatUInt128Slow(UInt128 value, string? format, IFormatPro UInt128ToNumber(value, ref number); char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, formatSpan, info); + NumberToStringFormat(ref vlb, ref number, formatSpan, info); } - return sb.ToString(); + string result = vlb.AsSpan().ToString(); + vlb.Dispose(); + return result; } } } - public static bool TryFormatUInt128(UInt128 value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + public static bool TryFormatUInt128(UInt128 value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + // Fast path for default format if (format.Length == 0) { @@ -1382,7 +1516,7 @@ public static bool TryFormatUInt128(UInt128 value, ReadOnlySpan format, IF return TryFormatUInt128Slow(value, format, provider, destination, out charsWritten); - static unsafe bool TryFormatUInt128Slow(UInt128 value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) + static unsafe bool TryFormatUInt128Slow(UInt128 value, ReadOnlySpan format, IFormatProvider? provider, Span destination, out int charsWritten) { char fmt = ParseFormatSpecifier(format, out int digits); char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison @@ -1404,19 +1538,21 @@ static unsafe bool TryFormatUInt128Slow(UInt128 value, ReadOnlySpan format UInt128ToNumber(value, ref number); - char* stackPtr = stackalloc char[CharStackBufferSize]; - ValueStringBuilder sb = new ValueStringBuilder(new Span(stackPtr, CharStackBufferSize)); + TChar* stackPtr = stackalloc TChar[CharStackBufferSize]; + var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize)); if (fmt != 0) { - NumberToString(ref sb, ref number, fmt, digits, info); + NumberToString(ref vlb, ref number, fmt, digits, info); } else { - NumberToStringFormat(ref sb, ref number, format, info); + NumberToStringFormat(ref vlb, ref number, format, info); } - return sb.TryCopyTo(destination, out charsWritten); + bool success = vlb.TryCopyTo(destination, out charsWritten); + vlb.Dispose(); + return success; } } } @@ -1446,25 +1582,27 @@ private static unsafe void Int32ToNumber(int value, ref NumberBuffer number) byte* dst = number.GetDigitsPointer(); while (--i >= 0) + { *dst++ = *p++; - *dst = (byte)('\0'); + } + *dst = (byte)'\0'; number.CheckConsistency(); } - public static string Int32ToDecStr(int value) - { - return value >= 0 ? + public static string Int32ToDecStr(int value) => + value >= 0 ? UInt32ToDecStr((uint)value) : NegativeInt32ToDecStr(value, -1, NumberFormatInfo.CurrentInfo.NegativeSign); - } private static unsafe string NegativeInt32ToDecStr(int value, int digits, string sNegative) { Debug.Assert(value < 0); if (digits < 1) + { digits = 1; + } int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits((uint)(-value))) + sNegative.Length; string result = string.FastAllocateString(bufferLength); @@ -1482,12 +1620,15 @@ private static unsafe string NegativeInt32ToDecStr(int value, int digits, string return result; } - private static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, string sNegative, Span destination, out int charsWritten) + private static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, ReadOnlySpan sNegative, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); Debug.Assert(value < 0); if (digits < 1) + { digits = 1; + } int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits((uint)(-value))) + sNegative.Length; if (bufferLength > destination.Length) @@ -1497,9 +1638,9 @@ private static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, strin } charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits); + TChar* p = UInt32ToDecChars(buffer + bufferLength, (uint)(-value), digits); Debug.Assert(p == buffer + sNegative.Length); for (int i = sNegative.Length - 1; i >= 0; i--) @@ -1514,7 +1655,9 @@ private static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, strin private static unsafe string Int32ToHexStr(int value, char hexBase, int digits) { if (digits < 1) + { digits = 1; + } int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits((uint)value)); string result = string.FastAllocateString(bufferLength); @@ -1526,10 +1669,14 @@ private static unsafe string Int32ToHexStr(int value, char hexBase, int digits) return result; } - private static unsafe bool TryInt32ToHexStr(int value, char hexBase, int digits, Span destination, out int charsWritten) + private static unsafe bool TryInt32ToHexStr(int value, char hexBase, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + if (digits < 1) + { digits = 1; + } int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits((uint)value)); if (bufferLength > destination.Length) @@ -1539,21 +1686,23 @@ private static unsafe bool TryInt32ToHexStr(int value, char hexBase, int digits, } charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits); + TChar* p = Int32ToHexChars(buffer + bufferLength, (uint)value, hexBase, digits); Debug.Assert(p == buffer); } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe char* Int32ToHexChars(char* buffer, uint value, int hexBase, int digits) + private static unsafe TChar* Int32ToHexChars(TChar* buffer, uint value, int hexBase, int digits) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + while (--digits >= 0 || value != 0) { byte digit = (byte)(value & 0xF); - *(--buffer) = (char)(digit + (digit < 10 ? (byte)'0' : hexBase)); + *(--buffer) = TChar.CastFrom(digit + (digit < 10 ? (byte)'0' : hexBase)); value >>= 4; } return buffer; @@ -1575,85 +1724,72 @@ private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number) byte* dst = number.GetDigitsPointer(); while (--i >= 0) + { *dst++ = *p++; - *dst = (byte)('\0'); + } + *dst = (byte)'\0'; number.CheckConsistency(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe void WriteTwoDigits(TChar* ptr, uint value) where TChar : unmanaged, IBinaryInteger + internal static unsafe void WriteTwoDigits(uint value, TChar* ptr) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); Debug.Assert(value <= 99); - if (typeof(TChar) == typeof(char)) - { - Unsafe.WriteUnaligned(ptr, - Unsafe.ReadUnaligned( - ref Unsafe.As( - ref Unsafe.Add(ref TwoDigitsChars.GetRawStringData(), (int)value * 2)))); - } - else - { - Unsafe.WriteUnaligned(ptr, - Unsafe.ReadUnaligned( - ref Unsafe.Add(ref MemoryMarshal.GetReference(TwoDigitsBytes), (int)value * 2))); - } + Unsafe.CopyBlockUnaligned( + ref *(byte*)ptr, + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(typeof(TChar) == typeof(char) ? TwoDigitsCharsAsBytes : TwoDigitsBytes), (uint)sizeof(TChar) * 2 * value), + (uint)sizeof(TChar) * 2); } + /// + /// Writes a value [ 0000 .. 9999 ] to the buffer starting at the specified offset. + /// This method performs best when the starting index is a constant literal. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe byte* UInt32ToDecChars(byte* bufferEnd, uint value) + internal static unsafe void WriteFourDigits(uint value, TChar* ptr) where TChar : unmanaged, IUtfChar { - if (value >= 10) - { - // Handle all values >= 100 two-digits at a time so as to avoid expensive integer division operations. - while (value >= 100) - { - bufferEnd -= 2; - (value, uint remainder) = Math.DivRem(value, 100); - WriteTwoDigits(bufferEnd, remainder); - } + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + Debug.Assert(value <= 9999); - // If there are two digits remaining, store them. - if (value >= 10) - { - bufferEnd -= 2; - WriteTwoDigits(bufferEnd, value); - return bufferEnd; - } - } + (value, uint remainder) = Math.DivRem(value, 100); - // Otherwise, store the single digit remaining. - *(--bufferEnd) = (byte)(value + '0'); - return bufferEnd; + ref byte charsArray = ref MemoryMarshal.GetArrayDataReference(typeof(TChar) == typeof(char) ? TwoDigitsCharsAsBytes : TwoDigitsBytes); + + Unsafe.CopyBlockUnaligned( + ref *(byte*)ptr, + ref Unsafe.Add(ref charsArray, (uint)sizeof(TChar) * 2 * value), + (uint)sizeof(TChar) * 2); + + Unsafe.CopyBlockUnaligned( + ref *(byte*)(ptr + 2), + ref Unsafe.Add(ref charsArray, (uint)sizeof(TChar) * 2 * remainder), + (uint)sizeof(TChar) * 2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe byte* UInt32ToDecChars(byte* bufferEnd, uint value, int digits) + internal static unsafe void WriteDigits(uint value, TChar* ptr, int count) where TChar : unmanaged, IUtfChar { - uint remainder; - while (value >= 100) - { - bufferEnd -= 2; - digits -= 2; - (value, remainder) = Math.DivRem(value, 100); - WriteTwoDigits(bufferEnd, remainder); - } - - while (value != 0 || digits > 0) + TChar* cur; + for (cur = ptr + count - 1; cur > ptr; cur--) { - digits--; - (value, remainder) = Math.DivRem(value, 10); - *(--bufferEnd) = (byte)(remainder + '0'); + uint temp = '0' + value; + value /= 10; + *cur = TChar.CastFrom(temp - (value * 10)); } - return bufferEnd; + Debug.Assert(value < 10); + Debug.Assert(cur == ptr); + *cur = TChar.CastFrom('0' + value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe char* UInt32ToDecChars(char* bufferEnd, uint value) + internal static unsafe TChar* UInt32ToDecChars(TChar* bufferEnd, uint value) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + if (value >= 10) { // Handle all values >= 100 two-digits at a time so as to avoid expensive integer division operations. @@ -1661,42 +1797,42 @@ ref Unsafe.As( { bufferEnd -= 2; (value, uint remainder) = Math.DivRem(value, 100); - WriteTwoDigits(bufferEnd, remainder); + WriteTwoDigits(remainder, bufferEnd); } // If there are two digits remaining, store them. if (value >= 10) { bufferEnd -= 2; - WriteTwoDigits(bufferEnd, value); + WriteTwoDigits(value, bufferEnd); return bufferEnd; } } // Otherwise, store the single digit remaining. - *(--bufferEnd) = (char)(value + '0'); + *(--bufferEnd) = TChar.CastFrom(value + '0'); return bufferEnd; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe char* UInt32ToDecChars(char* bufferEnd, uint value, int digits) + internal static unsafe TChar* UInt32ToDecChars(TChar* bufferEnd, uint value, int digits) where TChar : unmanaged, IUtfChar { - // Handle all values >= 100 two-digits at a time so as to avoid expensive integer division operations. + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + uint remainder; while (value >= 100) { bufferEnd -= 2; digits -= 2; (value, remainder) = Math.DivRem(value, 100); - WriteTwoDigits(bufferEnd, remainder); + WriteTwoDigits(remainder, bufferEnd); } - // Continue writing single digits until we've exhausted both the value and the requested number of digits. while (value != 0 || digits > 0) { digits--; (value, remainder) = Math.DivRem(value, 10); - *(--bufferEnd) = (char)(remainder + '0'); + *(--bufferEnd) = TChar.CastFrom(remainder + '0'); } return bufferEnd; @@ -1753,15 +1889,17 @@ private static unsafe string UInt32ToDecStr(uint value, int digits) return result; } - private static unsafe bool TryUInt32ToDecStr(uint value, Span destination, out int charsWritten) + private static unsafe bool TryUInt32ToDecStr(uint value, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + int bufferLength = FormattingHelpers.CountDigits(value); if (bufferLength <= destination.Length) { charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = UInt32ToDecChars(buffer + bufferLength, value); + TChar* p = UInt32ToDecChars(buffer + bufferLength, value); Debug.Assert(p == buffer); } return true; @@ -1771,16 +1909,18 @@ private static unsafe bool TryUInt32ToDecStr(uint value, Span destination, return false; } - private static unsafe bool TryUInt32ToDecStr(uint value, int digits, Span destination, out int charsWritten) + private static unsafe bool TryUInt32ToDecStr(uint value, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + int countedDigits = FormattingHelpers.CountDigits(value); int bufferLength = Math.Max(digits, countedDigits); if (bufferLength <= destination.Length) { charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = buffer + bufferLength; + TChar* p = buffer + bufferLength; p = digits > countedDigits ? UInt32ToDecChars(p, value, digits) : UInt32ToDecChars(p, value); @@ -1818,8 +1958,10 @@ private static unsafe void Int64ToNumber(long value, ref NumberBuffer number) byte* dst = number.GetDigitsPointer(); while (--i >= 0) + { *dst++ = *p++; - *dst = (byte)('\0'); + } + *dst = (byte)'\0'; number.CheckConsistency(); } @@ -1836,7 +1978,9 @@ private static unsafe string NegativeInt64ToDecStr(long value, int digits, strin Debug.Assert(value < 0); if (digits < 1) + { digits = 1; + } int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits((ulong)(-value))) + sNegative.Length; string result = string.FastAllocateString(bufferLength); @@ -1854,12 +1998,15 @@ private static unsafe string NegativeInt64ToDecStr(long value, int digits, strin return result; } - private static unsafe bool TryNegativeInt64ToDecStr(long value, int digits, string sNegative, Span destination, out int charsWritten) + private static unsafe bool TryNegativeInt64ToDecStr(long value, int digits, ReadOnlySpan sNegative, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); Debug.Assert(value < 0); if (digits < 1) + { digits = 1; + } int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits((ulong)(-value))) + sNegative.Length; if (bufferLength > destination.Length) @@ -1869,9 +2016,9 @@ private static unsafe bool TryNegativeInt64ToDecStr(long value, int digits, stri } charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = UInt64ToDecChars(buffer + bufferLength, (ulong)(-value), digits); + TChar* p = UInt64ToDecChars(buffer + bufferLength, (ulong)(-value), digits); Debug.Assert(p == buffer + sNegative.Length); for (int i = sNegative.Length - 1; i >= 0; i--) @@ -1886,7 +2033,9 @@ private static unsafe bool TryNegativeInt64ToDecStr(long value, int digits, stri private static unsafe string Int64ToHexStr(long value, char hexBase, int digits) { if (digits < 1) + { digits = 1; + } int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits((ulong)value)); string result = string.FastAllocateString(bufferLength); @@ -1898,10 +2047,14 @@ private static unsafe string Int64ToHexStr(long value, char hexBase, int digits) return result; } - private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits, Span destination, out int charsWritten) + private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + if (digits < 1) + { digits = 1; + } int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits((ulong)value)); if (bufferLength > destination.Length) @@ -1911,9 +2064,9 @@ private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits } charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = Int64ToHexChars(buffer + bufferLength, (ulong)value, hexBase, digits); + TChar* p = Int64ToHexChars(buffer + bufferLength, (ulong)value, hexBase, digits); Debug.Assert(p == buffer); } return true; @@ -1922,8 +2075,9 @@ private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits #if TARGET_64BIT [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - private static unsafe char* Int64ToHexChars(char* buffer, ulong value, int hexBase, int digits) + private static unsafe TChar* Int64ToHexChars(TChar* buffer, ulong value, int hexBase, int digits) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); #if TARGET_32BIT uint lower = (uint)value; uint upper = (uint)(value >> 32); @@ -1941,7 +2095,7 @@ private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits while (--digits >= 0 || value != 0) { byte digit = (byte)(value & 0xF); - *(--buffer) = (char)(digit + (digit < 10 ? (byte)'0' : hexBase)); + *(--buffer) = TChar.CastFrom(digit + (digit < 10 ? (byte)'0' : hexBase)); value >>= 4; } return buffer; @@ -1964,8 +2118,10 @@ private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number) byte* dst = number.GetDigitsPointer(); while (--i >= 0) + { *dst++ = *p++; - *dst = (byte)('\0'); + } + *dst = (byte)'\0'; number.CheckConsistency(); } @@ -1981,8 +2137,10 @@ private static uint Int64DivMod1E9(ref ulong value) #if TARGET_64BIT [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - internal static unsafe byte* UInt64ToDecChars(byte* bufferEnd, ulong value) + internal static unsafe TChar* UInt64ToDecChars(TChar* bufferEnd, ulong value) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + #if TARGET_32BIT while ((uint)(value >> 32) != 0) { @@ -1997,20 +2155,20 @@ private static uint Int64DivMod1E9(ref ulong value) { bufferEnd -= 2; (value, ulong remainder) = Math.DivRem(value, 100); - WriteTwoDigits(bufferEnd, (uint)remainder); + WriteTwoDigits((uint)remainder, bufferEnd); } // If there are two digits remaining, store them. if (value >= 10) { bufferEnd -= 2; - WriteTwoDigits(bufferEnd, (uint)value); + WriteTwoDigits((uint)value, bufferEnd); return bufferEnd; } } // Otherwise, store the single digit remaining. - *(--bufferEnd) = (byte)(value + '0'); + *(--bufferEnd) = TChar.CastFrom(value + '0'); return bufferEnd; #endif } @@ -2018,8 +2176,10 @@ private static uint Int64DivMod1E9(ref ulong value) #if TARGET_64BIT [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - internal static unsafe byte* UInt64ToDecChars(byte* bufferEnd, ulong value, int digits) + internal static unsafe TChar* UInt64ToDecChars(TChar* bufferEnd, ulong value, int digits) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + #if TARGET_32BIT while ((uint)(value >> 32) != 0) { @@ -2034,96 +2194,26 @@ private static uint Int64DivMod1E9(ref ulong value) bufferEnd -= 2; digits -= 2; (value, remainder) = Math.DivRem(value, 100); - WriteTwoDigits(bufferEnd, (uint)remainder); + WriteTwoDigits((uint)remainder, bufferEnd); } while (value != 0 || digits > 0) { digits--; (value, remainder) = Math.DivRem(value, 10); - *(--bufferEnd) = (byte)(remainder + '0'); + *(--bufferEnd) = TChar.CastFrom(remainder + '0'); } return bufferEnd; #endif } -#if TARGET_64BIT - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - internal static unsafe char* UInt64ToDecChars(char* bufferEnd, ulong value) + internal static unsafe string UInt64ToDecStr(ulong value) { -#if TARGET_32BIT - while ((uint)(value >> 32) != 0) + // For small numbers, consult a lazily-populated cache. + if (value < SmallNumberCacheLength) { - bufferEnd = UInt32ToDecChars(bufferEnd, Int64DivMod1E9(ref value), 9); - } - return UInt32ToDecChars(bufferEnd, (uint)value); -#else - if (value >= 10) - { - // Handle all values >= 100 two-digits at a time so as to avoid expensive integer division operations. - while (value >= 100) - { - bufferEnd -= 2; - (value, ulong remainder) = Math.DivRem(value, 100); - WriteTwoDigits(bufferEnd, (uint)remainder); - } - - // If there are two digits remaining, store them. - if (value >= 10) - { - bufferEnd -= 2; - WriteTwoDigits(bufferEnd, (uint)value); - return bufferEnd; - } - } - - // Otherwise, store the single digit remaining. - *(--bufferEnd) = (char)(value + '0'); - return bufferEnd; -#endif - } - -#if TARGET_64BIT - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - internal static unsafe char* UInt64ToDecChars(char* bufferEnd, ulong value, int digits) - { -#if TARGET_32BIT - while ((uint)(value >> 32) != 0) - { - bufferEnd = UInt32ToDecChars(bufferEnd, Int64DivMod1E9(ref value), 9); - digits -= 9; - } - return UInt32ToDecChars(bufferEnd, (uint)value, digits); -#else - ulong remainder; - while (value >= 100) - { - bufferEnd -= 2; - digits -= 2; - (value, remainder) = Math.DivRem(value, 100); - WriteTwoDigits(bufferEnd, (uint)remainder); - } - - while (value != 0 || digits > 0) - { - digits--; - (value, remainder) = Math.DivRem(value, 10); - *(--bufferEnd) = (char)(remainder + '0'); - } - - return bufferEnd; -#endif - } - - internal static unsafe string UInt64ToDecStr(ulong value) - { - // For small numbers, consult a lazily-populated cache. - if (value < SmallNumberCacheLength) - { - return UInt32ToDecStrForKnownSmallNumber((uint)value); + return UInt32ToDecStrForKnownSmallNumber((uint)value); } int bufferLength = FormattingHelpers.CountDigits(value); @@ -2141,7 +2231,9 @@ internal static unsafe string UInt64ToDecStr(ulong value) internal static unsafe string UInt64ToDecStr(ulong value, int digits) { if (digits <= 1) + { return UInt64ToDecStr(value); + } int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value)); string result = string.FastAllocateString(bufferLength); @@ -2154,15 +2246,17 @@ internal static unsafe string UInt64ToDecStr(ulong value, int digits) return result; } - private static unsafe bool TryUInt64ToDecStr(ulong value, Span destination, out int charsWritten) + private static unsafe bool TryUInt64ToDecStr(ulong value, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + int bufferLength = FormattingHelpers.CountDigits(value); if (bufferLength <= destination.Length) { charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = buffer + bufferLength; + TChar* p = buffer + bufferLength; p = UInt64ToDecChars(p, value); Debug.Assert(p == buffer); } @@ -2173,16 +2267,16 @@ private static unsafe bool TryUInt64ToDecStr(ulong value, Span destination return false; } - private static unsafe bool TryUInt64ToDecStr(ulong value, int digits, Span destination, out int charsWritten) + private static unsafe bool TryUInt64ToDecStr(ulong value, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { int countedDigits = FormattingHelpers.CountDigits(value); int bufferLength = Math.Max(digits, countedDigits); if (bufferLength <= destination.Length) { charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = buffer + bufferLength; + TChar* p = buffer + bufferLength; p = digits > countedDigits ? UInt64ToDecChars(p, value, digits) : UInt64ToDecChars(p, value); @@ -2219,8 +2313,10 @@ private static unsafe void Int128ToNumber(Int128 value, ref NumberBuffer number) byte* dst = number.GetDigitsPointer(); while (--i >= 0) + { *dst++ = *p++; - *dst = (byte)('\0'); + } + *dst = (byte)'\0'; number.CheckConsistency(); } @@ -2237,7 +2333,9 @@ private static unsafe string NegativeInt128ToDecStr(Int128 value, int digits, st Debug.Assert(Int128.IsNegative(value)); if (digits < 1) + { digits = 1; + } UInt128 absValue = (UInt128)(-value); @@ -2257,12 +2355,15 @@ private static unsafe string NegativeInt128ToDecStr(Int128 value, int digits, st return result; } - private static unsafe bool TryNegativeInt128ToDecStr(Int128 value, int digits, string sNegative, Span destination, out int charsWritten) + private static unsafe bool TryNegativeInt128ToDecStr(Int128 value, int digits, ReadOnlySpan sNegative, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); Debug.Assert(Int128.IsNegative(value)); if (digits < 1) + { digits = 1; + } UInt128 absValue = (UInt128)(-value); @@ -2274,9 +2375,9 @@ private static unsafe bool TryNegativeInt128ToDecStr(Int128 value, int digits, s } charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = UInt128ToDecChars(buffer + bufferLength, absValue, digits); + TChar* p = UInt128ToDecChars(buffer + bufferLength, absValue, digits); Debug.Assert(p == buffer + sNegative.Length); for (int i = sNegative.Length - 1; i >= 0; i--) @@ -2291,7 +2392,9 @@ private static unsafe bool TryNegativeInt128ToDecStr(Int128 value, int digits, s private static unsafe string Int128ToHexStr(Int128 value, char hexBase, int digits) { if (digits < 1) + { digits = 1; + } UInt128 uValue = (UInt128)value; @@ -2305,10 +2408,14 @@ private static unsafe string Int128ToHexStr(Int128 value, char hexBase, int digi return result; } - private static unsafe bool TryInt128ToHexStr(Int128 value, char hexBase, int digits, Span destination, out int charsWritten) + private static unsafe bool TryInt128ToHexStr(Int128 value, char hexBase, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + if (digits < 1) + { digits = 1; + } UInt128 uValue = (UInt128)value; @@ -2320,16 +2427,16 @@ private static unsafe bool TryInt128ToHexStr(Int128 value, char hexBase, int dig } charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = Int128ToHexChars(buffer + bufferLength, uValue, hexBase, digits); + TChar* p = Int128ToHexChars(buffer + bufferLength, uValue, hexBase, digits); Debug.Assert(p == buffer); } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe char* Int128ToHexChars(char* buffer, UInt128 value, int hexBase, int digits) + private static unsafe TChar* Int128ToHexChars(TChar* buffer, UInt128 value, int hexBase, int digits) where TChar : unmanaged, IUtfChar { ulong lower = value.Lower; ulong upper = value.Upper; @@ -2360,8 +2467,10 @@ private static unsafe void UInt128ToNumber(UInt128 value, ref NumberBuffer numbe byte* dst = number.GetDigitsPointer(); while (--i >= 0) + { *dst++ = *p++; - *dst = (byte)('\0'); + } + *dst = (byte)'\0'; number.CheckConsistency(); } @@ -2375,19 +2484,10 @@ private static ulong Int128DivMod1E19(ref UInt128 value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe byte* UInt128ToDecChars(byte* bufferEnd, UInt128 value, int digits) + internal static unsafe TChar* UInt128ToDecChars(TChar* bufferEnd, UInt128 value) where TChar : unmanaged, IUtfChar { - while (value.Upper != 0) - { - bufferEnd = UInt64ToDecChars(bufferEnd, Int128DivMod1E19(ref value), 19); - digits -= 19; - } - return UInt64ToDecChars(bufferEnd, value.Lower, digits); - } + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe char* UInt128ToDecChars(char* bufferEnd, UInt128 value) - { while (value.Upper != 0) { bufferEnd = UInt64ToDecChars(bufferEnd, Int128DivMod1E19(ref value), 19); @@ -2396,8 +2496,10 @@ private static ulong Int128DivMod1E19(ref UInt128 value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe char* UInt128ToDecChars(char* bufferEnd, UInt128 value, int digits) + internal static unsafe TChar* UInt128ToDecChars(TChar* bufferEnd, UInt128 value, int digits) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + while (value.Upper != 0) { bufferEnd = UInt64ToDecChars(bufferEnd, Int128DivMod1E19(ref value), 19); @@ -2428,7 +2530,9 @@ internal static unsafe string UInt128ToDecStr(UInt128 value) internal static unsafe string UInt128ToDecStr(UInt128 value, int digits) { if (digits <= 1) + { return UInt128ToDecStr(value); + } int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value)); string result = string.FastAllocateString(bufferLength); @@ -2441,16 +2545,16 @@ internal static unsafe string UInt128ToDecStr(UInt128 value, int digits) return result; } - private static unsafe bool TryUInt128ToDecStr(UInt128 value, int digits, Span destination, out int charsWritten) + private static unsafe bool TryUInt128ToDecStr(UInt128 value, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { int countedDigits = FormattingHelpers.CountDigits(value); int bufferLength = Math.Max(digits, countedDigits); if (bufferLength <= destination.Length) { charsWritten = bufferLength; - fixed (char* buffer = &MemoryMarshal.GetReference(destination)) + fixed (TChar* buffer = &MemoryMarshal.GetReference(destination)) { - char* p = buffer + bufferLength; + TChar* p = buffer + bufferLength; p = digits > countedDigits ? UInt128ToDecChars(p, value, digits) : UInt128ToDecChars(p, value); @@ -2511,9 +2615,9 @@ internal static unsafe char ParseFormatSpecifier(ReadOnlySpan format, out // Check if we are about to overflow past our limit of 9 digits if (n >= 100_000_000) { - throw new FormatException(SR.Argument_BadFormatSpecifier); + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); } - n = ((n * 10) + format[i++] - '0'); + n = (n * 10) + format[i++] - '0'; } // If we're at the end of the digits rather than having stopped because we hit something @@ -2533,8 +2637,10 @@ internal static unsafe char ParseFormatSpecifier(ReadOnlySpan format, out '\0'; } - internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info) + internal static unsafe void NumberToString(ref ValueListBuilder vlb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + number.CheckConsistency(); bool isCorrectlyRounded = (number.Kind == NumberBufferKind.FloatingPoint); @@ -2542,129 +2648,148 @@ internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref Number { case 'C': case 'c': - { - if (nMaxDigits < 0) - nMaxDigits = info.CurrencyDecimalDigits; + { + if (nMaxDigits < 0) + { + nMaxDigits = info.CurrencyDecimalDigits; + } - RoundNumber(ref number, number.Scale + nMaxDigits, isCorrectlyRounded); // Don't change this line to use digPos since digCount could have its sign changed. + RoundNumber(ref number, number.Scale + nMaxDigits, isCorrectlyRounded); // Don't change this line to use digPos since digCount could have its sign changed. - FormatCurrency(ref sb, ref number, nMaxDigits, info); + FormatCurrency(ref vlb, ref number, nMaxDigits, info); - break; - } + break; + } case 'F': case 'f': - { - if (nMaxDigits < 0) - nMaxDigits = info.NumberDecimalDigits; + { + if (nMaxDigits < 0) + { + nMaxDigits = info.NumberDecimalDigits; + } - RoundNumber(ref number, number.Scale + nMaxDigits, isCorrectlyRounded); + RoundNumber(ref number, number.Scale + nMaxDigits, isCorrectlyRounded); - if (number.IsNegative) - sb.Append(info.NegativeSign); + if (number.IsNegative) + { + vlb.Append(info.NegativeSignTChar()); + } - FormatFixed(ref sb, ref number, nMaxDigits, null, info.NumberDecimalSeparator, null); + FormatFixed(ref vlb, ref number, nMaxDigits, null, info.NumberDecimalSeparatorTChar(), null); - break; - } + break; + } case 'N': case 'n': - { - if (nMaxDigits < 0) - nMaxDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation + { + if (nMaxDigits < 0) + { + nMaxDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation + } - RoundNumber(ref number, number.Scale + nMaxDigits, isCorrectlyRounded); + RoundNumber(ref number, number.Scale + nMaxDigits, isCorrectlyRounded); - FormatNumber(ref sb, ref number, nMaxDigits, info); + FormatNumber(ref vlb, ref number, nMaxDigits, info); - break; - } + break; + } case 'E': case 'e': - { - if (nMaxDigits < 0) - nMaxDigits = DefaultPrecisionExponentialFormat; - nMaxDigits++; + { + if (nMaxDigits < 0) + { + nMaxDigits = DefaultPrecisionExponentialFormat; + } + nMaxDigits++; - RoundNumber(ref number, nMaxDigits, isCorrectlyRounded); + RoundNumber(ref number, nMaxDigits, isCorrectlyRounded); - if (number.IsNegative) - sb.Append(info.NegativeSign); + if (number.IsNegative) + { + vlb.Append(info.NegativeSignTChar()); + } - FormatScientific(ref sb, ref number, nMaxDigits, info, format); + FormatScientific(ref vlb, ref number, nMaxDigits, info, format); - break; - } + break; + } case 'G': case 'g': - { - bool noRounding = false; - if (nMaxDigits < 1) { - if ((number.Kind == NumberBufferKind.Decimal) && (nMaxDigits == -1)) + bool noRounding = false; + if (nMaxDigits < 1) { - noRounding = true; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant + if ((number.Kind == NumberBufferKind.Decimal) && (nMaxDigits == -1)) + { + noRounding = true; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant - if (number.Digits[0] == 0) + if (number.Digits[0] == 0) + { + // -0 should be formatted as 0 for decimal. This is normally handled by RoundNumber (which we are skipping) + goto SkipSign; + } + + goto SkipRounding; + } + else { - // -0 should be formatted as 0 for decimal. This is normally handled by RoundNumber (which we are skipping) - goto SkipSign; + // This ensures that the PAL code pads out to the correct place even when we use the default precision + nMaxDigits = number.DigitsCount; } - - goto SkipRounding; - } - else - { - // This ensures that the PAL code pads out to the correct place even when we use the default precision - nMaxDigits = number.DigitsCount; } - } - RoundNumber(ref number, nMaxDigits, isCorrectlyRounded); + RoundNumber(ref number, nMaxDigits, isCorrectlyRounded); - SkipRounding: - if (number.IsNegative) - sb.Append(info.NegativeSign); + SkipRounding: + if (number.IsNegative) + { + vlb.Append(info.NegativeSignTChar()); + } - SkipSign: - FormatGeneral(ref sb, ref number, nMaxDigits, info, (char)(format - ('G' - 'E')), noRounding); + SkipSign: + FormatGeneral(ref vlb, ref number, nMaxDigits, info, (char)(format - ('G' - 'E')), noRounding); - break; - } + break; + } case 'P': case 'p': - { - if (nMaxDigits < 0) - nMaxDigits = info.PercentDecimalDigits; - number.Scale += 2; + { + if (nMaxDigits < 0) + { + nMaxDigits = info.PercentDecimalDigits; + } + number.Scale += 2; - RoundNumber(ref number, number.Scale + nMaxDigits, isCorrectlyRounded); + RoundNumber(ref number, number.Scale + nMaxDigits, isCorrectlyRounded); - FormatPercent(ref sb, ref number, nMaxDigits, info); + FormatPercent(ref vlb, ref number, nMaxDigits, info); - break; - } + break; + } case 'R': case 'r': - { - format = (char)(format - ('R' - 'G')); - Debug.Assert((format == 'G') || (format == 'g')); - goto case 'G'; - } + { + format = (char)(format - ('R' - 'G')); + Debug.Assert(format is 'G' or 'g'); + goto case 'G'; + } default: - throw new FormatException(SR.Argument_BadFormatSpecifier); + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + break; } } - internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref NumberBuffer number, ReadOnlySpan format, NumberFormatInfo info) + internal static unsafe void NumberToStringFormat(ref ValueListBuilder vlb, ref NumberBuffer number, ReadOnlySpan format, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + number.CheckConsistency(); int digitCount; @@ -2707,16 +2832,23 @@ internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref case '#': digitCount++; break; + case '0': if (firstDigit == 0x7FFFFFFF) + { firstDigit = digitCount; + } digitCount++; lastDigit = digitCount; break; + case '.': if (decimalPos < 0) + { decimalPos = digitCount; + } break; + case ',': if (digitCount > 0 && decimalPos < 0) { @@ -2733,28 +2865,33 @@ internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref thousandCount = 1; } break; + case '%': scaleAdjust += 2; break; + case '\x2030': scaleAdjust += 3; break; + case '\'': case '"': - while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch) - ; + while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch); break; + case '\\': if (src < format.Length && pFormat[src] != 0) + { src++; + } break; + case 'E': case 'e': if ((src < format.Length && pFormat[src] == '0') || (src + 1 < format.Length && (pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0')) { - while (++src < format.Length && pFormat[src] == '0') - ; + while (++src < format.Length && pFormat[src] == '0'); scientific = true; } break; @@ -2763,14 +2900,20 @@ internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref } if (decimalPos < 0) + { decimalPos = digitCount; + } if (thousandPos >= 0) { if (thousandPos == decimalPos) + { scaleAdjust -= thousandCount * 3; + } else + { thousandSeps = true; + } } if (dig[0] != 0) @@ -2839,7 +2982,9 @@ internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref int groupTotalSizeCount = 0; int groupSizeLen = groupDigits.Length; // The length of groupDigits array. if (groupSizeLen != 0) + { groupTotalSizeCount = groupDigits[groupSizeIndex]; // The current running total of group size. + } int groupSize = groupTotalSizeCount; int totalDigits = digPos + ((adjust < 0) ? adjust : 0); // Actual number of digits in o/p @@ -2847,7 +2992,10 @@ internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref while (numDigits > groupTotalSizeCount) { if (groupSize == 0) + { break; + } + ++thousandsSepCtr; if (thousandsSepCtr >= thousandsSepPos.Length) { @@ -2868,7 +3016,9 @@ internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref } if (number.IsNegative && (section == 0) && (number.Scale != 0)) - sb.Append(info.NegativeSign); + { + vlb.Append(info.NegativeSignTChar()); + } bool decimalWritten = false; @@ -2889,12 +3039,12 @@ internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref { // digPos will be one greater than thousandsSepPos[thousandsSepCtr] since we are at // the character after which the groupSeparator needs to be appended. - sb.Append(*cur != 0 ? (char)(*cur++) : '0'); + vlb.Append(TChar.CastFrom(*cur != 0 ? (char)(*cur++) : '0')); if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) { if (digPos == thousandsSepPos[thousandsSepCtr] + 1) { - sb.Append(info.NumberGroupSeparator); + vlb.Append(info.NumberGroupSeparatorTChar()); thousandsSepCtr--; } } @@ -2909,129 +3059,160 @@ internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref { case '#': case '0': - { - if (adjust < 0) - { - adjust++; - ch = digPos <= firstDigit ? '0' : '\0'; - } - else { - ch = *cur != 0 ? (char)(*cur++) : digPos > lastDigit ? '0' : '\0'; - } - if (ch != 0) - { - sb.Append(ch); - if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) + if (adjust < 0) + { + adjust++; + ch = digPos <= firstDigit ? '0' : '\0'; + } + else { - if (digPos == thousandsSepPos[thousandsSepCtr] + 1) + ch = *cur != 0 ? (char)(*cur++) : digPos > lastDigit ? '0' : '\0'; + } + + if (ch != 0) + { + vlb.Append(TChar.CastFrom(ch)); + if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0) { - sb.Append(info.NumberGroupSeparator); - thousandsSepCtr--; + if (digPos == thousandsSepPos[thousandsSepCtr] + 1) + { + vlb.Append(info.NumberGroupSeparatorTChar()); + thousandsSepCtr--; + } } } + + digPos--; + break; } - digPos--; - break; - } case '.': - { - if (digPos != 0 || decimalWritten) { - // For compatibility, don't echo repeated decimals + if (digPos != 0 || decimalWritten) + { + // For compatibility, don't echo repeated decimals + break; + } + + // If the format has trailing zeros or the format has a decimal and digits remain + if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0)) + { + vlb.Append(info.NumberDecimalSeparatorTChar()); + decimalWritten = true; + } break; } - // If the format has trailing zeros or the format has a decimal and digits remain - if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0)) - { - sb.Append(info.NumberDecimalSeparator); - decimalWritten = true; - } - break; - } + case '\x2030': - sb.Append(info.PerMilleSymbol); + vlb.Append(info.PerMilleSymbolTChar()); break; + case '%': - sb.Append(info.PercentSymbol); + vlb.Append(info.PercentSymbolTChar()); break; + case ',': break; + case '\'': case '"': while (src < format.Length && pFormat[src] != 0 && pFormat[src] != ch) - sb.Append(pFormat[src++]); + { + AppendUnknownChar(ref vlb, pFormat[src++]); + } + if (src < format.Length && pFormat[src] != 0) + { src++; + } break; + case '\\': if (src < format.Length && pFormat[src] != 0) - sb.Append(pFormat[src++]); + { + AppendUnknownChar(ref vlb, pFormat[src++]); + } break; + case 'E': case 'e': - { - bool positiveSign = false; - int i = 0; - if (scientific) { - if (src < format.Length && pFormat[src] == '0') - { - // Handles E0, which should format the same as E-0 - i++; - } - else if (src + 1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0') + bool positiveSign = false; + int i = 0; + if (scientific) { - // Handles E+0 - positiveSign = true; - } - else if (src + 1 < format.Length && pFormat[src] == '-' && pFormat[src + 1] == '0') - { - // Handles E-0 - // Do nothing, this is just a place holder s.t. we don't break out of the loop. + if (src < format.Length && pFormat[src] == '0') + { + // Handles E0, which should format the same as E-0 + i++; + } + else if (src + 1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0') + { + // Handles E+0 + positiveSign = true; + } + else if (src + 1 < format.Length && pFormat[src] == '-' && pFormat[src + 1] == '0') + { + // Handles E-0 + // Do nothing, this is just a place holder s.t. we don't break out of the loop. + } + else + { + vlb.Append(TChar.CastFrom(ch)); + break; + } + + while (++src < format.Length && pFormat[src] == '0') + { + i++; + } + + if (i > 10) + { + i = 10; + } + + int exp = dig[0] == 0 ? 0 : number.Scale - decimalPos; + FormatExponent(ref vlb, info, exp, ch, i, positiveSign); + scientific = false; } else { - sb.Append(ch); - break; - } - - while (++src < format.Length && pFormat[src] == '0') - i++; - if (i > 10) - i = 10; + vlb.Append(TChar.CastFrom(ch)); + if (src < format.Length) + { + if (pFormat[src] == '+' || pFormat[src] == '-') + { + AppendUnknownChar(ref vlb, pFormat[src++]); + } - int exp = dig[0] == 0 ? 0 : number.Scale - decimalPos; - FormatExponent(ref sb, info, exp, ch, i, positiveSign); - scientific = false; - } - else - { - sb.Append(ch); // Copy E or e to output - if (src < format.Length) - { - if (pFormat[src] == '+' || pFormat[src] == '-') - sb.Append(pFormat[src++]); - while (src < format.Length && pFormat[src] == '0') - sb.Append(pFormat[src++]); + while (src < format.Length && pFormat[src] == '0') + { + AppendUnknownChar(ref vlb, pFormat[src++]); + } + } } + break; } - break; - } + default: - sb.Append(ch); + AppendUnknownChar(ref vlb, ch); break; } } } - if (number.IsNegative && (section == 0) && (number.Scale == 0) && (sb.Length > 0)) - sb.Insert(0, info.NegativeSign); + if (number.IsNegative && (section == 0) && (number.Scale == 0) && (vlb.Length > 0)) + { + vlb.Insert(0, info.NegativeSignTChar()); + } } - private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info) + private static void FormatCurrency(ref ValueListBuilder vlb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + string fmt = number.IsNegative ? s_negCurrencyFormats[info.CurrencyNegativePattern] : s_posCurrencyFormats[info.CurrencyPositivePattern]; @@ -3041,23 +3222,31 @@ private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer n switch (ch) { case '#': - FormatFixed(ref sb, ref number, nMaxDigits, info._currencyGroupSizes, info.CurrencyDecimalSeparator, info.CurrencyGroupSeparator); + FormatFixed(ref vlb, ref number, nMaxDigits, info._currencyGroupSizes, info.CurrencyDecimalSeparatorTChar(), info.CurrencyGroupSeparatorTChar()); break; + case '-': - sb.Append(info.NegativeSign); + vlb.Append(info.NegativeSignTChar()); break; + case '$': - sb.Append(info.CurrencySymbol); + vlb.Append(info.CurrencySymbolTChar()); break; + default: - sb.Append(ch); + vlb.Append(TChar.CastFrom(ch)); break; } } } - private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, int[]? groupDigits, string? sDecimal, string? sGroup) + private static unsafe void FormatFixed( + ref ValueListBuilder vlb, ref NumberBuffer number, + int nMaxDigits, int[]? groupDigits, + ReadOnlySpan sDecimal, ReadOnlySpan sGroup) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + int digPos = number.Scale; byte* dig = number.GetDigitsPointer(); @@ -3079,15 +3268,21 @@ private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuff { groupSize = groupDigits[groupSizeIndex]; if (groupSize == 0) + { break; + } bufferSize += sGroup.Length; if (groupSizeIndex < groupDigits.Length - 1) + { groupSizeIndex++; + } groupSizeCount += groupDigits[groupSizeIndex]; - if (groupSizeCount < 0 || bufferSize < 0) - throw new ArgumentOutOfRangeException(); // If we overflow + if ((groupSizeCount | bufferSize) < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(); // If we overflow + } } groupSize = groupSizeCount == 0 ? 0 : groupDigits[0]; // If you passed in an array with one entry as 0, groupSizeCount == 0 @@ -3097,12 +3292,12 @@ private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuff int digitCount = 0; int digLength = number.DigitsCount; int digStart = (digPos < digLength) ? digPos : digLength; - fixed (char* spanPtr = &MemoryMarshal.GetReference(sb.AppendSpan(bufferSize))) + fixed (TChar* spanPtr = &MemoryMarshal.GetReference(vlb.AppendSpan(bufferSize))) { - char* p = spanPtr + bufferSize - 1; + TChar* p = spanPtr + bufferSize - 1; for (int i = digPos - 1; i >= 0; i--) { - *(p--) = (i < digStart) ? (char)(dig[i]) : '0'; + *(p--) = TChar.CastFrom((i < digStart) ? (char)dig[i] : '0'); if (groupSize > 0) { @@ -3110,7 +3305,9 @@ private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuff if ((digitCount == groupSize) && (i != 0)) { for (int j = sGroup.Length - 1; j >= 0; j--) + { *(p--) = sGroup[j]; + } if (groupSizeIndex < groupDigits.Length - 1) { @@ -3130,38 +3327,67 @@ private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuff { do { - sb.Append(*dig != 0 ? (char)(*dig++) : '0'); + vlb.Append(TChar.CastFrom(*dig != 0 ? (char)(*dig++) : '0')); } while (--digPos > 0); } } else { - sb.Append('0'); + vlb.Append(TChar.CastFrom('0')); } if (nMaxDigits > 0) { Debug.Assert(sDecimal != null); - sb.Append(sDecimal); + vlb.Append(sDecimal); if ((digPos < 0) && (nMaxDigits > 0)) { int zeroes = Math.Min(-digPos, nMaxDigits); - sb.Append('0', zeroes); + for (int i = 0; i < zeroes; i++) + { + vlb.Append(TChar.CastFrom('0')); + } digPos += zeroes; nMaxDigits -= zeroes; } while (nMaxDigits > 0) { - sb.Append((*dig != 0) ? (char)(*dig++) : '0'); + vlb.Append(TChar.CastFrom((*dig != 0) ? (char)(*dig++) : '0')); nMaxDigits--; } } } - private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info) + /// Appends a char to the builder when the char is not known to be ASCII. + /// This requires a helper as if the character isn't ASCII, for UTF8 encoding it will result in multiple bytes added. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void AppendUnknownChar(ref ValueListBuilder vlb, char ch) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + + if (typeof(TChar) == typeof(char) || char.IsAscii(ch)) + { + vlb.Append(TChar.CastFrom(ch)); + } + else + { + AppendNonAsciiBytes(ref vlb, ch); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void AppendNonAsciiBytes(ref ValueListBuilder vlb, char ch) + { + var r = new Rune(ch); + r.EncodeToUtf8(MemoryMarshal.AsBytes(vlb.AppendSpan(r.Utf8SequenceLength))); + } + } + + private static void FormatNumber(ref ValueListBuilder vlb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info) where TChar : unmanaged, IUtfChar + { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + string fmt = number.IsNegative ? s_negNumberFormats[info.NumberNegativePattern] : PosNumberFormat; @@ -3171,60 +3397,74 @@ private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer num switch (ch) { case '#': - FormatFixed(ref sb, ref number, nMaxDigits, info._numberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator); + FormatFixed(ref vlb, ref number, nMaxDigits, info._numberGroupSizes, info.NumberDecimalSeparatorTChar(), info.NumberGroupSeparatorTChar()); break; + case '-': - sb.Append(info.NegativeSign); + vlb.Append(info.NegativeSignTChar()); break; + default: - sb.Append(ch); + vlb.Append(TChar.CastFrom(ch)); break; } } } - private static unsafe void FormatScientific(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, char expChar) + private static unsafe void FormatScientific(ref ValueListBuilder vlb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, char expChar) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + byte* dig = number.GetDigitsPointer(); - sb.Append((*dig != 0) ? (char)(*dig++) : '0'); + vlb.Append(TChar.CastFrom((*dig != 0) ? (char)(*dig++) : '0')); if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point - sb.Append(info.NumberDecimalSeparator); + { + vlb.Append(info.NumberDecimalSeparatorTChar()); + } while (--nMaxDigits > 0) - sb.Append((*dig != 0) ? (char)(*dig++) : '0'); + { + vlb.Append(TChar.CastFrom((*dig != 0) ? (char)(*dig++) : '0')); + } int e = number.Digits[0] == 0 ? 0 : number.Scale - 1; - FormatExponent(ref sb, info, e, expChar, 3, true); + FormatExponent(ref vlb, info, e, expChar, 3, true); } - private static unsafe void FormatExponent(ref ValueStringBuilder sb, NumberFormatInfo info, int value, char expChar, int minDigits, bool positiveSign) + private static unsafe void FormatExponent(ref ValueListBuilder vlb, NumberFormatInfo info, int value, char expChar, int minDigits, bool positiveSign) where TChar : unmanaged, IUtfChar { - sb.Append(expChar); + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + + vlb.Append(TChar.CastFrom(expChar)); if (value < 0) { - sb.Append(info.NegativeSign); + vlb.Append(info.NegativeSignTChar()); value = -value; } else { if (positiveSign) - sb.Append(info.PositiveSign); + { + vlb.Append(info.PositiveSignTChar()); + } } - char* digits = stackalloc char[MaxUInt32DecDigits]; - char* p = UInt32ToDecChars(digits + MaxUInt32DecDigits, (uint)value, minDigits); - sb.Append(p, (int)(digits + MaxUInt32DecDigits - p)); + TChar* digits = stackalloc TChar[MaxUInt32DecDigits]; + TChar* p = UInt32ToDecChars(digits + MaxUInt32DecDigits, (uint)value, minDigits); + vlb.Append(new ReadOnlySpan(p, (int)(digits + MaxUInt32DecDigits - p))); } - private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific) + private static unsafe void FormatGeneral(ref ValueListBuilder vlb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, char expChar, bool suppressScientific) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + int digPos = number.Scale; bool scientific = false; - if (!bSuppressScientific) + if (!suppressScientific) { // Don't switch to scientific notation if (digPos > nMaxDigits || digPos < -3) @@ -3240,34 +3480,41 @@ private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBu { do { - sb.Append((*dig != 0) ? (char)(*dig++) : '0'); - } while (--digPos > 0); + vlb.Append(TChar.CastFrom((*dig != 0) ? (char)(*dig++) : '0')); + } + while (--digPos > 0); } else { - sb.Append('0'); + vlb.Append(TChar.CastFrom('0')); } if (*dig != 0 || digPos < 0) { - sb.Append(info.NumberDecimalSeparator); + vlb.Append(info.NumberDecimalSeparatorTChar()); while (digPos < 0) { - sb.Append('0'); + vlb.Append(TChar.CastFrom('0')); digPos++; } while (*dig != 0) - sb.Append((char)(*dig++)); + { + vlb.Append(TChar.CastFrom(*dig++)); + } } if (scientific) - FormatExponent(ref sb, info, number.Scale - 1, expChar, 2, true); + { + FormatExponent(ref vlb, info, number.Scale - 1, expChar, 2, true); + } } - private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info) + private static void FormatPercent(ref ValueListBuilder vlb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { + Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); + string fmt = number.IsNegative ? s_negPercentFormats[info.PercentNegativePattern] : s_posPercentFormats[info.PercentPositivePattern]; @@ -3277,16 +3524,19 @@ private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer nu switch (ch) { case '#': - FormatFixed(ref sb, ref number, nMaxDigits, info._percentGroupSizes, info.PercentDecimalSeparator, info.PercentGroupSeparator); + FormatFixed(ref vlb, ref number, nMaxDigits, info._percentGroupSizes, info.PercentDecimalSeparatorTChar(), info.PercentGroupSeparatorTChar()); break; + case '-': - sb.Append(info.NegativeSign); + vlb.Append(info.NegativeSignTChar()); break; + case '%': - sb.Append(info.PercentSymbol); + vlb.Append(info.PercentSymbolTChar()); break; + default: - sb.Append(ch); + vlb.Append(TChar.CastFrom(ch)); break; } } @@ -3298,12 +3548,16 @@ internal static unsafe void RoundNumber(ref NumberBuffer number, int pos, bool i int i = 0; while (i < pos && dig[i] != '\0') + { i++; + } if ((i == pos) && ShouldRoundUp(dig, i, number.Kind, isCorrectlyRounded)) { while (i > 0 && dig[i - 1] == '9') + { i--; + } if (i > 0) { @@ -3319,7 +3573,9 @@ internal static unsafe void RoundNumber(ref NumberBuffer number, int pos, bool i else { while (i > 0 && dig[i - 1] == '0') + { i--; + } } if (i == 0) @@ -3375,7 +3631,9 @@ private static unsafe int FindSection(ReadOnlySpan format, int section) char ch; if (section == 0) + { return 0; + } fixed (char* pFormat = &MemoryMarshal.GetReference(format)) { @@ -3393,16 +3651,26 @@ private static unsafe int FindSection(ReadOnlySpan format, int section) case '"': while (src < format.Length && pFormat[src] != 0 && pFormat[src++] != ch) ; break; + case '\\': if (src < format.Length && pFormat[src] != 0) + { src++; + } break; + case ';': if (--section != 0) + { break; + } + if (src < format.Length && pFormat[src] != 0 && pFormat[src] != ';') + { return src; + } goto case '\0'; + case '\0': return 0; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs index fa60aa987cbbf..76d9473d7a490 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs @@ -27,7 +27,8 @@ namespace System.Runtime.InteropServices [NonVersionable] // This only applies to field layout public readonly struct NFloat : IBinaryFloatingPointIeee754, - IMinMaxValue + IMinMaxValue, + IUtf8SpanFormattable { private const NumberStyles DefaultNumberStyles = NumberStyles.Float | NumberStyles.AllowThousands; @@ -860,6 +861,9 @@ public int CompareTo(object? obj) /// true if the formatting was successful; otherwise, false. public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) => _value.TryFormat(destination, out charsWritten, format, provider); + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) => ((IUtf8SpanFormattable)_value).TryFormat(utf8Destination, out bytesWritten, format, provider); + // // IAdditiveIdentity // diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index ee9df96053b4c..9a95eecef3de4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -23,7 +23,8 @@ public readonly struct SByte IEquatable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IUtf8SpanFormattable { private readonly sbyte m_value; // Do not rename (binary serialization) @@ -121,6 +122,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt32(m_value, 0x000000FF, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatInt32(m_value, 0x000000FF, format, provider, utf8Destination, out bytesWritten); + } + public static sbyte Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 887b26c4854ad..7ca8590a3a36a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -31,7 +31,8 @@ public readonly struct Single IComparable, IEquatable, IBinaryFloatingPointIeee754, - IMinMaxValue + IMinMaxValue, + IUtf8SpanFormattable { private readonly float m_value; // Do not rename (binary serialization) @@ -357,6 +358,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); + } + // Parses a float from a String in the given style. If // a NumberFormatInfo isn't specified, the current culture's // NumberFormatInfo is assumed. diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs index 5a6eeb8d1c0c7..a004175374dc5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs @@ -664,16 +664,14 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, Read if (format.Length == 1) { - switch (format[0]) + switch (format[0] | 0x20) { case 'o': - case 'O': format = OFormat; provider = CultureInfo.InvariantCulture.DateTimeFormat; break; case 'r': - case 'R': format = RFormat; provider = CultureInfo.InvariantCulture.DateTimeFormat; break; @@ -743,16 +741,14 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri if (format.Length == 1) { - switch (format[0]) + switch (format[0] | 0x20) { case 'o': - case 'O': format = OFormat; dtfiToUse = CultureInfo.InvariantCulture.DateTimeFormat; break; case 'r': - case 'R': format = RFormat; dtfiToUse = CultureInfo.InvariantCulture.DateTimeFormat; break; @@ -925,26 +921,23 @@ public string ToString([StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] stri if (format.Length == 1) { - switch (format[0]) + switch (format[0] | 0x20) { case 'o': - case 'O': return string.Create(16, this, (destination, value) => { - bool b = DateTimeFormat.TryFormatTimeOnlyO(value.Hour, value.Minute, value.Second, value._ticks % TimeSpan.TicksPerSecond, destination); - Debug.Assert(b); + DateTimeFormat.TryFormatTimeOnlyO(value.Hour, value.Minute, value.Second, value._ticks % TimeSpan.TicksPerSecond, destination, out int charsWritten); + Debug.Assert(charsWritten == destination.Length); }); case 'r': - case 'R': return string.Create(8, this, (destination, value) => { - bool b = DateTimeFormat.TryFormatTimeOnlyR(value.Hour, value.Minute, value.Second, destination); - Debug.Assert(b); + DateTimeFormat.TryFormatTimeOnlyR(value.Hour, value.Minute, value.Second, destination, out int charsWritten); + Debug.Assert(charsWritten == destination.Length); }); case 't': - case 'T': return DateTimeFormat.Format(ToDateTime(), format, provider); default: @@ -971,7 +964,7 @@ public string ToString([StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] stri bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider) => TryFormatCore(utf8Destination, out bytesWritten, format, provider); - private bool TryFormatCore(Span destination, out int written, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider) where TChar : unmanaged, IBinaryInteger + private bool TryFormatCore(Span destination, out int written, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider) where TChar : unmanaged, IUtfChar { if (format.Length == 0) { @@ -980,34 +973,20 @@ private bool TryFormatCore(Span destination, out int written, [Str if (format.Length == 1) { - switch (format[0]) + switch (format[0] | 0x20) { case 'o': - case 'O': - if (!DateTimeFormat.TryFormatTimeOnlyO(Hour, Minute, Second, _ticks % TimeSpan.TicksPerSecond, destination)) - { - written = 0; - return false; - } - written = 16; - return true; + return DateTimeFormat.TryFormatTimeOnlyO(Hour, Minute, Second, _ticks % TimeSpan.TicksPerSecond, destination, out written); case 'r': - case 'R': - if (!DateTimeFormat.TryFormatTimeOnlyR(Hour, Minute, Second, destination)) - { - written = 0; - return false; - } - written = 8; - return true; + return DateTimeFormat.TryFormatTimeOnlyR(Hour, Minute, Second, destination, out written); case 't': - case 'T': return DateTimeFormat.TryFormat(ToDateTime(), destination, out written, format, provider); default: - throw new FormatException(SR.Argument_BadFormatSpecifier); + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + break; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 0ae4d6bee3400..5a6d33150c3a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -18,7 +18,8 @@ namespace System public readonly struct UInt128 : IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IUtf8SpanFormattable { internal const int Size = 16; @@ -119,6 +120,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt128(this, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatUInt128(this, format, provider, utf8Destination, out bytesWritten); + } + public static UInt128 Parse(string s) { ArgumentNullException.ThrowIfNull(s); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 025a6adddea1c..17a211c51b5ea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -23,7 +23,8 @@ public readonly struct UInt16 IEquatable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IUtf8SpanFormattable { private readonly ushort m_value; // Do not rename (binary serialization) @@ -113,6 +114,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatUInt32(m_value, format, provider, utf8Destination, out bytesWritten); + } + public static ushort Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index c84612fc8530a..9813330a72499 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -23,7 +23,8 @@ public readonly struct UInt32 IEquatable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IUtf8SpanFormattable { private readonly uint m_value; // Do not rename (binary serialization) @@ -123,6 +124,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatUInt32(m_value, format, provider, utf8Destination, out bytesWritten); + } + public static uint Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 91b569a71cf76..da40613f88f7b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -23,7 +23,8 @@ public readonly struct UInt64 IEquatable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IUtf8SpanFormattable { private readonly ulong m_value; // Do not rename (binary serialization) @@ -122,6 +123,12 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt64(m_value, format, provider, destination, out charsWritten); } + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return Number.TryFormatUInt64(m_value, format, provider, utf8Destination, out bytesWritten); + } + public static ulong Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index 2c24851d43cc6..c91992cba0805 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -32,7 +32,8 @@ public readonly struct UIntPtr ISerializable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IUtf8SpanFormattable { private readonly nuint _value; @@ -206,6 +207,10 @@ public int CompareTo(nuint value) public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan format = default, IFormatProvider? provider = null) => ((nuint_t)_value).TryFormat(destination, out charsWritten, format, provider); + /// + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) => + ((IUtf8SpanFormattable)(nuint_t)_value).TryFormat(utf8Destination, out bytesWritten, format, provider); + public static nuint Parse(string s) => (nuint)nuint_t.Parse(s); public static nuint Parse(string s, NumberStyles style) => (nuint)nuint_t.Parse(s, style); public static nuint Parse(string s, IFormatProvider? provider) => (nuint)nuint_t.Parse(s, provider); diff --git a/src/libraries/System.Private.CoreLib/src/System/Version.cs b/src/libraries/System.Private.CoreLib/src/System/Version.cs index c0af3c196944f..611b209b1eaf9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Version.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Version.cs @@ -185,7 +185,7 @@ public bool TryFormat(Span destination, out int charsWritten) => public bool TryFormat(Span destination, int fieldCount, out int charsWritten) => TryFormatCore(destination, fieldCount, out charsWritten); - private bool TryFormatCore(Span destination, int fieldCount, out int charsWritten) where TChar : unmanaged, IBinaryInteger + private bool TryFormatCore(Span destination, int fieldCount, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -219,7 +219,7 @@ static void ThrowArgumentException(string failureUpperBound) => return false; } - destination[0] = TChar.CreateTruncating('.'); + destination[0] = TChar.CastFrom('.'); destination = destination.Slice(1); totalCharsWritten++; } @@ -235,7 +235,7 @@ static void ThrowArgumentException(string failureUpperBound) => int valueCharsWritten; bool formatted = typeof(TChar) == typeof(char) ? ((uint)value).TryFormat(MemoryMarshal.Cast(destination), out valueCharsWritten) : - Utf8Formatter.TryFormat((uint)value, MemoryMarshal.Cast(destination), out valueCharsWritten); // TODO https://github.com/dotnet/runtime/issues/84527: Use UInt32's IUtf8SpanFormattable when available + ((IUtf8SpanFormattable)(uint)value).TryFormat(MemoryMarshal.Cast(destination), out valueCharsWritten, default, CultureInfo.InvariantCulture); if (!formatted) { diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index fb5e7d202fd89..a5699330cad7b 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1245,7 +1245,7 @@ public static void Free(void* ptr) { } [System.CLSCompliantAttribute(false)] public static void Fill(void* ptr, nuint byteCount, byte value) { throw null; } } - public readonly partial struct NFloat : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct NFloat : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly int _dummyPrimitive; public NFloat(double value) { throw null; } @@ -1479,6 +1479,7 @@ public static void Free(void* ptr) { } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static System.Runtime.InteropServices.NFloat Truncate(System.Runtime.InteropServices.NFloat x) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Runtime.InteropServices.NFloat result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Runtime.InteropServices.NFloat result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out System.Runtime.InteropServices.NFloat result) { throw null; } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 8ce544b882a85..c2ce193ceaf4f 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -754,7 +754,7 @@ public unsafe static void MemoryCopy(void* source, void* destination, long desti public unsafe static void MemoryCopy(void* source, void* destination, ulong destinationSizeInBytes, ulong sourceBytesToCopy) { } public static void SetByte(System.Array array, int index, byte value) { } } - public readonly partial struct Byte : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber + public readonly partial struct Byte : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber, System.IUtf8SpanFormattable { private readonly byte _dummyPrimitive; public const byte MaxValue = (byte)255; @@ -879,6 +879,7 @@ public static void SetByte(System.Array array, int index, byte value) { } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static byte TrailingZeroCount(byte value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out byte result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out byte result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out byte result) { throw null; } @@ -1925,7 +1926,7 @@ public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, S public override string ToString() { throw null; } public string ToString(System.IFormatProvider? provider) { throw null; } } - public readonly partial struct Decimal : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable + public readonly partial struct Decimal : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable, System.IUtf8SpanFormattable { private readonly int _dummyPrimitive; [System.Runtime.CompilerServices.DecimalConstantAttribute((byte)0, (byte)0, (uint)4294967295, (uint)4294967295, (uint)4294967295)] @@ -2119,6 +2120,7 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser public static ulong ToUInt64(decimal d) { throw null; } public static decimal Truncate(decimal d) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryGetBits(decimal d, System.Span destination, out int valuesWritten) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out decimal result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out decimal result) { throw null; } @@ -2177,7 +2179,7 @@ protected DivideByZeroException(System.Runtime.Serialization.SerializationInfo i public DivideByZeroException(string? message) { } public DivideByZeroException(string? message, System.Exception? innerException) { } } - public readonly partial struct Double : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Double : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly double _dummyPrimitive; public const double E = 2.718281828459045; @@ -2361,6 +2363,7 @@ public DivideByZeroException(string? message, System.Exception? innerException) public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static double Truncate(double x) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out double result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out double result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out double result) { throw null; } @@ -2798,7 +2801,7 @@ public enum GCNotificationStatus public static bool TryParseExact([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? input, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true), System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("GuidFormat")] string? format, out System.Guid result) { throw null; } public bool TryWriteBytes(System.Span destination) { throw null; } } - public readonly partial struct Half : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Half : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly int _dummyPrimitive; public static System.Half E { get { throw null; } } @@ -3016,6 +3019,7 @@ public enum GCNotificationStatus public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static System.Half Truncate(System.Half x) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Half result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out System.Half result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Half result) { throw null; } @@ -3144,7 +3148,7 @@ public InsufficientMemoryException() { } public InsufficientMemoryException(string? message) { } public InsufficientMemoryException(string? message, System.Exception? innerException) { } } - public readonly partial struct Int128 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Int128 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly int _dummyPrimitive; [System.CLSCompliantAttribute(false)] @@ -3316,6 +3320,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static System.Int128 TrailingZeroCount(System.Int128 value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int128 result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Int128 result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out System.Int128 result) { throw null; } @@ -3323,7 +3328,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Int128 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Int128 result) { throw null; } } - public readonly partial struct Int16 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Int16 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly short _dummyPrimitive; public const short MaxValue = (short)32767; @@ -3449,6 +3454,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static short TrailingZeroCount(short value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out short result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out short result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out short result) { throw null; } @@ -3456,7 +3462,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out short result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out short result) { throw null; } } - public readonly partial struct Int32 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Int32 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly int _dummyPrimitive; public const int MaxValue = 2147483647; @@ -3582,6 +3588,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static int TrailingZeroCount(int value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out int result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out int result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out int result) { throw null; } @@ -3589,7 +3596,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out int result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out int result) { throw null; } } - public readonly partial struct Int64 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Int64 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly long _dummyPrimitive; public const long MaxValue = (long)9223372036854775807; @@ -3715,6 +3722,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static long TrailingZeroCount(long value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out long result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out long result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out long result) { throw null; } @@ -3722,7 +3730,7 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out long result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out long result) { throw null; } } - public readonly partial struct IntPtr : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Runtime.Serialization.ISerializable + public readonly partial struct IntPtr : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Runtime.Serialization.ISerializable, System.IUtf8SpanFormattable { private readonly unsafe void* _dummyPrimitive; public static readonly nint Zero; @@ -3855,6 +3863,7 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static nint TrailingZeroCount(nint value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nint result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out nint result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out nint result) { throw null; } @@ -4716,7 +4725,7 @@ public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, S public static bool operator !=(System.RuntimeTypeHandle left, object? right) { throw null; } } [System.CLSCompliantAttribute(false)] - public readonly partial struct SByte : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct SByte : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly sbyte _dummyPrimitive; public const sbyte MaxValue = (sbyte)127; @@ -4842,6 +4851,7 @@ public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, S public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static sbyte TrailingZeroCount(sbyte value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out sbyte result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out sbyte result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out sbyte result) { throw null; } @@ -4855,7 +4865,7 @@ public sealed partial class SerializableAttribute : System.Attribute { public SerializableAttribute() { } } - public readonly partial struct Single : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Single : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable { private readonly float _dummyPrimitive; public const float E = 2.7182817f; @@ -5039,6 +5049,7 @@ public SerializableAttribute() { } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static float Truncate(float x) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out float result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out float result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out float result) { throw null; } @@ -6260,7 +6271,7 @@ public TypeUnloadedException(string? message) { } public TypeUnloadedException(string? message, System.Exception? innerException) { } } [System.CLSCompliantAttribute(false)] - public readonly partial struct UInt128 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber + public readonly partial struct UInt128 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber, System.IUtf8SpanFormattable { private readonly int _dummyPrimitive; [System.CLSCompliantAttribute(false)] @@ -6437,6 +6448,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static System.UInt128 TrailingZeroCount(System.UInt128 value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt128 result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.UInt128 result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out System.UInt128 result) { throw null; } @@ -6445,7 +6457,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.UInt128 result) { throw null; } } [System.CLSCompliantAttribute(false)] - public readonly partial struct UInt16 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber + public readonly partial struct UInt16 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber, System.IUtf8SpanFormattable { private readonly ushort _dummyPrimitive; public const ushort MaxValue = (ushort)65535; @@ -6570,6 +6582,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static ushort TrailingZeroCount(ushort value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ushort result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out ushort result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out ushort result) { throw null; } @@ -6578,7 +6591,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out ushort result) { throw null; } } [System.CLSCompliantAttribute(false)] - public readonly partial struct UInt32 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber + public readonly partial struct UInt32 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber, System.IUtf8SpanFormattable { private readonly uint _dummyPrimitive; public const uint MaxValue = (uint)4294967295; @@ -6703,6 +6716,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static uint TrailingZeroCount(uint value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out uint result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out uint result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out uint result) { throw null; } @@ -6711,7 +6725,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out uint result) { throw null; } } [System.CLSCompliantAttribute(false)] - public readonly partial struct UInt64 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber + public readonly partial struct UInt64 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber, System.IUtf8SpanFormattable { private readonly ulong _dummyPrimitive; public const ulong MaxValue = (ulong)18446744073709551615; @@ -6836,6 +6850,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } public static ulong TrailingZeroCount(ulong value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ulong result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out ulong result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out ulong result) { throw null; } @@ -6844,7 +6859,7 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out ulong result) { throw null; } } [System.CLSCompliantAttribute(false)] - public readonly partial struct UIntPtr : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber, System.Runtime.Serialization.ISerializable + public readonly partial struct UIntPtr : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber, System.Runtime.Serialization.ISerializable, System.IUtf8SpanFormattable { private readonly unsafe void* _dummyPrimitive; public static readonly nuint Zero; @@ -6972,6 +6987,7 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser public ulong ToUInt64() { throw null; } public static nuint TrailingZeroCount(nuint value) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nuint result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out nuint result) { throw null; } public static bool TryParse(System.ReadOnlySpan s, out nuint result) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 994ce15426fef..b4e389485006a 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -68,6 +68,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/BooleanTests.cs b/src/libraries/System.Runtime/tests/System/BooleanTests.cs index 18ef70e5e22f0..835f038afd74d 100644 --- a/src/libraries/System.Runtime/tests/System/BooleanTests.cs +++ b/src/libraries/System.Runtime/tests/System/BooleanTests.cs @@ -185,26 +185,20 @@ public void GetTypeCode_Invoke_ReturnsBoolean() [InlineData(false, "False")] public static void TryFormat(bool i, string expected) { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); + // Just right and longer than needed + for (int additional = 0; additional < 2; additional++) + { + var actual = new char[expected.Length + additional]; + Assert.True(i.TryFormat(actual.AsSpan(), out int charsWritten)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual.AsSpan(0, charsWritten))); + } // Too short if (expected.Length > 0) { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten)); + var actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out int charsWritten)); Assert.Equal(0, charsWritten); } } diff --git a/src/libraries/System.Runtime/tests/System/ByteTests.cs b/src/libraries/System.Runtime/tests/System/ByteTests.cs index f243c4ac06400..94c41f34aa95d 100644 --- a/src/libraries/System.Runtime/tests/System/ByteTests.cs +++ b/src/libraries/System.Runtime/tests/System/ByteTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Text; using Xunit; namespace System.Tests @@ -357,45 +358,7 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(byte i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(byte i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); } } diff --git a/src/libraries/System.Runtime/tests/System/DecimalTests.cs b/src/libraries/System.Runtime/tests/System/DecimalTests.cs index a40c21787bfb7..e4ef2ace97a34 100644 --- a/src/libraries/System.Runtime/tests/System/DecimalTests.cs +++ b/src/libraries/System.Runtime/tests/System/DecimalTests.cs @@ -2368,43 +2368,7 @@ public static void TryFormat() try { - char[] actual; - int charsWritten; - - // Just right - actual = new char[localExpected.Length]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected, new string(actual)); - - // Longer than needed - actual = new char[localExpected.Length + 1]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected, new string(actual, 0, charsWritten)); - - // Too short - if (localExpected.Length > 0) - { - actual = new char[localExpected.Length - 1]; - Assert.False(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(0, charsWritten); - } - - if (localFormat != null) - { - // Upper localFormat - actual = new char[localExpected.Length]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat.ToUpperInvariant(), localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[localExpected.Length]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat.ToLowerInvariant(), localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected.ToLowerInvariant(), new string(actual)); - } + NumberFormatTestHelper.TryFormatNumberTest(localI, localFormat, localProvider, localExpected); } catch (Exception exc) { diff --git a/src/libraries/System.Runtime/tests/System/DoubleTests.cs b/src/libraries/System.Runtime/tests/System/DoubleTests.cs index cff858f4e357d..b2a93df11b480 100644 --- a/src/libraries/System.Runtime/tests/System/DoubleTests.cs +++ b/src/libraries/System.Runtime/tests/System/DoubleTests.cs @@ -941,28 +941,7 @@ public static void TryFormat() try { - char[] actual; - int charsWritten; - - // Just right - actual = new char[localExpected.Length]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected, new string(actual)); - - // Longer than needed - actual = new char[localExpected.Length + 1]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected, new string(actual, 0, charsWritten)); - - // Too short - if (localExpected.Length > 0) - { - actual = new char[localExpected.Length - 1]; - Assert.False(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(0, charsWritten); - } + NumberFormatTestHelper.TryFormatNumberTest(localI, localFormat, localProvider, localExpected, formatCasingMatchesOutput: false); } catch (Exception exc) { diff --git a/src/libraries/System.Runtime/tests/System/HalfTests.cs b/src/libraries/System.Runtime/tests/System/HalfTests.cs index 19f1fc0793d13..cae21d055f4d4 100644 --- a/src/libraries/System.Runtime/tests/System/HalfTests.cs +++ b/src/libraries/System.Runtime/tests/System/HalfTests.cs @@ -1001,28 +1001,7 @@ public static void TryFormat() try { - char[] actual; - int charsWritten; - - // Just right - actual = new char[localExpected.Length]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected, new string(actual)); - - // Longer than needed - actual = new char[localExpected.Length + 1]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected, new string(actual, 0, charsWritten)); - - // Too short - if (localExpected.Length > 0) - { - actual = new char[localExpected.Length - 1]; - Assert.False(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(0, charsWritten); - } + NumberFormatTestHelper.TryFormatNumberTest(localI, localFormat, localProvider, localExpected, formatCasingMatchesOutput: false); } catch (Exception exc) { diff --git a/src/libraries/System.Runtime/tests/System/Int128Tests.cs b/src/libraries/System.Runtime/tests/System/Int128Tests.cs index 3be0daacfaf3d..1161a6d763a79 100644 --- a/src/libraries/System.Runtime/tests/System/Int128Tests.cs +++ b/src/libraries/System.Runtime/tests/System/Int128Tests.cs @@ -410,46 +410,8 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(Int128 i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format is not null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(Int128 i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); [Fact] public static void TestNegativeNumberParsingWithHyphen() diff --git a/src/libraries/System.Runtime/tests/System/Int16Tests.cs b/src/libraries/System.Runtime/tests/System/Int16Tests.cs index 053b99c82b8b9..60ffdcac3158a 100644 --- a/src/libraries/System.Runtime/tests/System/Int16Tests.cs +++ b/src/libraries/System.Runtime/tests/System/Int16Tests.cs @@ -381,45 +381,7 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(short i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(short i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); } } diff --git a/src/libraries/System.Runtime/tests/System/Int32Tests.cs b/src/libraries/System.Runtime/tests/System/Int32Tests.cs index 6666c00727d8a..e619fff5ea5dc 100644 --- a/src/libraries/System.Runtime/tests/System/Int32Tests.cs +++ b/src/libraries/System.Runtime/tests/System/Int32Tests.cs @@ -796,46 +796,8 @@ public static void ToString_C_EmptyPercentGroup_Success() [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(int i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(int i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); [Fact] public static void TestNegativeNumberParsingWithHyphen() diff --git a/src/libraries/System.Runtime/tests/System/Int64Tests.cs b/src/libraries/System.Runtime/tests/System/Int64Tests.cs index 1687f16f606b4..052ae58bdd818 100644 --- a/src/libraries/System.Runtime/tests/System/Int64Tests.cs +++ b/src/libraries/System.Runtime/tests/System/Int64Tests.cs @@ -395,46 +395,8 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(long i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(long i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); [Fact] public static void TestNegativeNumberParsingWithHyphen() diff --git a/src/libraries/System.Runtime/tests/System/IntPtrTests.cs b/src/libraries/System.Runtime/tests/System/IntPtrTests.cs index 79f6bc1ca7400..24cc1d3d85856 100644 --- a/src/libraries/System.Runtime/tests/System/IntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System/IntPtrTests.cs @@ -950,45 +950,7 @@ public static void ToString_C_EmptyPercentGroup_Success() [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(nint i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(nint i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); } } diff --git a/src/libraries/System.Runtime/tests/System/NumberFormatTestHelper.cs b/src/libraries/System.Runtime/tests/System/NumberFormatTestHelper.cs new file mode 100644 index 0000000000000..6cfb12928cd08 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/NumberFormatTestHelper.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; +using Xunit; + +namespace System.Tests +{ + internal static class NumberFormatTestHelper + { + internal static void TryFormatNumberTest(T i, string format, IFormatProvider provider, string expected, bool formatCasingMatchesOutput = true) where T : ISpanFormattable, IUtf8SpanFormattable + { + // UTF16 + { + char[] actual; + int charsWritten; + + // Just right and longer than needed + for (int additional = 0; additional < 2; additional++) + { + actual = new char[expected.Length + additional]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected, new string(actual.AsSpan(0, charsWritten))); + } + + // Too short + if (expected.Length > 0) + { + actual = new char[expected.Length - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (formatCasingMatchesOutput && format != null) + { + // Upper format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), new string(actual)); + + // Lower format + actual = new char[expected.Length]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expected.Length, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), new string(actual)); + } + } + + // UTF8 + { + byte[] actual; + int charsWritten; + int expectedLength = Encoding.UTF8.GetByteCount(expected); + + // Just right and longer than needed + for (int additional = 0; additional < 2; additional++) + { + actual = new byte[expectedLength + additional]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(expectedLength, charsWritten); + Assert.Equal(expected, Encoding.UTF8.GetString(actual.AsSpan(0, charsWritten))); + } + + // Too short + if (expectedLength > 0) + { + actual = new byte[expectedLength - 1]; + Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); + Assert.Equal(0, charsWritten); + } + + if (formatCasingMatchesOutput && format != null) + { + // Upper format + actual = new byte[expectedLength]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); + Assert.Equal(expectedLength, charsWritten); + Assert.Equal(expected.ToUpperInvariant(), Encoding.UTF8.GetString(actual.AsSpan(0, charsWritten))); + + // Lower format + actual = new byte[expectedLength]; + Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); + Assert.Equal(expectedLength, charsWritten); + Assert.Equal(expected.ToLowerInvariant(), Encoding.UTF8.GetString(actual.AsSpan(0, charsWritten))); + } + } + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/SByteTests.cs b/src/libraries/System.Runtime/tests/System/SByteTests.cs index 415b5662a1b74..7a2b9b946f4b7 100644 --- a/src/libraries/System.Runtime/tests/System/SByteTests.cs +++ b/src/libraries/System.Runtime/tests/System/SByteTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Text; using Xunit; namespace System.Tests @@ -374,45 +375,7 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(sbyte i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(sbyte i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); } } diff --git a/src/libraries/System.Runtime/tests/System/SingleTests.cs b/src/libraries/System.Runtime/tests/System/SingleTests.cs index 54db87a2cb28f..07414d8759668 100644 --- a/src/libraries/System.Runtime/tests/System/SingleTests.cs +++ b/src/libraries/System.Runtime/tests/System/SingleTests.cs @@ -863,28 +863,7 @@ public static void TryFormat() try { - char[] actual; - int charsWritten; - - // Just right - actual = new char[localExpected.Length]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected, new string(actual)); - - // Longer than needed - actual = new char[localExpected.Length + 1]; - Assert.True(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(localExpected.Length, charsWritten); - Assert.Equal(localExpected, new string(actual, 0, charsWritten)); - - // Too short - if (localExpected.Length > 0) - { - actual = new char[localExpected.Length - 1]; - Assert.False(localI.TryFormat(actual.AsSpan(), out charsWritten, localFormat, localProvider)); - Assert.Equal(0, charsWritten); - } + NumberFormatTestHelper.TryFormatNumberTest(localI, localFormat, localProvider, localExpected, formatCasingMatchesOutput: false); } catch (Exception exc) { diff --git a/src/libraries/System.Runtime/tests/System/UInt128Tests.cs b/src/libraries/System.Runtime/tests/System/UInt128Tests.cs index 799cbd39c3534..ba67e15edb155 100644 --- a/src/libraries/System.Runtime/tests/System/UInt128Tests.cs +++ b/src/libraries/System.Runtime/tests/System/UInt128Tests.cs @@ -401,46 +401,8 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(UInt128 i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(UInt128 i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); [Fact] public static void Runtime75416() diff --git a/src/libraries/System.Runtime/tests/System/UInt16Tests.cs b/src/libraries/System.Runtime/tests/System/UInt16Tests.cs index 5482dc96081be..d43738f0e64a5 100644 --- a/src/libraries/System.Runtime/tests/System/UInt16Tests.cs +++ b/src/libraries/System.Runtime/tests/System/UInt16Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Text; using Xunit; namespace System.Tests @@ -354,45 +355,7 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(ushort i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(ushort i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); } } diff --git a/src/libraries/System.Runtime/tests/System/UInt32Tests.cs b/src/libraries/System.Runtime/tests/System/UInt32Tests.cs index 89a0bd8823142..d85edb2f401b3 100644 --- a/src/libraries/System.Runtime/tests/System/UInt32Tests.cs +++ b/src/libraries/System.Runtime/tests/System/UInt32Tests.cs @@ -373,45 +373,7 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(uint i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(uint i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); } } diff --git a/src/libraries/System.Runtime/tests/System/UInt64Tests.cs b/src/libraries/System.Runtime/tests/System/UInt64Tests.cs index dd8ee1a0f8a48..c8fae97320698 100644 --- a/src/libraries/System.Runtime/tests/System/UInt64Tests.cs +++ b/src/libraries/System.Runtime/tests/System/UInt64Tests.cs @@ -385,45 +385,7 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(ulong i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(ulong i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); } } diff --git a/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs b/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs index 08051f2f6b74c..ff8a857b9cda8 100644 --- a/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System/UIntPtrTests.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -526,45 +525,7 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP [Theory] [MemberData(nameof(ToString_TestData))] - public static void TryFormat(nuint i, string format, IFormatProvider provider, string expected) - { - char[] actual; - int charsWritten; - - // Just right - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual)); - - // Longer than needed - actual = new char[expected.Length + 1]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected, new string(actual, 0, charsWritten)); - - // Too short - if (expected.Length > 0) - { - actual = new char[expected.Length - 1]; - Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider)); - Assert.Equal(0, charsWritten); - } - - if (format != null) - { - // Upper format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToUpperInvariant(), new string(actual)); - - // Lower format - actual = new char[expected.Length]; - Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider)); - Assert.Equal(expected.Length, charsWritten); - Assert.Equal(expected.ToLowerInvariant(), new string(actual)); - } - } + public static void TryFormat(nuint i, string format, IFormatProvider provider, string expected) => + NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); } }