Background and motivation
In .NET 6, as part of rolling out the improved support for string interpolation, we added some TryWrite extension methods to MemoryExtensions that enable interpolating directly into a Span<char>, e.g.
Span<char> span = ...;
bool wrote = span.TryWrite($"The current day/time is {DateTime.Now}", out int charsWritten);
We should consider enabling the same capability for writing UTF8 into a Span<byte>.
API Proposal
namespace System
{
public static class MemoryExtensions
{
+ public static bool TryWrite(this Span<byte> utf8Destination, [InterpolatedStringHandlerArgument("destination")] ref TryWriteUtf8InterpolatedStringHandler handler, out int bytesWritten);
+ public static bool TryWrite(this Span<byte> utf8Destination, IFormatProvider? provider, [InterpolatedStringHandlerArgument("destination", "provider")] ref TryWriteUtf8InterpolatedStringHandler handler, out int bytesWritten);
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [InterpolatedStringHandlerAttribute]
+ public ref struct TryWriteUtf8InterpolatedStringHandler
+ {
+ public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span<byte> utf8Destination, out bool shouldAppend);
+ public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span<byte> utf8Destination, IFormatProvider? provider, out bool shouldAppend);
+ public bool AppendLiteral(string value);
+ // public bool AppendLiteral(ReadOnlySpan<byte> value); // if the C# compiler supports interpolation with u8 literals
+ public bool AppendFormatted(scoped ReadOnlySpan<char> value);
+ public bool AppendFormatted(scoped ReadOnlySpan<char> value, int alignment = 0, string? format = null);
+ public bool AppendFormatted(scoped ReadOnlySpan<byte> utf8Value);
+ public bool AppendFormatted(scoped ReadOnlySpan<byte> utf8Value, int alignment = 0, string? format = null);
+ public bool AppendFormatted<T>(T value);
+ public bool AppendFormatted<T>(T value, string? format);
+ public bool AppendFormatted<T>(T value, int alignment);
+ public bool AppendFormatted<T>(T value, int alignment, string? format);
+ public bool AppendFormatted(object? value, int alignment = 0, string? format = null);
+ public bool AppendFormatted(string? value);
+ public bool AppendFormatted(string? value, int alignment = 0, string? format = null);
}
}
This is essentially the exact same design as for MemoryExtensions.TryWrite, with a few specific differences:
- The
TryWrite methods take a Span<byte> instead of a Span<char>.
- In addition to
ReadOnlySpan<char>-based AppendFormatted methods, there are also ReadOnlySpan<byte>-based AppendFormatted methods, the latter of which is expected to be UTF8 data, like a u8 literal. The ReadOnlySpan<byte> data would be memcpy'd into the destination, whereas the ReadOnlySpan<char> data would be Encoding.UTF8-encoded into the destination.
- The implementation of
Append<T> will check for IUtf8SpanFormattable before it checks for ISpanFormattable, prefering to use a type's built-in UTF8 formatting support if supplied.
Also note that the AppendLiteral method takes a string value, as that's what's supported by the C# language. If C# were to ever support u8 literals in string interpolation, we could add an appropriate AppendLiteral(ReadOnlySpan<byte> utf8Value) overload.
API Usage
Span<byte> utf8 = ...;
bool wrote = utf8.TryWrite($"The current day/time is {DateTime.Now}", out int bytesWritten);
Alternative Designs
No response
Risks
No response
Background and motivation
In .NET 6, as part of rolling out the improved support for string interpolation, we added some TryWrite extension methods to MemoryExtensions that enable interpolating directly into a
Span<char>, e.g.We should consider enabling the same capability for writing UTF8 into a
Span<byte>.API Proposal
namespace System { public static class MemoryExtensions { + public static bool TryWrite(this Span<byte> utf8Destination, [InterpolatedStringHandlerArgument("destination")] ref TryWriteUtf8InterpolatedStringHandler handler, out int bytesWritten); + public static bool TryWrite(this Span<byte> utf8Destination, IFormatProvider? provider, [InterpolatedStringHandlerArgument("destination", "provider")] ref TryWriteUtf8InterpolatedStringHandler handler, out int bytesWritten); + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandlerAttribute] + public ref struct TryWriteUtf8InterpolatedStringHandler + { + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span<byte> utf8Destination, out bool shouldAppend); + public TryWriteInterpolatedStringHandler(int literalLength, int formattedCount, Span<byte> utf8Destination, IFormatProvider? provider, out bool shouldAppend); + public bool AppendLiteral(string value); + // public bool AppendLiteral(ReadOnlySpan<byte> value); // if the C# compiler supports interpolation with u8 literals + public bool AppendFormatted(scoped ReadOnlySpan<char> value); + public bool AppendFormatted(scoped ReadOnlySpan<char> value, int alignment = 0, string? format = null); + public bool AppendFormatted(scoped ReadOnlySpan<byte> utf8Value); + public bool AppendFormatted(scoped ReadOnlySpan<byte> utf8Value, int alignment = 0, string? format = null); + public bool AppendFormatted<T>(T value); + public bool AppendFormatted<T>(T value, string? format); + public bool AppendFormatted<T>(T value, int alignment); + public bool AppendFormatted<T>(T value, int alignment, string? format); + public bool AppendFormatted(object? value, int alignment = 0, string? format = null); + public bool AppendFormatted(string? value); + public bool AppendFormatted(string? value, int alignment = 0, string? format = null); } }This is essentially the exact same design as for
MemoryExtensions.TryWrite, with a few specific differences:TryWritemethods take aSpan<byte>instead of aSpan<char>.ReadOnlySpan<char>-basedAppendFormattedmethods, there are alsoReadOnlySpan<byte>-basedAppendFormattedmethods, the latter of which is expected to be UTF8 data, like a u8 literal. TheReadOnlySpan<byte>data would be memcpy'd into the destination, whereas theReadOnlySpan<char>data would beEncoding.UTF8-encoded into the destination.Append<T>will check forIUtf8SpanFormattablebefore it checks forISpanFormattable, prefering to use a type's built-in UTF8 formatting support if supplied.Also note that the
AppendLiteralmethod takes astring value, as that's what's supported by the C# language. If C# were to ever support u8 literals in string interpolation, we could add an appropriateAppendLiteral(ReadOnlySpan<byte> utf8Value)overload.API Usage
Alternative Designs
No response
Risks
No response