File: //usr/share/rubygems-integration/all/gems/amq-protocol-2.3.2/codegen/protocol.rb.pytemplate
# encoding: utf-8
# encoding: binary
# THIS IS AN AUTOGENERATED FILE, DO NOT MODIFY
# IT DIRECTLY ! FOR CHANGES, PLEASE UPDATE FILES
# IN THE ./codegen DIRECTORY OF THE AMQ-PROTOCOL REPOSITORY.<% import codegen_helpers as helpers %><% import re, os, codegen %>
require "amq/pack"
require "amq/protocol/table"
require "amq/protocol/frame"
require "amq/protocol/constants"
require "amq/protocol/exceptions"
module AMQ
module Protocol
PROTOCOL_VERSION = "${spec.major}.${spec.minor}.${spec.revision}".freeze
PREAMBLE = "${'AMQP\\x00\\x%02x\\x%02x\\x%02x' % (spec.major, spec.minor, spec.revision)}".freeze
DEFAULT_PORT = ${spec.port}
# @return [Array] Collection of subclasses of AMQ::Protocol::Class.
def self.classes
Protocol::Class.classes
end
# @return [Array] Collection of subclasses of AMQ::Protocol::Method.
def self.methods
Protocol::Method.methods
end
% for tuple in spec.constants:
% if tuple[2] == "soft-error" or tuple[2] == "hard-error":
class ${codegen.to_ruby_class_name(tuple[0])} < ${codegen.to_ruby_class_name(tuple[2])}
VALUE = ${tuple[1]}
end
% endif
% endfor
class Class
@classes = Array.new
def self.method_id
@method_id
end
def self.name
@name
end
def self.inherited(base)
if self == Protocol::Class
@classes << base
end
end
def self.classes
@classes
end
end
class Method
@methods = Array.new
def self.method_id
@method_id
end
def self.name
@name
end
def self.index
@index
end
def self.inherited(base)
if self == Protocol::Method
@methods << base
end
end
def self.methods
@methods
end
def self.split_headers(user_headers)
properties, headers = {}, {}
user_headers.each do |key, value|
# key MUST be a symbol since symbols are not garbage-collected
if Basic::PROPERTIES.include?(key)
properties[key] = value
else
headers[key] = value
end
end
return [properties, headers]
end
def self.encode_body(body, channel, frame_size)
return [] if body.empty?
# 8 = 1 + 2 + 4 + 1
# 1 byte of frame type
# 2 bytes of channel number
# 4 bytes of frame payload length
# 1 byte of payload trailer FRAME_END byte
limit = frame_size - 8
return [BodyFrame.new(body, channel)] if body.bytesize < limit
# Otherwise String#slice on 1.9 will operate with code points,
# and we need bytes. MK.
body.force_encoding("ASCII-8BIT") if RUBY_VERSION.to_f >= 1.9
array = Array.new
while body && !body.empty?
payload, body = body[0, limit], body[limit, body.length - limit]
array << BodyFrame.new(payload, channel)
end
array
end
def self.instantiate(*args, &block)
self.new(*args, &block)
end
end
% for klass in spec.classes :
class ${klass.constant_name} < Protocol::Class
@name = "${klass.name}"
@method_id = ${klass.index}
% if klass.fields: ## only the Basic class has fields (refered as properties in the JSON)
PROPERTIES = [
% for field in klass.fields:
:${field.ruby_name}, # ${spec.resolveDomain(field.domain)}
% endfor
]
% for f in klass.fields:
# <% i = klass.fields.index(f) %>1 << ${15 - i}
def self.encode_${f.ruby_name}(value)
buffer = ''
% for line in helpers.genSingleEncode(spec, "value", f.domain):
${line}
% endfor
[${i}, ${"0x%04x" % ( 1 << (15-i),)}, buffer]
end
% endfor
% endif
% if klass.name == "basic" :
def self.encode_properties(body_size, properties)
pieces, flags = [], 0
properties.reject {|key, value| value.nil?}.each do |key, value|
i, f, result = self.__send__(:"encode_#{key}", value)
flags |= f
pieces[i] = result
end
# result = [${klass.index}, 0, body_size, flags].pack('n2Qn')
result = [${klass.index}, 0].pack(PACK_UINT16_X2)
result += AMQ::Pack.pack_uint64_big_endian(body_size)
result += [flags].pack(PACK_UINT16)
pieces_joined = pieces.join(EMPTY_STRING)
result.force_encoding(pieces_joined.encoding) + pieces_joined
end
# THIS DECODES ONLY FLAGS
DECODE_PROPERTIES = {
% for f in klass.fields:
${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)} => :${f.ruby_name},
% endfor
}
DECODE_PROPERTIES_TYPE = {
% for f in klass.fields:
${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)} => :${spec.resolveDomain(f.domain)},
% endfor
}
# Hash doesn't give any guarantees on keys order, we will do it in a
# straightforward way
DECODE_PROPERTIES_KEYS = [
% for f in klass.fields:
${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)},
% endfor
]
def self.decode_properties(data)
offset, data_length, properties = 0, data.bytesize, {}
compressed_index = data[offset, 2].unpack(PACK_UINT16)[0]
offset += 2
while data_length > offset
DECODE_PROPERTIES_KEYS.each do |key|
next unless compressed_index >= key
compressed_index -= key
name = DECODE_PROPERTIES[key] || raise(RuntimeError.new("No property found for index #{index.inspect}!"))
case DECODE_PROPERTIES_TYPE[key]
when :shortstr
size = data[offset, 1].unpack(PACK_CHAR)[0]
offset += 1
result = data[offset, size]
when :octet
size = 1
result = data[offset, size].unpack(PACK_CHAR).first
when :timestamp
size = 8
result = Time.at(data[offset, size].unpack(PACK_UINT64_BE).last)
when :table
size = 4 + data[offset, 4].unpack(PACK_UINT32)[0]
result = Table.decode(data[offset, size])
end
properties[name] = result
offset += size
end
end
properties
end
% endif
% for method in klass.methods:
class ${method.constant_name} < Protocol::Method
@name = "${klass.name}.${method.name}"
@method_id = ${method.index}
@index = ${method.binary()}
@packed_indexes = [${klass.index}, ${method.index}].pack(PACK_UINT16_X2).freeze
% if (spec.type == "client" and method.accepted_by("client")) or (spec.type == "server" and method.accepted_by("server") or spec.type == "all"):
# @return
def self.decode(data)
offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method
% for line in helpers.genDecodeMethodDefinition(spec, method):
${line}
% endfor
% if (method.klass.name == "connection" or method.klass.name == "channel") and method.name == "close":
self.new(${', '.join([f.ruby_name for f in method.arguments])})
% else:
self.new(${', '.join([f.ruby_name for f in method.arguments])})
% endif
end
% if len(method.arguments) > 0:
attr_reader ${', '.join([":" + f.ruby_name for f in method.arguments])}
% endif
def initialize(${', '.join([f.ruby_name for f in method.arguments])})
% for f in method.arguments:
@${f.ruby_name} = ${f.ruby_name}
% endfor
end
% endif
def self.has_content?
% if method.hasContent:
true
% else:
false
% endif
end
% if (spec.type == "client" and method.accepted_by("server")) or (spec.type == "server" and method.accepted_by("client")) or spec.type == "all":
# @return
# ${method.params()}
% if klass.name == "connection":
def self.encode(${(", ").join(method.not_ignored_args())})
% else:
def self.encode(${(", ").join(["channel"] + method.not_ignored_args())})
% endif
% for argument in method.ignored_args():
${codegen.convert_to_ruby(argument)}
% endfor
% if klass.name == "connection":
channel = 0
% endif
buffer = @packed_indexes.dup
% for line in helpers.genEncodeMethodDefinition(spec, method):
${line}
% endfor
% if "payload" in method.args() or "user_headers" in method.args():
frames = [MethodFrame.new(buffer, channel)]
% if "user_headers" in method.args():
properties, _headers = self.split_headers(user_headers)
if properties.nil? or properties.empty?
raise RuntimeError.new("Properties can not be empty!")
end
properties_payload = Basic.encode_properties(payload.bytesize, properties)
frames << HeaderFrame.new(properties_payload, channel)
% endif
% if "payload" in method.args():
frames += self.encode_body(payload, channel, frame_size)
frames
% endif
% else:
MethodFrame.new(buffer, channel)
% endif
end
% endif
end
% endfor
end
% endfor
METHODS = begin
Method.methods.inject(Hash.new) do |hash, klass|
hash.merge!(klass.index => klass)
end
end
end
end