amq-protocol 0.7.0.beta2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -0
- data/lib/amq/protocol/table.rb +99 -113
- data/lib/amq/protocol/table_value_decoder.rb +151 -0
- data/lib/amq/protocol/table_value_encoder.rb +117 -0
- data/lib/amq/protocol/type_constants.rb +26 -0
- data/lib/amq/protocol/version.rb +1 -1
- data/spec/amq/protocol/table_spec.rb +121 -1
- data/spec/amq/protocol/value_decoder_spec.rb +66 -0
- data/spec/amq/protocol/value_encoder_spec.rb +138 -0
- metadata +16 -13
    
        data/Gemfile
    CHANGED
    
    
    
        data/lib/amq/protocol/table.rb
    CHANGED
    
    | @@ -1,6 +1,9 @@ | |
| 1 1 | 
             
            # encoding: binary
         | 
| 2 2 |  | 
| 3 3 | 
             
            require "amq/protocol/client"
         | 
| 4 | 
            +
            require "amq/protocol/type_constants"
         | 
| 5 | 
            +
            require "amq/protocol/table_value_encoder"
         | 
| 6 | 
            +
            require "amq/protocol/table_value_decoder"
         | 
| 4 7 |  | 
| 5 8 | 
             
            # We will need to introduce concept of mappings, because
         | 
| 6 9 | 
             
            # AMQP 0.9, 0.9.1 and RabbitMQ uses different letters for entities
         | 
| @@ -8,146 +11,129 @@ require "amq/protocol/client" | |
| 8 11 | 
             
            module AMQ
         | 
| 9 12 | 
             
              module Protocol
         | 
| 10 13 | 
             
                class Table
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # Behaviors
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  include TypeConstants
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # API
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
             | 
| 11 26 | 
             
                  class InvalidTableError < StandardError
         | 
| 12 27 | 
             
                    def initialize(key, value)
         | 
| 13 28 | 
             
                      super("Invalid table value on key #{key}: #{value.inspect} (#{value.class})")
         | 
| 14 29 | 
             
                    end
         | 
| 15 30 | 
             
                  end
         | 
| 16 31 |  | 
| 17 | 
            -
                  TYPE_STRING = 'S'.freeze
         | 
| 18 | 
            -
                  TYPE_INTEGER = 'I'.freeze
         | 
| 19 | 
            -
                  TYPE_HASH = 'F'.freeze
         | 
| 20 | 
            -
                  TYPE_TIME = 'T'.freeze
         | 
| 21 | 
            -
                  TYPE_DECIMAL = 'D'.freeze
         | 
| 22 | 
            -
                  TYPE_BOOLEAN = 't'.freeze
         | 
| 23 | 
            -
                  TYPE_SIGNED_8BIT = 'b'.freeze
         | 
| 24 | 
            -
                  TYPE_SIGNED_16BIT = 's'.freeze
         | 
| 25 | 
            -
                  TYPE_SIGNED_64BIT = 'l'.freeze
         | 
| 26 | 
            -
                  TYPE_32BIT_FLOAT = 'f'.freeze
         | 
| 27 | 
            -
                  TYPE_64BIT_FLOAT = 'd'.freeze
         | 
| 28 | 
            -
                  TYPE_VOID = 'V'.freeze
         | 
| 29 | 
            -
                  TYPE_BYTE_ARRAY = 'x'.freeze
         | 
| 30 | 
            -
                  TEN = '10'.freeze
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  BOOLEAN_TRUE  = "\x01".freeze
         | 
| 33 | 
            -
                  BOOLEAN_FALSE = "\x00".freeze
         | 
| 34 | 
            -
             | 
| 35 32 |  | 
| 36 33 | 
             
                  def self.encode(table)
         | 
| 37 34 | 
             
                    buffer = String.new
         | 
| 35 | 
            +
             | 
| 38 36 | 
             
                    table ||= {}
         | 
| 37 | 
            +
             | 
| 39 38 | 
             
                    table.each do |key, value|
         | 
| 40 39 | 
             
                      key = key.to_s # it can be a symbol as well
         | 
| 41 40 | 
             
                      buffer << key.bytesize.chr + key
         | 
| 42 41 |  | 
| 43 42 | 
             
                      case value
         | 
| 44 | 
            -
                      when String then
         | 
| 45 | 
            -
                        buffer << TYPE_STRING
         | 
| 46 | 
            -
                        buffer << [value.bytesize].pack(PACK_UINT32)
         | 
| 47 | 
            -
                        buffer << value
         | 
| 48 | 
            -
                      when Integer then
         | 
| 49 | 
            -
                        buffer << TYPE_INTEGER
         | 
| 50 | 
            -
                        buffer << [value].pack(PACK_UINT32)
         | 
| 51 | 
            -
                      when Float then
         | 
| 52 | 
            -
                        buffer << TYPE_64BIT_FLOAT
         | 
| 53 | 
            -
                        buffer << [value].pack(PACK_64BIT_FLOAT)
         | 
| 54 | 
            -
                      when true, false then
         | 
| 55 | 
            -
                        buffer << TYPE_BOOLEAN
         | 
| 56 | 
            -
                        buffer << (value ? BOOLEAN_TRUE : BOOLEAN_FALSE)
         | 
| 57 43 | 
             
                      when Hash then
         | 
| 58 | 
            -
                        buffer << TYPE_HASH | 
| 44 | 
            +
                        buffer << TYPE_HASH
         | 
| 59 45 | 
             
                        buffer << self.encode(value)
         | 
| 60 | 
            -
                      when Time then
         | 
| 61 | 
            -
                        buffer << TYPE_TIME
         | 
| 62 | 
            -
                        buffer << [value.to_i].pack(PACK_INT64).reverse # FIXME: there has to be a more efficient way
         | 
| 63 | 
            -
                      when nil then
         | 
| 64 | 
            -
                        buffer << TYPE_VOID
         | 
| 65 46 | 
             
                      else
         | 
| 66 | 
            -
                         | 
| 67 | 
            -
                        if defined?(BigDecimal) && value.is_a?(BigDecimal)
         | 
| 68 | 
            -
                          buffer << TYPE_DECIMAL
         | 
| 69 | 
            -
                          if value.exponent < 0
         | 
| 70 | 
            -
                            decimals = -value.exponent
         | 
| 71 | 
            -
                            # p [value.exponent] # normalize
         | 
| 72 | 
            -
                            raw = (value * (decimals ** 10)).to_i
         | 
