summaryrefslogtreecommitdiff
path: root/qpid/ruby/lib/qpid/codec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/ruby/lib/qpid/codec.rb')
-rw-r--r--qpid/ruby/lib/qpid/codec.rb457
1 files changed, 457 insertions, 0 deletions
diff --git a/qpid/ruby/lib/qpid/codec.rb b/qpid/ruby/lib/qpid/codec.rb
new file mode 100644
index 0000000000..a3b5d101c4
--- /dev/null
+++ b/qpid/ruby/lib/qpid/codec.rb
@@ -0,0 +1,457 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'qpid/packer.rb'
+require 'iconv'
+
+module Qpid
+
+ class Codec
+
+ include Qpid::Packer
+
+ attr_reader :spec
+
+ def initialize(spec = "")
+ @spec = spec
+ end
+
+ def write_void(v)
+ unless v.nil?
+ raise Exception.new("void not nil: #{v}")
+ end
+ end
+
+ def read_void
+ return nil
+ end
+
+ def write_bit(b)
+ unless b
+ raise Exception.new("bit is nil: #{b}")
+ end
+ end
+
+ def read_bit
+ return true
+ end
+
+ def read_uint8
+ return unpack("C", 1)
+ end
+
+ def write_uint8(n)
+ return pack("C", n)
+ end
+
+ def read_int8
+ return unpack("c", 1)
+ end
+
+ def write_int8(n)
+ pack("c", n)
+ end
+
+ def read_char
+ return unpack("c", 1)
+ end
+
+ def write_char(c)
+ pack("c")
+ end
+
+ def read_boolean
+ return read_uint8 != 0
+ end
+
+ def write_boolean(b)
+ n = 0
+ n = 1 if b != 0
+ write_uint8(n)
+ end
+
+ def read_uint16
+ return unpack("n", 2)
+ end
+
+ def write_uint16(n)
+ pack("n", n)
+ end
+
+ def read_int16
+ # XXX: holy moly.. pack/unpack doesn't have signed network byte order. Crazy hackery.
+ val = unpack("n", 2)
+ val -= 2 ** 16 if val >= 2 ** 15
+ return val
+ end
+
+ def write_int16(n)
+ # XXX: Magically this one works even though it's not signed.
+ pack("n", n)
+ end
+
+ def read_uint32
+ return unpack("N", 4)
+ end
+
+ def write_uint32(n)
+ pack("N", n)
+ end
+
+ def read_int32
+ # Again no pack/unpack for signed int
+ return unpack("N", 4)
+ end
+
+ def write_int32(n)
+ # FIXME
+ pack("N", n)
+ end
+
+ def read_float
+ return unpack("g", 4)
+ end
+
+ def write_float(n)
+ pack("g", n)
+ end
+
+ def read_sequence_no
+ return read_uint32.to_serial
+ end
+
+ def write_sequence_no(n)
+ write_uint32(n.value)
+ end
+
+ def encode_64bit(num, signed = false)
+ b = []
+
+ if num < 0 && signed
+ num += 2 ** 64
+ end
+
+ (0..7).each do |c|
+ d = 7 - c
+ b[c] = (num & (0xff << d * 8)) >> d * 8
+ end
+ pack('C8', *b)
+ end
+
+
+ def decode_64bit(signed = false)
+ # Silly ruby pack/unpack does not implement 64 bit network byte order
+ # encode/decode.
+ a = unpack('C8', 8)
+ num = 0
+ (0..7).each do |c|
+ d = 7 - c
+ num |= a[c] << 8 * d
+ end
+
+ if signed && num >= 2 ** 63
+ num -= 2 ** 64
+ end
+ return num
+ end
+
+ def read_uint64
+ return decode_64bit
+ end
+
+ def write_uint64(n)
+ encode_64bit(n)
+ end
+
+ def read_int64
+ return decode_64bit(signed = true)
+ end
+
+ def write_int64(n)
+ encode_64bit(n, signed = true)
+ end
+
+ def read_datetime
+ return read_uint64
+ end
+
+ def write_datetime(n)
+ write_uint64(n)
+ end
+
+ def read_double
+ return unpack("G", 8)
+ end
+
+ def write_double(n)
+ pack("G", n)
+ end
+
+ def read_vbin8
+ # XXX
+ return read(read_uint8)
+ end
+
+ def write_vbin8(b)
+ # XXX
+ write_uint8(b.length)
+ write(b)
+ end
+
+ def read_str8
+ # FIXME: Check iconv.. I think this will throw if there are odd characters.
+ return Iconv.conv("ASCII", "UTF-8", read_vbin8)
+ end
+
+ def write_str8(s)
+ write_vbin8(Iconv.conv("UTF-8", "ASCII", s))
+ end
+
+ def read_str16
+ return Iconv.conv("ASCII", "UTF-8", read_vbin16)
+ end
+
+ def write_str16(s)
+ write_vbin16(Iconv.conv("UTF-8", "ASCII", s))
+ end
+
+ def read_vbin16
+ # XXX: Using read method?
+ return read(read_uint16)
+ end
+
+ def write_vbin16(b)
+ write_uint16(b.length)
+ write(b)
+ end
+
+ def read_sequence_set
+ # FIXME: Need datatypes
+ result = RangedSet.new
+ size = read_uint16
+ nranges = size / 8
+ nranges.times do |i|
+ lower = read_sequence_no
+ upper = read_sequence_no
+ result.add(lower, upper)
+ end
+ return result
+ end
+
+ def write_sequence_set(ss)
+ size = 8 * ss.ranges.length
+ write_uint16(size)
+ ss.ranges.each do |range|
+ write_sequence_no(range.lower)
+ write_sequence_no(range.upper)
+ end
+ end
+
+ def read_vbin32
+ return read(read_uint32)
+ end
+
+ def write_vbin32(b)
+ write_uint32(b.length)
+ write(b)
+ end
+
+ def write_map(m)
+ sc = StringCodec.new(@spec)
+ unless m.nil?
+ sc.write_uint32(m.size)
+ m.each do |k, v|
+ unless type = @spec.encoding(v.class)
+ raise Exception.new("no encoding for: #{v.class}")
+ end
+ sc.write_str8(k)
+ sc.write_uint8(type.code)
+ type.encode(sc, v)
+ end
+ end
+ write_vbin32(sc.encoded)
+ end
+
+ def read_map
+ sc = StringCodec.new(@spec, read_vbin32)
+ return nil unless sc.encoded
+ count = sc.read_uint32
+ result = nil
+ if count
+ result = {}
+ until sc.encoded.empty?
+ k = sc.read_str8
+ code = sc.read_uint8
+ type = @spec.types[code]
+ v = type.decode(sc)
+ result[k] = v
+ end
+ end
+ return result
+ end
+
+ def write_array(a)
+ sc = StringCodec.new(@spec)
+ unless a.nil?
+ if a.length > 0
+ type = @spec.encoding(a[0].class)
+ else
+ type = @spec.encoding(nil.class)
+ end
+ sc.write_uint8(type.code)
+ sc.write_uint32(a.size)
+ a.each { |o| type.encode(sc, o) }
+ end
+ write_vbin32(sc.encoded)
+ end
+
+ def read_array
+ sc = StringCodec.new(@spec, read_vbin32)
+ return nil if not sc.encoded
+ type = @spec.types[sc.read_uint8]
+ count = sc.read_uint32
+ result = nil
+ if count
+ result = []
+ count.times { |i| result << (type.decode(sc)) }
+ end
+ return result
+ end
+
+ def write_list(l)
+ sc = StringCodec.new(@spec)
+ unless l.nil?
+ sc.write_uint32(l.length)
+ l.each do |o|
+ type = @spec.encoding(o.class)
+ sc.write_uint8(type.code)
+ type.encode(sc, o)
+ end
+ end
+ write_vbin32(sc.encoded)
+ end
+
+ def read_list
+ sc = StringCodec.new(@spec, read_vbin32)
+ return nil if not sc.encoded
+ count = sc.read_uint32
+ result = nil
+ if count
+ result = []
+ count.times do |i|
+ type = @spec.types[sc.read_uint8]
+ result << type.decode(sc)
+ end
+ end
+ return result
+ end
+
+ def read_struct32
+ size = read_uint32
+ code = read_uint16
+ type = @spec.structs[code]
+ # XXX: BLEH!
+ fields = type.decode_fields(self)
+ return Qpid::struct(type, fields)
+ end
+
+ def write_struct32(value)
+ type = value.st_type
+ sc = StringCodec.new(@spec)
+ sc.write_uint16(type.code)
+ type.encode_fields(sc, value)
+ write_vbin32(sc.encoded)
+ end
+
+ def read_control
+ cntrl = @spec.controls[read_uint16]
+ return Qpid::struct(cntrl, cntrl.decode_fields(self))
+ end
+
+ def write_control(ctrl)
+ type = ctrl.st_type
+ write_uint16(type.code)
+ type.encode_fields(self, ctrl)
+ end
+
+ def read_command
+ type = @spec.commands[read_uint16]
+ hdr = @spec[:header].decode(self)
+ cmd = Qpid::struct(type, type.decode_fields(self))
+ return hdr, cmd
+ end
+
+ def write_command(hdr, cmd)
+ type = cmd.st_type
+ write_uint16(type.code)
+ hdr.st_type.encode(self, hdr)
+ type.encode_fields(self, cmd)
+ end
+
+ def read_size(width)
+ if width > 0
+ return send(:"read_uint#{width * 8}")
+ end
+ end
+
+ def write_size(width, n)
+ if width > 0
+ send(:"write_uint#{width * 8}", n)
+ end
+ end
+
+ def read_uuid
+ return unpack("a16", 16)
+ end
+
+ def write_uuid(s)
+ pack("a16", s)
+ end
+
+ def read_bin128
+ return unpack("a16", 16)
+ end
+
+ def write_bin128(b)
+ pack("a16", b)
+ end
+
+ end
+
+ class StringCodec < Codec
+
+ def initialize(spec, encoded = "")
+ @spec = spec
+ @encoded = encoded
+ end
+
+ attr_reader :encoded
+
+ def write(s)
+ @encoded += s
+ end
+
+ def read(n)
+ return "" if n.nil?
+ result = @encoded[0...n]
+ @encoded = @encoded[n...@encoded.size] || ""
+ return result
+ end
+ end
+end