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];
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#の配列ではコピーが発生するため)
Span<T> / Memory<T> 使い分け
- まずは
Span<T>(バイナリ操作ならSpan<byte>) - 制限に引っかかるのであれば
Memory<T> - その上で読み込みのみなら
ReadOnlySpan<T>/ReadOnlyMemory<T>に置き換え
変換
Memory<T> -> Span<T> は Spanプロパティでカンタンにできるが、
Span<T> -> Memory<T> が問題。
Memory<byte> m = new Memory<byte>(span.ToArray());
配列を経由するので大量に呼び出す時はメモリに注意。
※特にバイナリ操作だと問題になりがち。
サイズが小さい時は stackalloc
Span<byte>を使う際、stackallocを使うとメモリの「スタック領域」が使われ、高速になる(通常は「ヒープ領域」)。
Span<byte> span = stackalloc byte[100];
容量制限(2GBぐらいらしい?)に注意すれば、Span<T>との組み合わせで安全に使える。
※Span<T>登場以前は以前はUnsafeな操作だった。