blob: 009b1eef536847dab719c3e27acb818691be8d84 [file] [log] [blame]
#
# 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
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.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.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.type
write_uint16(type.code)
hdr.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