バイナリ正規シリアライゼーション(BCS)は、構造化データのためのバイナリエンコーディング形式です。元々はDiemで設計され、Moveの標準シリアライゼーション形式となりました。BCSはシンプルで効率的、決定論的であり、どのプログラミング言語でも実装が容易です。
完全な形式仕様は、BCSリポジトリで入手可能です。
BCSは、256ビットまでの符号なし整数、オプション、ブール値、ユニット(空の値)、固定長および可変長のシーケンス、マップをサポートするバイナリ形式です。このフォーマットは決定論的になるように設計されており、同じデータは常に同じバイトにシリアライズされます。
"BCSは自己記述形式ではありません。そのため、メッセージをデシリアライズするには、事前にメッセージの型とレイアウトを知っている必要があります"(READMEより)
整数はリトルエンディアン形式で格納され、可変長整数は可変長エンコーディング方式を使用してエンコードされます。シーケンスはULEB128としてその長さが前に付けられ、列挙型はバリアントのインデックスの後にデータが格納され、マップはキーと値のペアの順序付きシーケンスとして格納されます。
構造体はフィールドのシーケンスとして扱われ、フィールドは構造体で定義された順序でシリアライズされます。フィールドは、トップレベルのデータと同じルールを使用してシリアライズされます。
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
モジュールは、このプロセスを支援するための様々な関数を提供しています。