MessagePackを使ってみる

この記事は、簡単な方法でMessagePackに触れて、あぁこうやってこれだけコンパクトにデータを表現出来るのね、という便利さを体感していただくためのものです。

## MessagePackとは？
MessagePackとは、

- 高速なシリアライズ／デシリアライズ（→オブジェクト⇔バイナリ列の変換）が可能
- データをコンパクトに（→データ長を短く）表現可能

なデータ表現形式の一つです。要するに「MessagePackを使えば、オブジェクトを高速にバイナリ列へ変換できて、しかもデータ長を短く出来ますよ」ということです。
また、特定の言語に依存しない共通形式で様々なデータ型を表現しているため、異なるOS／プログラミング言語／環境におけるシステム間でのデータやりとりに向いていることも特徴の一つです。

より詳細な内容が知りたい場合は、参考[[1]](https://github.com/frsyuki/univ-msgpack-paper/blob/master/furuhashi-master-last-iso-pdfa.pdf?raw=true)または[[2]](http://msgpack.org/)をご覧ください。

## インストール手順（msgpackRを例にして）
拙作の”[msgpackR](http://cran.r-project.org/web/packages/msgpackR/index.html)”を例にして、MessagePackを使えるまでの設定手順を説明します。

※下記1.2.は一回だけ実行すれば良いですが、3.はmsgpackRを利用する際にRを起動する度に必要となります。

### 1. Rのインストール
msgpackRはR言語で書かれており、R言語上で実行することが出来ます。
まずはR実行環境を準備します。

[こちらのページ](http://www.okada.jp.org/RWiki/?R%20%A4%CE%A5%A4%A5%F3%A5%B9%A5%C8%A1%BC%A5%EB)を参考にして、Rのインストールを行ってください。

### 2. msgpackRのインストール
Rを起動して、
install.packages("msgpackR")
と入力します。

```
> install.packages("msgpackR")
 URL 'http://cran.ism.ac.jp/bin/macosx/contrib/3.0/msgpackR_1.1.tgz' を試しています 
Content type 'application/x-gzip' length 19988 bytes (19 Kb)
 開かれた URL 
==================================================
downloaded 19 Kb


 ダウンロードされたパッケージは、以下にあります 
 	/var/folders/ty/2dsz01_j0kgfjhw8zcvfc5p00000gn/T//RtmphshvcU/downloaded_packages 
```


### 3. msgpackRの読み込み
下記コマンドをRで実行します。
もしエラーメッセージが表示される場合は、正常にパッケージがインストールされていない可能性があります。

```
> library(msgpackR)
```

以上で準備は完了です。

## 使用例
###1. 数値の配列
シリアライズにはpack関数を、デシリアライズにはunpack関数を使用します。
例えば数値の配列[1,2,3]をシリアライズする場合、下記コマンドを実行します。

```
> pack(c(1,2,3))
[1] 93 01 02 03
```

分かりづらいですが、4バイトのバイナリ列が表示されています。
それぞれのバイトの意味ですが、[MessagePackの表現形式の仕様](http://wiki.msgpack.org/display/MSGPACK/Format+specification#Formatspecification-TypeChart)を見ると、下記の通りであることが分かります。

- 0x93：長さ3の配列を表す
- 0x01：数値1を表す
- 0x02：数値2を表す
- 0x03：数値3を表す

※c()はベクトルを作成する関数であり、引数で与えられた値で配列を作るものだと思ってください

MessagePackはコンパクトさが売りだと説明しましたが、データ量は以下の通りです。
　前：7バイト（[1,2,3]）
　後：4バイト
そのまま配列で表現すると7バイトですが、MessagePackでは4バイトで表現可能です。


###2. 文字列

```
> pack("sample")
[1] a6 73 61 6d 70 6c 65
```

データサイズ
　前：8バイト（”sample”）
　後：7バイト

0xA6は「長さ6バイトのバイナリデータ」が続くことを表しており、
- 0x73：s
- 0x61：a
- 0x6D：m
- 0x70：p
- 0x6C：l
- 0x65：e
を表します。
もちろん日本語を扱うことも出来ます。

```
> pack("あいうえお")
 [1] af e3 81 82 e3 81 84 e3 81 86 e3 81 88 e3 81 8a
```


###3. 辞書
R言語には辞書型のデータ型はありませんが、msgpackRでは下記のような「ラベル付き変数」を辞書型と判定しています。

```
> sample <- c(TRUE, FALSE)
> names(sample) <- c("compact", "slow")
> sample
compact    slow 
   TRUE   FALSE 
```

つまり、変数sampleはJSONで言うところの{"compact":true, "slow":false}であるとします。
上記の変数をシリアライズすると、下記のようになります。

```
> pack(sample)
 [1] 82 a7 63 6f 6d 70 61 63 74 c3 a4 73 6c 6f 77 c2
```

データサイズ
　前：30バイト（{"compact":true, "slow":false}）
　後：16バイト
約半分のサイズで表現することが可能ですね。

###4. 行列（配列の配列）
R言語は統計解析でよく用いられる言語であることから、行列も頻繁に利用されます。
msgpackRでは行列を「配列の配列」と見なしてシリアライズします。

```
> matrix(c(1,2,3,4,5,6),2,3)
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> pack(matrix(c(1,2,3,4,5,6),2,3))
[1] 92 93 01 03 05 93 02 04 06
```

matrix関数によって、2行3列の行列を作成しています。
それを配列の配列、つまり

- 0x92：長さ2の配列
- 0x93：長さ3の配列

ことから、2行3列の行列を「『長さ3の配列』を要素とする長さ2の配列」と捉えています。

行列にラベルを与えれば、「要素が辞書型のデータである配列」と解釈されてシリアライズされます。

```
> mat <- matrix(c(1,2,3,4,5,6),2,3)
> colnames(mat) <- c("A","B","C")
> mat
     A B C
[1,] 1 3 5
[2,] 2 4 6
> pack(mat)
 [1] 92 83 a1 41 01 a1 42 03 a1 43 05 83 a1 41 02 a1 42 04 a1 43 06
> 
```

- 0x92：長さ2の配列
- 0x83：長さ3の辞書

であることからそれが分かります。

###5. 復元方法
デシリアライズにはunpack関数を使用します。
pack関数でバイナリ列化されたデータを簡単に復元できます。

```
> bin <- pack(c(1,2,3))
> unpack(bin)
[1] 1 2 3
> bin <- pack("sample")
> unpack(bin)
[1] "sample"
> bin <- pack(sample)
> unpack(bin)
compact    slow 
   TRUE   FALSE 
> bin <- pack(mat)
> msgpack.matrix(unpack(bin))
     A B C
[1,] 1 3 5
[2,] 2 4 6
```

ここで、バイナリ列→行列への復元について、そのままではmatrixでは無くlistへ変換されてしまうので、
matrixへの整形を行うmsgpack.matrix関数を使用しています。
msgpack.matrix関数を使わないと以下のように表示されます。

```
> bin <- pack(mat)
> unpack(bin)
[[1]]
A B C 
1 3 5 

[[2]]
A B C 
2 4 6 
```

###6. ファイルへの保存方法
msgpackRではシリアライズされたバイナリ列を簡単にファイルへ保存することが出来ます。
msgpack.writeResult関数を使用します。

```
msgpack.writeResult("file.msg", pack(c(1,2,3)))
```

第1引数にファイル名、第2引数に保存したい変数名を指定します。

###7. ファイルの読み込み方法
msgpack.writeResult関数で保存されたファイルを読み込むためにはunpack関数を使用します。
unpack関数の引数が文字列であれば、それをファイル名だと判断してデータの読み込みを実施します。

```
> unpack("file.msg")
[1] 1 2 3
```

## まとめ
拙作msgpackRを例として、MessagePackの使用例を見てきました。
Java, C++, Python, PHPなど多くのメジャーなプログラミング言語でMessagePackが実装されているので、
興味のある方は、皆様が普段使用されている言語でMessagePackを使ってみてはいかがでしょうか。

## 終わりに
- MessagePackについての理解が浅い！or間違っている！
- msgpackRのバグを発見した
- 書いている内容が意味不明…

という場合は[@mikiya0417](https://twitter.com/mikiya0417)までご連絡ください。すぐ対応します。。。

## 参考
[1] [分散システムのためのメッセージ表現手法に関する研究 - 古橋 貞之](https://github.com/frsyuki/univ-msgpack-paper/blob/master/furuhashi-master-last-iso-pdfa.pdf?raw=true)
[2] [MessagePack: It's like JSON. but fast and small.](http://msgpack.org/)