voile.bindat

Binary data serializer/deserializer
enum Endian littleEndian;
リトルエンディアンのデータに付与するUDA
enum Endian bigEndian;
ビッグエンディアンのデータに付与するUDA
enum Endian systemEndian;
システムのエンディアンがリトルかビッグかで切り替えが行われるUDA
template preLength(T)
配列の長さがバイナリの前方に現れる場合のUDA
enum auto arrayLength(string pred);
ArrayLength arrayLength()(string pred);
配列の長さを計算によって行うUDA
enum bool isInputBinary(InputRange);
入力対象のバイナリデータ型
enum bool isOutputBinary(OutputRange);
出力対象のバイナリデータ型
template isBasicArray(T)
基本型要素をもつ配列
template hasBinaryConvertionProxy(T)
template hasBinaryConvertionProxy(T, string memberName)
Proxy
  1. 以下の関数を持つ
    • void writeBinary(InputRange)(ref InputRange) const;
    • void readBinary(OutputRange)(ref OutputRange);
  2. 【未実装】以下の関数を持つ
    • size_t binaryLength() const @property;
    • immutable(ubyte)[] toBinary() const;
    • void fromBinary(in byte[]);
  3. 【未実装】型の宣言に以下のUDAを持つ
    • @convBy!Proxy
      • Proxy: void to(OutputRange)(in T src, ref OutputRange r);
      • Proxy: void from(InputRange)(ref InputRange r, ref T dst);
  4. メンバの宣言に以下のUDAを持つ
    • @convBy!Proxy
      • Proxy type3: void to(OutputRange)(in T src, ref OutputRange);
      • Proxy type3: void from(InputRange)(ref InputRange, ref T dst);
  5. 【未実装】メンバの宣言に以下のUDAを持つ
    • @preLength!Size
    • @convBy!Proxy : immutable(ubyte)[]
  6. 【未実装】メンバの宣言に以下のUDAを持つ
    • @arrayLength!"..."
    • @convBy!Proxy : immutable(ubyte)[]
@trusted void serializeToBinDat(Endian endian, OutputRange, T)(ref OutputRange r, in T data)
if(isOutputBinary!OutputRange && isBasicType!T);
void serializeToBinDat(Endian endian, OutputRange, T : ubyte)(ref OutputRange r, in T data)
if(isOutputBinary!OutputRange && isBasicType!T);
@trusted void serializeToBinDat(OutputRange, T)(ref OutputRange r, in T data)
if(isOutputBinary!OutputRange && isBasicType!T);
void serializeToBinDat(OutputRange, T : ubyte)(ref OutputRange r, in T data)
if(isOutputBinary!OutputRange && isBasicType!T);
void serializeToBinDat(Endian endian, OutputRange, T)(ref OutputRange r, in T data)
if(isOutputBinary!OutputRange && isBasicArray!T);
void serializeToBinDat(OutputRange, T)(ref OutputRange r, in T data)
if(isOutputBinary!OutputRange && isArray!T);
void serializeToBinDat(OutputRange, T)(ref OutputRange r, in T data)
if(isOutputBinary!OutputRange && is(T == struct));
void serializeToBinDat(OutputRange, T : Endata!U, U)(ref OutputRange r, in T data)
if(isOutputBinary!OutputRange && is(T == struct));
@trusted immutable(ubyte)[] serializeToBinDat(T)(in T data);
@trusted immutable(ubyte)[] serializeToBinDat(Endian endian, T)(in T data)
if(isBasicType!T || isArray!T);
バイナリのシリアライズ
Examples: 基本型と列挙値
ubyte a = 10;
assert(a.serializeToBinDat!littleEndian() == [10]);
uint b = 10;
assert(b.serializeToBinDat!bigEndian() == [0, 0, 0, 10]);

version (LittleEndian)
	assert(b.serializeToBinDat() == [10, 0, 0, 0]);
version (BigEndian)
	assert(b.serializeToBinDat() == [0, 0, 0, 10]);

enum E { a, b }
E e = E.a;
assert(e.serializeToBinDat!littleEndian() == [0, 0, 0, 0]);
e = E.b;
assert(e.serializeToBinDat!littleEndian() == [1, 0, 0, 0]);
assert(e.serializeToBinDat!bigEndian()    == [0, 0, 0, 1]);
Examples: 配列
ubyte[] a = [10, 20];
assert(a.serializeToBinDat!littleEndian() == [10, 20]);
uint[] b = [10, 20];
assert(b.serializeToBinDat!bigEndian() == [0, 0, 0, 10, 0, 0, 0, 20]);

