Protocol Buffer のシリアライゼーションを理解する
Published: 2023/8/10
Protocol Buffer とは、 Google が定義した RPC のための、メッセージ serialization 方式ないしその記述言語。
wire 形式
message := (tag value)*
を基本形式とする。
Encoding
This topic explains how Protocol Buffers encodes data to files or to the wire.
protobuf.dev
上記の疑似BNF記法にて、基本的な serialize 方式は割とすべてまとまっている。 具体的な各フィールドの処理については、例えば以下の記事が参考になる。
つくって学ぶ Protocol Buffers エンコーディング
この記事は、Merpay Tech Openness Month 2021 の 15 日目の記事です。こんにちは。メルペイのバックエンドエンジニアの @ktr です。メルペイではマイクロサービスアーキテクチャを採用しており、それぞれのマイク
engineering.mercari.com

repeated と map
repeated
: あるメッセージ中に、同じフィールド番号のエントリーが複数回表れるとき、それを配列化して deserialize する。
逆にシリアライズの際には、各要素を同じフィールド番号で繰り返しメッセージのエントリーとしてエンコードされていく。
packed repeated という repeated のエンコーディング方式もあり、この場合 LEN
形式のシリアライズが行われ、中身は 0 個以上の pack 対象数値データの binary representation となる。
数値データは、フィールドの型さえ決まれば、エンコード方式が varint, i32, i64 のどの形式であっても、どこまでが一つの要素に対応しているのかが、バイナリデータから直接判定することができるため、 packed が可能となっている。
逆にそういった性質がない数値系以外は packed repeated はできない。
map
: 以下の syntax sugar;
message Hoge {
message Entry {
optional 型 key = 1;
optional 型 value = 2;
}
repeated Entry map;
}
https://protobuf.dev/programming-guides/encoding/#maps
optional と oneofs, おまけに empty message
各 descriptor (各フィールドをどのように シリアライズ・デシリアライズするか)に対して、 no presense と explicit presense という概念がある。
no presense のフィールドは、もしそのフィールドの値が、型のデフォルト値であった場合には、メッセージをシリアライズする際に、そのフィールド自体を省くことで実現される。
explicit presence のフィールドである場合には、例えデフォルト値であったとしても、 tag value
としてシリアライズに含まれることになる。
この概念についてまとめて記述された資料が以下。
https://github.com/protocolbuffers/protobuf/blob/main/docs/field_presence.md
optional
をフィールドに付与することで、対象のフィールドが explicit presence 方式であることを強制できる。
意味論的には、そのフィールドは nullable である、といった意味合いになる。
oneofs
は、そもそも何のフィールドが利用されたのかを表現しなければならないため、その中に指定された個別のフィールドは強制的に explicit presence となる。
意味論的には、「指定されたフィールドたちのうち、高々一つが存在しうる」であって、存在しない場合もありうるため、 optional 的な、そもそもこの oneofs というグループの値、それ自体が何かしら存在していかどうかも、クエリできるようなコードが生成される。
Protocol Buffersのoneofを使う【Go + gRPC】 - Qiita
Protocol Buffersのoneofを使って実装しようとしたときに微妙に悩んだので、使用方法を備忘録として残しておきます。Protocol Buffers、Go、gRpc、どれも触り始めて…
qiita.com

また、 optional でない message は、原理的に no presence でいけるような気がするが、このセクションの最初に共有した field presence の資料によれば、この場合においても explicit presence となる模様。 つまり、 optional じゃないような submessage フィールドがあって、そこにすべてがデフォルト値となるような submessage 値を代入していたとしても、最終的には 0 byte な LEN として親の message ではシリアライズされる、模様。