457 lines
8.9 KiB
Ruby
457 lines
8.9 KiB
Ruby
#
|
|
# 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
|