#!/usr/bin/env 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. # # Usage: output_directory xml_spec_file [xml_spec_file...] # $: << '..' require 'cppgen' class StructGen < CppGen def initialize(outdir, amqp) super(outdir, amqp) end SizeMap={ "Octet"=>1, "Short"=>2, "Long"=>4, "LongLong"=>8, "int8"=>1, "int16"=>2, "int32"=>4, "int64"=>8, "uint8"=>1, "uint16"=>2, "uint32"=>4, "uint64"=>8, "timestamp"=>8 } StringSizeMap={ "LongString"=>4, "MediumString"=>2, "ShortString"=>1 } SizeType={ 1=>"Octet", 2=>"Short", 4=>"Long", 8=>"LongLong" } ValueTypes=["uint8_t", "uint16_t", "uint32_t", "uint64_t"] def is_packed(s) s.pack and s.pack != "0" end def execution_header?(s) s.is_a? AmqpMethod and not s.parent.control? # s.kind_of? AmqpMethod and s.parent.name.include?("010") and not s.parent.control? end def has_bitfields_only(s) s.fields.select {|f| f.type_ != "bit"}.empty? end def default_initialisation(s) params = s.fields.select {|f| ValueTypes.include?(f.cpptype.name) || (!is_packed(s) && f.type_ == "bit")} strings = params.collect {|f| "#{f.cppname}(#{f.default_value})"} strings << "flags(0)" if (is_packed(s)) if strings.empty? return "" else return " : " + strings.join(", ") end end def printable_form(f) if (f.cpptype.name == "uint8_t") return "(int) " + f.cppname elsif (f.type_ == "bit") return "get#{f.name.caps}()" else return f.cppname end end def flag_mask(s, i) pos = s.pack.to_i*8 - 8 - (i/8)*8 + (i % 8) return "(1 << #{pos})" end def encode_packed_struct(s) genl s.cpp_pack_type.encode('flags', 'buffer') process_packed_fields(s) { |f, i| encode_packed_field(s, f, i) unless f.type_ == "bit" } end def decode_packed_struct(s) genl "#{s.cpp_pack_type.decode('flags', 'buffer')}" process_packed_fields(s) { |f, i| decode_packed_field(s, f, i) unless f.type_ == "bit" } end def size_packed_struct(s) genl "total += #{s.pack};" process_packed_fields(s) { |f, i| size_packed_field(s, f, i) unless f.type_ == "bit" } end def print_packed_struct(s) process_packed_fields(s) { |f, i| print_packed_field(s, f, i) } end def encode_packed_field(s, f, i) genl "if (flags & #{flag_mask(s, i)})" indent { genl f.cpptype.encode(f.cppname,"buffer") } end def decode_packed_field(s, f, i) genl "if (flags & #{flag_mask(s, i)})" indent { genl f.cpptype.decode(f.cppname,"buffer") } end def size_packed_field(s, f, i) genl "if (flags & #{flag_mask(s, i)})" indent { generate_size(f, []) } end def print_packed_field(s, f, i) classname = s.cppname if (s.kind_of? AmqpMethod) classname = s.body_name end genl "if (flags & #{flag_mask(s, i)})" indent { unless (classname == "ConnectionStartOkBody" && f.name == "response") genl "out << \"#{f.name}=\" << #{printable_form(f)} << \"; \";" else genl "out << \"response=\" << \"xxxxxx\" << \"; \";" end } end def generate_encode(f, combined) if (f.type_ == "bit") genl "uint8_t #{f.cppname}_bits = #{f.cppname};" count = 0 combined.each { |c| genl "#{f.cppname}_bits |= #{c.cppname} << #{count += 1};" } genl "buffer.putOctet(#{f.cppname}_bits);" else genl f.cpptype.encode(f.cppname,"buffer") end end def generate_decode(f, combined) if (f.type_ == "bit") genl "uint8_t #{f.cppname}_bits = buffer.getOctet();" genl "#{f.cppname} = 1 & #{f.cppname}_bits;" count = 0 combined.each { |c| genl "#{c.cppname} = (1 << #{count += 1}) & #{f.cppname}_bits;" } else genl f.cpptype.decode(f.cppname,"buffer") end end def generate_size(f, combined) if (f.type_ == "bit") names = ([f] + combined).collect {|g| g.cppname} genl "total += 1;//#{names.join(", ")}" else size = SizeMap[f.cpptype.encoded] if (size) genl "total += #{size};//#{f.cppname}" elsif (f.cpptype.name == "SequenceNumberSet") genl "total += #{f.cppname}.encodedSize();" elsif (size = StringSizeMap[f.cpptype.encoded]) genl "total += #{size} + #{f.cppname}.size();" else genl "total += #{f.cppname}.encodedSize();" end end end def check_field(f) if (size = StringSizeMap[f.cpptype.encoded] and size < 4) limit = 2 ** (size * 8) genl "if (#{f.cppname}.size() >= #{limit}) throw IllegalArgumentException(\"Value for #{f.cppname} is too large\");" end end def process_packed_fields(s) s.fields.each { |f| yield f, s.fields.index(f) } end def process_fields(s) last = nil count = 0 bits = [] s.fields.each { |f| if (last and last.bit? and f.bit? and count < 7) count += 1 bits << f else if (last and last.bit?) yield last, bits count = 0 bits = [] end if (not f.bit?) yield f end last = f end } if (last and last.bit?) yield last, bits end end def all_fields_via_accessors(s) s.fields.collect { |f| "get#{f.name.caps}()" }.join(", ") end def methodbody_extra_defs(s) if (s.parent.control?) genl "virtual uint8_t type() const { return 0;/*control segment*/ }" end gen < ResultType invoke(T& invocable) const { return invocable.#{s.cppname}(#{all_fields_via_accessors(s)}); } using AMQMethodBody::accept; void accept(MethodBodyConstVisitor& v) const { v.visit(*this); } boost::intrusive_ptr clone() const { return BodyFactory::copy(*this); } ClassId amqpClassId() const { return CLASS_ID; } MethodId amqpMethodId() const { return METHOD_ID; } bool isContentBearing() const { return #{s.content ? "true" : "false" }; } bool resultExpected() const { return #{s.result ? "true" : "false"}; } bool responseExpected() const { return #{s.responses().empty? ? "false" : "true"}; } EOS end def define_constructor(name, s) if (s.fields.size > 0) genl "#{name}(" if (s.kind_of? AmqpMethod) indent {gen "ProtocolVersion, "} end indent { gen s.fields.collect { |f| "#{f.cpptype.param} _#{f.cppname}" }.join(",\n") } genl ") : " if (is_packed(s)) initialisers = s.fields.select { |f| f.type_ != "bit"}.collect { |f| "#{f.cppname}(_#{f.cppname})"} initialisers << "flags(0)" indent { gen initialisers.join(",\n") } genl "{" indent { process_packed_fields(s) { |f, i| genl "set#{f.name.caps}(_#{f.cppname});" if f.type_ == "bit"} process_packed_fields(s) { |f, i| genl "flags |= #{flag_mask(s, i)};" unless f.type_ == "bit"} s.fields.each { |f| check_field(f) } } genl "}" else indent { gen s.fields.collect { |f| " #{f.cppname}(_#{f.cppname})" }.join(",\n") } genl "{" indent { s.fields.each { |f| check_field(f) } } genl "}" end end #default constructors: if (s.kind_of? AmqpMethod) genl "#{name}(ProtocolVersion=ProtocolVersion()) #{default_initialisation(s)} {}" end if (s.kind_of? AmqpStruct) genl "#{name}() #{default_initialisation(s)} {}" end end def define_packed_field_accessors(s, f, i) if (s.kind_of? AmqpMethod) define_packed_field_accessors_for_method(s, f, i) else define_packed_field_accessors_for_struct(s, f, i) end end def define_packed_field_accessors_for_struct(s, f, i) if (f.type_ == "bit") genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" indent { genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};" genl "else flags &= ~#{flag_mask(s, i)};" } genl "}" genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" else genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" indent { genl "#{f.cppname} = _#{f.cppname};" genl "flags |= #{flag_mask(s, i)};" check_field(f) } genl "}" genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return #{f.cppname}; }" if (f.cpptype.name == "FieldTable") genl "#{f.cpptype.name}& #{s.cppname}::get#{f.name.caps}() {" indent { genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set' genl "return #{f.cppname};" } genl "}" end genl "bool #{s.cppname}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" genl "void #{s.cppname}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }" end genl "" end def define_packed_field_accessors_for_method(s, f, i) if (f.type_ == "bit") genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" indent { genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};" genl "else flags &= ~#{flag_mask(s, i)};" } genl "}" genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" else genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" indent { genl "#{f.cppname} = _#{f.cppname};" genl "flags |= #{flag_mask(s, i)};" check_field(f) } genl "}" genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return #{f.cppname}; }" if (f.cpptype.name == "FieldTable") genl "#{f.cpptype.name}& #{s.body_name}::get#{f.name.caps}() {" indent { genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set' genl "return #{f.cppname};" } genl "}" end genl "bool #{s.body_name}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" genl "void #{s.body_name}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }" end genl "" end def define_packed_accessors(s) process_packed_fields(s) { |f, i| define_packed_field_accessors(s, f, i) } end def declare_packed_accessors(f) genl "QPID_COMMON_EXTERN void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname});"; genl "QPID_COMMON_EXTERN #{f.cpptype.ret} get#{f.name.caps}() const;" if (f.cpptype.name == "FieldTable") genl "QPID_COMMON_EXTERN #{f.cpptype.name}& get#{f.name.caps}();" end if (f.type_ != "bit") #extra 'accessors' for packed fields: genl "QPID_COMMON_EXTERN bool has#{f.name.caps}() const;" genl "QPID_COMMON_EXTERN void clear#{f.name.caps}Flag();" end end def define_accessors(f) genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" indent { genl "#{f.cppname} = _#{f.cppname};" check_field(f) } genl "}" genl "#{f.cpptype.ret} get#{f.name.caps}() const { return #{f.cppname}; }" if (f.cpptype.name == "FieldTable") genl "#{f.cpptype.name}& get#{f.name.caps}() { return #{f.cppname}; }" end end def define_struct(s) classname = s.cppname inheritance = "" if (s.kind_of? AmqpMethod) classname = s.body_name if (execution_header?(s)) inheritance = ": public ModelMethod" else inheritance = ": public AMQMethodBody" end else public_api("qpid/framing/#{classname}.h") # Non-method structs are public end h_file("qpid/framing/#{classname}.h") { if (s.kind_of? AmqpMethod) gen < #include "qpid/framing/amqp_types_full.h" #include "qpid/framing/reply_exceptions.h" #include "qpid/CommonImportExport.h" namespace qpid { namespace framing { class QPID_COMMON_CLASS_EXTERN #{classname} #{inheritance} { EOS if (is_packed(s)) indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" unless f.type_ == "bit"} } indent { genl "#{s.cpp_pack_type.name} flags;" } else indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" } } end genl "public:" if (s.kind_of? AmqpMethod) indent { genl "static const ClassId CLASS_ID = #{s.parent.code};" } indent { genl "static const MethodId METHOD_ID = #{s.code};" } end if (s.kind_of? AmqpStruct) if (s.code) indent { genl "static const uint16_t TYPE = #{s.full_code};" } end end indent { define_constructor(classname, s) genl "" if (is_packed(s)) s.fields.each { |f| declare_packed_accessors(f) } else s.fields.each { |f| define_accessors(f) } end } if (s.kind_of? AmqpMethod) methodbody_extra_defs(s) end if (s.kind_of? AmqpStruct) indent {genl "QPID_COMMON_EXTERN friend std::ostream& operator<<(std::ostream&, const #{classname}&);" } end gen < 0 || execution_header?(s)) buffer = "buffer" else buffer = "/*buffer*/" end gen <