Open3

dotnet/C#でバイナリ操作したい

いぬいぬいぬいぬ

バイナリストリームから定義した構造体で読み取る

Span<byte>MemoryMarshal.Cast<byte, TStruct>と定義したTStructで読み取れる。強い。

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
   public readonly int A;
   public readonly int B;
}

Span<byte> buffer = new []{/*...めっちゃ長いバイナリストリーム...*/};
int start = 1234;
MyStruct a = MemoryMarshal.Cast<byte, MyStruct>(buffer.Slice(start, 2))[0];

https://qiita.com/aka-nse/items/cea3c6f91413c3582b5f#4-それでも僕はバイナリが読みたいんだ

System.Memoryで導入

いぬいぬいぬいぬ

.NET Standard 2.0 でも Span<T> / Memory<T>

.NET Standard 2.0 (古い.NET Framework 4.*系も対応)でも、nugetでSystem.Memory導入することで使える(Slow Span)。Slowと言ってもbyte[]IList<byte>より効率良いらしい。
(C#の配列ではコピーが発生するため)

https://www.nuget.org/packages/System.Memory/

Span<T> / Memory<T> 使い分け

  1. まずはSpan<T>(バイナリ操作なら Span<byte>
  2. 制限に引っかかるのであればMemory<T>
  3. その上で読み込みのみならReadOnlySpan<T> / ReadOnlyMemory<T>に置き換え

変換

https://qiita.com/Funny_Silkie/items/ea21dd1e3d06d48ffdad

https://qiita.com/kenichiuda/items/510a3443181e3602e2eb

Memory<T> -> Span<T> は Spanプロパティでカンタンにできるが、
Span<T> -> Memory<T> が問題。

Memory<byte> m = new Memory<byte>(span.ToArray());

配列を経由するので大量に呼び出す時はメモリに注意。
※特にバイナリ操作だと問題になりがち。

いぬいぬいぬいぬ

サイズが小さい時は stackalloc

Span<byte>を使う際、stackallocを使うとメモリの「スタック領域」が使われ、高速になる(通常は「ヒープ領域」)。

https://it-trend.jp/development_tools/article/32-0041

Span<byte> span = stackalloc byte[100];

容量制限(2GBぐらいらしい?)に注意すれば、Span<T>との組み合わせで安全に使える。
Span<T>登場以前は以前はUnsafeな操作だった。