version (LittleEndian)
	assert(b.serializeToBinDat() == [10, 0, 0, 0, 20, 0, 0, 0]);
version (BigEndian)
	assert(b.serializeToBinDat() == [0, 0, 0, 10, 0, 0, 0, 20]);

uint[2] c = [1,2];
assert(c.serializeToBinDat!littleEndian() == [1, 0, 0, 0, 2, 0, 0, 0]);

ubyte[8] d = [1,2,3,4,5,6,7,8];
assert(d.serializeToBinDat() == [1,2,3,4,5,6,7,8]);
Examples: エンディアン変換
struct A
{
	@bigEndian ushort a;
	@littleEndian ushort b;
}
auto a = A(10, 20);
assert(a.serializeToBinDat() == [0, 10, 20, 0]);
Examples: 構造体
struct A
{
	@preLength!ushort @bigEndian ubyte[] data;
}
auto a = A([10, 20]);
assert(a.serializeToBinDat() == [0, 2, 10, 20]);

struct B
{
	@bigEndian uint length;
	@arrayLength!"length" @littleEndian ushort[] data;
}
auto b = B(2, [10, 20]);
assert(b.serializeToBinDat() == [0, 0, 0, 2, 10, 0, 20, 0]);

struct C
{
	A a;
	@bigEndian ushort[4] x;
	B b;
}
auto c = C(A([10, 20]), [1,2,3,4], B(2, [10, 20]));
assert(c.serializeToBinDat() == [
	0, 2, 10, 20,
	0,1, 0,2, 0,3, 0,4,
	0, 0, 0, 2, 10, 0, 20, 0]);
Examples: Endata
@littleEndian enum A: short
{
	@bigEndian    @data!int a,
	@littleEndian @data!short b
}
Endata!A a;
a.a = 10;
assert(a.serializeToBinDat() == [0x00, 0x00, 0x00, 0x00, 0x00, 0x0a]);
a.b = 11;
assert(a.serializeToBinDat() == [0x01, 0x00, 0x0b, 0x00]);
Examples: Proxy
struct A
{
	int a;
	void writeBinary(OutputRange)(ref OutputRange r) const
	if (isOutputBinary!OutputRange)
	{
		put(r, cast(ubyte)(a + 100));
	}
	void readBinary(InputRange)(ref InputRange r)
	if (isInputBinary!InputRange)
	{
		a = r.front - 100;
		r.popFront();
	}
}
static assert(hasBinaryConvertionProxy!A);
A a = A(-10);
assert(a.serializeToBinDat() == [90]);

import std.datetime;
struct B
{
	static struct Proxy
	{
		static void to(OutputRange)(in DateTime d, ref OutputRange r) @trusted
		{
			r.serializeToBinDat!bigEndian(cast(ushort)d.year);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.month);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.day);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.hour);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.minute);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.second);
		}
		static void from(InputRange)(ref InputRange r, ref DateTime d) @trusted
		{
			ushort year;
			ubyte  month;
			ubyte  day;
			ubyte  hour;
			ubyte  minute;
			ubyte  second;
			year.deserializeFromBinDat!bigEndian(r);
			month.deserializeFromBinDat!bigEndian(r);
			day.deserializeFromBinDat!bigEndian(r);
			hour.deserializeFromBinDat!bigEndian(r);
			minute.deserializeFromBinDat!bigEndian(r);
			second.deserializeFromBinDat!bigEndian(r);
			d = DateTime(year, month, day, hour, minute, second);
		}
	}
	@convBy!Proxy DateTime dt;
}
B b = B(DateTime(2020, 12, 25));
assert(b.serializeToBinDat() == [0x07, 0xE4, 12, 25, 0, 0, 0]);
void deserializeFromBinDat(Endian endian, T, InputRange)(ref T dst, ref InputRange r)
if(isInputBinary!InputRange && isBasicType!T);
void deserializeFromBinDat(T, InputRange)(ref T dst, ref InputRange r)
if(isInputBinary!InputRange && isBasicType!T);
void deserializeFromBinDat(Endian endian, T, InputRange)(ref T dst, ref InputRange r)
if(isInputBinary!InputRange && isBasicArray!T);
void deserializeFromBinDat(T, InputRange)(ref T dst, ref InputRange r)
if(isInputBinary!InputRange && isArray!T);
void deserializeFromBinDat(T, InputRange)(ref T dst, ref InputRange r)
if(isInputBinary!InputRange && is(T == struct));
void deserializeFromBinDat(T : Endata!U, InputRange, U)(ref T dst, ref InputRange r)
if(isInputBinary!InputRange && is(T == struct));
void deserializeFromBinDat(Endian endian, T, InputRange)(ref T dst, InputRange r)
if(isInputBinary!InputRange);
void deserializeFromBinDat(T, InputRange)(ref T dst, InputRange r)
if(isInputBinary!InputRange);
T deserializeFromBinDat(T, InputRange)(InputRange r)
if(isInputBinary!InputRange);
バイナリのデシリアライズ
Examples: 基本型と列挙値
//ubyte[] bin(ubyte[] dat...) { return dat; }
template bin(args...) {
	ubyte[args.length] inst = [args[]];
	ubyte[] bin() { return inst[]; }
}
ubyte a;
a.deserializeFromBinDat!littleEndian(bin!(10));
assert(a == 10);

