MessagePack のエンコーディングフォーマット 概要編

MessagePack 互換実装を作るためには、どうあれ MessagePack のエンコーディングがどのようなフォーマットなのか調べる必要がある。
というわけで、まずはフォーマットの概略をまとめてみた。

長さの扱い

MessagePack のエンコード方式では、エンコード結果の長さが一定の型と、長さが不定な型とで扱いがおおむね以下のように分かれている。

  • 長さが一定な型: 上位ビットがエンコード種別、残りのビットが値
  • 長さが不定な型: 最初のバイトがエンコード種別で残りビットが長さビット、後続バイトが値
    • 長さが長すぎる場合、2 byte 目以降が長さ情報に占有され、データ本体がもっと後になることもある。

ここで、「エンコード種別」とは、たとえば「値が 0 以上 128 未満の整数型」「長さが *** 以下の Array 型」といったものである。エンコード種別は、MessagePack にとっての型情報(整数, 真偽値, ...)だけでなく、さらにエンコードするに当たっての区別が行われたものなのである。

エンコード種別一覧

下に、エンコード種別の情報をまとめてみた。MessagePack のエンコードにおいては、それぞれの値が以下のエンコード種別のどれかに振り分けられ、それに応じてエンコード結果の上位ビットの値が決定される。

下の表を作るに当たっては、msgpack レポジトリ内の msgpack/pack_template.h にある pack 実装を参照した。MessagePack では、エンコードするためのビット操作は pack/unpack_template.h でほとんど実装されており、c や cpp ディレクトリ以下のソースコードはそのラッパーに過ぎないようだ。

先頭 byte 先頭 bit エンコード種別の通り名*1 値か長さの条件 エンコード後のバイト数
0xd3 1101 0011 signed 64 負の整数型 [-264, -231) 9
0xd2 1101 0010 signed 32 負の整数型 [-231, -215) 5
0xd1 1101 0001 signed 16 負の整数型 [-215, -27) 3
0xd0 1101 0000 signed 8 負の整数型 [-27, -25) 2
0xe0 - 0xff 111 fixnum 負の整数型 [-25, 0) 1
0x00 - 0x7f 0 fixnum 正の整数型 [0, 27) 1
0xcc 1101 0000 unsigned 8 正の整数型 [27, 28) 2
0xcd 1101 0001 unsigned 16 正の整数型 [28, 216) 3
0xce 1101 0010 unsigned 32 正の整数型 [216, 232) 5
0xcf 1101 0011 unsigned 64 正の整数型 [216, 264) 9
0xca 1100 1110 float? 浮動小数 N/A *2 5
0xcb 1100 1111 double? 浮動小数 N/A *3 9
0xc0 1100 0000 nil nil nil のみ 1
0xc3 1100 0011 true bool 型 true のみ 1
0xc2 1100 0010 false bool 型 false のみ 1
0xa0 - 0xaf 1010 Raw? Raw 型 [0, 16) 1 + バイト列
0xda 1101 1010 Raw? Raw 型 [16, 216) 3 + バイト列
0xdb 1101 1011 Raw? Raw 型 [216, 232) 5 + バイト列
0x90 - 0x9f 1001 Array? Array 型 [0, 16) 1 + 要素たち
0xdc 1101 1100 Array? Array 型 [16, 216) 3 + 要素たち
0xdd 1101 1101 Array? Array 型 [216, 232) 5 + 要素たち
0x80 - 0x8f 1000 Map? Map 型 [0, 16) 1 + 要素たち
0xde 1101 1110 Map? Map 型 [16, 216) 3 + 要素たち
0xdf 1101 1111 Map? Map 型 [216, 232) 5 + 要素たち

なお、fixnum やその前後のエンコード種別を表す bit がどのようにして決まっているかは、以下の表を見るとわかりやすい*4

hex bin note
-2^5 -1 1101 1111 signed 8 の最大値
-2^5 1110 0000 fixnum の最小値
-1 1111 1111 負の fixnum の一部
0 0000 0000 正の*5 fixnum の一部
1 0000 0001 正の fixnum の一部
2^7 -1 0111 1111 fixnum の最大値
2^7 1000 0000 unsigned 8 の最小値

なお、「値か長さの条件」とは、コンテナ型や raw 型においては長さが、それ以外の型においては値そのものが満たすべき条件、という意味合いである。また、「値か長さの条件」における表記は、たとえば [0, 128) であれば 0以上128未満 という意味合いである。

機会があれば、今後のエントリで上のエンコード種別ごとの具体的なフォーマットを述べようかと思う。

*1:MessagePack のコメントに書かれている名前

*2:C/C++ の float を int32_t にキャストしているだけなので、環境依存

*3:float に同じく

*4:バイナリアンな人ならこういう表が無くても一瞬で分かるのだろうなぁ・・・orz

*5:数学的な意味での正数かどうかは疑問だけど