MessagePack for C/C++/Ruby アップデート

MessagePackのC,C++,Ruby版をバージョンアップしました。C/C++版は0.4.2、Ruby版は0.3.3です。

主に strict-aliasing rule (goo)に関する問題を修正しました。互換性はAPI・ABI両方で維持されています。

Unpacker#each

Ruby版では、実験的に each(とfeedとfill)メソッドをデシリアライザに実装してみました*1
ストリーム(ソケットやファイルなど)から、シリアライズされたデータを次々に受信しながら、オブジェクトを1つずつ取り出してくれます。
イベント駆動型のサーバを実装したいときや、MessagePackでシリアライズしたデータが詰め込まれたファイルからオブジェクトを読み出したいときに便利です。高速でもあります。

require 'rubygems'
require 'msgpack'

# パイプの場合(標準入出力でも可)
rpipe, wpipe = IO.pipe

# シリアライズしたデータをパイプに書き込んでいく
t = Thread.new do
  # 10個のオブジェクトを続けて書き込む
  10.times {|i|
    wpipe.write ["object #{i}", i].to_msgpack
  }
  wpipe.close
end

# MessagePack::Unpackerのインスタンスを作る
# コンストラクタにIO(ソケットでもファイルでもOK)を渡す
pac = MessagePack::Unpacker.new(rpipe)

begin
  # Unpacker#eachでオブジェクトを1つずつ取り出す
  pac.each {|obj|
    p obj
  }
rescue EOFError
  puts "end of file."
end

実行すると↓このようにオブジェクトを1つずつ取り出せます:

["object 0", 0]
["object 1", 1]
["object 2", 2]
["object 3", 3]
["object 4", 4]
["object 5", 5]
["object 6", 6]
["object 7", 7]
["object 8", 8]
["object 9", 9]
end of file.


パイプやソケット以外に、ファイルでも使えます。このプログラムは、MessagePackでシリアライズされたデータをファイルから読み出し、JSONに変換してから標準出力に出力します:

require 'rubygems'
require 'msgpack'
require 'json'

# シリアライズしたデータを
# ファイルに書き出していく
File.open("out.mpac", "w") do |f|
  10.times {|i|
    obj = {"time"=>Time.now.to_s, "num"=>i}
    f.write obj.to_msgpack
  }
end

# ファイルを開いて...
File.open("out.mpac") do |f|
  pac = MessagePack::Unpacker.new(f)
  pac.each {|obj|  # データを次々に取り出していく
    puts obj.to_json  # JOSN形式で表示してみる
  } rescue break
end

実行すると↓こうなります:

{"time":"Tue Mar 02 17:20:10 +0900 2010","num":0}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":1}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":2}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":3}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":4}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":5}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":6}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":7}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":8}
{"time":"Tue Mar 02 17:20:10 +0900 2010","num":9}


イベント駆動I/OライブラリのEventMachineRevとの相性もぴったりです:

require 'rubygems'
require 'msgpack'
require 'json'
require 'rev'

class MySocket < Rev::TCPSocket
  def initialize(*arg)
    super
    # コネクション毎にデシリアライザを作る
    @pac = MessagePack::Unpacker.new
  end

  # データが届いたら呼ばれる
  def on_read(data)
    # デシリアライザにfeedする
    @pac.feed(data)
    @pac.each {|obj|  # eachでオブジェクトを取り出す
      on_message(obj)
    }
  end

  def on_message(msg)
    puts "message received: #{msg.inspect}"
    write(msg.to_json)   # jsonに変換して送り返す
  end
end

th = Thread.new do
  # 8081/tcpでサーバを起動
  evloop = Rev::Loop.new
  evloop.attach Rev::TCPServer.new('0.0.0.0', 8081, MySocket)
  evloop.run
end

# 適当にデータを投げてみる
sock = TCPSocket.new('0.0.0.0', 8081)
sock.write( {'id'=>1, 'msg'=>"this is test"}.to_msgpack )
sock.flush
str = sock.sysread(1024)
puts "message returned: #{str}"

ここではMessagePackで受信したデータをJSONに変換して返すサーバを実装してみました:

message received: {"msg"=>"this is test", "id"=>1}
message returned: {"msg":"this is test","id":1}

*1:[http://twitter.com/methane:title=@methane]さんが実装したPython版のMessagePackにインスパイアされました。イケてます。