| 73 | 
            -
                            #pieces.append(struct.pack('>cBI', 'D', decimals, raw)) # byte integer
         | 
| 74 | 
            -
                            buffer << [decimals + 1, raw].pack(PACK_UCHAR_UINT32) # somewhat like floating point
         | 
| 75 | 
            -
                          else
         | 
| 76 | 
            -
                            # per spec, the "decimals" octet is unsigned (!)
         | 
| 77 | 
            -
                            buffer << [0, value.to_i].pack(PACK_UCHAR_UINT32)
         | 
| 78 | 
            -
                          end
         | 
| 79 | 
            -
                        else
         | 
| 80 | 
            -
                          raise InvalidTableError.new(key, value)
         | 
| 81 | 
            -
                        end
         | 
| 47 | 
            +
                        buffer << TableValueEncoder.encode(value)
         | 
| 82 48 | 
             
                      end
         | 
| 83 49 | 
             
                    end
         | 
| 84 50 |  | 
| 85 51 | 
             
                    [buffer.bytesize].pack(PACK_UINT32) + buffer
         | 
| 86 52 | 
             
                  end
         | 
| 87 53 |  | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
             | 
| 91 56 |  | 
| 92 57 | 
             
                  def self.decode(data)
         | 
| 93 | 
            -
                    table | 
| 94 | 
            -
                     | 
| 95 | 
            -
             | 
| 96 | 
            -
                     | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
                      offset  | 
| 101 | 
            -
                      type =  | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
                        offset += 8
         | 
| 140 | 
            -
                      when TYPE_VOID
         | 
| 141 | 
            -
                        value = nil
         | 
| 142 | 
            -
                      when TYPE_BYTE_ARRAY
         | 
| 143 | 
            -
                      else
         | 
| 144 | 
            -
                        raise "Not a valid type: #{type.inspect}\nData: #{data.inspect}\nUnprocessed data: #{data[offset..-1].inspect}\nOffset: #{offset}\nTotal size: #{size}\nProcessed data: #{table.inspect}"
         | 
| 145 | 
            -
                      end
         | 
| 146 | 
            -
                      table[key] = value
         | 
| 58 | 
            +
                    table        = Hash.new
         | 
| 59 | 
            +
                    table_length = data.unpack(PACK_UINT32).first
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    return table if table_length.zero?
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    offset       = 4
         | 
| 64 | 
            +
                    while offset <= table_length
         | 
| 65 | 
            +
                      key, offset  = decode_table_key(data, offset)
         | 
| 66 | 
            +
                      type, offset = TableValueDecoder.decode_value_type(data, offset)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                      table[key] = case type
         | 
| 69 | 
            +
                                   when TYPE_STRING
         | 
| 70 | 
            +
                                     v, offset = TableValueDecoder.decode_string(data, offset)
         | 
| 71 | 
            +
                                     v
         | 
| 72 | 
            +
                                   when TYPE_INTEGER
         | 
| 73 | 
            +
                                     v, offset = TableValueDecoder.decode_integer(data, offset)
         | 
| 74 | 
            +
                                     v
         | 
| 75 | 
            +
                                   when TYPE_DECIMAL
         | 
| 76 | 
            +
                                     v, offset = TableValueDecoder.decode_big_decimal(data, offset)
         | 
| 77 | 
            +
                                     v
         | 
| 78 | 
            +
                                   when TYPE_TIME
         | 
| 79 | 
            +
                                     v, offset = TableValueDecoder.decode_time(data, offset)
         | 
| 80 | 
            +
                                     v
         | 
| 81 | 
            +
                                   when TYPE_HASH
         | 
| 82 | 
            +
                                     v, offset = TableValueDecoder.decode_hash(data, offset)
         | 
| 83 | 
            +
                                     v
         | 
| 84 | 
            +
                                   when TYPE_BOOLEAN
         | 
| 85 | 
            +
                                     v, offset = TableValueDecoder.decode_boolean(data, offset)
         | 
| 86 | 
            +
                                     v
         | 
| 87 | 
            +
                                   when TYPE_SIGNED_8BIT  then raise NotImplementedError.new
         | 
| 88 | 
            +
                                   when TYPE_SIGNED_16BIT then raise NotImplementedError.new
         | 
| 89 | 
            +
                                   when TYPE_SIGNED_64BIT then raise NotImplementedError.new
         | 
| 90 | 
            +
                                   when TYPE_32BIT_FLOAT then
         | 
| 91 | 
            +
                                     v, offset = TableValueDecoder.decode_32bit_float(data, offset)
         | 
| 92 | 
            +
                                     v
         | 
| 93 | 
            +
                                   when TYPE_64BIT_FLOAT then
         | 
| 94 | 
            +
                                     v, offset = TableValueDecoder.decode_64bit_float(data, offset)
         | 
| 95 | 
            +
                                     v
         | 
| 96 | 
            +
                                   when TYPE_VOID
         | 
| 97 | 
            +
                                     nil
         | 
| 98 | 
            +
                                   when TYPE_ARRAY
         | 
| 99 | 
            +
                                     v, offset = TableValueDecoder.decode_array(data, offset)
         | 
| 100 | 
            +
                                     v
         | 
| 101 | 
            +
                                   else
         | 
| 102 | 
            +
                                     raise ArgumentError, "Not a valid type: #{type.inspect}\nData: #{data.inspect}\nUnprocessed data: #{data[offset..-1].inspect}\nOffset: #{offset}\nTotal size: #{table_length}\nProcessed data: #{table.inspect}"
         | 
| 103 | 
            +
                                   end
         | 
| 147 104 | 
             
                    end
         | 
| 148 105 |  | 
| 149 106 | 
             
                    table
         | 
| 107 | 
            +
                  end # self.decode
         | 
| 108 | 
            +
             | 
| 109 | 
            +
             | 
| 110 | 
            +
                  def self.length(data)
         | 
| 111 | 
            +
                    data.unpack(PACK_UINT32).first
         | 
| 150 112 | 
             
                  end
         | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 113 | 
            +
             | 
| 114 | 
            +
             | 
| 115 | 
            +
                  def self.hash_size(value)
         | 
| 116 | 
            +
                    acc = 0
         | 
| 117 | 
            +
                    value.each do |k, v|
         | 
| 118 | 
            +
                      acc += (1 + k.to_s.bytesize)
         | 
| 119 | 
            +
                      acc += TableValueEncoder.field_value_size(v)
         | 
| 120 | 
            +
                    end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    acc
         | 
| 123 | 
            +
                  end # self.hash_size(value)
         | 
