protobufのバイナリを眺めてみる

# protocol buffers のバイナリを眺めてみる
* [protocol buffers](https://developers.google.com/protocol-buffers) を利用することで色々いい感じになります
* 複雑なことの必要がなく、簡単に利用することができます
* ですが、高速なシリアライゼーションの恩恵を受ける上で、protobuf のバイナリを1度眺めておくことで、効率的なモデルの設計ができるようになるかもしれません
* こちらの資料を参考にしています  
  https://developers.google.com/protocol-buffers/docs/encoding
* [swift-protobuf](https://github.com/apple/swift-protobuf) を使っています
* 検証に利用したコードはこちら

https://gist.github.com/fuziki/20997cb680c2c1869158beb23770d652

# protocol buffers の使い方

* proto ファイルでスキーマを定義します

```protobuf
syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
```

* protoc を使って、swift のコードを作成します

```
protoc --swift_out=generated protos/*.proto 
```

* swift のコードは指定した構造の struct がされます

```swift
struct SearchRequest {
  // SwiftProtobuf.Message conformance is added in an extension below. See the
  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
  // methods supported on all messages.
  var query: String = String()
  var pageNumber: Int32 = 0
  var resultPerPage: Int32 = 0
  var unknownFields = SwiftProtobuf.UnknownStorage()
  init() {}
}
```

* この構造体を利用して、protobuf のバイナリを取得できます

```swift
var request = SearchRequest()
request.pageNumber = 1
let data = try request.serializedData()
```

# protobuf のバイナリを眺めてみる
* まずは最も簡単なスキーマとそれに対応するバイナリを見てみます
* id が 1 の int64 型の `number` のプロパティを持つ Number1Entity です

```protobuf
message Number1Entity {
    int64 number = 1;
}
```

* protoc を使って swift の構造体を作成し、`number` に `10` を設定してバイナリを生成します

```swift
var entity = Number1Entity()
entity.number = 10
let data: Data = try entity.serializedData()
```

* バイナリは `080a` となりました
* 2進数表記だと、`00001000 00001010` です
* これをビッドごとの意味で色付けをしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/955b7051-3963-cdb4-5a80-0e0ea9e108b6.png" width="400">

* それぞれのパラメータについて見ていきます

## Type
* 1バイト目の下位3ビットには、Type が格納されています
* 上の画像のオレンジ部分です
* Type は、0, 1, 2, 5 の4種類の可能性があります

|Type	|Meaning	|Used For|
| ---- | ---- | ---- |
|0	|Varint	|int32, int64, uint32, uint64, sint32, sint64, bool, enum|
|1	|64-bit	|fixed64, sfixed64, double|
|2	|Length-delimited	|string, bytes, embedded messages, packed repeated fields|
|5	|32-bit	|fixed32, sfixed32, float|

* Type によってこの後の処理が変わります
* `080a` の例では、Type は 0 なので、`Varint` です
* 確かにスキーマで int64 と定義しています

## ID
* 1バイト目の2〜5ビットまでの4ビットに id が入っています
* 上の画像の緑部分です
* 緑の部分は `0001` なので、id は 1 となります
* 確かにスキーマで、1と定義しています

## Verint
* 2バイト目には、データの本体が入っています
* type が Verint なので、最上位ビットは further (後述)のフラグなので、下位7ビットにデータが入っています
* 上の画像では紫の部分です
* `0001010` = 10 なので代入した通りです

## Verintの可変長数値表現
* 先ほどまで見ていた構造体では、id が4ビット、データ本体が7ビットで表現されています
* 当然15より大きいidを使いたいですし、127より大きいデータを使いたいです
* そこで、protobufでは最上位ビットを使って、可変長数値表現を実現しています
* 今度は、number に127より大きい数字である99999を代入してバイナリを生成してみます

```swift
var entity = Number1Entity()
entity.number = 99999
let data: Data = try entity.serializedData()
```

* バイナリは、`089f8d06` となりました
* これを色分けしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/26f338fb-fac2-ff5a-b922-c443e8f4ef44.png" width="500">

* 1バイト目は変化がないですが、2バイト目以降のデータが変わっています
* 2バイト目の最上位ビットを見ると1になっており、次のバイトまでデータが繋がっていることがわかります
* 3バイト目の最上位ビットも立っているので、次のバイトまで繋がっています
* 4バイト目の最上位ビットは立っていないので、このバイトで終わりであることが分かります
* 2~4バイトのそれぞれ下位7ビットを、リトルエンディアンなので繋げます
* 結果は、`000011000011010011111` = 99999 で代入した値です
* このことから、varintを利用する場合、7bitごとの数値が効率が良いことが分かります

## idの可変長数値表現

* 次に、id が99999の場合も見てみます

```protobuf
message Number99999Entity {
    int64 number99999 = 99999;
}
```

* こちらのスキーマを使った構造体の `number99999` に 10を代入してバイナリを生成します

```swift
var entity = Number99999Entity()
entity.number99999 = 10
let data: Data = try entity.serializedData()
```

* 結果は `f8e9300a` でした
* 色分けしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/7584ad61-d915-2f68-75c3-1a484b00cbde.png" width="500">

* id も可変長数値表現が使えます
* これも緑の部分をリトルエンディアンで並び替えると、`000011000011010011111` = 99999 となるので、スキーマで設定した通りになっていることが分かります
* このことから、id は 4, 11, 18...ビットが効率良いことが分かります

## 64-bit, 32-bit
* 次に、固定64bit, 32bitを見てみます
* double, float, fixed64 などの型が含まれます

```protobuf
message Fixed64Entity {
    fixed64 number = 1;
}
```

* fixed64 型の number プロパティに10を代入してバイナリを取得します

```swift
var entity = Fixed64Entity()
entity.number = 10
let data: Data = try entity.serializedData()
```

* 結果は、`0x090a00000000000000` でした
* 色分けしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/ed477e84-013f-4f67-d2da-b7123bfa62cc.png" width="500">

* 可変長数値表現ではなく、固定長で8バイトのデータになります
* このことから、値がとても大きい場合や、負の数の頻度が高い場合は、固定長の方が有利であることが分かります

## Length-delimited (文字列)
* Length-delimited では文字列などを格納できます
* まずは文字列を見てみます

```protobuf
message Text2Entity {
    string text = 2;
}
```

* 文字列に "abc" を代入してバイナリを生成します

```swift
var entity = Text2Entity()
entity.text = "abc"
let data: Data = try entity.serializedData()
```

* 結果は `1203616263` でした(16進数です)
* 色分けしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/811d3723-6b85-d74c-903e-919f325af681.png" width="500">

* 今回の type は、2 なのでLength-delimited です(オレンジ部分)
* id も 2 であることが分かります(緑部分)
* Length-delimited の場合は、データ部分の最初に可変長数値表現でサイズ情報が入っています
* 今回は、`0000011` なので3バイトです(青部分)
* 後ろの3バイトが文字列データです(紫部分)
* `0x61` = 'a' なので、設定した文字列になっていることが分かります

## 2つのプロパティ
* これまではプロパティが1つの場合を見てきました
* 今回は、数値と文字列の2のプロパティを持つ message のバイナリを見ていきます

```protobuf
message TwoPropertyEntity {
    int64 number = 1;
    string text = 2;
}
```

* number = 10, text = "abc" でバイナリを生成します

```swift
var entity = TwoPropertyEntity()
entity.number = 10
entity.text = "abc"
let data: Data = try entity.serializedData()
```

* 結果は、`080a1203616263` でした
* `080a` と `1203616263` に分割でき、これまで見た値と同値になっています
* `080a` は id が 1 の値が 10 のバイナリです
* `1203616263` は id が 2 の値が "abc" のバイナリです
* 色付けしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/230e52cd-f21d-ee5e-c835-118a8518a00d.png" width="500">

* protobuf はバイナリをくっ付けることで、2つのメッセージをマージできるようです

## Oneof
* protobuf には [oneof](https://developers.google.com/protocol-buffers/docs/proto3#oneof) という機能があります
* oneof の中の1つのプロパティが設定できます

```protobuf
message OneOfEntity {
    int64 number = 1;
    oneof one {
        string ofText = 2;
        int64 ofNumber = 3;
    }
}
```

* oneof のプロパティに ofNumber を設定してバイナリを生成しました

```swift
var entity = OneOfEntity()
entity.number = 10
entity.one = .ofNumber(44)
let data: Data = try entity.serializedData()
```

* 結果は、`080a182c` でした
* これを色付けしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/9f51458e-6060-867e-c28f-595e3ad163b3.png" width="400">

* oneof は定義したうちの1つが設定されるだけで特にネストなどされず、通常のプロパティと同じ感じです

## 配列

* int64 の配列を見てみます

```protobuf
message RepeatedNumberEntity {
    repeated int64 numbers = 1;
}
```

* 配列には、10と130を設定します

```swift
var entity = RepeatedNumberEntity()
entity.numbers = [10, 130]
let data: Data = try entity.serializedData()
```

* 結果は、`0a030a8201` でした
* 色付けしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/df3eb930-ca76-7cb1-3a06-c12e0a877ec7.png" width="500">

* repeated なので、type は Length-delimited です
* Length-delimited の body 部分(紫色部分)は varint になっています
* 1バイト目は、`1010` = 10
* 2〜3バイト目は、`10000010` = 130で指定した値になっていることが分かります

## 辞書式

* 辞書式を見ていきます

```protobuf
message MapEntity {
    map<string, string> dictionary = 1;
}
```

* バイナリを生成します

```
var entity = MapEntity()
entity.dictionary = [
    "a": "A",
    "b": "BB"
]
let data: Data = try entity.serializedData()
```

* 結果は、`0a060a01611201410a070a016212024242` でした
* 色付けします

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/a146b79e-fdec-f2b0-9d13-fbfa0e77dfb7.png" width=500">

* id が1の Length-delimited が2つ入っています
* `0a0161120141` の部分に着目します

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/a979d13d-1bae-c630-bd41-9630da9f38a6.png" width="400">

* id が1に "a"、id が2に"A" が入っていることが分かります
* もう1つ目のbody部分にもキーが id 1 に、値が id 2 に入っています
* それぞれのキーと値のペアが同じ構造体が同じ id で入っていると辞書式となるようです

## message のプロパティ

* 1番最初に見た、Number1Entity をプロパティに持つ message です

```protobuf
message ParentEntity {
    Number1Entity child = 1;
}
```

* バイナリを生成します

```swift
var child = Number1Entity()
child.number = 10

var entity = ParentEntity()
entity.child = child

let data: Data = try! entity.serializedData()
```

* 結果は `0a02080a` でした
* 色付けしました

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/1fbc474c-fefa-9fd4-a1c2-8a677f00980c.png" width="400">

* id が1で、typeがLength-delimitedです
* body(紫色)部分は、080aです
* これは、1番最初に見た、idが1の値がvarintの10のバイナリと同じです
* つまり、messageのプロパティのbodyにはそのmessageのバイナリが入っているようです

# まとめ
* さまざまな protobuf のバイナリを生成し、眺めて見ました
* バイナリ自体はかなりシンプルな構造でした
* 実際に、値を変えて動かすと、バイナリとその構造が動いていくのが見えるので、とても楽しかったです