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

Implement IUtf8SpanFormattable on DateTime, DateTimeOffset, DateOnly, TimeOnly, TimeSpan, Char, Rune #84469

Merged
merged 3 commits into from
Apr 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -130,8 +130,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.G.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.L.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.O.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Date.R.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Decimal.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Decimal.E.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\Utf8Formatter.Decimal.F.cs" />
Expand Down Expand Up @@ -400,7 +398,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Icu.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Nls.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.WebAssembly.cs" Condition="'$(TargetsWasi)' == 'true' or '$(TargetsBrowser)' == 'true'"/>
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.WebAssembly.cs" Condition="'$(TargetsWasi)' == 'true' or '$(TargetsBrowser)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\ThaiBuddhistCalendar.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanFormat.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TimeSpanParse.cs" />
Expand Down Expand Up @@ -2604,4 +2602,4 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Numerics\IUnaryPlusOperators.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Numerics\IUnsignedNumber.cs" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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;

Expand Down Expand Up @@ -48,7 +49,7 @@ public static void FillWithAsciiZeros(Span<byte> buffer)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteDigits(ulong value, Span<byte> buffer)
public static void WriteDigits<TChar>(ulong value, Span<TChar> buffer) where TChar : unmanaged, IBinaryInteger<TChar>
{
// 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.
Expand All @@ -57,11 +58,11 @@ public static void WriteDigits(ulong value, Span<byte> buffer)
{
ulong temp = '0' + value;
value /= 10;
buffer[i] = (byte)(temp - (value * 10));
buffer[i] = TChar.CreateTruncating(temp - (value * 10));
}

Debug.Assert(value < 10);
buffer[0] = (byte)('0' + value);
buffer[0] = TChar.CreateTruncating('0' + value);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -92,37 +93,44 @@ public static void WriteDigitsWithGroupSeparator(ulong value, Span<byte> buffer)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteDigits(uint value, Span<byte> buffer)
public static void WriteDigits<TChar>(uint value, Span<TChar> buffer) where TChar : unmanaged, IBinaryInteger<TChar>
{
// 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.
Debug.Assert(buffer.Length > 0);

for (int i = buffer.Length - 1; i >= 1; i--)
{
uint temp = '0' + value;
value /= 10;
buffer[i] = (byte)(temp - (value * 10));
buffer[i] = TChar.CreateTruncating(temp - (value * 10));
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
}

Debug.Assert(value < 10);
buffer[0] = (byte)('0' + value);
buffer[0] = TChar.CreateTruncating('0' + value);
}

/// <summary>
/// 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.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void WriteFourDecimalDigits(uint value, Span<byte> buffer, int startingIndex = 0)
public static unsafe void WriteFourDecimalDigits<TChar>(uint value, Span<TChar> buffer, int startingIndex = 0) where TChar : unmanaged, IBinaryInteger<TChar>
{
Debug.Assert(value <= 9999);
Debug.Assert(startingIndex <= buffer.Length - 4);

(value, uint remainder) = Math.DivRem(value, 100);
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
fixed (TChar* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
Number.WriteTwoDigits(bufferPtr + startingIndex, value);
Number.WriteTwoDigits(bufferPtr + startingIndex + 2, remainder);
if (typeof(TChar) == typeof(char))
{
Number.WriteTwoDigits((char*)bufferPtr + startingIndex, value);
Number.WriteTwoDigits((char*)bufferPtr + startingIndex + 2, remainder);
}
else
{
Number.WriteTwoDigits((byte*)bufferPtr + startingIndex, value);
Number.WriteTwoDigits((byte*)bufferPtr + startingIndex + 2, remainder);
}
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -131,14 +139,21 @@ public static unsafe void WriteFourDecimalDigits(uint value, Span<byte> buffer,
/// This method performs best when the starting index is a constant literal.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void WriteTwoDecimalDigits(uint value, Span<byte> buffer, int startingIndex = 0)
public static unsafe void WriteTwoDecimalDigits<TChar>(uint value, Span<TChar> buffer, int startingIndex = 0) where TChar : unmanaged, IBinaryInteger<TChar>
{
Debug.Assert(value <= 99);
Debug.Assert(startingIndex <= buffer.Length - 2);

fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
fixed (TChar* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
Number.WriteTwoDigits(bufferPtr + startingIndex, value);
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
if (typeof(TChar) == typeof(char))
{
Number.WriteTwoDigits((char*)bufferPtr + startingIndex, value);
}
else
{
Number.WriteTwoDigits((byte*)bufferPtr + startingIndex, value);
}
}
}

Expand All @@ -147,6 +162,11 @@ public static void CopyFourBytes(ReadOnlySpan<byte> source, Span<byte> destinati
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination),
Unsafe.ReadUnaligned<uint>(ref MemoryMarshal.GetReference(source)));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CopyFourChars(ReadOnlySpan<char> source, Span<char> destination) =>
Unsafe.WriteUnaligned(ref Unsafe.As<char, byte>(ref MemoryMarshal.GetReference(destination)),
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<char, byte>(ref MemoryMarshal.GetReference(source))));

#endregion UTF-8 Helper methods

//
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ public static bool TryFormat(DateTimeOffset value, Span<byte> destination, out i

return symbol switch
{
'R' => TryFormatDateTimeR(value.UtcDateTime, destination, out bytesWritten),
'R' => DateTimeFormat.TryFormatR(value.UtcDateTime, new TimeSpan(DateTimeFormat.NullOffset), destination, out bytesWritten),
'l' => TryFormatDateTimeL(value.UtcDateTime, destination, out bytesWritten),
'O' => TryFormatDateTimeO(value.DateTime, value.Offset, 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),
};
Expand Down Expand Up @@ -74,9 +74,9 @@ public static bool TryFormat(DateTime value, Span<byte> destination, out int byt

return symbol switch
{
'R' => TryFormatDateTimeR(value, destination, out bytesWritten),
'R' => DateTimeFormat.TryFormatR(value, new TimeSpan(DateTimeFormat.NullOffset), destination, out bytesWritten),
'l' => TryFormatDateTimeL(value, destination, out bytesWritten),
'O' => TryFormatDateTimeO(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten),
'O' => DateTimeFormat.TryFormatO(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten),
'G' => TryFormatDateTimeG(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten),
_ => FormattingHelpers.TryFormatThrowFormatException(out bytesWritten),
};
Expand Down
6 changes: 5 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public readonly struct Char
ISpanFormattable,
IBinaryInteger<char>,
IMinMaxValue<char>,
IUnsignedNumber<char>
IUnsignedNumber<char>,
IUtf8SpanFormattable
{
//
// Member Variables
Expand Down Expand Up @@ -191,6 +192,9 @@ bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, Re
return false;
}

bool IUtf8SpanFormattable.TryFormat(Span<byte> destination, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider? provider) =>
new Rune(this).TryEncodeToUtf8(destination, out bytesWritten);

string IFormattable.ToString(string? format, IFormatProvider? formatProvider) => ToString(m_value);

public static char Parse(string s)
Expand Down
Loading