バイナリ正規シリアライゼーション

バイナリ正規シリアライゼーション(BCS)は、構造化データのためのバイナリエンコーディング形式です。元々はDiemで設計され、Moveの標準シリアライゼーション形式となりました。BCSはシンプルで効率的、決定論的であり、どのプログラミング言語でも実装が容易です。

完全な形式仕様は、BCSリポジトリで入手可能です。

フォーマット

BCSは、256ビットまでの符号なし整数、オプション、ブール値、ユニット(空の値)、固定長および可変長のシーケンス、マップをサポートするバイナリ形式です。このフォーマットは決定論的になるように設計されており、同じデータは常に同じバイトにシリアライズされます。

"BCSは自己記述形式ではありません。そのため、メッセージをデシリアライズするには、事前にメッセージの型とレイアウトを知っている必要があります"(READMEより)

整数はリトルエンディアン形式で格納され、可変長整数は可変長エンコーディング方式を使用してエンコードされます。シーケンスはULEB128としてその長さが前に付けられ、列挙型はバリアントのインデックスの後にデータが格納され、マップはキーと値のペアの順序付きシーケンスとして格納されます。

構造体はフィールドのシーケンスとして扱われ、フィールドは構造体で定義された順序でシリアライズされます。フィールドは、トップレベルのデータと同じルールを使用してシリアライズされます。

BCSの使用

Sui フレームワークには、データのエンコードとデコードのためのsui::bcsモジュールが含まれています。エンコード関数はVMにネイティブであり、デコード関数はMoveで実装されています。

エンコーディング

データをエンコードするには、データ参照をバイトベクトルに変換するbcs::to_bytes関数を使用します。この関数は、構造体を含むあらゆる型のエンコードをサポートしています。

// ファイル: [move-stdlib/sources/bcs.move](<https://github.com/MystenLabs/sui/blob/main/external-crates/move/crates/move-stdlib/sources/bcs.move>)
public native fun to_bytes<T>(t: &T): vector<u8>;

以下の例は、BCSを使用して構造体をエンコードする方法を示しています。to_bytes関数は任意の値を取り、バイトのベクトルとしてエンコードできます。

use sui::bcs;

// 0x01 - 値1の単一バイト(falseの場合は0)
let bool_bytes = bcs::to_bytes(&true);
// 0x2a - 単一バイトのみ
let u8_bytes = bcs::to_bytes(&42u8);
// 0x2a00000000000000 - 8バイト
let u64_bytes = bcs::to_bytes(&42u64);
// アドレスは32バイトの固定シーケンス
// 0x0000000000000000000000000000000000000000000000000000000000000002
let addr = bcs::to_bytes(&@sui);

構造体のエンコーディング

構造体は単純な型と同様にエンコードされます。以下はBCSを使用して構造体をエンコードする方法です:

let data = CustomData {
    num: 42,
    string: b"hello, world!".to_string(),
    value: true
};

let struct_bytes = bcs::to_bytes(&data);

let mut custom_bytes = vector[];
custom_bytes.append(bcs::to_bytes(&42u8));
custom_bytes.append(bcs::to_bytes(&b"hello, world!".to_string()));
custom_bytes.append(bcs::to_bytes(&true));

// 構造体は単なるフィールドのシーケンスなので、バイトは同じはずです!
assert!(&struct_bytes == &custom_bytes, 0);

デコーディング

BCSは自己記述ではなく、Moveは静的型付けであるため、デコードには事前にデータ型の知識が必要です。sui::bcsモジュールは、このプロセスを支援するための様々な関数を提供しています。