| 124 | 
            +
             | 
| 125 | 
            +
             | 
| 126 | 
            +
                  def self.decode_table_key(data, offset)
         | 
| 127 | 
            +
                    key_length = data.slice(offset, 1).unpack(PACK_CHAR).first
         | 
| 128 | 
            +
                    offset += 1
         | 
| 129 | 
            +
                    key = data.slice(offset, key_length)
         | 
| 130 | 
            +
                    offset += key_length
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                    [key, offset]
         | 
| 133 | 
            +
                  end # self.decode_table_key(data, offset)
         | 
| 134 | 
            +
             | 
| 135 | 
            +
             | 
| 136 | 
            +
             | 
| 137 | 
            +
                end # Table
         | 
| 138 | 
            +
              end # Protocol
         | 
| 139 | 
            +
            end # AMQ
         | 
| @@ -0,0 +1,151 @@ | |
| 1 | 
            +
            # encoding: binary
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "amq/protocol/client"
         | 
| 4 | 
            +
            require "amq/protocol/type_constants"
         | 
| 5 | 
            +
            require "amq/protocol/table"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module AMQ
         | 
| 8 | 
            +
              module Protocol
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                class TableValueDecoder
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  #
         | 
| 13 | 
            +
                  # Behaviors
         | 
| 14 | 
            +
                  #
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  include TypeConstants
         | 
| 17 | 
            +
             | 
| 18 | 
            +
             | 
| 19 | 
            +
                  #
         | 
| 20 | 
            +
                  # API
         | 
| 21 | 
            +
                  #
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def self.decode_array(data, initial_offset)
         | 
| 24 | 
            +
                    array_length = data.slice(initial_offset, 4).unpack(PACK_UINT32).first
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    ary    = Array.new
         | 
| 27 | 
            +
                    offset = initial_offset + 4
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    while offset <= (initial_offset + array_length)
         | 
| 30 | 
            +
                      type, offset = decode_value_type(data, offset)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      i = case type
         | 
| 33 | 
            +
                             when TYPE_STRING
         | 
| 34 | 
            +
                               v, offset = decode_string(data, offset)
         | 
| 35 | 
            +
                               v
         | 
| 36 | 
            +
                             when TYPE_INTEGER
         | 
| 37 | 
            +
                               v, offset = decode_integer(data, offset)
         | 
| 38 | 
            +
                               v
         | 
| 39 | 
            +
                             when TYPE_DECIMAL
         | 
| 40 | 
            +
                               v, offset = decode_big_decimal(data, offset)
         | 
| 41 | 
            +
                               v
         | 
| 42 | 
            +
                             when TYPE_TIME
         | 
| 43 | 
            +
                               v, offset = decode_time(data, offset)
         | 
| 44 | 
            +
                               v
         | 
| 45 | 
            +
                             when TYPE_HASH
         | 
| 46 | 
            +
                               v, offset = decode_hash(data, offset)
         | 
| 47 | 
            +
                               v
         | 
| 48 | 
            +
                             when TYPE_BOOLEAN
         | 
| 49 | 
            +
                               v, offset = decode_boolean(data, offset)
         | 
| 50 | 
            +
                               v
         | 
| 51 | 
            +
                             when TYPE_SIGNED_8BIT then raise NotImplementedError.new
         | 
| 52 | 
            +
                             when TYPE_SIGNED_16BIT then raise NotImplementedError.new
         | 
| 53 | 
            +
                             when TYPE_SIGNED_64BIT then raise NotImplementedError.new
         | 
| 54 | 
            +
                             when TYPE_32BIT_FLOAT then
         | 
| 55 | 
            +
                               v, offset = decode_32bit_float(data, offset)
         | 
| 56 | 
            +
                               v
         | 
| 57 | 
            +
                             when TYPE_64BIT_FLOAT then
         | 
| 58 | 
            +
                               v, offset = decode_64bit_float(data, offset)
         | 
| 59 | 
            +
                               v
         | 
| 60 | 
            +
                             when TYPE_VOID
         | 
| 61 | 
            +
                               nil
         | 
| 62 | 
            +
                             when TYPE_ARRAY
         | 
| 63 | 
            +
                               v, offset = TableValueDecoder.decode_array(data, offset)
         | 
| 64 | 
            +
                               v
         | 
| 65 | 
            +
                             else
         | 
| 66 | 
            +
                               raise ArgumentError.new("unsupported type: #{type.inspect}")
         | 
| 67 | 
            +
                             end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                      ary << i
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
             | 
| 73 | 
            +
                    [ary, initial_offset + array_length + 4]
         | 
| 74 | 
            +
                  end # self.decode_array(data, initial_offset)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
             | 
| 77 | 
            +
                  def self.decode_string(data, offset)
         | 
| 78 | 
            +
                    length = data.slice(offset, 4).unpack(PACK_UINT32).first
         | 
| 79 | 
            +
                    offset += 4
         | 
| 80 | 
            +
                    v = data.slice(offset, length)
         | 
| 81 | 
            +
                    offset += length
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    [v, offset]
         | 
| 84 | 
            +
                  end # self.decode_string(data, offset)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
             | 
| 87 | 
            +
                  def self.decode_integer(data, offset)
         | 
| 88 | 
            +
                    v = data.slice(offset, 4).unpack(PACK_UINT32).first
         | 
| 89 | 
            +
                    offset += 4
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    [v, offset]
         | 
| 92 | 
            +
                  end # self.decode_integer(data, offset)
         | 
| 93 | 
            +
             | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def self.decode_big_decimal(data, offset)
         | 
| 96 | 
            +
                    decimals, raw = data.slice(offset, 5).unpack(PACK_UCHAR_UINT32)
         | 
| 97 | 
            +
                    offset += 5
         | 
| 98 | 
            +
                    v = BigDecimal.new(raw.to_s) * (BigDecimal.new(TEN) ** -decimals)
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    [v, offset]
         | 
| 101 | 
            +
                  end # self.decode_big_decimal(data, offset)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
             | 
| 104 | 
            +
                  def self.decode_time(data, offset)
         | 
| 105 | 
            +
                    timestamp = data.slice(offset, 8).unpack(PACK_UINT32_X2).last
         | 
| 106 | 
            +
                    v = Time.at(timestamp)
         | 
| 107 | 
            +
                    offset += 8
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    [v, offset]
         | 
| 110 | 
            +
                  end # self.decode_time(data, offset)
         | 
| 111 | 
            +
             | 