uint b;
b.deserializeFromBinDat!bigEndian(bin!(0, 0, 0, 10));
assert(b == 10);

version (LittleEndian)
	b.deserializeFromBinDat(bin!(10, 0, 0, 0));
version (BigEndian)
	b.deserializeFromBinDat(bin!(0, 0, 0, 10));
assert(b == 10);

enum E { a, b }
E e;
e.deserializeFromBinDat!littleEndian(bin!(0, 0, 0, 0));
assert(e == E.a);
e.deserializeFromBinDat!littleEndian(bin!(1, 0, 0, 0));
assert(e == E.b);
e.deserializeFromBinDat!bigEndian(bin!(0, 0, 0, 1));
assert(e == E.b);
Examples: 配列
template bin(args...) {
	ubyte[args.length] inst = [args[]];
	ubyte[] bin() { return inst[]; }
}
ubyte[] a = new ubyte[2];
a.deserializeFromBinDat!littleEndian(bin!(10, 20));
assert(a == [10, 20]);
uint[] b = new uint[2];
b.deserializeFromBinDat!bigEndian(bin!(0, 0, 0, 10, 0, 0, 0, 20));
assert(b == [10, 20]);

version (LittleEndian)
	b.deserializeFromBinDat(bin!(10, 0, 0, 0, 20, 0, 0, 0));
version (BigEndian)
	b.deserializeFromBinDat(bin!(0, 0, 0, 10, 0, 0, 0, 20));
assert(b == [10, 20]);

uint[2] c;
c.deserializeFromBinDat!littleEndian(bin!(1, 0, 0, 0, 2, 0, 0, 0));
assert(c == [1, 2]);

ubyte[8] d;
d.deserializeFromBinDat(bin!(1, 0, 0, 0, 2, 0, 0, 0));
assert(d == [1, 0, 0, 0, 2, 0, 0, 0]);
Examples: エンディアン変換
template bin(args...) {
	ubyte[args.length] inst = [args[]];
	ubyte[] bin() { return inst[]; }
}
struct A
{
	@bigEndian ushort a;
	@littleEndian ushort b;
}
A a;
a.deserializeFromBinDat(bin!(0, 10, 20, 0));
assert(a == A(10, 20));
Examples: 構造体
template bin(args...) {
	ubyte[args.length] inst = [args[]];
	ubyte[] bin() { return inst[]; }
}
struct A
{
	@preLength!ushort @bigEndian ubyte[] data;
}
A a;
a.deserializeFromBinDat(bin!(0, 2, 10, 20));
assert (a == A([10, 20]));

struct B
{
	@bigEndian uint length;
	@arrayLength!"length" @littleEndian ushort[] data;
}
B b;
b.deserializeFromBinDat(bin!(0, 0, 0, 2, 10, 0, 20, 0));
assert(b == B(2, [10, 20]));

struct C
{
	A a;
	@bigEndian ushort[4] x;
	B b;
}
C c;
c.deserializeFromBinDat(bin!(
	0, 2, 10, 20,
	0,1, 0,2, 0,3, 0,4,
	0, 0, 0, 2, 10, 0, 20, 0));
assert(c == C(A([10, 20]), [1,2,3,4], B(2, [10, 20])));
Examples: Endata
template bin(args...) {
	ubyte[args.length] inst = [args[]];
	ubyte[] bin() { return inst[]; }
}
@littleEndian enum A: short
{
	@bigEndian    @data!int a,
	@littleEndian @data!short b,
	c
}
Endata!A a;
a.deserializeFromBinDat(bin!(0x00, 0x00, 0x00, 0x00, 0x00, 0x0a));
Endata!A tmp;
tmp.a = 10;
assert(a == tmp);

