| # |
| # 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 |