ネコのために鐘は鳴る

寺院に住み着くパソコ〇好き

(C#) エンディアン固定でシリアライズ

今回のオチ

  • System.Buffers.Binary.BinaryPrimitives クラスを使いましょう

エンディアンを固定でシリアライズしたい

longの値をbyte[]にしたい時、普通はSystem.BitConverter.GetBytes(long)で困らないんですが、BitConverterエンディアンは固定じゃないんですよね。ランタイム依存です。普通はリトルエンディアンなんですが、一部 mono 環境なんかでビッグエンディアンな環境があるらしいんですよ、奥さん。

実行時にリトルエンディアン環境なのか、ビッグエンディアン環境なのかはBitConverter.IsLittleEndianプロパティで確認できます。ついでにこのプロパティは Intrinsic なので JIT 定数になり、if文を書くと JIT 時に分岐が消えます。愛してる定数。

たとえばファイルフォーマットなんかでエンディアンが決まってるもの扱う時に、BitConverterを使うと厳密にはランタイム依存で正しく動きません。雑に動けばいいならばBitConverterでいいんです、だって普通はリトルエンディアンだもん。

逆に TCP/IP なんかはビッグエンディアン固定です。(まあここは普通ライブラリ使うので直に書くことはないですが。)

 

長々と書いて何が言いたいのかというと、System.Buffers.Binary.BinaryPrimitivesを使うと、エンディアン固定で直列化できますよという話です。使い方はメソッド名見れば一目瞭然なんですが、例えばlongをリトルエンディアンでSpan<byte>にする時は

long value = 100L;
Span<byte> buf = stackalloc byte[8];
BinaryPrimitives.WriteInt64LittleEndian(buf, value);

です。ビッグエンディアン用のメソッドもちゃんとあります。

1つだけ残念なことは、BinaryPrimitivesクラスは .NET Standard 2.1, .NET Core 2.1 以降でしか使えないことです。 .NET Framework にはありません。だって Windows はリトルエンディアンだもん (2回目)。