lib/amqp/client/table.rb in amqp-client-1.1.0 vs lib/amqp/client/table.rb in amqp-client-1.1.1
- old
+ new
@@ -4,19 +4,24 @@
class Client
# Encode and decode an AMQP table to/from hash, only used internally
# @api private
module Table
# Encodes a hash into a byte array
+ # @param hash [Hash]
# @return [String] Byte array
def self.encode(hash)
- tbl = StringIO.new
- hash.each do |k, v|
+ return "" if hash.empty?
+
+ arr = []
+ fmt = String.new
+ hash.each do |k, value|
key = k.to_s
- tbl.write [key.bytesize, key].pack("Ca*")
- tbl.write encode_field(v)
+ arr.push(key.bytesize, key)
+ fmt << "Ca*"
+ encode_field(value, arr, fmt)
end
- tbl.string
+ arr.pack(fmt)
end
# Decodes an AMQP table into a hash
# @return [Hash<String, Object>]
def self.decode(bytes)
@@ -25,56 +30,70 @@
while pos < bytes.bytesize
key_len = bytes.getbyte(pos)
pos += 1
key = bytes.byteslice(pos, key_len).force_encoding("utf-8")
pos += key_len
- rest = bytes.byteslice(pos, bytes.bytesize - pos)
- len, value = decode_field(rest)
+ len, value = decode_field(bytes, pos)
pos += len + 1
hash[key] = value
end
hash
end
# Encoding a single value in a table
+ # @return [nil]
# @api private
- def self.encode_field(value)
+ def self.encode_field(value, arr, fmt)
case value
when Integer
if value > 2**31
- ["l", value].pack("a q>")
+ arr.push("l", value)
+ fmt << "aq>"
else
- ["I", value].pack("a l>")
+ arr.push("I", value)
+ fmt << "al>"
end
when Float
- ["d", value].pack("a G")
+ arr.push("d", value)
+ fmt << "aG"
when String
- ["S", value.bytesize, value].pack("a L> a*")
+ arr.push("S", value.bytesize, value)
+ fmt << "aL>a*"
when Time
- ["T", value.to_i].pack("a Q>")
+ arr.push("T", value.to_i)
+ fmt << "aQ>"
when Array
- bytes = value.map { |e| encode_field(e) }.join
- ["A", bytes.bytesize, bytes].pack("a L> a*")
+ value_arr = []
+ value_fmt = String.new
+ value.each { |e| encode_field(e, value_arr, value_fmt) }
+ bytes = value_arr.pack(value_fmt)
+ arr.push("A", bytes.bytesize, bytes)
+ fmt << "aL>a*"
when Hash
bytes = Table.encode(value)
- ["F", bytes.bytesize, bytes].pack("a L> a*")
+ arr.push("F", bytes.bytesize, bytes)
+ fmt << "aL>a*"
when true
- ["t", 1].pack("a C")
+ arr.push("t", 1)
+ fmt << "aC"
when false
- ["t", 0].pack("a C")
+ arr.push("t", 0)
+ fmt << "aC"
when nil
- ["V"].pack("a")
+ arr << "V"
+ fmt << "a"
else raise ArgumentError, "unsupported table field type: #{value.class}"
end
+ nil
end
# Decodes a single value
- # @return [Array<Integer, Object>] Bytes read and the parsed object
+ # @return [Array<Integer, Object>] Bytes read and the parsed value
# @api private
- def self.decode_field(bytes)
- type = bytes[0]
- pos = 1
+ def self.decode_field(bytes, pos)
+ type = bytes[pos]
+ pos += 1
case type
when "S"
len = bytes.byteslice(pos, 4).unpack1("L>")
pos += 4
[4 + len, bytes.byteslice(pos, len).force_encoding("utf-8")]
@@ -82,12 +101,14 @@
len = bytes.byteslice(pos, 4).unpack1("L>")
pos += 4
[4 + len, decode(bytes.byteslice(pos, len))]
when "A"
len = bytes.byteslice(pos, 4).unpack1("L>")
+ pos += 4
+ array_end = pos + len
a = []
- while pos < len
- length, value = decode_field(bytes.byteslice(pos, -1))
+ while pos < array_end
+ length, value = decode_field(bytes, pos)
pos += length + 1
a << value
end
[4 + len, a]
when "t"