Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix perf regressions in Utf8Formatter for integers #85277

Merged
merged 1 commit into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ namespace System.Buffers
/// </summary>
public bool HasPrecision => _precision != NoPrecision;

/// <summary>Gets the precision if one was specified; otherwise, 0.</summary>
internal byte PrecisionOrZero => _precision != NoPrecision ? _precision : (byte)0;

/// <summary>
/// true if the StandardFormat == default(StandardFormat)
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.CompilerServices;

namespace System.Buffers.Text
{
/// <summary>
Expand Down Expand Up @@ -30,7 +32,7 @@ public static partial class Utf8Formatter
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
public static bool TryFormat(byte value, Span<byte> destination, out int bytesWritten, StandardFormat format = default) =>
FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);
TryFormat((uint)value, destination, out bytesWritten, format);

/// <summary>
/// Formats an SByte as a UTF8 string.
Expand All @@ -55,7 +57,7 @@ public static bool TryFormat(byte value, Span<byte> destination, out int bytesWr
/// </exceptions>
[CLSCompliant(false)]
public static bool TryFormat(sbyte value, Span<byte> destination, out int bytesWritten, StandardFormat format = default) =>
FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);
TryFormat(value, 0xFF, destination, out bytesWritten, format);

/// <summary>
/// Formats a Unt16 as a UTF8 string.
Expand All @@ -80,7 +82,7 @@ public static bool TryFormat(sbyte value, Span<byte> destination, out int bytesW
/// </exceptions>
[CLSCompliant(false)]
public static bool TryFormat(ushort value, Span<byte> destination, out int bytesWritten, StandardFormat format = default) =>
FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);
TryFormat((uint)value, destination, out bytesWritten, format);

/// <summary>
/// Formats an Int16 as a UTF8 string.
Expand All @@ -104,7 +106,7 @@ public static bool TryFormat(ushort value, Span<byte> destination, out int bytes
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
public static bool TryFormat(short value, Span<byte> destination, out int bytesWritten, StandardFormat format = default) =>
FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);
TryFormat(value, 0xFFFF, destination, out bytesWritten, format);

/// <summary>
/// Formats a UInt32 as a UTF8 string.
Expand All @@ -127,9 +129,38 @@ public static bool TryFormat(short value, Span<byte> destination, out int bytesW
/// <exceptions>
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CLSCompliant(false)]
public static bool TryFormat(uint value, Span<byte> destination, out int bytesWritten, StandardFormat format = default) =>
FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);
public static bool TryFormat(uint value, Span<byte> destination, out int bytesWritten, StandardFormat format = default)
{
if (format.IsDefault)
{
return Number.TryUInt32ToDecStr(value, destination, out bytesWritten);
}

switch (format.Symbol | 0x20)
{
case 'd':
return Number.TryUInt32ToDecStr(value, format.PrecisionOrZero, destination, out bytesWritten);

case 'x':
return Number.TryInt32ToHexStr((int)value, Number.GetHexBase(format.Symbol), format.PrecisionOrZero, destination, out bytesWritten);

case 'n':
return FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);

case 'g' or 'r':
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
if (format.HasPrecision)
{
ThrowGWithPrecisionNotSupported();
}
goto case 'd';

default:
ThrowHelper.ThrowFormatException_BadFormatSpecifier();
goto case 'd';
}
}

/// <summary>
/// Formats an Int32 as a UTF8 string.
Expand All @@ -153,7 +184,43 @@ public static bool TryFormat(uint value, Span<byte> destination, out int bytesWr
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
public static bool TryFormat(int value, Span<byte> destination, out int bytesWritten, StandardFormat format = default) =>
FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);
TryFormat(value, ~0, destination, out bytesWritten, format);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryFormat(int value, int hexMask, Span<byte> destination, out int bytesWritten, StandardFormat format = default)
{
if (format.IsDefault)
{
return value >= 0 ?
Number.TryUInt32ToDecStr((uint)value, destination, out bytesWritten) :
Number.TryNegativeInt32ToDecStr(value, format.PrecisionOrZero, "-"u8, destination, out bytesWritten);
}

switch (format.Symbol | 0x20)
{
case 'd':
return value >= 0 ?
Number.TryUInt32ToDecStr((uint)value, format.PrecisionOrZero, destination, out bytesWritten) :
Number.TryNegativeInt32ToDecStr(value, format.PrecisionOrZero, "-"u8, destination, out bytesWritten);

case 'x':
return Number.TryInt32ToHexStr(value & hexMask, Number.GetHexBase(format.Symbol), format.PrecisionOrZero, destination, out bytesWritten);

case 'n':
return FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);

case 'g' or 'r':
if (format.HasPrecision)
{
ThrowGWithPrecisionNotSupported();
}
goto case 'd';

default:
ThrowHelper.ThrowFormatException_BadFormatSpecifier();
goto case 'd';
}
}

/// <summary>
/// Formats a UInt64 as a UTF8 string.
Expand All @@ -176,9 +243,38 @@ public static bool TryFormat(int value, Span<byte> destination, out int bytesWri
/// <exceptions>
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CLSCompliant(false)]
public static bool TryFormat(ulong value, Span<byte> destination, out int bytesWritten, StandardFormat format = default) =>
FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);
public static bool TryFormat(ulong value, Span<byte> destination, out int bytesWritten, StandardFormat format = default)
{
if (format.IsDefault)
{
return Number.TryUInt64ToDecStr(value, destination, out bytesWritten);
}

switch (format.Symbol | 0x20)
{
case 'd':
return Number.TryUInt64ToDecStr(value, format.PrecisionOrZero, destination, out bytesWritten);

case 'x':
return Number.TryInt64ToHexStr((long)value, Number.GetHexBase(format.Symbol), format.PrecisionOrZero, destination, out bytesWritten);

case 'n':
return FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);

case 'g' or 'r':
if (format.HasPrecision)
{
ThrowGWithPrecisionNotSupported();
}
goto case 'd';

default:
ThrowHelper.ThrowFormatException_BadFormatSpecifier();
goto case 'd';
}
}