| 112 | 
            +
             | 
| 113 | 
            +
                  def self.decode_boolean(data, offset)
         | 
| 114 | 
            +
                    integer = data.slice(offset, 2).unpack(PACK_CHAR).first # 0 or 1
         | 
| 115 | 
            +
                    offset += 1
         | 
| 116 | 
            +
                    [(integer == 1), offset]
         | 
| 117 | 
            +
                  end # self.decode_boolean(data, offset)
         | 
| 118 | 
            +
             | 
| 119 | 
            +
             | 
| 120 | 
            +
                  def self.decode_32bit_float(data, offset)
         | 
| 121 | 
            +
                    v = data.slice(offset, 4).unpack(PACK_32BIT_FLOAT).first
         | 
| 122 | 
            +
                    offset += 4
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                    [v, offset]
         | 
| 125 | 
            +
                  end # self.decode_32bit_float(data, offset)
         | 
| 126 | 
            +
             | 
| 127 | 
            +
             | 
| 128 | 
            +
                  def self.decode_64bit_float(data, offset)
         | 
| 129 | 
            +
                    v = data.slice(offset, 8).unpack(PACK_64BIT_FLOAT).first
         | 
| 130 | 
            +
                    offset += 8
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                    [v, offset]
         | 
| 133 | 
            +
                  end # self.decode_64bit_float(data, offset)
         | 
| 134 | 
            +
             | 
| 135 | 
            +
             | 
| 136 | 
            +
                  def self.decode_value_type(data, offset)
         | 
| 137 | 
            +
                    [data.slice(offset, 1), offset + 1]
         | 
| 138 | 
            +
                  end # self.decode_value_type(data, offset)
         | 
| 139 | 
            +
             | 
| 140 | 
            +
             | 
| 141 | 
            +
             | 
| 142 | 
            +
                  def self.decode_hash(data, offset)
         | 
| 143 | 
            +
                    length = data.slice(offset, 4).unpack(PACK_UINT32).first
         | 
| 144 | 
            +
                    v = Table.decode(data.slice(offset, length + 4))
         | 
| 145 | 
            +
                    offset += 4 + length
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    [v, offset]
         | 
| 148 | 
            +
                  end # self.decode_hash(data, offset)
         | 
| 149 | 
            +
                end # TableValueDecoder
         | 
| 150 | 
            +
              end # Protocol
         | 
| 151 | 
            +
            end # AMQ
         | 
| @@ -0,0 +1,117 @@ | |
| 1 | 
            +
            # encoding: binary
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "amq/protocol/client"
         | 
| 4 | 
            +
            require "amq/protocol/type_constants"
         | 
| 5 | 
            +
            require "amq/protocol/table"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module AMQ
         | 
| 8 | 
            +
              module Protocol
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                class TableValueEncoder
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  #
         | 
| 13 | 
            +
                  # Behaviors
         | 
| 14 | 
            +
                  #
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  include TypeConstants
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  #
         | 
| 19 | 
            +
                  # API
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def self.encode(value)
         | 
| 23 | 
            +
                    accumulator = String.new
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    case value
         | 
| 26 | 
            +
                    when String then
         | 
| 27 | 
            +
                      accumulator << TYPE_STRING
         | 
| 28 | 
            +
                      accumulator << [value.bytesize].pack(PACK_UINT32)
         | 
| 29 | 
            +
                      accumulator << value
         | 
| 30 | 
            +
                    when Symbol then
         | 
| 31 | 
            +
                      v = value.to_s
         | 
| 32 | 
            +
                      accumulator << TYPE_STRING
         | 
| 33 | 
            +
                      accumulator << [v.bytesize].pack(PACK_UINT32)
         | 
| 34 | 
            +
                      accumulator << v
         | 
| 35 | 
            +
                    when Integer then
         | 
| 36 | 
            +
                      accumulator << TYPE_INTEGER
         | 
| 37 | 
            +
                      accumulator << [value].pack(PACK_UINT32)
         | 
| 38 | 
            +
                    when Float then
         | 
| 39 | 
            +
                      accumulator << TYPE_64BIT_FLOAT
         | 
| 40 | 
            +
                      accumulator << [value].pack(PACK_64BIT_FLOAT)
         | 
| 41 | 
            +
                    when true, false then
         | 
| 42 | 
            +
                      accumulator << TYPE_BOOLEAN
         | 
| 43 | 
            +
                      accumulator << (value ? BOOLEAN_TRUE : BOOLEAN_FALSE)
         | 
| 44 | 
            +
                    when Time then
         | 
| 45 | 
            +
                      accumulator << TYPE_TIME
         | 
| 46 | 
            +
                      accumulator << [value.to_i].pack(PACK_INT64).reverse # FIXME: there has to be a more efficient way
         | 
| 47 | 
            +
                    when nil then
         | 
| 48 | 
            +
                      accumulator << TYPE_VOID
         | 
| 49 | 
            +
                    when Array then
         | 
| 50 | 
            +
                      accumulator << TYPE_ARRAY
         | 
| 51 | 
            +
                      accumulator << [self.array_size(value)].pack(PACK_UINT32)
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                      value.each { |v| accumulator << self.encode(v) }
         | 
| 54 | 
            +
                    when Hash then
         | 
| 55 | 
            +
                      accumulator << TYPE_HASH
         | 
| 56 | 
            +
                      accumulator << AMQ::Protocol::Table.encode(value)
         | 
| 57 | 
            +
                    else
         | 
| 58 | 
            +
                      # We don't want to require these libraries.
         | 
| 59 | 
            +
                      if defined?(BigDecimal) && value.is_a?(BigDecimal)
         | 
| 60 | 
            +
                        accumulator << TYPE_DECIMAL
         | 
| 61 | 
            +
                        if value.exponent < 0
         | 
| 62 | 
            +
                          decimals = -value.exponent
         | 
| 63 | 
            +
                          raw = (value * (decimals ** 10)).to_i
         | 
| 64 | 
            +
                          accumulator << [decimals + 1, raw].pack(PACK_UCHAR_UINT32) # somewhat like floating point
         | 
| 65 | 
            +
                        else
         | 
| 66 | 
            +
                          # per spec, the "decimals" octet is unsigned (!)
         | 
| 67 | 
            +
                          accumulator << [0, value.to_i].pack(PACK_UCHAR_UINT32)
         | 
| 68 | 
            +
                        end
         | 
| 69 | 
            +
                      else
         | 
| 70 | 
            +
                        raise ArgumentError.new("Unsupported value #{value.inspect} of type #{value.class.name}")
         | 
