diff options
Diffstat (limited to 'qpid/ruby/lib/qpid/codec.rb')
-rw-r--r-- | qpid/ruby/lib/qpid/codec.rb | 457 |
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 |