C#のStringが扱う文字コードはUTF-16ですが、UTF-8が広く使われるようになった現状ではUTF-8→UTF-16の変換コストなどがかかりパフォーマンス上の懸念点となっていました。 そこで最近のパフォーマンス改善の一環として、C#にUTF-8な文字列を扱えるようにしようという流れがcsharplangやcorefxlabで起き、実際に作業が始まっているようです。
昔、個人的に作成していたCrossFormattedTextが設計的に古い(PCL)なため.NET Standardなものを作り直そうと考えているのですが、どうせならUTF-16,8両方に対応したいところです。
そういうわけもあり、Utf8String関連の動向を調べる機会があったので記事に書いておきます。(といっても英語詳しく読んでないので大した内容じゃないです……)
GitHub - MeilCli/SharedText: prototype project, remake of CrossFormattedText
prototype project, remake of CrossFormattedText. Contribute to MeilCli/SharedText development by creating an account on GitHub.
github.com
↑いちおう.NET Standard置き換えのプロトタイプ作ったので、API RequestとかあればIssue立てオナシャスm(__)m
デザインスペック
現在Corefxlabを見ると、Utf8Stringのデザインに関するIssueは2つあります。
Scenarios and Design Philosophy - UTF-8 string support · Issue #2368 · dotnet/corefxlab
(This is a living document. Expect edits.) Scenarios High-performance networking stacks (This includes HttpClient, ASP.NET's Kestrel, and similar APIs.) High-performance networking stacks want to b...
github.com
Utf8String design proposal · Issue #2350 · dotnet/corefxlab
Utf8String design discussion - last edited 14-Sep-19 Utf8String design overview Audience and scenarios Utf8String and related concepts are meant for modern internet-facing applications that need to...
github.com
実際の作業はあまり進んでないように見えるので今回はこの2つを眺めながら、どういった実装になるのか推測という感じになります。また、これ以外にもC#の言語的サポートに関してはcsharplangにIssueが立っています。
全般
このようなコアなことに関する記事を見る読者の皆様ならだいたい察しがついていることでしょうが、内部的にはReadOnlySpan<byte>
やReadOnlyMemory<byte>
のようにスライス可能な1バイト数値型を使いたいようです。
UTF-8 - Wikipedia
ja.wikipedia.org
ところで、Wikipediaにも書いていますがUTF-8では1つの文字コードを表すのに8bit=1byteを複数用いて表現します。
この1byteの数値をCode unit
という単位で表現します。これをC#側のほうでは(UTF-16なCode unitであるchar
に合わせて)Char8
という型を用意したいようですが、実態はbyte
かsbyte
になりそうです。
また、文字コードを表す単位としてCode point
とScalar value
というのがあります。ここの説明がよくわからなかったのですが、どうやらCode point
は歴史的経緯のあるサロゲートコードの範囲を含めていて、Scalar value
は含めていないといったもののようです。そしてこのScalar value
はUnicodeScalar
という型として用意するようです。
もう1つ、厄介なことにUnicodeでは複数の文字を連結して1つの文字として扱う場合があります。人の絵文字とかのことですね。それはGrapheme
と呼ばれるようですが、デザインスペック的にはサポートする予定はなさそうです。
UnicodeScalar
さきほど出たScalar value
を表すUnicodeScalar
型ですが、こちらはUTF-8専用ではなくUTF-16の文字も表せるようにするようです。なので名前がUnicode Scalarなんでしょうね。
namespace System.Text
{
public readonly struct UnicodeScalar
{
public int Utf16SequenceLength { get; } // 1 or 2
public int Utf8SequenceLength { get; } // 1 ~ 4
public int ToUtf16(Span<char> output);
public int ToUtf8(Span<byte> output);
}
}
Issueのほうからざっくりと抽出してみましたが、このUnicodeScalar
型は面白くて、利用者側でSpanを作成し、そこに値を入れるToUtf16
とToUtf8
メソッドがあります。これを利用していくことになりますが、利用者側は当然、値の長さを知らないといけないので長さを取れるプロパティも実装するようです。
パフォーマンスのためにSpanを使うとこうなるのはしょうがないという感じですが、低レベルAPI感をとても感じます。
一応仮の実装はされているようなので、貼っておきます。
corefxlab/src/System.Text.Utf8/System/Text/UnicodeScalar.cs at f691fc4b11238a806e9ae50cff6834e1df25ae1d · dotnet/corefxlab
This repo is for experimentation and exploring new ideas that may or may not make it into the main corefx repo. - dotnet/corefxlab
github.com
Utf8String, Utf8StringSegment
Utf8String
の仕様策定の過程で、structがいいとかstructだとつらいとかどうのこうのがあったようですが、最終的にはUtf8String
型はクラスとなるようです。
そこで、パフォーマンスのために構造体なUtf8StringSegment
も用意するようです。(どのような場面で使用されるかはよくわからなかった)
この2つの型ですが、API的にはString
と同じようなものを用意するようです。違う点で言うならばUnicodeScalar
型の引数のあるAPIも用意する予定の点。前述の通りUnicodeScalar
はUTF-16, UTF-8を表せる型なので既存のString
にもメソッドが追加されるのではという期待もできるのですが、どうなんでしょうかね。
おわりに
今回見てきたIssueですが、2つとも6月に立てられてからあまり話が進んでないようでした。Corefxlabには実際にutf8string
というブランチがあるのですがこちらも6月ぐらいからコミットがないようです。
最初に載せた自分の作業リポジトリもCorefxlabの動向次第な面があるので放置せざるを得ないのですが、UTF-16の呪縛から逃げれるかと思うと非常に楽しみなのでUtf8String
が待ち遠しいです。