/// <summary>
/// Formats an Int64 as a UTF8 string.
Expand All @@ -201,7 +297,44 @@ public static bool TryFormat(ulong value, Span<byte> destination, out int bytesW
/// <exceptions>
/// <cref>System.FormatException</cref> if the format is not valid for this data type.
/// </exceptions>
public static bool TryFormat(long value, Span<byte> destination, out int bytesWritten, StandardFormat format = default) =>
FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryFormat(long value, Span<byte> destination, out int bytesWritten, StandardFormat format = default)
{
if (format.IsDefault)
{
return value >= 0 ?
Number.TryUInt64ToDecStr((ulong)value, destination, out bytesWritten) :
Number.TryNegativeInt64ToDecStr(value, format.PrecisionOrZero, "-"u8, destination, out bytesWritten);
}

switch (format.Symbol | 0x20)
{
case 'd':
return value >= 0 ?
Number.TryUInt64ToDecStr((ulong)value, format.PrecisionOrZero, destination, out bytesWritten) :
Number.TryNegativeInt64ToDecStr(value, format.PrecisionOrZero, "-"u8, destination, out bytesWritten);

case 'x':
return Number.TryInt64ToHexStr(value, Number.GetHexBase(format.Symbol), format.PrecisionOrZero, destination, out bytesWritten);

case 'n':
return FormattingHelpers.TryFormat(value, destination, out bytesWritten, format);

case 'g' or 'r':
if (format.HasPrecision)
{
ThrowGWithPrecisionNotSupported();
}
goto case 'd';

default:
ThrowHelper.ThrowFormatException_BadFormatSpecifier();
goto case 'd';
}
}

private static void ThrowGWithPrecisionNotSupported() =>
// With a precision, 'G' can produce exponential format, even for integers.
throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ private static bool TryCopyTo<TChar>(string source, Span<TChar> destination, out
}
}

private static char GetHexBase(char fmt)
internal static char GetHexBase(char fmt)
{
// The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase
// hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase.
Expand Down Expand Up @@ -1675,7 +1675,7 @@ private static unsafe string NegativeInt32ToDecStr(int value, int digits, string
return result;
}

private static unsafe bool TryNegativeInt32ToDecStr<TChar>(int value, int digits, ReadOnlySpan<TChar> sNegative, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
internal static unsafe bool TryNegativeInt32ToDecStr<TChar>(int value, int digits, ReadOnlySpan<TChar> sNegative, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
{
Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
Debug.Assert(value < 0);
Expand Down Expand Up @@ -1724,7 +1724,7 @@ private static unsafe string Int32ToHexStr(int value, char hexBase, int digits)
return result;
}

private static unsafe bool TryInt32ToHexStr<TChar>(int value, char hexBase, int digits, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
internal static unsafe bool TryInt32ToHexStr<TChar>(int value, char hexBase, int digits, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
{
Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));

Expand Down Expand Up @@ -1999,7 +1999,7 @@ private static unsafe string UInt32ToDecStr(uint value, int digits)
return result;
}

private static unsafe bool TryUInt32ToDecStr<TChar>(uint value, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
internal static unsafe bool TryUInt32ToDecStr<TChar>(uint value, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
{
Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));

Expand All @@ -2019,7 +2019,7 @@ private static unsafe bool TryUInt32ToDecStr<TChar>(uint value, Span<TChar> dest
return false;
}

private static unsafe bool TryUInt32ToDecStr<TChar>(uint value, int digits, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
internal static unsafe bool TryUInt32ToDecStr<TChar>(uint value, int digits, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
{
Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));

Expand Down Expand Up @@ -2108,7 +2108,7 @@ private static unsafe string NegativeInt64ToDecStr(long value, int digits, strin
return result;
}

private static unsafe bool TryNegativeInt64ToDecStr<TChar>(long value, int digits, ReadOnlySpan<TChar> sNegative, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
internal static unsafe bool TryNegativeInt64ToDecStr<TChar>(long value, int digits, ReadOnlySpan<TChar> sNegative, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
{
Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));
Debug.Assert(value < 0);
Expand Down Expand Up @@ -2157,7 +2157,7 @@ private static unsafe string Int64ToHexStr(long value, char hexBase, int digits)
return result;
}

private static unsafe bool TryInt64ToHexStr<TChar>(long value, char hexBase, int digits, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
internal static unsafe bool TryInt64ToHexStr<TChar>(long value, char hexBase, int digits, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
{
Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));

Expand Down Expand Up @@ -2427,7 +2427,7 @@ internal static unsafe string UInt64ToDecStr(ulong value, int digits)
return result;
}

private static unsafe bool TryUInt64ToDecStr<TChar>(ulong value, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
internal static unsafe bool TryUInt64ToDecStr<TChar>(ulong value, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
{
Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte));

Expand All @@ -2448,7 +2448,7 @@ private static unsafe bool TryUInt64ToDecStr<TChar>(ulong value, Span<TChar> des
return false;
}

private static unsafe bool TryUInt64ToDecStr<TChar>(ulong value, int digits, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
internal static unsafe bool TryUInt64ToDecStr<TChar>(ulong value, int digits, Span<TChar> destination, out int charsWritten) where TChar : unmanaged, IUtfChar<TChar>
{
int countedDigits = FormattingHelpers.CountDigits(value);
int bufferLength = Math.Max(digits, countedDigits);
Expand Down