a.deserializeFromBinDat(bin!(0x01, 0x00, 0x0b, 0x00));
tmp.b = 11;
assert(a == tmp);

a.deserializeFromBinDat(bin!(0x02, 0x00));
tmp.initialize!(A.c);
assert(a == tmp);
assert(tmp.serializeToBinDat() == bin!(0x02, 0x00));
Examples: Proxy
template bin(args...) {
	ubyte[args.length] inst = [args[]];
	ubyte[] bin() { return inst[]; }
}
struct A
{
	int a;
	void writeBinary(OutputRange)(ref OutputRange r) const
	if (isOutputBinary!OutputRange)
	{
		put(r, cast(ubyte)(a + 100));
	}
	void readBinary(InputRange)(ref InputRange r)
	if (isInputBinary!InputRange)
	{
		a = r.front - 100;
		r.popFront();
	}
}
A a;
a.deserializeFromBinDat(bin!(90));
assert(a == A(-10));

import std.datetime;
struct B
{
	static struct Proxy
	{
		static void to(OutputRange)(in DateTime d, ref OutputRange r) @trusted
		{
			r.serializeToBinDat!bigEndian(cast(ushort)d.year);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.month);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.day);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.hour);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.minute);
			r.serializeToBinDat!bigEndian(cast(ubyte)d.second);
		}
		static void from(InputRange)(ref InputRange r, ref DateTime d) @trusted
		{
			ushort year;
			ubyte  month;
			ubyte  day;
			ubyte  hour;
			ubyte  minute;
			ubyte  second;
			year.deserializeFromBinDat!bigEndian(r);
			month.deserializeFromBinDat!bigEndian(r);
			day.deserializeFromBinDat!bigEndian(r);
			hour.deserializeFromBinDat!bigEndian(r);
			minute.deserializeFromBinDat!bigEndian(r);
			second.deserializeFromBinDat!bigEndian(r);
			d = DateTime(year, month, day, hour, minute, second);
		}
	}
	@convBy!Proxy DateTime dt;
}
B b;
b.deserializeFromBinDat(bin!(0x07, 0xE4, 12, 25, 0, 0, 0));
assert(b == B(DateTime(2020, 12, 25)));
Examples: バイナリの解析
  • [0-2] タグタイプ
    • if [0-2]が 0x0001
      • [2-4] 長さ
      • [4-X] ペイロード
    • if [0-2]が 0x0002
      • [2] コマンド種別
        • if [2] が 0x01
          • [3-5] 付与データ
        • if [2] が 0x02
          • (なし)
        • if [2] が 0x03
          • [3.0] フラグ1
          • [3.1-3.3] フラグ2
          • [3.3-3.6] reserved
          • [3.7] フラグ8
import voile.munion;
struct TypeAPayload
{
	@bigEndian @preLength!ushort ubyte[] payload;
}
struct TypeBPayload
{
	struct CommandAPayload
	{
		@bigEndian ushort payload;
	}
	struct CommandCPayload
	{
		ubyte flag1;
		ubyte flag2;
		ubyte flag7;
	}
	enum CommandType: ubyte
	{
		@data!CommandAPayload
		a = 0x0001,
		b = 0x0002,
		@data!CommandCPayload
		c = 0x0003,
	}
	@bigEndian Endata!CommandType commandType;
}
@bigEndian enum TagType: ushort
{
	@data!TypeAPayload
	a = 0x0001,
	@data!TypeBPayload
	b = 0x0002,
}
Endata!TagType bindat;

static immutable ubyte[] binary1 = [
	0x00, 0x01, // [0-2] タグタイプ
	0x00, 0x04, // [2-4] 長さ
	0x00, 0x01, 0x02, 0x03 // [4-X] ペイロード
];
bindat.deserializeFromBinDat(binary1[]);
assert(bindat.tag == TagType.a);
assert(bindat.a.payload == [0x00, 0x01, 0x02, 0x03]);

static immutable ubyte[] binary2 = [
	0x00, 0x02, // [0-2] タグタイプ
	0x01, // [2] コマンド種別
	0x00, 0x01 // [3-5] 付与データ
];
bindat.deserializeFromBinDat(binary2[]);
assert(bindat.tag == TagType.b);
assert(bindat.b.commandType.tag == TypeBPayload.CommandType.a);
assert(bindat.b.commandType.a.payload == 0x0001);