2

構造化されたバイナリ パケットをパック/アンパックできるカスタム EM::Protocol モジュールを作成しようとしています。パケット構造は、文字列、その他の簡単に解析可能な形式、またはある種の DSL として、名前/形式のペアとして定義する必要があります。

アイデアを理解するための簡単なコード:

module PacketProtocol
    def self.included(base)
        base.extend ClassMethods
    end

    def receive_data(data)
        # retrieve packet header
        # find matching packet definition
        # packet.unpack(data)
    end

    module ClassMethods
        def packet(defn)
            # create an instance of Packet (see blow) and shove it
            # somewhere i can get to later.
        end
    end
end

module MyHandler
    include PacketProtocol
    packet '<id:S><len:S><msg:A%{len}>'
end

EM.run do
     EM.start_server '0.0.0.0', 8080, MyHandler
end

私の目標は、ランタイムの複雑さを最小限に抑えることです。パケット定義は実行ごとに静的であるため、この (粗雑な) 実装は避けたいと思います。

class Packet
    FmtSize = {
        'S' => 2, 
        'A' => Proc.new {|fmt| fmt[1..-1].to_i }
    }

    def initialize(defn)
        @fields = defn.scan(/<([^>]+):([^>]+)>/)
    end

    def pack(data)
        data.values.pack @fields.map { |name, fmt| fmt % data }.join
    end

    def unpack(defn)
        data = {}
        posn = 0
        @fields.each do |name, len|
            fmt = len % data
            len = FmtSizes[fmt[0]]
            len = len.call(fmt) if len.class == Proc
            data[name.to_sym] = bytes[posn..posn + len - 1].unpack(fmt)[0]
            posn += len
        end
        data
    end
end

data = { :id => 1, :len => 5, :msg = 'Hello' }
packet = Packet.new '<id:S><len:S><msg:A%{len}>'
packed = packet.pack(data)

require 'benchmark'

Benchmark.bm(7) do |x|
    x.report('slow') {
        100000.times do
            unpacked = packet.unpack(packed)
        end
    }
    x.report('fast') {
        100000.times do
            data = {}
            data[:id] = packed[0..1].unpack('S' % data)
            data[:len] = packed[2..3].unpack('S' % data)
            data[:msg] = packed[4..8].unpack('A%{len}' % data)
        end
    }
end

# output:
#              user     system      total        real
# slow     1.970000   0.000000   1.970000 (  1.965525)
# fast     0.140000   0.000000   0.140000 (  0.146227)

2 つの例のうち、Packet クラスを使用すると、数倍遅くなるようです。

それで。質問は:

実行時にコードを生成できる方法 (または宝石) はありますか (単純に文字列を評価する以外に)?

編集:

BinDataが見つかりました。機能セットは優れていますが、ベンチマークもはるかに遅くなります。

4

0 に答える 0