| 71 | 
            +
                      end # if
         | 
| 72 | 
            +
                    end # case
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    accumulator
         | 
| 75 | 
            +
                  end # self.encode(value)
         | 
| 76 | 
            +
             | 
| 77 | 
            +
             | 
| 78 | 
            +
             | 
| 79 | 
            +
             | 
| 80 | 
            +
                  def self.field_value_size(value)
         | 
| 81 | 
            +
                    # the type tag takes 1 byte
         | 
| 82 | 
            +
                    acc = 1
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    case value
         | 
| 85 | 
            +
                    when String then
         | 
| 86 | 
            +
                      acc += (value.bytesize + 4)
         | 
| 87 | 
            +
                    when Integer then
         | 
| 88 | 
            +
                      acc += 4
         | 
| 89 | 
            +
                    when Float then
         | 
| 90 | 
            +
                      acc += 8
         | 
| 91 | 
            +
                    when Time, DateTime then
         | 
| 92 | 
            +
                      acc += 8
         | 
| 93 | 
            +
                    when true, false then
         | 
| 94 | 
            +
                      acc += 1
         | 
| 95 | 
            +
                    when nil then
         | 
| 96 | 
            +
                      # nothing, type tag alone is enough
         | 
| 97 | 
            +
                    when Hash then
         | 
| 98 | 
            +
                      acc += (4 + Table.hash_size(value))
         | 
| 99 | 
            +
                    when Array then
         | 
| 100 | 
            +
                      acc += (4 + self.array_size(value))
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    acc
         | 
| 104 | 
            +
                  end # self.field_value_size(value)
         | 
| 105 | 
            +
             | 
| 106 | 
            +
             | 
| 107 | 
            +
                  def self.array_size(value)
         | 
| 108 | 
            +
                    acc = 0
         | 
| 109 | 
            +
                    value.each { |v| acc += self.field_value_size(v) }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    acc
         | 
| 112 | 
            +
                  end # self.array_size(value)
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                end # TableValueEncoder
         | 
| 115 | 
            +
             | 
| 116 | 
            +
              end # Protocol
         | 
| 117 | 
            +
            end # AMQ
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # encoding: binary
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module AMQ
         | 
| 4 | 
            +
              module Protocol
         | 
| 5 | 
            +
                module TypeConstants
         | 
| 6 | 
            +
                  TYPE_STRING       = 'S'.freeze
         | 
| 7 | 
            +
                  TYPE_INTEGER      = 'I'.freeze
         | 
| 8 | 
            +
                  TYPE_HASH         = 'F'.freeze
         | 
| 9 | 
            +
                  TYPE_TIME         = 'T'.freeze
         | 
| 10 | 
            +
                  TYPE_DECIMAL      = 'D'.freeze
         | 
| 11 | 
            +
                  TYPE_BOOLEAN      = 't'.freeze
         | 
| 12 | 
            +
                  TYPE_SIGNED_8BIT  = 'b'.freeze
         | 
| 13 | 
            +
                  TYPE_SIGNED_16BIT = 's'.freeze
         | 
| 14 | 
            +
                  TYPE_SIGNED_64BIT = 'l'.freeze
         | 
| 15 | 
            +
                  TYPE_32BIT_FLOAT  = 'f'.freeze
         | 
| 16 | 
            +
                  TYPE_64BIT_FLOAT  = 'd'.freeze
         | 
| 17 | 
            +
                  TYPE_VOID         = 'V'.freeze
         | 
| 18 | 
            +
                  TYPE_BYTE_ARRAY   = 'x'.freeze
         | 
| 19 | 
            +
                  TYPE_ARRAY        = 'A'.freeze
         | 
| 20 | 
            +
                  TEN               = '10'.freeze
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  BOOLEAN_TRUE  = "\x01".freeze
         | 
| 23 | 
            +
                  BOOLEAN_FALSE = "\x00".freeze
         | 
| 24 | 
            +
                end # TypeConstants
         | 
| 25 | 
            +
              end # Protocol
         | 
| 26 | 
            +
            end # AMQ
         | 
    
        data/lib/amq/protocol/version.rb
    CHANGED
    
    
| @@ -84,7 +84,7 @@ module AMQ | |
| 84 84 | 
             
                    end # DATA.each
         | 
| 85 85 |  | 
| 86 86 |  | 
| 87 | 
            -
                    it "is capable of decoding  | 
| 87 | 
            +
                    it "is capable of decoding boolean table values" do
         | 
| 88 88 | 
             
                      input1   = { "boolval" => true }
         | 
| 89 89 | 
             
                      Table.decode(Table.encode(input1)).should == input1
         | 
| 90 90 |  | 
| @@ -95,6 +95,76 @@ module AMQ | |
| 95 95 |  | 
| 96 96 |  | 
| 97 97 |  | 
| 98 | 
            +
                    it "is capable of decoding string table values" do
         | 
| 99 | 
            +
                      input   = { "stringvalue" => "string" }
         | 
| 100 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
             | 
| 104 | 
            +
             | 
| 105 | 
            +
                    it "is capable of decoding integer table values" do
         | 
| 106 | 
            +
                      input   = { "intvalue" => 10 }
         | 
| 107 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
             | 
| 111 | 
            +
             | 
| 112 | 
            +
                    it "is capable of decoding long table values" do
         | 
| 113 | 
            +
                      input   = { "longvalue" => 912598613 }
         | 
| 114 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
             | 
| 118 | 
            +
             | 
| 119 | 
            +
                    it "is capable of decoding float table values" do
         | 
| 120 | 
            +
                      input   = { "floatvalue" => 100.0 }
         | 
| 121 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
             | 
| 125 | 
            +
             | 
| 126 | 
            +
                    it "is capable of decoding time table values" do
         | 
| 127 | 
            +
                      input   = { "intvalue" => Time.parse("2011-07-14 01:17:46 +0400") }
         | 
| 128 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 129 | 
            +
                    end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
             | 
| 132 | 
            +
             | 
| 133 | 
            +
                    it "is capable of decoding empty hash table values" do
         | 
| 134 | 
            +
                      input   = { "hashvalue" => Hash.new }
         | 
| 135 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
             | 
| 139 | 
            +
             | 
| 140 | 
            +
                    xit "is capable of decoding empty array table values" do
         | 
| 141 | 
            +
                      input   = { "arrayvalue" => Array.new }
         | 
| 142 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
             | 
