diff options
author | Tony Garnock-Jones <tonyg@kcbbs.gen.nz> | 2009-04-25 15:23:04 +0100 |
---|---|---|
committer | Tony Garnock-Jones <tonyg@kcbbs.gen.nz> | 2009-04-25 15:23:04 +0100 |
commit | 8bf174bc0a3d682ff9c8c11435008f1adf3c288f (patch) | |
tree | 8459cbf3aa335577bf71ac778200931c9667e19d | |
parent | f21388d2c17dd248304e67da4751382eaa9078fc (diff) | |
download | rabbitmq-c-github-ask-8bf174bc0a3d682ff9c8c11435008f1adf3c288f.tar.gz |
Start work on codegen
-rw-r--r-- | .hgignore | 9 | ||||
-rw-r--r-- | configure.ac | 7 | ||||
-rw-r--r-- | librabbitmq/Makefile.am | 12 | ||||
-rw-r--r-- | librabbitmq/codegen.py | 342 | ||||
-rw-r--r-- | librabbitmq/f.c | 10 |
5 files changed, 379 insertions, 1 deletions
@@ -6,4 +6,11 @@ ^install-sh$ ^ltmain.sh$ ^missing$ -(^|/)Makefile\.in$ +(^|/)\.libs$ +(^|/)\.deps$ +\.(o|lo)$ +^librabbitmq/amqp_framing\.[ch]$ +^Makefile(\.in)?$ +^librabbitmq/Makefile(\.in)?$ +^config\.h$ +^libtool$ diff --git a/configure.ac b/configure.ac index f1fa0e9..9e27ae0 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,13 @@ if test "x$GCC" = "xyes"; then fi fi +sibling_codegen_dir=$(pwd)/../rabbitmq-codegen +AMQP_CODEGEN_DIR=$(test -d $sibling_codegen_dir && echo $sibling_codegen_dir || echo codegen) +AMQP_SPEC_JSON_PATH=$AMQP_CODEGEN_DIR/amqp-0.8.json + +AC_SUBST(AMQP_CODEGEN_DIR) +AC_SUBST(AMQP_SPEC_JSON_PATH) + AC_OUTPUT( Makefile librabbitmq/Makefile diff --git a/librabbitmq/Makefile.am b/librabbitmq/Makefile.am index 2e7f7c2..bfbd233 100644 --- a/librabbitmq/Makefile.am +++ b/librabbitmq/Makefile.am @@ -1,3 +1,15 @@ lib_LTLIBRARIES = librabbitmq.la librabbitmq_la_SOURCES = f.c +nodist_librabbitmq_la_SOURCES = amqp_framing.c +librabbitmq_la_INCLUDES = amqp_framing.h +BUILT_SOURCES = amqp_framing.h amqp_framing.c +CLEANFILES = amqp_framing.h amqp_framing.c + +PYTHON = python2.5 + +amqp_framing.h: $(AMQP_SPEC_JSON_PATH) codegen.py + PYTHONPATH=$(AMQP_CODEGEN_DIR) $(PYTHON) codegen.py header $< $@ + +amqp_framing.c: $(AMQP_SPEC_JSON_PATH) codegen.py + PYTHONPATH=$(AMQP_CODEGEN_DIR) $(PYTHON) codegen.py body $< $@ diff --git a/librabbitmq/codegen.py b/librabbitmq/codegen.py new file mode 100644 index 0000000..d73db73 --- /dev/null +++ b/librabbitmq/codegen.py @@ -0,0 +1,342 @@ +## The contents of this file are subject to the Mozilla Public License +## Version 1.1 (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.mozilla.org/MPL/ +## +## Software distributed under the License is distributed on an "AS IS" +## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +## License for the specific language governing rights and limitations +## under the License. +## +## The Original Code is RabbitMQ. +## +## The Initial Developers of the Original Code are LShift Ltd, +## Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd. +## +## Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd, +## Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd +## are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial +## Technologies LLC, and Rabbit Technologies Ltd. +## +## Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift +## Ltd. Portions created by Cohesive Financial Technologies LLC are +## Copyright (C) 2007-2009 Cohesive Financial Technologies +## LLC. Portions created by Rabbit Technologies Ltd are Copyright +## (C) 2007-2009 Rabbit Technologies Ltd. +## +## All Rights Reserved. +## +## Contributor(s): ______________________________________. +## + +from __future__ import nested_scopes + +from amqp_codegen import * +import string +import re + +cTypeMap = { + 'octet': 'uint8_t', + 'shortstr': 'amqp_bytes_t', + 'longstr': 'amqp_bytes_t', + 'short': 'uint16_t', + 'long': 'uint32_t', + 'longlong': 'uint64_t', + 'bit': 'amqp_boolean_t', + 'table': 'amqp_table_t', + 'timestamp': 'uint64_t', +} + +def convertTable(d): + if len(d) == 0: + return "[]" + else: raise 'Non-empty table defaults not supported', d + +def convertBytes(x): + return "(amqp_bytes_t) { .length = %d, .bytes = \"%s\" }" % (len(x), x) + +defaultValueTypeConvMap = { + bool : lambda x: x and "1" or "0", + str : convertBytes, + int : lambda x: str(x), + float : lambda x: str(x), + dict: convertTable, + unicode: lambda x: convertBytes(x.encode("utf-8")) +} + +def c_ize(s): + s = s.replace('-', '_') + s = s.replace(' ', '_') + return s + +AmqpMethod.defName = lambda m: cConstantName(c_ize(m.klass.name) + '_' + c_ize(m.name) + "_method") +AmqpMethod.structName = lambda m: "amqp_" + c_ize(m.klass.name) + '_' + c_ize(m.name) + "_t" + +def cConstantName(s): + return 'AMQP_' + '_'.join(re.split('[- ]', s.upper())) + +class PackedMethodBitField: + def __init__(self, index): + self.index = index + self.domain = 'bit' + self.contents = [] + + def extend(self, f): + self.contents.append(f) + + def count(self): + return len(self.contents) + + def full(self): + return self.count() == 8 + +def genErl(spec): + def cType(domain): + return cTypeMap[spec.resolveDomain(domain)] + + def fieldTypeList(fields): + return '[' + ', '.join([cType(f.domain) for f in fields]) + ']' + + def fieldNameList(fields): + return '[' + ', '.join([c_ize(f.name) for f in fields]) + ']' + + def fieldTempList(fields): + return '[' + ', '.join(['F' + str(f.index) for f in fields]) + ']' + + def fieldMapList(fields): + return ', '.join([c_ize(f.name) + " = F" + str(f.index) for f in fields]) + + def genLookupMethodName(m): + print ' case %s: return "%s";' % (m.defName(), m.defName()) + + def packMethodFields(fields): + packed = [] + bitfield = None + for f in fields: + if cType(f.domain) == 'bit': + if not(bitfield) or bitfield.full(): + bitfield = PackedMethodBitField(f.index) + packed.append(bitfield) + bitfield.extend(f) + else: + bitfield = None + packed.append(f) + return packed + + def methodFieldFragment(f): + type = cType(f.domain) + p = 'F' + str(f.index) + if type == 'shortstr': + return p+'Len:8/unsigned, '+p+':'+p+'Len/binary' + elif type == 'longstr': + return p+'Len:32/unsigned, '+p+':'+p+'Len/binary' + elif type == 'octet': + return p+':8/unsigned' + elif type == 'shortint': + return p+':16/unsigned' + elif type == 'longint': + return p+':32/unsigned' + elif type == 'longlongint': + return p+':64/unsigned' + elif type == 'timestamp': + return p+':64/unsigned' + elif type == 'bit': + return p+'Bits:8' + elif type == 'table': + return p+'Len:32/unsigned, '+p+'Tab:'+p+'Len/binary' + else: + return 'UNIMPLEMENTED' + + def genFieldPostprocessing(packed): + for f in packed: + type = cType(f.domain) + if type == 'bit': + for index in range(f.count()): + print " F%d = ((F%dBits band %d) /= 0)," % \ + (f.index + index, + f.index, + 1 << index) + elif type == 'table': + print " F%d = rabbit_binary_parser:parse_table(F%dTab)," % \ + (f.index, f.index) + else: + pass + + def genDecodeMethodFields(m): + packedFields = packMethodFields(m.arguments) + binaryPattern = ', '.join([methodFieldFragment(f) for f in packedFields]) + if binaryPattern: + restSeparator = ', ' + else: + restSeparator = '' + recordConstructorExpr = '#%s{%s}' % (m.structName(), fieldMapList(m.arguments)) + print "decode_method_fields(%s, <<%s>>) ->" % (m.defName(), binaryPattern) + genFieldPostprocessing(packedFields) + print " %s;" % (recordConstructorExpr,) + + def genDecodeProperties(c): + print "decode_properties(%d, PropBin) ->" % (c.index) + print " %s = rabbit_binary_parser:parse_properties(%s, PropBin)," % \ + (fieldTempList(c.fields), fieldTypeList(c.fields)) + print " #'P_%s'{%s};" % (c_ize(c.name), fieldMapList(c.fields)) + + def genFieldPreprocessing(packed): + for f in packed: + type = cType(f.domain) + if type == 'bit': + print " F%dBits = (%s)," % \ + (f.index, + ' bor '.join(['(bitvalue(F%d) bsl %d)' % (x.index, x.index - f.index) + for x in f.contents])) + elif type == 'table': + print " F%dTab = rabbit_binary_generator:generate_table(F%d)," % (f.index, f.index) + print " F%dLen = size(F%dTab)," % (f.index, f.index) + elif type in ['shortstr', 'longstr']: + print " F%dLen = size(F%d)," % (f.index, f.index) + else: + pass + + def genEncodeMethodFields(m): + packedFields = packMethodFields(m.arguments) + print "encode_method_fields(#%s{%s}) ->" % (m.structName(), fieldMapList(m.arguments)) + genFieldPreprocessing(packedFields) + print " <<%s>>;" % (', '.join([methodFieldFragment(f) for f in packedFields])) + + def genEncodeProperties(c): + print "encode_properties(#'P_%s'{%s}) ->" % (c_ize(c.name), fieldMapList(c.fields)) + print " rabbit_binary_generator:encode_properties(%s, %s);" % \ + (fieldTypeList(c.fields), fieldTempList(c.fields)) + + def genLookupException(c,v,cls): + # We do this because 0.8 uses "soft error" and 8.1 uses "soft-error". + mCls = c_ize(cls).upper() + if mCls == 'SOFT_ERROR': genLookupException1(c,'false') + elif mCls == 'HARD_ERROR': genLookupException1(c, 'true') + elif mCls == '': pass + else: raise 'Unknown constant class', cls + + def genLookupException1(c,hardErrorBoolStr): + n = cConstantName(c) + print 'lookup_amqp_exception(%s) -> {%s, ?%s, <<"%s">>};' % \ + (n.lower(), hardErrorBoolStr, n, n) + + methods = spec.allMethods() + + print '#include <stdlib.h>' + print '#include <string.h>' + print '#include <stdio.h>' + print + print '#include "amqp_framing.h"' + + print """ +char const *amqp_method_name(uint32_t methodNumber) { + switch (methodNumber) {""" + for m in methods: genLookupMethodName(m) + print """ default: return NULL; + } +} +""" + + print """ +amqp_boolean_t amqp_method_has_content(uint32_t methodNumber) { + switch (methodNumber) {""" + for m in methods: + if m.hasContent: + print ' case %s: return 1;' % (m.defName()) + print """ default: return 0; + } +} +""" + + for m in methods: genDecodeMethodFields(m) + print "decode_method_fields(Name, BinaryFields) ->" + print " rabbit_misc:frame_error(Name, BinaryFields)." + + for c in spec.allClasses(): genDecodeProperties(c) + print "decode_properties(ClassId, _BinaryFields) -> exit({unknown_class_id, ClassId})." + + for m in methods: genEncodeMethodFields(m) + print "encode_method_fields(Record) -> exit({unknown_method_name, element(1, Record)})." + + for c in spec.allClasses(): genEncodeProperties(c) + print "encode_properties(Record) -> exit({unknown_properties_record, Record})." + + for (c,v,cls) in spec.constants: genLookupException(c,v,cls) + print "lookup_amqp_exception(Code) ->" + print " rabbit_log:warning(\"Unknown AMQP error code '~p'~n\", [Code])," + print " {true, ?INTERNAL_ERROR, <<\"INTERNAL_ERROR\">>}." + + +def genHrl(spec): + def cType(domain): + return cTypeMap[spec.resolveDomain(domain)] + + def fieldNameList(fields): + return ', '.join([c_ize(f.name) for f in fields]) + + def fieldDeclList(fields): + return ''.join([" %s %s;\n" % (cType(f.domain), c_ize(f.name)) for f in fields]) + + def propDeclList(fields): + return ''.join([" %s %s;\n" % (cType(f.domain), c_ize(f.name)) + for f in fields + if spec.resolveDomain(f.domain) != 'bit']) + + def fieldNameListDefaults(fields): + def fillField(field): + result = c_ize(f.name) + if field.defaultvalue != None: + conv_fn = defaultValueTypeConvMap[type(field.defaultvalue)] + result += ' = ' + conv_fn(field.defaultvalue) + return result + return ', '.join([fillField(f) for f in fields]) + + methods = spec.allMethods() + + print "#ifndef LIBRABBITMQ_AMQP_FRAMING_H" + print "#define LIBRABBITMQ_AMQP_FRAMING_H" + print + print "#define AMQP_PROTOCOL_VERSION_MAJOR %d" % (spec.major) + print "#define AMQP_PROTOCOL_VERSION_MINOR %d" % (spec.minor) + print "#define AMQP_PROTOCOL_PORT %d" % (spec.port) + + for (c,v,cls) in spec.constants: + print "#define %s %s" % (cConstantName(c), v) + print + + print "/* Method field records. */" + for m in methods: + methodid = m.klass.index << 16 | m.index + print "#define %s ((uint32_t) 0x%.08X) /* %d, %d; %d */" % \ + (m.defName(), + methodid, + m.klass.index, + m.index, + methodid) + print "typedef struct {\n%s} %s;\n" % (fieldDeclList(m.arguments), m.structName()) + + print "/* Class property records. */" + for c in spec.allClasses(): + index = 0 + for f in c.fields: + if index % 16 == 15: + index = index + 1 + shortnum = index / 16 + partialindex = 15 - (index % 16) + bitindex = shortnum * 16 + partialindex + print '#define %s_FLAG (1 << %d)' % (cConstantName(c.name + '_' + f.name), bitindex) + index = index + 1 + print "typedef struct {\n uint32_t _flags;\n%s} %s;\n" % \ + (fieldDeclList(c.fields), \ + 'amqp_%s_properties_t' % (c_ize(c.name),)) + + print "#endif" + +def generateErl(specPath): + genErl(AmqpSpec(specPath)) + +def generateHrl(specPath): + genHrl(AmqpSpec(specPath)) + +if __name__ == "__main__": + do_main(generateHrl, generateErl) diff --git a/librabbitmq/f.c b/librabbitmq/f.c index 07cd41b..e4bcf4d 100644 --- a/librabbitmq/f.c +++ b/librabbitmq/f.c @@ -1,3 +1,13 @@ +struct x { + int l; + char *b; +}; + +int bar(struct x v) { + return 0; +} + int foo(void) { + bar((struct x) { .l = 123, .b = "hi"}); return 123; } |