blob: 148dee07bb11204d32b5bdc471c85ad59ddd7f9a [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.
#
module Qpid08
# is there a better way to do this?
class StringWriter
def initialize(str = "")
@str = str
end
def write(value)
@str << value
end
def to_s()
return @str
end
end
class EOF < Exception; end
class Encoder
def initialize(out)
@out = out
@bits = []
end
attr_reader(:out)
def encode(type, value)
send(type, value)
end
def bit(b)
@bits << b
end
def octet(o)
pack("C", o)
end
def short(s)
pack("n", s)
end
def long(l)
pack("N", l)
end
def longlong(l)
lower = l & 0xffffffff
upper = (l & ~0xffffffff) >> 32
long(upper)
long(lower)
end
def timestamp(l)
longlong(l)
end
def shortstr(s)
# shortstr is actually octetstr
octet(s.length)
write(s)
end
def longstr(s)
case s
when Hash
table(s)
else
long(s.length)
write(s)
end
end
def table(t)
t = {} if t.nil?
enc = Encoder.new(StringWriter.new())
t.each {|key, value|
enc.shortstr(key)
# I offer this chicken to the gods of polymorphism. May they
# choke on it.
case value
when String
type = :longstr
desc = "S"
when Numeric
type = :long
desc = "I"
else
raise Exception.new("unknown table value: #{value.class}")
end
enc.write(desc)
enc.encode(type, value)
}
longstr(enc.out.to_s())
end
def write(str)
flushbits()
@out.write(str)
# puts "OUT #{str.inspect()}"
end
def pack(fmt, *args)
write(args.pack(fmt))
end
def flush()
flushbits()
end
private
def flushbits()
if @bits.empty? then return end
bytes = []
index = 0
@bits.each {|b|
bytes << 0 if index == 0
if b then bytes[-1] |= 1 << index end
index = (index + 1) % 8
}
@bits.clear()
bytes.each {|b|
octet(b)
}
end
end
class StringReader
def initialize(str)
@str = str
@index = 0
end
def read(n)
result = @str[@index, n]
@index += result.length
return result
end
end
class Decoder
def initialize(_in)
@in = _in
@bits = []
end
def decode(type)
return send(type)
end
def bit()
if @bits.empty?
byte = octet()
7.downto(0) {|i|
@bits << (byte[i] == 1)
}
end
return @bits.pop()
end
def octet()
return unpack("C", 1)
end
def short()
return unpack("n", 2)
end
def long()
return unpack("N", 4)
end
def longlong()
upper = long()
lower = long()
return upper << 32 | lower
end
def timestamp()
return longlong()
end
def shortstr()
# shortstr is actually octetstr
return read(octet())
end
def longstr()
return read(long())
end
def table()
dec = Decoder.new(StringReader.new(longstr()))
result = {}
while true
begin
key = dec.shortstr()
rescue EOF
break
end
desc = dec.read(1)
case desc
when "S"
value = dec.longstr()
when "I"
value = dec.long()
else
raise Exception.new("unrecognized descriminator: #{desc.inspect()}")
end
result[key] = value
end
return result
end
def read(n)
return "" if n == 0
result = @in.read(n)
if result.nil? or result.empty?
raise EOF.new()
else
# puts " IN #{result.inspect()}"
return result
end
end
def unpack(fmt, size)
result = read(size).unpack(fmt)
if result.length == 1
return result[0]
else
return result
end
end
end
end