| 146 | 
            +
                    xit "is capable of decoding single string value array table values" do
         | 
| 147 | 
            +
                      input   = { "arrayvalue" => ["amq-protocol"] }
         | 
| 148 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
             | 
| 152 | 
            +
             | 
| 153 | 
            +
                    it "is capable of decoding simple nested hash table values" do
         | 
| 154 | 
            +
                      input   = { "hashvalue" => { "a" => "b" } }
         | 
| 155 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 156 | 
            +
                    end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
             | 
| 159 | 
            +
             | 
| 160 | 
            +
                    it "is capable of decoding nil table values" do
         | 
| 161 | 
            +
                      input   = { "nil" => nil }
         | 
| 162 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 163 | 
            +
                    end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
             | 
| 166 | 
            +
             | 
| 167 | 
            +
             | 
| 98 168 | 
             
                    it "is capable of decoding tables" do
         | 
| 99 169 | 
             
                      input   = {
         | 
| 100 170 | 
             
                        "boolval"      => true,
         | 
| @@ -108,6 +178,56 @@ module AMQ | |
| 108 178 | 
             
                      Table.decode(Table.encode(input)).should == input
         | 
| 109 179 | 
             
                    end
         | 
| 110 180 |  | 
| 181 | 
            +
             | 
| 182 | 
            +
             | 
| 183 | 
            +
                    it "is capable of decoding deeply nested tables" do
         | 
| 184 | 
            +
                      input   = {
         | 
| 185 | 
            +
                        "hashval"    => {
         | 
| 186 | 
            +
                          "protocol" => {
         | 
| 187 | 
            +
                            "name"  => "AMQP",
         | 
| 188 | 
            +
                            "major" => 0,
         | 
| 189 | 
            +
                            "minor" => "9",
         | 
| 190 | 
            +
                            "rev"   => 1.0,
         | 
| 191 | 
            +
                            "spec"  => {
         | 
| 192 | 
            +
                              "url"  => "http://bit.ly/hw2ELX",
         | 
| 193 | 
            +
                              "utf8" => "à bientôt"
         | 
| 194 | 
            +
                            }
         | 
| 195 | 
            +
                          },
         | 
| 196 | 
            +
                          "true"     => true,
         | 
| 197 | 
            +
                          "false"    => false,
         | 
| 198 | 
            +
                          "nil"      => nil
         | 
| 199 | 
            +
                        }
         | 
| 200 | 
            +
                      }
         | 
| 201 | 
            +
                      Table.decode(Table.encode(input)).should == input
         | 
| 202 | 
            +
                    end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
             | 
| 205 | 
            +
             | 
| 206 | 
            +
                    it "is capable of decoding array values in tables" do
         | 
| 207 | 
            +
                      input1   = {
         | 
| 208 | 
            +
                        "arrayval1" => [198, 3, 77, 8.0, ["inner", "array", { "oh" => "well", "it" => "should work", "3" => 6 }], "two", { "a" => "value", "is" => nil }],
         | 
| 209 | 
            +
                        "arrayval2" => [198, 3, 77, "two", { "a" => "value", "is" => nil }, 8.0, ["inner", "array", { "oh" => "well", "it" => "should work", "3" => 6 }]]
         | 
| 210 | 
            +
                      }
         | 
| 211 | 
            +
                      Table.decode(Table.encode(input1)).should == input1
         | 
| 212 | 
            +
             | 
| 213 | 
            +
             | 
| 214 | 
            +
                      input2 = {
         | 
| 215 | 
            +
                                   "coordinates" => {
         | 
| 216 | 
            +
                                     "latitude"  => 59.35,
         | 
| 217 | 
            +
                                     "longitude" => 18.066667
         | 
| 218 | 
            +
                                   },
         | 
| 219 | 
            +
                                   "time"         => @now,
         | 
| 220 | 
            +
                                   "participants" => 11,
         | 
| 221 | 
            +
                                   "venue"        => "Stockholm",
         | 
| 222 | 
            +
                                   "true_field"   => true,
         | 
| 223 | 
            +
                                   "false_field"  => false,
         | 
| 224 | 
            +
                                   "nil_field"    => nil,
         | 
| 225 | 
            +
                                   "ary_field"    => ["one", 2.0, 3]
         | 
| 226 | 
            +
                                 }
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                      Table.decode(Table.encode(input2)).should == input2
         | 
| 229 | 
            +
                    end
         | 
| 230 | 
            +
             | 
| 111 231 | 
             
                  end # describe
         | 
| 112 232 | 
             
                end
         | 
| 113 233 | 
             
              end
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            # -*- coding: utf-8 -*-
         | 
| 2 | 
            +
            require File.expand_path('../../../spec_helper', __FILE__)
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'time'
         | 
| 5 | 
            +
            require "amq/protocol/table_value_decoder"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module AMQ
         | 
| 8 | 
            +
              module Protocol
         | 
| 9 | 
            +
                describe TableValueDecoder do
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  it "is capable of decoding basic arrays TableValueEncoder encodes" do
         | 
| 12 | 
            +
                    input1 = [1, 2, 3]
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    value, offset = described_class.decode_array(TableValueEncoder.encode(input1), 1)
         | 
| 15 | 
            +
                    value.size.should == 3
         | 
| 16 | 
            +
                    value.first.should == 1
         | 
| 17 | 
            +
                    value.should == input1
         | 
| 18 | 
            +
             | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 21 | 
            +
                    input2 = ["one", 2, "three"]
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    value, offset = described_class.decode_array(TableValueEncoder.encode(input2), 1)
         | 
| 24 | 
            +
                    value.size.should == 3
         | 
| 25 | 
            +
                    value.first.should == "one"
         | 
| 26 | 
            +
                    value.should == input2
         | 
| 27 | 
            +
             | 
| 28 | 
            +
             | 
| 29 | 
            +
             | 
| 30 | 
            +
                    input3 = ["one", 2, "three", 4.0, 5000000.0]
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    value, offset = described_class.decode_array(TableValueEncoder.encode(input3), 1)
         | 
| 33 | 
            +
                    value.size.should == 5
         | 
| 34 | 
            +
                    value.last.should == 5000000.0
         | 
| 35 | 
            +
                    value.should == input3
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
             | 
| 39 | 
            +
             | 
| 40 | 
            +
                  it "is capable of decoding arrays TableValueEncoder encodes" do
         | 
| 41 | 
            +
                    input1 = [{ "one" => 2 }, 3]
         | 
| 42 | 
            +
                    data1  = TableValueEncoder.encode(input1)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    # puts(TableValueEncoder.encode({ "one" => 2 }).inspect)
         | 
| 45 | 
            +
                    # puts(TableValueEncoder.encode(input1).inspect)
         | 
| 46 | 
            +
             | 
| 47 | 
            +
             | 
| 48 | 
            +
                    value, offset = described_class.decode_array(data1, 1)
         | 
| 49 | 
            +
                    value.size.should == 2
         | 
| 50 | 
            +
                    value.first.should == Hash["one" => 2]
         | 
| 51 | 
            +
                    value.should == input1
         | 
| 52 | 
            +
             | 
| 53 | 
            +
             | 
| 54 | 
            +
             | 
| 55 | 
            +
                    input2 = ["one", 2, { "three" => { "four" => 5.0 } }]
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    value, offset = described_class.decode_array(TableValueEncoder.encode(input2), 1)
         | 
| 58 | 
            +
                    value.size.should == 3
         | 
| 59 | 
            +
                    value.last["three"]["four"].should == 5.0
         | 
| 60 | 
            +
                    value.should == input2
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
             | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
            end
         | 
| @@ -0,0 +1,138 @@ | |
| 1 | 
            +
            # -*- coding: utf-8 -*-
         | 
| 2 | 
            +
            require File.expand_path('../../../spec_helper', __FILE__)
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'time'
         | 
| 5 | 
            +
            require "amq/protocol/table_value_encoder"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module AMQ
         | 
| 8 | 
            +
              module Protocol
         | 
| 9 | 
            +
                describe TableValueEncoder do
         | 
| 10 | 
            +
             | 
| 11 | 
            +
             | 
| 12 | 
            +
                  it "calculates size of string field values" do
         | 
| 13 | 
            +
                    described_class.field_value_size("amqp").should == 9
         | 
| 14 | 
            +
                    described_class.encode("amqp").bytesize.should == 9
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    described_class.field_value_size("amq-protocol").should == 17
         | 
| 17 | 
            +
                    described_class.encode("amq-protocol").bytesize.should == 17
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    described_class.field_value_size("à bientôt").should == 16
         | 
| 20 | 
            +
                    described_class.encode("à bientôt").bytesize.should == 16
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  it "calculates size of integer field values" do
         | 
| 24 | 
            +
                    described_class.field_value_size(10).should == 5
         | 
| 25 | 
            +
                    described_class.encode(10).bytesize.should == 5
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  it "calculates size of float field values (considering them to be 64-bit)" do
         | 
| 29 | 
            +
                    described_class.field_value_size(10.0).should == 9
         | 
| 30 | 
            +
                    described_class.encode(10.0).bytesize.should == 9
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    described_class.field_value_size(120000.0).should == 9
         | 
| 33 | 
            +
                    described_class.encode(120000.0).bytesize.should == 9
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  it "calculates size of boolean field values" do
         | 
| 37 | 
            +
                    described_class.field_value_size(true).should == 2
         | 
| 38 | 
            +
                    described_class.encode(true).bytesize.should == 2
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    described_class.field_value_size(false).should == 2
         | 
| 41 | 
            +
                    described_class.encode(false).bytesize.should == 2
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  it "calculates size of void field values" do
         | 
| 45 | 
            +
                    described_class.field_value_size(nil).should == 1
         | 
| 46 | 
            +
                    described_class.encode(nil).bytesize.should == 1
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  it "calculates size of time field values" do
         | 
| 50 | 
            +
                    t = Time.parse("2011-07-14 01:17:46 +0400")
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    described_class.field_value_size(t).should == 9
         | 
| 53 | 
            +
                    described_class.encode(t).bytesize.should == 9
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
             | 
| 57 | 
            +
                  it "calculates size of basic table field values" do
         | 
| 58 | 
            +
                    input1   = { "key" => "value" }
         | 
| 59 | 
            +
                    described_class.field_value_size(input1).should == 19
         | 
| 60 | 
            +
                    described_class.encode(input1).bytesize.should == 19
         | 
| 61 | 
            +
             | 
| 62 | 
            +
             | 
| 63 | 
            +
                    input2   = { "intval" => 1 }
         | 
| 64 | 
            +
                    described_class.field_value_size(input2).should == 17
         | 
| 65 | 
            +
                    described_class.encode(input2).bytesize.should == 17
         | 
| 66 | 
            +
             | 
| 67 | 
            +
             | 
| 68 | 
            +
                    input3   = { "intval" => 1, "key" => "value" }
         | 
| 69 | 
            +
                    described_class.field_value_size(input3).should == 31
         | 
| 70 | 
            +
                    described_class.encode(input3).bytesize.should == 31
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
             | 
| 74 | 
            +
                  it "calculates size of table field values" do
         | 
| 75 | 
            +
                    input1   = {
         | 
| 76 | 
            +
                      "hashval"    => {
         | 
| 77 | 
            +
                        "protocol" => {
         | 
| 78 | 
            +
                          "name"  => "AMQP",
         | 
| 79 | 
            +
                          "major" => 0,
         | 
| 80 | 
            +
                          "minor" => "9",
         | 
| 81 | 
            +
                          "rev"   => 1.0,
         | 
| 82 | 
            +
                          "spec"  => {
         | 
| 83 | 
            +
                            "url"  => "http://bit.ly/hw2ELX",
         | 
| 84 | 
            +
                            "utf8" => one_point_eight? ? "à bientôt" : "à bientôt".force_encoding(::Encoding::ASCII_8BIT)
         | 
| 85 | 
            +
                          }
         | 
| 86 | 
            +
                        },
         | 
| 87 | 
            +
                        "true"     => true,
         | 
| 88 | 
            +
                        "false"    => false,
         | 
| 89 | 
            +
                        "nil"      => nil
         | 
| 90 | 
            +
                      }
         | 
| 91 | 
            +
                    }
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    described_class.field_value_size(input1).should == 162
         | 
| 94 | 
            +
                    # puts(described_class.encode(input1).inspect)
         | 
| 95 | 
            +
                    described_class.encode(input1).bytesize.should == 162
         | 
| 96 | 
            +
             | 
| 97 | 
            +
             | 
| 98 | 
            +
             | 
| 99 | 
            +
                    input2   = {
         | 
| 100 | 
            +
                      "boolval"      => true,
         | 
| 101 | 
            +
                      "intval"       => 1,
         | 
| 102 | 
            +
                      "strval"       => "Test",
         | 
| 103 | 
            +
                      "timestampval" => Time.parse("2011-07-14 01:17:46 +0400"),
         | 
| 104 | 
            +
                      "floatval"     => 3.14,
         | 
| 105 | 
            +
                      "longval"      => 912598613,
         | 
| 106 | 
            +
                      "hashval"      => { "protocol" => "AMQP091", "true" => true, "false" => false, "nil" => nil }
         | 
| 107 | 
            +
                    }
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    described_class.field_value_size(input2).should == 150
         | 
| 110 | 
            +
                    described_class.encode(input2).bytesize.should == 150
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  it "calculates size of basic array field values" do
         | 
| 114 | 
            +
                    input1 = [1, 2, 3]
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    described_class.field_value_size(input1).should == 20
         | 
| 117 | 
            +
                    described_class.encode(input1).bytesize.should == 20
         | 
| 118 | 
            +
             | 
| 119 | 
            +
             | 
| 120 | 
            +
                    input2 = ["one", "two", "three"]
         | 
| 121 | 
            +
                    described_class.field_value_size(input2).should == 31
         | 
| 122 | 
            +
                    described_class.encode(input2).bytesize.should == 31
         | 
| 123 | 
            +
             | 
| 124 | 
            +
             | 
| 125 | 
            +
                    input3 = ["one", 2, "three"]
         | 
| 126 | 
            +
                    described_class.field_value_size(input3).should == 28
         | 
| 127 | 
            +
                    described_class.encode(input3).bytesize.should == 28
         | 
| 128 | 
            +
             | 
| 129 | 
            +
             | 
| 130 | 
            +
                    input4 = ["one", 2, "three", ["four", 5, [6.0]]]
         | 
| 131 | 
            +
                    described_class.field_value_size(input4).should == 61
         | 
| 132 | 
            +
                    described_class.encode(input4).bytesize.should == 61
         | 
| 133 | 
            +
                  end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
             | 
| 136 | 
            +
                end # TableValueEncoder
         | 
| 137 | 
            +
              end # Protocol
         | 
| 138 | 
            +
            end # AMQ
         | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: amq-protocol
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 5 | 
            -
              prerelease:  | 
| 4 | 
            +
              hash: 3
         | 
| 5 | 
            +
              prerelease: 
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 0
         | 
| 8 8 | 
             
              - 7
         | 
| 9 9 | 
             
              - 0
         | 
| 10 | 
            -
               | 
| 11 | 
            -
              - 2
         | 
| 12 | 
            -
              version: 0.7.0.beta2
         | 
| 10 | 
            +
              version: 0.7.0
         | 
| 13 11 | 
             
            platform: ruby
         | 
| 14 12 | 
             
            authors: 
         | 
| 15 13 | 
             
            - Jakub Stastny
         | 
| @@ -20,7 +18,8 @@ autorequire: | |
| 20 18 | 
             
            bindir: bin
         | 
| 21 19 | 
             
            cert_chain: []
         | 
| 22 20 |  | 
| 23 | 
            -
            date: 2011-07- | 
| 21 | 
            +
            date: 2011-07-17 00:00:00 +04:00
         | 
| 22 | 
            +
            default_executable: 
         | 
| 24 23 | 
             
            dependencies: []
         | 
| 25 24 |  | 
| 26 25 | 
             
            description: "  amq-protocol is an AMQP 0.9.1 serialization library for Ruby. It is not an\n  AMQP client: amq-protocol only handles serialization and deserialization.\n  If you want to write your own AMQP client, this gem can help you with that.\n"
         | 
| @@ -53,6 +52,9 @@ files: | |
| 53 52 | 
             
            - lib/amq/protocol/client.rb
         | 
| 54 53 | 
             
            - lib/amq/protocol/frame.rb
         | 
| 55 54 | 
             
            - lib/amq/protocol/table.rb
         | 
| 55 | 
            +
            - lib/amq/protocol/table_value_decoder.rb
         | 
| 56 | 
            +
            - lib/amq/protocol/table_value_encoder.rb
         | 
| 57 | 
            +
            - lib/amq/protocol/type_constants.rb
         | 
| 56 58 | 
             
            - lib/amq/protocol/version.rb
         | 
| 57 59 | 
             
            - post-processing.rb
         | 
| 58 60 | 
             
            - protocol.rb.pytemplate
         | 
| @@ -67,9 +69,12 @@ files: | |
| 67 69 | 
             
            - spec/amq/protocol/queue_spec.rb
         | 
| 68 70 | 
             
            - spec/amq/protocol/table_spec.rb
         | 
| 69 71 | 
             
            - spec/amq/protocol/tx_spec.rb
         | 
| 72 | 
            +
            - spec/amq/protocol/value_decoder_spec.rb
         | 
| 73 | 
            +
            - spec/amq/protocol/value_encoder_spec.rb
         | 
| 70 74 | 
             
            - spec/amq/protocol_spec.rb
         | 
| 71 75 | 
             
            - spec/spec_helper.rb
         | 
| 72 76 | 
             
            - tasks.rb
         | 
| 77 | 
            +
            has_rdoc: true
         | 
| 73 78 | 
             
            homepage: http://github.com/ruby-amqp/amq-protocol
         | 
| 74 79 | 
             
            licenses: []
         | 
| 75 80 |  | 
| @@ -90,18 +95,16 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 90 95 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 91 96 | 
             
              none: false
         | 
| 92 97 | 
             
              requirements: 
         | 
| 93 | 
            -
              - - " | 
| 98 | 
            +
              - - ">="
         | 
| 94 99 | 
             
                - !ruby/object:Gem::Version 
         | 
| 95 | 
            -
                  hash:  | 
| 100 | 
            +
                  hash: 3
         | 
| 96 101 | 
             
                  segments: 
         | 
| 97 | 
            -
                  -  | 
| 98 | 
            -
                   | 
| 99 | 
            -
                  - 1
         | 
| 100 | 
            -
                  version: 1.3.1
         | 
| 102 | 
            +
                  - 0
         | 
| 103 | 
            +
                  version: "0"
         | 
| 101 104 | 
             
            requirements: []
         | 
| 102 105 |  | 
| 103 106 | 
             
            rubyforge_project: amq-protocol
         | 
| 104 | 
            -
            rubygems_version: 1. | 
| 107 | 
            +
            rubygems_version: 1.6.2
         | 
| 105 108 | 
             
            signing_key: 
         | 
| 106 109 | 
             
            specification_version: 3
         | 
| 107 110 | 
             
            summary: AMQP 0.9.1 encoder & decoder.
         |