たごもりすメモ

コードとかその他の話とか。

msgpack-inspect を作った

MessagePackJSONぽいけどバイナリでデータサイズが小さく抑えられ、またシリアライズ/デシリアライズが比較的高速であるとして広く使われておるところであります。なんか #linedevday にいるせいで口調がおかしいな。

が、バイナリなせいでデータを作ったあとその内容が正しいかどうか確認するのがいまいち面倒くさく、いちいちunpackするスクリプトを書いて中身を見る必要がある。JSONみたいに目で見て思った通りの表現になっているかどうかを判別するのは普通の人間にはなかなか難しい*1
このため開発時にデータがバグってるのかコードがバグってるのかが分かりづらく、MessagePackを利用したアプリケーションおよびサービスを開発する上で問題になっていた。

ので、MessagePackのバイナリデータを食わせると内容を分かりやすくダンプしてくれるツールを作った! のです!

msgpack-inspect

github.com

Rubyで書いてあるがmrubyでクロスコンパイルしてあり、releaseページから各プラットフォーム向けのバイナリがダウンロードできる、ので、ダウンロードして展開すればすぐ使える。あとrubygems.orgにもリリースしてあって、CRuby/JRubyがあればその上に gem install msgpack-inspect でインストールもできる。

これはどういうものかというと、msgpackのバイナリが書かれているファイルを引数にして起動すると、以下のように(デフォルトではyamlで)各バイナリオブジェクトの format(型情報)、ヘッダ部の16進ダンプ、データ部の16進ダンプ、(存在する場合は)長さやtypeなどを表示し、その上でunpackした結果のデータも表示してくれる。

$ msgpack-inspect msgpack-example.bin 
---
- format: "false"
  header: "0xc2"
  data: "0xc2"
  value: false
- format: "true"
  header: "0xc3"
  data: "0xc3"
  value: true
- format: "nil"
  header: "0xc0"
  data: "0xc0"
  value: null
- format: "fixint"
  header: "0x01"
  data: "0x01"
  value: 1
- format: "uint8"
  header: "0xcc"
  data: "0xff"
  value: 255
- format: "fixarray"
  header: "0x93"
  length: 3
  children:
    - format: "fixstr"
      header: "0xa1"
      length: 1
      data: "0x61"
      value: "a"
    - format: "fixstr"
      header: "0xa1"
      length: 1
      data: "0x62"
      value: "b"
    - format: "fixstr"
      header: "0xa1"
      length: 1
      data: "0x63"
      value: "c"
- format: "fixmap"
  header: "0x83"
  length: 3
  children:
    - key:
        format: "fixstr"
        header: "0xa4"
        length: 4
        data: "0x686f6765"
        value: "hoge"
      value:
        format: "fixint"
        header: "0x6f"
        data: "0x6f"
        value: 111
    - key:
        format: "fixstr"
        header: "0xa3"
        length: 3
        data: "0x706f73"
        value: "pos"
      value:
        format: "fixarray"
        header: "0x93"
        length: 3
        children:
          - format: "fixstr"
            header: "0xa1"
            length: 1
            data: "0x78"
            value: "x"
          - format: "fixstr"
            header: "0xa1"
            length: 1
            data: "0x79"
            value: "y"
          - format: "fixstr"
            header: "0xa1"
            length: 1
            data: "0x7a"
            value: "z"
    - key:
        format: "fixstr"
        header: "0xa4"
        length: 4
        data: "0x7a65726f"
        value: "zero"
      value:
        format: "fixarray"
        header: "0x90"
        length: 0
        children: []

もちろん map や array などは子要素も再帰的に解析してダンプしてくれるという超絶便利なもので、これがあれば開発が進みまくることは間違いない。
なおダンプの出力フォーマットはオプションで指定でき、現状では yaml, json および jsonl *2 をサポートしていたり、引数としてハイフンを与えることで標準入力からの読み込みができたりする。

$ msgpack-inspect -h
Usage: msgpack-inspect [options] FILE"

Options:
  -f, --format FORMAT   Output format of inspection result (yaml/json/jsonl) [default: yaml]
  -r, --require LIB     (Not supported in binary executable)
  -h, --help            Show this message
  -v, --version         Show version of this software

CRuby/JRuby 実装に限り、オブジェクトのunpack時に ext type 定義が書かれているファイルを読み込んでそれを使うこともできる。MessagePack::DefaultFactory に register_type しておく必要があるけど。これで ext type を使っているデータ表現でもテストはばっちり!

ということで、大変便利なものを作った気がします。みなさんぜひどうぞ。

*1:……が、じつは訓練されるとだんだん分かるようになってくる、んだけども

*2:トップレベルオブジェクトひとつごとに1行のJSON表現で出力