免責事項
- 現時点ではPreviewの機能です。
- Visual Studio Version 16.7.0 Preview 6.0(.NET Core 3.1.400-preview-015203)時点での結果です。
- 効果は当然ですがソフトウェアに依ります。
- 置き換え方が間違っていたらごめんなさい。
イントロ
C# 9.0から、unsafeコンテキスト限定ですがFunction Pointersが使用できるようになります。
(まだ多少構文が変わるそうですが。ところで日本語ではそのまま「関数ポインター」でいいのだろうか?)
従来からあるデリゲートは高機能ですが、オーバーヘッドが多少あります。[^要出典]
実際のソフトウェアで、Function Pointersに置き換えることで高速化されるか実験してみました。
対象ソフトウェア
最近私がPull Requestを送っていた Cysharp/ZStringというライブラリでは、値を文字列にする処理を、型毎にデリゲートで保持しています。
そのため、(JITで消え去りそうですけど)少なからずデリゲート呼び出しのコストがかかっているはずです。
Function Pointersにするにはうってつけ?のハズです。
先に結果だけ書くと、数パーセント速くなってそうでした。
計測
Nugetの最新版(2.2.0)と、それをベースにFunction Pointersに置き換えたコードをBenchmarkDotNetで比較します。
コードとベンチマークコードはGithubに一応置いておきますが、書き捨てるつもりなのでしばらくしたら消すかもしれません。
この記事の最後にpatchとして置いておきます。
ベンチマーク結果のMethodで、末尾に_
が付いているのがリリース版(デリゲート版)、N
が付いているのが置き換えたコード(Function Pointer版)です。
なお、ZString にはUtf16用のクラスとUtf8用のクラスがありますが、面倒なのでUtf16用しか置き換えと計測をしていません。
ベンチマークコードはFormatBenchmark.csをdotnet run -c Release
で実行しています。
結果(1回目)
Ratio列に注目してください。
多少速くなっていそうな雰囲気があります。
どうしても計測時環境によりブレがありますので、もう一回測ってみます。
Method | FormatString | Mean | Error | StdDev | Ratio | RatioSD | Code Size | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|---|
Format_ | This (...)orld. [62] | 225.71 ns | 1.708 ns | 1.598 ns | 1.00 | 0.00 | 3147 B | 0.0181 | - | - | 152 B |
FormatN | This (...)orld. [62] | 192.91 ns | 2.135 ns | 1.997 ns | 0.85 | 0.01 | 3458 B | 0.0181 | - | - | 152 B |
Utf16PreparedFormat_ | This (...)orld. [62] | 85.97 ns | 1.762 ns | 2.097 ns | 1.00 | 0.00 | 2597 B | 0.0181 | - | - | 152 B |
Utf16PreparedFormatN | This (...)orld. [62] | 84.32 ns | 1.686 ns | 1.731 ns | 0.98 | 0.02 | 2567 B | 0.0181 | - | - | 152 B |
Utf16StringBuilderAppendFormat_ | This (...)orld. [62] | 201.26 ns | 0.881 ns | 0.824 ns | 1.00 | 0.00 | 7425 B | - | - | - | - |
Utf16StringBuilderAppendFormatN | This (...)orld. [62] | 177.24 ns | 0.850 ns | 0.795 ns | 0.88 | 0.01 | 7924 B | - | - | - | - |
Format_ | x:{0}, y:{1} | 137.85 ns | 0.466 ns | 0.436 ns | 1.00 | 0.00 | 3147 B | 0.0057 | - | - | 48 B |
FormatN | x:{0}, y:{1} | 135.39 ns | 0.747 ns | 0.699 ns | 0.98 | 0.01 | 3458 B | 0.0057 | - | - | 48 B |
Utf16PreparedFormat_ | x:{0}, y:{1} | 74.40 ns | 0.694 ns | 0.649 ns | 1.00 | 0.00 | 2597 B | 0.0057 | - | - | 48 B |
Utf16PreparedFormatN | x:{0}, y:{1} | 74.23 ns | 0.627 ns | 0.586 ns | 1.00 | 0.01 | 2567 B | 0.0057 | - | - | 48 B |
Utf16StringBuilderAppendFormat_ | x:{0}, y:{1} | 141.08 ns | 0.671 ns | 0.628 ns | 1.00 | 0.00 | 7423 B | - | - | - | - |
Utf16StringBuilderAppendFormatN | x:{0}, y:{1} | 131.62 ns | 0.580 ns | 0.543 ns | 0.93 | 0.01 | 7922 B | - | - | - | - |
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores
.NET Core SDK=3.1.400-preview-015203
[Host] : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT
DefaultJob : .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT
結果(2回目)
遅くなっているケースもありますが、傾向として速くなってそうです。
Method | FormatString | Mean | Error | StdDev | Ratio | RatioSD | Code Size | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|---|---|
Format_ | This (...)orld. [62] | 225.79 ns | 1.438 ns | 1.345 ns | 1.00 | 0.00 | 3147 B | 0.0181 | - | - | 152 B |
FormatN | This (...)orld. [62] | 192.97 ns | 1.582 ns | 1.321 ns | 0.85 | 0.01 | 3458 B | 0.0181 | - | - | 152 B |
Utf16PreparedFormat_ | This (...)orld. [62] | 85.96 ns | 1.667 ns | 1.784 ns | 1.00 | 0.00 | 2597 B | 0.0181 | - | - | 152 B |
Utf16PreparedFormatN | This (...)orld. [62] | 90.81 ns | 1.874 ns | 1.924 ns | 1.06 | 0.02 | 2567 B | 0.0181 | - | - | 152 B |
Utf16StringBuilderAppendFormat_ | This (...)orld. [62] | 191.97 ns | 0.974 ns | 0.911 ns | 1.00 | 0.00 | 7276 B | - | - | - | - |
Utf16StringBuilderAppendFormatN | This (...)orld. [62] | 176.76 ns | 0.877 ns | 0.821 ns | 0.92 | 0.01 | 7924 B | - | - | - | - |
Format_ | x:{0}, y:{1} | 137.57 ns | 0.816 ns | 0.763 ns | 1.00 | 0.00 | 3147 B | 0.0057 | - | - | 48 B |
FormatN | x:{0}, y:{1} | 137.22 ns | 0.797 ns | 0.746 ns | 1.00 | 0.01 | 3458 B | 0.0057 | - | - | 48 B |
Utf16PreparedFormat_ | x:{0}, y:{1} | 73.85 ns | 0.731 ns | 0.683 ns | 1.00 | 0.00 | 2597 B | 0.0057 | - | - | 48 B |
Utf16PreparedFormatN | x:{0}, y:{1} | 76.60 ns | 0.685 ns | 0.641 ns | 1.04 | 0.01 | 2567 B | 0.0057 | - | - | 48 B |
Utf16StringBuilderAppendFormat_ | x:{0}, y:{1} | 140.42 ns | 0.659 ns | 0.584 ns | 1.00 | 0.00 | 7425 B | - | - | - | - |
Utf16StringBuilderAppendFormatN | x:{0}, y:{1} | 125.47 ns | 0.545 ns | 0.510 ns | 0.89 | 0.00 | 7922 B | - | - | - | - |
結論
- 対象は数パーセントは効果あり。
- しかし、関数ポインターできるのは静的関数のみで、設計にかなり制約が出てくる。 (例えば、外からデリゲートを渡しづらくなる)
これよりもっとデリゲートを使っているとか、複雑さを犠牲にしてでも1%でも速くしたいとかで無ければ、置き換えるのは最後の手段にしておきましょう。
コードとベンチマークコード
追記:実はコードは少し前(Preview 4.0か5.0)に作っていて、その時はFunction Pointerとvoid*
との相互変換ができませんでした。(ドキュメントでは許可されている)
そのため、ちょっと回りくどいコードになっています。
しかし、Preview 6.0ではvoid*
との相互変換できるようになっていたため、今ならもう少しオリジナルのコードに近いものにできそうです。
From bab609ea44ed09d24537c279de043d8072615f5d Mon Sep 17 00:00:00 2001
From: udaken <u*********@gmail.com>
Date: Wed, 5 Aug 2020 23:12:23 +0900
Subject: [PATCH] function pointers
---
global.json | 6 +
.../BenchmarkVsReleasedVersion.csproj | 2 +-
.../BuiltinTypesBenchmark.cs | 12 +-
.../FormatBenchmark.cs | 12 +-
sandbox/BenchmarkVsReleasedVersion/Program.cs | 3 +-
.../Assets/Scripts/ZString/PreparedFormat.cs | 64 +++---
.../Utf16ValueStringBuilder.AppendFormat.cs | 2 +-
...Utf16ValueStringBuilder.CreateFormatter.cs | 217 ++++++++++--------
.../ZString/Utf16ValueStringBuilder.cs | 77 ++++---
src/ZString/PreparedFormat.cs | 64 +++---
src/ZString/PreparedFormat.tt | 2 +-
.../Utf16ValueStringBuilder.AppendFormat.cs | 2 +-
.../Utf16ValueStringBuilder.AppendFormat.tt | 2 +-
...Utf16ValueStringBuilder.CreateFormatter.cs | 217 ++++++++++--------
...Utf16ValueStringBuilder.CreateFormatter.tt | 42 ++--
src/ZString/Utf16ValueStringBuilder.cs | 77 ++++---
src/ZString/ZString.csproj | 2 +
17 files changed, 437 insertions(+), 366 deletions(-)
create mode 100644 global.json
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..5679eb1
--- /dev/null
+++ b/global.json
@@ -0,0 +1,6 @@
+{
+ "sdk": {
+ "allowPrerelease" : true,
+ "version" : "3.1.400-preview-015203"
+ }
+}
diff --git a/sandbox/BenchmarkVsReleasedVersion/BenchmarkVsReleasedVersion.csproj b/sandbox/BenchmarkVsReleasedVersion/BenchmarkVsReleasedVersion.csproj
index 7e34c82..4e03240 100644
--- a/sandbox/BenchmarkVsReleasedVersion/BenchmarkVsReleasedVersion.csproj
+++ b/sandbox/BenchmarkVsReleasedVersion/BenchmarkVsReleasedVersion.csproj
@@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.1" Condition="$(TargetFramework) != 'netstandard3.1'" />
- <PackageReference Include="ZString" Version="2.1.3" />
+ <PackageReference Include="ZString" Version="2.2.0" />
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
</ItemGroup>
diff --git a/sandbox/BenchmarkVsReleasedVersion/BuiltinTypesBenchmark.cs b/sandbox/BenchmarkVsReleasedVersion/BuiltinTypesBenchmark.cs
index 20b8ab4..5de265c 100644
--- a/sandbox/BenchmarkVsReleasedVersion/BuiltinTypesBenchmark.cs
+++ b/sandbox/BenchmarkVsReleasedVersion/BuiltinTypesBenchmark.cs
@@ -96,13 +96,13 @@ namespace BenchmarkVsReleasedVersion
_byte, _dt, _dto, _decimal, _double, _guid, _short, _float, _ts, _uint, _ulong, _null, _string, _bool, _enum, _char);
}
- [BenchmarkCategory("CreatePreparedFormat"), Benchmark(Baseline = true)]
+ //[BenchmarkCategory("CreatePreparedFormat"), Benchmark(Baseline = true)]
public object CreatePreparedFormat_()
{
return new PF16(_format);
}
- [BenchmarkCategory("CreatePreparedFormat"), Benchmark]
+ //[BenchmarkCategory("CreatePreparedFormat"), Benchmark]
public object CreatePreparedFormatN()
{
return new NPF16(_format);
@@ -122,14 +122,14 @@ namespace BenchmarkVsReleasedVersion
_byte, _dt, _dto, _decimal, _double, _guid, _short, _float, _ts, _uint, _ulong, _null, _string, _bool, _enum, _char);
}
- [BenchmarkCategory("Utf8PreparedFormat"), Benchmark(Baseline = true)]
+ //[BenchmarkCategory("Utf8PreparedFormat"), Benchmark(Baseline = true)]
public string Utf8PreparedFormat_()
{
return _utf8preparedFormat_.Format(
_byte, _dt, _dto, _decimal, _double, _guid, _short, _float, _ts, _uint, _ulong, _null, _string, _bool, _enum, _char);
}
- [BenchmarkCategory("Utf8PreparedFormat"), Benchmark]
+ //[BenchmarkCategory("Utf8PreparedFormat"), Benchmark]
public string Utf8PreparedFormatN()
{
return _utf8preparedFormatN.Format(
@@ -154,7 +154,7 @@ namespace BenchmarkVsReleasedVersion
return zsh.Length;
}
- [BenchmarkCategory("Utf8StringBuilderAppendFormat"), Benchmark(Baseline = true)]
+ //[BenchmarkCategory("Utf8StringBuilderAppendFormat"), Benchmark(Baseline = true)]
public int Utf8StringBuilderAppendFormat_()
{
using var zsh = ZString.CreateUtf8StringBuilder();
@@ -163,7 +163,7 @@ namespace BenchmarkVsReleasedVersion
return zsh.Length;
}
- [BenchmarkCategory("Utf8StringBuilderAppendFormat"), Benchmark]
+ //[BenchmarkCategory("Utf8StringBuilderAppendFormat"), Benchmark]
public int Utf8StringBuilderAppendFormatN()
{
using var zsh = NZString.CreateUtf8StringBuilder();
diff --git a/sandbox/BenchmarkVsReleasedVersion/FormatBenchmark.cs b/sandbox/BenchmarkVsReleasedVersion/FormatBenchmark.cs
index 14a49c2..a6326cb 100644
--- a/sandbox/BenchmarkVsReleasedVersion/FormatBenchmark.cs
+++ b/sandbox/BenchmarkVsReleasedVersion/FormatBenchmark.cs
@@ -61,13 +61,13 @@ namespace BenchmarkVsReleasedVersion
return NZString.Format(FormatString, x, y);
}
- [BenchmarkCategory("CreatePreparedFormat"), Benchmark(Baseline = true)]
+ //[BenchmarkCategory("CreatePreparedFormat"), Benchmark(Baseline = true)]
public object CreatePreparedFormat_()
{
return new Utf16PreparedFormat<int, int>(FormatString);
}
- [BenchmarkCategory("CreatePreparedFormat"), Benchmark]
+ //[BenchmarkCategory("CreatePreparedFormat"), Benchmark]
public object CreatePreparedFormatN()
{
return new NewZString::Cysharp.Text.Utf16PreparedFormat<int, int>(FormatString);
@@ -85,13 +85,13 @@ namespace BenchmarkVsReleasedVersion
return _utf16preparedFormatN.Format(x, y);
}
- [BenchmarkCategory("Utf8PreparedFormat"), Benchmark(Baseline = true)]
+ //[BenchmarkCategory("Utf8PreparedFormat"), Benchmark(Baseline = true)]
public string Utf8PreparedFormat_()
{
return _utf8preparedFormat_.Format(x, y);
}
- [BenchmarkCategory("Utf8PreparedFormat"), Benchmark]
+ //[BenchmarkCategory("Utf8PreparedFormat"), Benchmark]
public string Utf8PreparedFormatN()
{
return _utf8preparedFormatN.Format(x, y);
@@ -113,7 +113,7 @@ namespace BenchmarkVsReleasedVersion
return zsh.Length;
}
- [BenchmarkCategory("Utf8StringBuilderAppendFormat"), Benchmark(Baseline = true)]
+ //[BenchmarkCategory("Utf8StringBuilderAppendFormat"), Benchmark(Baseline = true)]
public int Utf8StringBuilderAppendFormat_()
{
using var zsh = ZString.CreateUtf8StringBuilder();
@@ -121,7 +121,7 @@ namespace BenchmarkVsReleasedVersion
return zsh.Length;
}
- [BenchmarkCategory("Utf8StringBuilderAppendFormat"), Benchmark]
+ //[BenchmarkCategory("Utf8StringBuilderAppendFormat"), Benchmark]
public int Utf8StringBuilderAppendFormatN()
{
using var zsh = NZString.CreateUtf8StringBuilder();
diff --git a/sandbox/BenchmarkVsReleasedVersion/Program.cs b/sandbox/BenchmarkVsReleasedVersion/Program.cs
index 45ee687..8574158 100644
--- a/sandbox/BenchmarkVsReleasedVersion/Program.cs
+++ b/sandbox/BenchmarkVsReleasedVersion/Program.cs
@@ -19,7 +19,8 @@ namespace BenchmarkVsReleasedVersion
public BenchmarkConfig()
{
AddDiagnoser(MemoryDiagnoser.Default);
- AddJob(Job.ShortRun.WithWarmupCount(1).WithIterationCount(1));
+ //AddJob(Job.ShortRun.WithWarmupCount(1).WithIterationCount(1));
+ AddJob(Job.Default);
}
}
diff --git a/src/ZString.Unity/Assets/Scripts/ZString/PreparedFormat.cs b/src/ZString.Unity/Assets/Scripts/ZString/PreparedFormat.cs
index c343a57..d7232bd 100644
--- a/src/ZString.Unity/Assets/Scripts/ZString/PreparedFormat.cs
+++ b/src/ZString.Unity/Assets/Scripts/ZString/PreparedFormat.cs
@@ -4,7 +4,7 @@ using System.Buffers;
namespace Cysharp.Text
{
- public sealed partial class Utf16PreparedFormat<T1>
+ public sealed unsafe partial class Utf16PreparedFormat<T1>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -80,7 +80,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -171,7 +171,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -277,7 +277,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -398,7 +398,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -534,7 +534,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -685,7 +685,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -851,7 +851,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1032,7 +1032,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1228,7 +1228,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1439,7 +1439,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1665,7 +1665,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1906,7 +1906,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -2162,7 +2162,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -2433,7 +2433,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -2719,7 +2719,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3020,7 +3020,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1>
+ public sealed unsafe partial class Utf8PreparedFormat<T1>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3097,7 +3097,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3189,7 +3189,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3296,7 +3296,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3418,7 +3418,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3555,7 +3555,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3707,7 +3707,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3874,7 +3874,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4056,7 +4056,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4253,7 +4253,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4465,7 +4465,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4692,7 +4692,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4934,7 +4934,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -5191,7 +5191,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -5463,7 +5463,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -5750,7 +5750,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
{
public string FormatString { get; }
public int MinSize { get; }
diff --git a/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.cs b/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.cs
index 515186d..9afcc41 100644
--- a/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.cs
+++ b/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.cs
@@ -2,7 +2,7 @@
namespace Cysharp.Text
{
- public partial struct Utf16ValueStringBuilder
+ public unsafe partial struct Utf16ValueStringBuilder
{
/// <summary>Appends the string returned by processing a composite format string, each format item is replaced by the string representation of arguments.</summary>
public void AppendFormat<T1>(string format, T1 arg1)
diff --git a/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.cs b/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.cs
index 8d939c7..77b85ea 100644
--- a/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.cs
+++ b/src/ZString.Unity/Assets/Scripts/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.cs
@@ -1,146 +1,167 @@
・ソusing System;
+using System.Runtime.CompilerServices;
namespace Cysharp.Text
{
public partial struct Utf16ValueStringBuilder
{
- static object CreateFormatter(Type type)
+ static unsafe void RegisterPrimitives()
{
- if (type == typeof(System.SByte))
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool SByteFunc(System.SByte x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return new TryFormat<System.SByte>((System.SByte x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Int16))
- {
- return new TryFormat<System.Int16>((System.Int16 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Int32))
- {
- return new TryFormat<System.Int32>((System.Int32 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Int64))
- {
- return new TryFormat<System.Int64>((System.Int64 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Byte))
- {
- return new TryFormat<System.Byte>((System.Byte x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.UInt16))
- {
- return new TryFormat<System.UInt16>((System.UInt16 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.UInt32))
- {
- return new TryFormat<System.UInt32>((System.UInt32 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.UInt64))
- {
- return new TryFormat<System.UInt64>((System.UInt64 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Single))
- {
- return new TryFormat<System.Single>((System.Single x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Double))
- {
- return new TryFormat<System.Double>((System.Double x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.TimeSpan))
- {
- return new TryFormat<System.TimeSpan>((System.TimeSpan x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.DateTime))
- {
- return new TryFormat<System.DateTime>((System.DateTime x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.DateTimeOffset))
- {
- return new TryFormat<System.DateTimeOffset>((System.DateTimeOffset x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Decimal))
- {
- return new TryFormat<System.Decimal>((System.Decimal x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Guid))
- {
- return new TryFormat<System.Guid>((System.Guid x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Byte?))
- {
- return CreateNullableFormatter<System.Byte>();
- }
- if (type == typeof(System.DateTime?))
+ FormatterCache<System.SByte>.TryFormatDelegate = &SByteFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool Int16Func(System.Int16 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.DateTime>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.DateTimeOffset?))
+ FormatterCache<System.Int16>.TryFormatDelegate = &Int16Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool Int32Func(System.Int32 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.DateTimeOffset>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Decimal?))
+ FormatterCache<System.Int32>.TryFormatDelegate = &Int32Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool Int64Func(System.Int64 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Decimal>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Double?))
+ FormatterCache<System.Int64>.TryFormatDelegate = &Int64Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool ByteFunc(System.Byte x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Double>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Int16?))
+ FormatterCache<System.Byte>.TryFormatDelegate = &ByteFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UInt16Func(System.UInt16 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Int16>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Int32?))
+ FormatterCache<System.UInt16>.TryFormatDelegate = &UInt16Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UInt32Func(System.UInt32 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Int32>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Int64?))
+ FormatterCache<System.UInt32>.TryFormatDelegate = &UInt32Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UInt64Func(System.UInt64 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Int64>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.SByte?))
+ FormatterCache<System.UInt64>.TryFormatDelegate = &UInt64Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool SingleFunc(System.Single x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.SByte>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Single?))
+ FormatterCache<System.Single>.TryFormatDelegate = &SingleFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool DoubleFunc(System.Double x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Single>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.TimeSpan?))
+ FormatterCache<System.Double>.TryFormatDelegate = &DoubleFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool TimeSpanFunc(System.TimeSpan x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.TimeSpan>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.UInt16?))
+ FormatterCache<System.TimeSpan>.TryFormatDelegate = &TimeSpanFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool DateTimeFunc(System.DateTime x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.UInt16>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.UInt32?))
+ FormatterCache<System.DateTime>.TryFormatDelegate = &DateTimeFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool DateTimeOffsetFunc(System.DateTimeOffset x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.UInt32>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.UInt64?))
+ FormatterCache<System.DateTimeOffset>.TryFormatDelegate = &DateTimeOffsetFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool DecimalFunc(System.Decimal x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.UInt64>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Guid?))
+ FormatterCache<System.Decimal>.TryFormatDelegate = &DecimalFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool GuidFunc(System.Guid x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Guid>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.IntPtr))
+ FormatterCache<System.Guid>.TryFormatDelegate = &GuidFunc;
+
+ FormatterCache<System.Byte?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.DateTime?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.DateTimeOffset?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Decimal?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Double?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Int16?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Int32?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Int64?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.SByte?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Single?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.TimeSpan?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.UInt16?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.UInt32?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.UInt64?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Guid?>.TryFormatDelegate = &NullableFormat;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool IntPtrFunc(System.IntPtr x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
// ignore format
- return new TryFormat<System.IntPtr>((System.IntPtr x, Span<char> dest, out int written, ReadOnlySpan<char> _) => System.IntPtr.Size == 4
+ return System.IntPtr.Size == 4
? x.ToInt32().TryFormat(dest, out written, default)
- : x.ToInt64().TryFormat(dest, out written, default));
+ : x.ToInt64().TryFormat(dest, out written, default);
}
- if (type == typeof(System.UIntPtr))
+ FormatterCache<System.IntPtr>.TryFormatDelegate = &IntPtrFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UIntPtrFunc(System.UIntPtr x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
// ignore format
- return new TryFormat<System.UIntPtr>((System.UIntPtr x, Span<char> dest, out int written, ReadOnlySpan<char> _) => System.UIntPtr.Size == 4
+ return System.UIntPtr.Size == 4
? x.ToUInt32().TryFormat(dest, out written, default)
- : x.ToUInt64().TryFormat(dest, out written, default));
+ : x.ToUInt64().TryFormat(dest, out written, default);
}
- return null;
+ FormatterCache<System.UIntPtr>.TryFormatDelegate = &UIntPtrFunc;
+
}
}
}
\ No newline at end of file
diff --git a/src/ZString.Unity/Assets/Scripts/ZString/Utf16ValueStringBuilder.cs b/src/ZString.Unity/Assets/Scripts/ZString/Utf16ValueStringBuilder.cs
index 7a4368a..1b9ad83 100644
--- a/src/ZString.Unity/Assets/Scripts/ZString/Utf16ValueStringBuilder.cs
+++ b/src/ZString.Unity/Assets/Scripts/ZString/Utf16ValueStringBuilder.cs
@@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
namespace Cysharp.Text
{
- public partial struct Utf16ValueStringBuilder : IDisposable, IBufferWriter<char>, IResettableBufferWriter<char>
+ public unsafe partial struct Utf16ValueStringBuilder : IDisposable, IBufferWriter<char>, IResettableBufferWriter<char>
{
public delegate bool TryFormat<T>(T value, Span<char> destination, out int charsWritten, ReadOnlySpan<char> format);
@@ -32,6 +32,7 @@ namespace Cysharp.Text
newLine2 = newLine[1];
crlf = true;
}
+ RegisterPrimitives();
}
[ThreadStatic]
@@ -232,7 +233,7 @@ namespace Cysharp.Text
}
/// <summary>Appends the string representation of a specified value to this instance.</summary>
- public void Append<T>(T value)
+ public unsafe void Append<T>(T value)
{
if (!FormatterCache<T>.TryFormatDelegate(value, buffer.AsSpan(index), out var written, default))
{
@@ -361,7 +362,7 @@ namespace Cysharp.Text
/// are removed from this builder.
/// </remarks>
public void Replace(string oldValue, string newValue) => Replace(oldValue, newValue, 0, Length);
-
+
public void Replace(ReadOnlySpan<char> oldValue, ReadOnlySpan<char> newValue) => Replace(oldValue, newValue, 0, Length);
/// <summary>
@@ -578,58 +579,62 @@ namespace Cysharp.Text
index += written;
}
+ static class CustomTryFormat<T>
+ {
+ public static TryFormat<T> formatMethod;
+ public static bool Dispatch(T value, Span<char> dest, out int written, ReadOnlySpan<char> format)
+ {
+ return formatMethod(value, dest, out written, format);
+ }
+ }
+
/// <summary>
/// Register custom formatter
/// </summary>
public static void RegisterTryFormat<T>(TryFormat<T> formatMethod)
{
- FormatterCache<T>.TryFormatDelegate = formatMethod;
+ CustomTryFormat<T>.formatMethod = formatMethod;
+ FormatterCache<T>.TryFormatDelegate = &CustomTryFormat<T>.Dispatch;
}
- static TryFormat<T?> CreateNullableFormatter<T>() where T : struct
+ static unsafe bool NullableFormat<T>(T? x, Span<char> dest, out int written, ReadOnlySpan<char> format) where T : struct
{
- return new TryFormat<T?>((T? x, Span<char> dest, out int written, ReadOnlySpan<char> format) =>
+ if (x == null)
{
- if (x == null)
- {
- written = 0;
- return true;
- }
- return FormatterCache<T>.TryFormatDelegate(x.Value, dest, out written, format);
- });
+ written = 0;
+ return true;
+ }
+ return FormatterCache<T>.TryFormatDelegate(x.Value, dest, out written, format);
}
- /// <summary>
- /// Supports the Nullable type for a given struct type.
- /// </summary>
- public static void EnableNullableFormat<T>() where T : struct
+ public unsafe static class FormatterCache<T>
{
- RegisterTryFormat<T?>(CreateNullableFormatter<T>());
- }
+ private static unsafe delegate*<T, Span<char>, out int, ReadOnlySpan<char>, bool> _TryFormatDelegate;
- public static class FormatterCache<T>
- {
- public static TryFormat<T> TryFormatDelegate;
- static FormatterCache()
+ public static delegate*<T , Span<char> , out int , ReadOnlySpan<char> , bool> TryFormatDelegate
{
- var formatter = (TryFormat<T>)CreateFormatter(typeof(T));
- if (formatter == null)
+ get
{
- if (typeof(T).IsEnum)
+ if (_TryFormatDelegate == null)
{
- formatter = new TryFormat<T>(EnumUtil<T>.TryFormatUtf16);
- }
- else if (typeof(T) == typeof(string))
- {
- formatter = new TryFormat<T>(TryFormatString);
- }
- else
- {
- formatter = new TryFormat<T>(TryFormatDefault);
+ if (typeof(T).IsEnum)
+ {
+ _TryFormatDelegate = &EnumUtil<T>.TryFormatUtf16;
+ }
+ else if (typeof(T) == typeof(string))
+ {
+ _TryFormatDelegate = &TryFormatString;
+ }
+ else
+ {
+ _TryFormatDelegate = &TryFormatDefault;
+ }
}
+
+ return _TryFormatDelegate;
}
- TryFormatDelegate = formatter;
+ set => _TryFormatDelegate = value;
}
static bool TryFormatString(T value, Span<char> dest, out int written, ReadOnlySpan<char> format)
diff --git a/src/ZString/PreparedFormat.cs b/src/ZString/PreparedFormat.cs
index c343a57..d7232bd 100644
--- a/src/ZString/PreparedFormat.cs
+++ b/src/ZString/PreparedFormat.cs
@@ -4,7 +4,7 @@ using System.Buffers;
namespace Cysharp.Text
{
- public sealed partial class Utf16PreparedFormat<T1>
+ public sealed unsafe partial class Utf16PreparedFormat<T1>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -80,7 +80,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -171,7 +171,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -277,7 +277,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -398,7 +398,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -534,7 +534,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -685,7 +685,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -851,7 +851,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1032,7 +1032,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1228,7 +1228,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1439,7 +1439,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1665,7 +1665,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -1906,7 +1906,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -2162,7 +2162,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -2433,7 +2433,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -2719,7 +2719,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
+ public sealed unsafe partial class Utf16PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3020,7 +3020,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1>
+ public sealed unsafe partial class Utf8PreparedFormat<T1>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3097,7 +3097,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3189,7 +3189,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3296,7 +3296,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3418,7 +3418,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3555,7 +3555,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3707,7 +3707,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -3874,7 +3874,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4056,7 +4056,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4253,7 +4253,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4465,7 +4465,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4692,7 +4692,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -4934,7 +4934,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -5191,7 +5191,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -5463,7 +5463,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>
{
public string FormatString { get; }
public int MinSize { get; }
@@ -5750,7 +5750,7 @@ namespace Cysharp.Text
}
}
- public sealed partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
+ public sealed unsafe partial class Utf8PreparedFormat<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>
{
public string FormatString { get; }
public int MinSize { get; }
diff --git a/src/ZString/PreparedFormat.tt b/src/ZString/PreparedFormat.tt
index 4087bea..e3af3be 100644
--- a/src/ZString/PreparedFormat.tt
+++ b/src/ZString/PreparedFormat.tt
@@ -13,7 +13,7 @@ namespace Cysharp.Text
{
<# foreach(var utf in utfTypes) { var isUtf16 = (utf == "Utf16"); #>
<# for(var i = 1; i <= TypeParamMax; i++) { #>
- public sealed partial class <#= utf #>PreparedFormat<<#= CreateTypeArgument(i) #>>
+ public sealed unsafe partial class <#= utf #>PreparedFormat<<#= CreateTypeArgument(i) #>>
{
public string FormatString { get; }
public int MinSize { get; }
diff --git a/src/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.cs b/src/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.cs
index 515186d..9afcc41 100644
--- a/src/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.cs
+++ b/src/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.cs
@@ -2,7 +2,7 @@
namespace Cysharp.Text
{
- public partial struct Utf16ValueStringBuilder
+ public unsafe partial struct Utf16ValueStringBuilder
{
/// <summary>Appends the string returned by processing a composite format string, each format item is replaced by the string representation of arguments.</summary>
public void AppendFormat<T1>(string format, T1 arg1)
diff --git a/src/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.tt b/src/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.tt
index 12b53b5..474d8b0 100644
--- a/src/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.tt
+++ b/src/ZString/Utf16/Utf16ValueStringBuilder.AppendFormat.tt
@@ -9,7 +9,7 @@ using System;
namespace Cysharp.Text
{
- public partial struct Utf16ValueStringBuilder
+ public unsafe partial struct Utf16ValueStringBuilder
{
<# for(var i = 1; i <= TypeParamMax; i++) { #>
/// <summary>Appends the string returned by processing a composite format string, each format item is replaced by the string representation of arguments.</summary>
diff --git a/src/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.cs b/src/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.cs
index 8d939c7..77b85ea 100644
--- a/src/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.cs
+++ b/src/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.cs
@@ -1,146 +1,167 @@
・ソusing System;
+using System.Runtime.CompilerServices;
namespace Cysharp.Text
{
public partial struct Utf16ValueStringBuilder
{
- static object CreateFormatter(Type type)
+ static unsafe void RegisterPrimitives()
{
- if (type == typeof(System.SByte))
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool SByteFunc(System.SByte x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return new TryFormat<System.SByte>((System.SByte x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Int16))
- {
- return new TryFormat<System.Int16>((System.Int16 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Int32))
- {
- return new TryFormat<System.Int32>((System.Int32 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Int64))
- {
- return new TryFormat<System.Int64>((System.Int64 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Byte))
- {
- return new TryFormat<System.Byte>((System.Byte x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.UInt16))
- {
- return new TryFormat<System.UInt16>((System.UInt16 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.UInt32))
- {
- return new TryFormat<System.UInt32>((System.UInt32 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.UInt64))
- {
- return new TryFormat<System.UInt64>((System.UInt64 x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Single))
- {
- return new TryFormat<System.Single>((System.Single x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Double))
- {
- return new TryFormat<System.Double>((System.Double x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.TimeSpan))
- {
- return new TryFormat<System.TimeSpan>((System.TimeSpan x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.DateTime))
- {
- return new TryFormat<System.DateTime>((System.DateTime x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.DateTimeOffset))
- {
- return new TryFormat<System.DateTimeOffset>((System.DateTimeOffset x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Decimal))
- {
- return new TryFormat<System.Decimal>((System.Decimal x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Guid))
- {
- return new TryFormat<System.Guid>((System.Guid x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
- }
- if (type == typeof(System.Byte?))
- {
- return CreateNullableFormatter<System.Byte>();
- }
- if (type == typeof(System.DateTime?))
+ FormatterCache<System.SByte>.TryFormatDelegate = &SByteFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool Int16Func(System.Int16 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.DateTime>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.DateTimeOffset?))
+ FormatterCache<System.Int16>.TryFormatDelegate = &Int16Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool Int32Func(System.Int32 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.DateTimeOffset>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Decimal?))
+ FormatterCache<System.Int32>.TryFormatDelegate = &Int32Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool Int64Func(System.Int64 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Decimal>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Double?))
+ FormatterCache<System.Int64>.TryFormatDelegate = &Int64Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool ByteFunc(System.Byte x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Double>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Int16?))
+ FormatterCache<System.Byte>.TryFormatDelegate = &ByteFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UInt16Func(System.UInt16 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Int16>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Int32?))
+ FormatterCache<System.UInt16>.TryFormatDelegate = &UInt16Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UInt32Func(System.UInt32 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Int32>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Int64?))
+ FormatterCache<System.UInt32>.TryFormatDelegate = &UInt32Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UInt64Func(System.UInt64 x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Int64>();
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.SByte?))
+ FormatterCache<System.UInt64>.TryFormatDelegate = &UInt64Func;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool SingleFunc(System.Single x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.SByte>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Single?))
+ FormatterCache<System.Single>.TryFormatDelegate = &SingleFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool DoubleFunc(System.Double x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Single>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.TimeSpan?))
+ FormatterCache<System.Double>.TryFormatDelegate = &DoubleFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool TimeSpanFunc(System.TimeSpan x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.TimeSpan>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.UInt16?))
+ FormatterCache<System.TimeSpan>.TryFormatDelegate = &TimeSpanFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool DateTimeFunc(System.DateTime x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.UInt16>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.UInt32?))
+ FormatterCache<System.DateTime>.TryFormatDelegate = &DateTimeFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool DateTimeOffsetFunc(System.DateTimeOffset x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.UInt32>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.UInt64?))
+ FormatterCache<System.DateTimeOffset>.TryFormatDelegate = &DateTimeOffsetFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool DecimalFunc(System.Decimal x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.UInt64>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.Guid?))
+ FormatterCache<System.Decimal>.TryFormatDelegate = &DecimalFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool GuidFunc(System.Guid x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return CreateNullableFormatter<System.Guid>();
+ return x.TryFormat(dest, out written, format);
}
- if (type == typeof(System.IntPtr))
+ FormatterCache<System.Guid>.TryFormatDelegate = &GuidFunc;
+
+ FormatterCache<System.Byte?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.DateTime?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.DateTimeOffset?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Decimal?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Double?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Int16?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Int32?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Int64?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.SByte?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Single?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.TimeSpan?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.UInt16?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.UInt32?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.UInt64?>.TryFormatDelegate = &NullableFormat;
+
+ FormatterCache<System.Guid?>.TryFormatDelegate = &NullableFormat;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool IntPtrFunc(System.IntPtr x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
// ignore format
- return new TryFormat<System.IntPtr>((System.IntPtr x, Span<char> dest, out int written, ReadOnlySpan<char> _) => System.IntPtr.Size == 4
+ return System.IntPtr.Size == 4
? x.ToInt32().TryFormat(dest, out written, default)
- : x.ToInt64().TryFormat(dest, out written, default));
+ : x.ToInt64().TryFormat(dest, out written, default);
}
- if (type == typeof(System.UIntPtr))
+ FormatterCache<System.IntPtr>.TryFormatDelegate = &IntPtrFunc;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UIntPtrFunc(System.UIntPtr x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
// ignore format
- return new TryFormat<System.UIntPtr>((System.UIntPtr x, Span<char> dest, out int written, ReadOnlySpan<char> _) => System.UIntPtr.Size == 4
+ return System.UIntPtr.Size == 4
? x.ToUInt32().TryFormat(dest, out written, default)
- : x.ToUInt64().TryFormat(dest, out written, default));
+ : x.ToUInt64().TryFormat(dest, out written, default);
}
- return null;
+ FormatterCache<System.UIntPtr>.TryFormatDelegate = &UIntPtrFunc;
+
}
}
}
\ No newline at end of file
diff --git a/src/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.tt b/src/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.tt
index 0ccd705..3684c0f 100644
--- a/src/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.tt
+++ b/src/ZString/Utf16/Utf16ValueStringBuilder.CreateFormatter.tt
@@ -32,47 +32,57 @@
};
#>
using System;
+using System.Runtime.CompilerServices;
namespace Cysharp.Text
{
public partial struct Utf16ValueStringBuilder
{
- static object CreateFormatter(Type type)
+ static unsafe void RegisterPrimitives()
{
<# foreach(var t in spanFormattablesA) { #>
- if (type == typeof(<#= t.FullName #>))
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool <#= t.Name #>Func(<#= t.FullName #> x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return new TryFormat<<#= t.FullName #>>((<#= t.FullName #> x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
+ return format.Length == 0 ? FastNumberWriter.TryWriteInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
+ FormatterCache<<#= t.FullName #>>.TryFormatDelegate = &<#= t.Name #>Func;
+
<# } #>
<# foreach(var t in spanFormattablesB) { #>
- if (type == typeof(<#= t.FullName #>))
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool <#= t.Name #>Func(<#= t.FullName #> x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return new TryFormat<<#= t.FullName #>>((<#= t.FullName #> x, Span<char> dest, out int written, ReadOnlySpan<char> format) => format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format));
+ return format.Length == 0 ? FastNumberWriter.TryWriteUInt64(dest, out written, x) : x.TryFormat(dest, out written, format);
}
+ FormatterCache<<#= t.FullName #>>.TryFormatDelegate = &<#= t.Name #>Func;
+
<# } #>
<# foreach(var t in spanFormattablesC) { #>
- if (type == typeof(<#= t.FullName #>))
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool <#= t.Name #>Func(<#= t.FullName #> x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
- return new TryFormat<<#= t.FullName #>>((<#= t.FullName #> x, Span<char> dest, out int written, ReadOnlySpan<char> format) => x.TryFormat(dest, out written, format));
+ return x.TryFormat(dest, out written, format);
}
+ FormatterCache<<#= t.FullName #>>.TryFormatDelegate = &<#= t.Name #>Func;
+
<# } #>
<# foreach(var t in spanFormattables) { #>
- if (type == typeof(<#= t.FullName #>?))
- {
- return CreateNullableFormatter<<#= t.FullName #>>();
- }
+ FormatterCache<<#= t.FullName #>?>.TryFormatDelegate = &NullableFormat;
+
<# } #>
-<# foreach(var t in new [] {typeof(IntPtr), typeof(UIntPtr)}) { var u = t == typeof(UIntPtr); #>
- if (type == typeof(<#= t.FullName #>))
+<# foreach(var t in new [] {typeof(IntPtr), typeof(UIntPtr)} ) { var u = t == typeof(UIntPtr); #>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool <#= t.Name #>Func(<#= t.FullName #> x, Span<char> dest, out int written, ReadOnlySpan<char> format)
{
// ignore format
- return new TryFormat<<#= t.FullName #>>((<#= t.FullName #> x, Span<char> dest, out int written, ReadOnlySpan<char> _) => <#= t #>.Size == 4
+ return <#= t #>.Size == 4
? x.To<#= u ? "U" : "" #>Int32().TryFormat(dest, out written, default)
- : x.To<#= u ? "U" : "" #>Int64().TryFormat(dest, out written, default));
+ : x.To<#= u ? "U" : "" #>Int64().TryFormat(dest, out written, default);
}
+ FormatterCache<<#= t.FullName #>>.TryFormatDelegate = &<#= t.Name #>Func;
+
<# } #>
- return null;
}
}
}
\ No newline at end of file
diff --git a/src/ZString/Utf16ValueStringBuilder.cs b/src/ZString/Utf16ValueStringBuilder.cs
index 7a4368a..1b9ad83 100644
--- a/src/ZString/Utf16ValueStringBuilder.cs
+++ b/src/ZString/Utf16ValueStringBuilder.cs
@@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
namespace Cysharp.Text
{
- public partial struct Utf16ValueStringBuilder : IDisposable, IBufferWriter<char>, IResettableBufferWriter<char>
+ public unsafe partial struct Utf16ValueStringBuilder : IDisposable, IBufferWriter<char>, IResettableBufferWriter<char>
{
public delegate bool TryFormat<T>(T value, Span<char> destination, out int charsWritten, ReadOnlySpan<char> format);
@@ -32,6 +32,7 @@ namespace Cysharp.Text
newLine2 = newLine[1];
crlf = true;
}
+ RegisterPrimitives();
}
[ThreadStatic]
@@ -232,7 +233,7 @@ namespace Cysharp.Text
}
/// <summary>Appends the string representation of a specified value to this instance.</summary>
- public void Append<T>(T value)
+ public unsafe void Append<T>(T value)
{
if (!FormatterCache<T>.TryFormatDelegate(value, buffer.AsSpan(index), out var written, default))
{
@@ -361,7 +362,7 @@ namespace Cysharp.Text
/// are removed from this builder.
/// </remarks>
public void Replace(string oldValue, string newValue) => Replace(oldValue, newValue, 0, Length);
-
+
public void Replace(ReadOnlySpan<char> oldValue, ReadOnlySpan<char> newValue) => Replace(oldValue, newValue, 0, Length);
/// <summary>
@@ -578,58 +579,62 @@ namespace Cysharp.Text
index += written;
}
+ static class CustomTryFormat<T>
+ {
+ public static TryFormat<T> formatMethod;
+ public static bool Dispatch(T value, Span<char> dest, out int written, ReadOnlySpan<char> format)
+ {
+ return formatMethod(value, dest, out written, format);
+ }
+ }
+
/// <summary>
/// Register custom formatter
/// </summary>
public static void RegisterTryFormat<T>(TryFormat<T> formatMethod)
{
- FormatterCache<T>.TryFormatDelegate = formatMethod;
+ CustomTryFormat<T>.formatMethod = formatMethod;
+ FormatterCache<T>.TryFormatDelegate = &CustomTryFormat<T>.Dispatch;
}
- static TryFormat<T?> CreateNullableFormatter<T>() where T : struct
+ static unsafe bool NullableFormat<T>(T? x, Span<char> dest, out int written, ReadOnlySpan<char> format) where T : struct
{
- return new TryFormat<T?>((T? x, Span<char> dest, out int written, ReadOnlySpan<char> format) =>
+ if (x == null)
{
- if (x == null)
- {
- written = 0;
- return true;
- }
- return FormatterCache<T>.TryFormatDelegate(x.Value, dest, out written, format);
- });
+ written = 0;
+ return true;
+ }
+ return FormatterCache<T>.TryFormatDelegate(x.Value, dest, out written, format);
}
- /// <summary>
- /// Supports the Nullable type for a given struct type.
- /// </summary>
- public static void EnableNullableFormat<T>() where T : struct
+ public unsafe static class FormatterCache<T>
{
- RegisterTryFormat<T?>(CreateNullableFormatter<T>());
- }
+ private static unsafe delegate*<T, Span<char>, out int, ReadOnlySpan<char>, bool> _TryFormatDelegate;
- public static class FormatterCache<T>
- {
- public static TryFormat<T> TryFormatDelegate;
- static FormatterCache()
+ public static delegate*<T , Span<char> , out int , ReadOnlySpan<char> , bool> TryFormatDelegate
{
- var formatter = (TryFormat<T>)CreateFormatter(typeof(T));
- if (formatter == null)
+ get
{
- if (typeof(T).IsEnum)
+ if (_TryFormatDelegate == null)
{
- formatter = new TryFormat<T>(EnumUtil<T>.TryFormatUtf16);
- }
- else if (typeof(T) == typeof(string))
- {
- formatter = new TryFormat<T>(TryFormatString);
- }
- else
- {
- formatter = new TryFormat<T>(TryFormatDefault);
+ if (typeof(T).IsEnum)
+ {
+ _TryFormatDelegate = &EnumUtil<T>.TryFormatUtf16;
+ }
+ else if (typeof(T) == typeof(string))
+ {
+ _TryFormatDelegate = &TryFormatString;
+ }
+ else
+ {
+ _TryFormatDelegate = &TryFormatDefault;
+ }
}
+
+ return _TryFormatDelegate;
}
- TryFormatDelegate = formatter;
+ set => _TryFormatDelegate = value;
}
static bool TryFormatString(T value, Span<char> dest, out int written, ReadOnlySpan<char> format)
diff --git a/src/ZString/ZString.csproj b/src/ZString/ZString.csproj
index eab6ce5..bb765cb 100644
--- a/src/ZString/ZString.csproj
+++ b/src/ZString/ZString.csproj
@@ -18,6 +18,7 @@
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
+ <LangVersion>9.0</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -187,6 +188,7 @@
<!-- Copy files for Unity -->
<PropertyGroup>
<DestinationRoot>$(MSBuildProjectDirectory)\..\ZString.Unity\Assets\Scripts\ZString\</DestinationRoot>
+ <AssemblyName>NewZString</AssemblyName>
</PropertyGroup>
<ItemGroup>
<TargetFiles1 Include="$(MSBuildProjectDirectory)\**\*.cs" Exclude="**\bin\**\*.*;**\obj\**\*.*;_InternalVisibleTo.cs" />
--
2.27.0.windows.1