Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Protoype for nonallocating string formatting #2595

Closed
wants to merge 5 commits into from
Closed
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
30 changes: 30 additions & 0 deletions corefxlab.sln
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Experimental.Coll
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Experimental.Collections.Tests", "tests\Microsoft.Experimental.Collections.Tests\Microsoft.Experimental.Collections.Tests.csproj", "{71D6F42F-95F7-486D-A659-22503A6D3D46}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.ValueBuilder", "src\System.Text.ValueBuilder\System.Text.ValueBuilder.csproj", "{A679FE55-5C11-4CE2-A634-B78AC8C76C29}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Text.ValueBuilder.Tests", "tests\System.Text.ValueBuilder.Tests\System.Text.ValueBuilder.Tests.csproj", "{20F82CD0-49EE-4A2A-945C-22BC34B136D3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Security.Cryptography.Asn1.Experimental", "src\System.Security.Cryptography.Asn1.Experimental\System.Security.Cryptography.Asn1.Experimental.csproj", "{D117AC96-F53F-47A8-81DF-F4A7CCEDB58E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Security.Cryptography.Asn1.Experimental.Tests", "tests\System.Security.Cryptography.Asn1.Experimental.Tests\System.Security.Cryptography.Asn1.Experimental.Tests.csproj", "{C58BC591-9C3F-44E8-8408-7C57B1806B21}"
Expand Down Expand Up @@ -616,6 +620,30 @@ Global
{71D6F42F-95F7-486D-A659-22503A6D3D46}.Release|x64.Build.0 = Release|Any CPU
{71D6F42F-95F7-486D-A659-22503A6D3D46}.Release|x86.ActiveCfg = Release|Any CPU
{71D6F42F-95F7-486D-A659-22503A6D3D46}.Release|x86.Build.0 = Release|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Debug|x64.ActiveCfg = Debug|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Debug|x64.Build.0 = Debug|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Debug|x86.ActiveCfg = Debug|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Debug|x86.Build.0 = Debug|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Release|Any CPU.Build.0 = Release|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Release|x64.ActiveCfg = Release|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Release|x64.Build.0 = Release|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Release|x86.ActiveCfg = Release|Any CPU
{A679FE55-5C11-4CE2-A634-B78AC8C76C29}.Release|x86.Build.0 = Release|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Debug|x64.ActiveCfg = Debug|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Debug|x64.Build.0 = Debug|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Debug|x86.ActiveCfg = Debug|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Debug|x86.Build.0 = Debug|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Release|Any CPU.Build.0 = Release|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Release|x64.ActiveCfg = Release|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Release|x64.Build.0 = Release|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Release|x86.ActiveCfg = Release|Any CPU
{20F82CD0-49EE-4A2A-945C-22BC34B136D3}.Release|x86.Build.0 = Release|Any CPU
{D117AC96-F53F-47A8-81DF-F4A7CCEDB58E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D117AC96-F53F-47A8-81DF-F4A7CCEDB58E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D117AC96-F53F-47A8-81DF-F4A7CCEDB58E}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -687,6 +715,8 @@ Global
{70873943-22E2-4254-9CE6-A0186586DCEC} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
{25E150ED-EAE6-4EB0-BA94-2FE25826EEDE} = {4B000021-5278-4F2A-B734-DE49F55D4024}
{71D6F42F-95F7-486D-A659-22503A6D3D46} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
{A679FE55-5C11-4CE2-A634-B78AC8C76C29} = {4B000021-5278-4F2A-B734-DE49F55D4024}
{20F82CD0-49EE-4A2A-945C-22BC34B136D3} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
{D117AC96-F53F-47A8-81DF-F4A7CCEDB58E} = {4B000021-5278-4F2A-B734-DE49F55D4024}
{C58BC591-9C3F-44E8-8408-7C57B1806B21} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
EndGlobalSection
Expand Down
14 changes: 14 additions & 0 deletions src/System.Text.ValueBuilder/System.Text.ValueBuilder.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\tools\common.props" />
<PropertyGroup>
<Description>Low allocation string building.</Description>
<Copyright>Microsoft Corporation, All rights reserved.</Copyright>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageTags>.NET formatting parsing encoding UTF8</PackageTags>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
</ItemGroup>
</Project>
28 changes: 28 additions & 0 deletions src/System.Text.ValueBuilder/System/FormatString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System
{
/// <summary>
/// Simple wrapper for a format specifier string. We could potentially use this to indicate to C# that we have a direct
/// string formatting method.
/// </summary>
/// <remarks>
/// <see cref="IO.FormattingTextWriter"/> for examples of how this would be used.
/// </remarks>
public readonly ref struct FormatString
{
public ReadOnlySpan<char> Format { get; }

public FormatString(ReadOnlySpan<char> format)
{
Format = format;
}

public int Length => Format.Length;

public static implicit operator FormatString(string format) => new FormatString(format);
public static implicit operator FormatString(ReadOnlySpan<char> format) => new FormatString(format);
}
}
41 changes: 41 additions & 0 deletions src/System.Text.ValueBuilder/System/IO/FormattingTextWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Text;

namespace System.IO
{
public class FormattingTextWriter : TextWriter
{
private Encoding _encoding;

public FormattingTextWriter(Encoding encoding)
{
_encoding = encoding;
}

public override Encoding Encoding => _encoding;

/// <remarks>
/// If you were to call Write($"Some string {value}"), C# would see that we have a Write() that takes
/// <see cref="FormatString"/> and would prefer that. It would create a <see cref="Variant"/> around
/// value and pass it via <see cref="Variant.ToSpan(in Variant)"/>.
///
/// Additionally, if you call Write($"Some string {value}", CultureInfo.CurrentCulture) it would get
/// transformed into a call to this specific method. General rules:
///
/// 1. When invoking a method with an interpolated string...
/// 2. If there is an overload that matches with FormatString, ReadOnlySpan(Variant) in the $ position...
/// 3. Use it
/// </remarks>
public void Write(FormatString format, ReadOnlySpan<Variant> args, IFormatProvider formatProvider = null)
{
ValueStringBuilder vsb = new ValueStringBuilder(format.Length);
vsb.Append(format, args, formatProvider);

Write(vsb.AsSpan());
vsb.Dispose();
}
}
}
Loading