MessagePack for Lua を公開しました

先週から少しずつ書いていた MessagePack for Lua が動くようになったので一旦公開しようと思います。

MessagePack とは

MessagePack とは 古橋くん(id:viver, @frsyuki)が開発した、高速なバイナリシリアライズ形式です。「速くてコンパクトなJSON」と言われる通り、JSONとほぼ同等のことを行えます。本当に速くてコンパクトなので、高頻度でJSON形式のデータをやりとりしているようなアプリケーションでは、 MessagePack に切り替えるだけでもかなりの効率改善につながるかと思います。

弊社の製品でも、RPC的な部分でものすごくお世話になっております(^q^) あ、関係ないですが10日夜8:00から社内セミナーをustで公開するので是非見てくださいね。 Twitter@preferred_jp をフォローすると情報が流れてくると思います。論文紹介やアニメやサッカーの話題など色々流れてくるので是非フォローしちゃってください。

動作環境とインストールに必要なもの

今のところ Ubuntu9.10 でしか動作確認をしていません。明日 Mac でも動作確認をしてみます。 Windows 対応は未定です(cygwin ならコンパイルできるかも)。

MessagePack for Lua を使うためには、 MessagePack と liblua5.1 が必要です。 liblua に関しては、 apt や yum などでインストールすることをお勧めします。

インストール

http://cloud.github.com/downloads/nobu-k/mplua/mplua-0.1.0.tar.gz から MessagePack for Lua をダウンロードしてください。

あとは下記のコマンドを打つことでインストールが完了します。

$ tar zxvf mplua-0.1.0.tar.gz
$ cd mplua-0.1.0
$ ./configure
$ make
$ sudo make install

ソースコード

$ git clone http://github.com/nobu-k/mplua.git

で落とせます。

使い方

インターフェイスPython binding がものすごく格好良かったので、真似させてもらいました。

以下がサンプルコードです。

シリアライズ
require "msgpack"

p = msgpack.Packer()
s1 = p:pack({1, 2, 3})
s2 = p:pack({4, 5, 6})

print(string.byte(s1, 1, #s1)) -- 147 1 2 3
print(string.byte(s2, 1, #s2)) -- 147 4 5 6

packは可変長引数を受け取るので、次のように書くことも可能です。

data = p:pack(1, 2, 3, "strings", {"a", "r", "r", "a", "y", "s"},
              {t = "a", b = "l", e = "s"; 1, 2, 3, 4})
シリアライズ
require "msgpack"

buf = string.rep('\147\1\2\3', 5)
u = msgpack.Unpacker()

u:feed(string.sub(buf, 1, 9))
for o in u do
  print(unpack(o))
end
print('--------')
u:feed(string.sub(buf, 10))
for o in u do
  print(unpack(o))
end

-- 出力
1	2	3
1	2	3
--------
1	2	3
1	2	3
1	2	3

ちなみに、feedも可変長引数を受けとることができます。

現状でシリアライズ/デシリアライズできるのは

  • 数値
  • bool
  • 文字列
  • 配列
  • テーブル

です。 LuaPHP と同じく、配列とテーブル(ハッシュ) を区別しません(内部的にはしていますが)。しかし、 MessagePack for Lua では、配列とテーブルを区別してシリアライズすることが可能です。配列の定義は、「0より大きい整数値キーのみで構成され、キーの最大値がテーブルの要素数と等しいテーブル」です。それ以外はすべてテーブルとして扱われます。

また、数値は、一応「整数」と「実数」を区別してシリアライズしています。しかし、 Lua では数値をすべて double (64bit 浮動小数点数) で持つため、 整数の値は 52bit 程度しか正確に表現することができません。そのため、他言語とやりとりを行った場合に少し問題が起こる可能性があります。

今後の予定

今はまだしょぼいことしかできませんが、ちょっとずつ拡張して行こうと思っています。

コールバック対応

例えば、シリアライザでは

function packer (s)
  write(s)
end

p = msgpack.Packer(packer)

とすると、 p:pack が文字列を返すのではなく、 packer 関数が呼ばれるようになったりなど。デシリアライザでは

function feeder ()
  -- どっかからデータを読み込み
  return 読み込んだデータ
end

u = msgpack.Unpacker(feeder)
for o in u do
  -- feeder から入力を読み込んでデシリアライズ
end

みたいなのにも対応しようと思っています。関数の仕様は変わると思いますが。

ネイティブ packer, unpacker の対応

Lua の魅力の一つとして C/C++ とデータのやりとりがものすごく自然にできる点があげられます。現バージョンでは Lua 内で完結していますが、ネイティブな packer, unpacker も Lua から使えるようにする予定です。そうすることで、 Lua で作成した packer を C から使ったり、その逆を行ったりすることができるようになります。

カスタムシリアライザ/デシリアライザ

metamethod __serialize, __deserialize (もしくは __pack, __unpack) を定義したテーブル、ユーザデータに関しては、その関数に Packer, Unpacker を渡して柔軟なシリアライズを可能にしようかと考えています。具体的にどんな感じにするかは悩み中。

と言うか、現状だとテーブルに関数が含まれてるとシリアライズに失敗しちゃうので、その辺の対応もしないといけないんですよね。

高速化

後は高速化ですね。せっかく id:viver が頑張って速いのを書いたのに、ライブラリの層で減速させてしまっては申し訳ないです。

おわりに

そんなわけで、出来上がったばかりの MessagePack for Lua の使い方と今後の予定を紹介しました。現在 MessagePack 普及運動を行っておりますので、是非、 Lua binding だけでなく、他のも使っていただけるとうれしいです。 MessagePack RPC も開発されているので、頑張って国産品で thrift に勝ちたいですね。

漏れてたらごめんなさい!!

あと、MessagePack for Lua の技術メモ的な内容も後日書こうと思っています〜。効率的な配列判定方法や現在抱えている問題などを書く予定。

最後になりますが、僕は Lua 初心者なので、もし Lua を使い込んでいる方がソースを見る機会がありましたら、しょぼいところをコメントして頂けるとうれしいです。また、こういうちゃんとした形でソフトウェアを公開するのも初めてなので、オープンソース的な作法がなってないところもあるかと思います。その辺も指導していただけると感激です。よろしくお願いします〜。