File: table_value_encoder.rb

package info (click to toggle)
ruby-amq-protocol 2.3.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 504 kB
  • sloc: ruby: 5,222; python: 248; makefile: 4
file content (121 lines) | stat: -rw-r--r-- 3,380 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# encoding: binary

require "amq/protocol/type_constants"
require "date"

require "amq/protocol/float_32bit"

module AMQ
  module Protocol

    class TableValueEncoder

      #
      # Behaviors
      #

      include TypeConstants

      #
      # API
      #

      def self.encode(value)
        accumulator = String.new

        case value
        when String then
          accumulator << TYPE_STRING
          accumulator << [value.bytesize].pack(PACK_UINT32)
          accumulator << value.dup.force_encoding(accumulator.encoding)
        when Symbol then
          str = value.to_s
          accumulator << TYPE_STRING
          accumulator << [str.bytesize].pack(PACK_UINT32)
          accumulator << str.force_encoding(accumulator.encoding)
        when Integer then
          accumulator << TYPE_SIGNED_64BIT
          accumulator << [value].pack(PACK_INT64_BE)
        when AMQ::Protocol::Float32Bit then
          accumulator << TYPE_32BIT_FLOAT
          accumulator << [value.value].pack(PACK_32BIT_FLOAT)
        when Float then
          accumulator << TYPE_64BIT_FLOAT
          accumulator << [value].pack(PACK_64BIT_FLOAT)
        when true, false then
          accumulator << TYPE_BOOLEAN
          accumulator << (value ? BOOLEAN_TRUE : BOOLEAN_FALSE)
        when Time then
          accumulator << TYPE_TIME
          accumulator << [value.to_i].pack(PACK_INT64_BE)
        when nil then
          accumulator << TYPE_VOID
        when Array then
          accumulator << TYPE_ARRAY
          accumulator << [self.array_size(value)].pack(PACK_UINT32)

          value.each { |v| accumulator << self.encode(v) }
        when Hash then
          accumulator << TYPE_HASH
          accumulator << AMQ::Protocol::Table.encode(value)
        else
          # We don't want to require these libraries.
          if defined?(BigDecimal) && value.is_a?(BigDecimal)
            accumulator << TYPE_DECIMAL
            if value.exponent < 0
              decimals = -value.exponent
              raw = (value * (decimals ** 10)).to_i
              accumulator << [decimals + 1, raw].pack(PACK_UCHAR_UINT32) # somewhat like floating point
            else
              # per spec, the "decimals" octet is unsigned (!)
              accumulator << [0, value.to_i].pack(PACK_UCHAR_UINT32)
            end
          else
            raise ArgumentError.new("Unsupported value #{value.inspect} of type #{value.class.name}")
          end # if
        end # case

        accumulator
      end # self.encode(value)




      def self.field_value_size(value)
        # the type tag takes 1 byte
        acc = 1

        case value
        when String then
          acc += (value.bytesize + 4)
        when Integer then
          acc += 8
        when Float then
          acc += 8
        when Time, DateTime then
          acc += 8
        when true, false then
          acc += 1
        when nil then
          # nothing, type tag alone is enough
        when Hash then
          acc += (4 + Table.hash_size(value))
        when Array then
          acc += (4 + self.array_size(value))
        end

        acc
      end # self.field_value_size(value)


      def self.array_size(value)
        acc = 0
        value.each { |v| acc += self.field_value_size(v) }

        acc
      end # self.array_size(value)

    end # TableValueEncoder

  end # Protocol
end # AMQ