diff --git a/amqp0-9-1.stripped.xml b/amqp0-9-1.stripped.xml new file mode 100644 index 0000000..762519b --- /dev/null +++ b/amqp0-9-1.stripped.xml @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/specgen.py b/specgen.py new file mode 100644 index 0000000..7e21969 --- /dev/null +++ b/specgen.py @@ -0,0 +1,229 @@ +import xml.dom.minidom + +def constify(s): + s = s.replace('-', '_') + s = s.replace(' ', '_') + s = s.upper() + return s + +def cify(s): + s = constify(s) + s = s.lower() + return s + +def camelify(s): + s = constify(s) + s = s.split('_') + s = [s[0].lower()] + [w.capitalize() for w in s[1:]] + s = ''.join(s) + return s + +ctypemap = { + 'shortstr': 'cmsg_bytes_t', + 'longstr': 'cmsg_bytes_t', + 'table': 'cmsg_bytes_t', # TODO fix + 'octet': 'uint8_t', + 'short': 'uint16_t', + 'long': 'uint32_t', + 'longlong': 'uint64_t', + 'bit': 'uint8_t', + 'timestamp': 'uint64_t', +} + +class Constant: + def __init__(self, e): + self.name = e.getAttribute('name') + self.value = e.getAttribute('value') + self.class_ = e.getAttribute('class') or None + + def getName(self): + if self.class_: + return 'CMSG_AMQP_ERROR_' + constify(self.name) + else: + return 'CMSG_AMQP_' + constify(self.name) + + def getValue(self): + return self.value + +class Field: + def __init__(self, e): + self.name = e.getAttribute('name') + self.domain = e.getAttribute('domain') or e.getAttribute('type') + # self.reserved = bool(e.getAttribute('reserved')) + self.type = resolveDomain(self.domain) + + def getName(self): + return cify(self.name) + + def ctype(self): + return ctypemap[str(self.type)] + +class Entity: + def __init__(self, parent, e): + self.parent = parent + self.name = e.getAttribute('name') + self.index = int(e.getAttribute('index')) + self.fields = [Field(ee) \ + for ee in e.getElementsByTagName('field') \ + if ee.parentNode is e] + + def getName(self): + if self.parent: + return self.parent.getName() + '_' + cify(self.name) + else: + return cify(self.name) + + def printStructDefExtras(self): + pass + + def printStructDef(self, suffix): + if self.fields: + print + print 'typedef struct cmsg_amqp_%s_%s_t_ {' % (self.getName(), suffix) + self.printStructDefExtras() + for f in self.fields: + print ' %s %s;' % (f.ctype(), f.getName()) + print '} cmsg_amqp_%s_%s_t;' % (self.getName(), suffix) + +class BitWriter: + def __init__(self): + self.bit_offset = 0 + + def flush(self): + if self.bit_offset: + print ' write_amqp_octet(bit_buffer);' + self.bit_offset = 0 + + def emit(self, valueExpr): + if self.bit_offset == 0: + print ' bit_buffer = 0;' + print ' if (%s) bit_buffer |= 0x%02x;' % (valueExpr, 1 << self.bit_offset) + self.bit_offset += 1 + if self.bit_offset == 8: + self.flush() + +class Method(Entity): + def __init__(self, parent, e): + Entity.__init__(self, parent, e) + self.has_content = bool(e.getAttribute('content')) + self.synchronous = bool(e.getAttribute('synchronous')) + self.responses = [ee.getAttribute('name') for ee in e.getElementsByTagName('response')] + + def methodId(self): + return self.parent.index << 16 | self.index + + def printParseClause(self): + bit_offset = 0 + print ' case 0x%08x: /* %s */ ' % (self.methodId(), self.getName()) + for f in self.fields: + if f.type == 'bit': + if bit_offset == 0: + print ' if (!parse_amqp_octet(&bit_buffer, &input, &offset))' + print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;' + print ' output->body.%s.%s = (bit_buffer & 0x%02x) != 0;' % \ + (self.getName(), f.getName(), 1 << bit_offset) + bit_offset += 1 + if bit_offset == 8: bit_offset = 0 + else: + print ' if (!parse_amqp_%s(&output->body.%s.%s, &input, &offset))' % \ + (f.type, self.getName(), f.getName()) + print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;' + print ' return 0;' + + def printWriteClause(self): + bw = BitWriter() + print ' case 0x%08x: /* %s */ ' % (self.methodId(), self.getName()) + for f in self.fields: + if f.type == 'bit': + bw.emit('output->body.%s.%s' % (self.getName(), f.getName())) + else: + bw.flush() + print ' write_amqp_%s(output->body.%s.%s);' % \ + (f.type, self.getName(), f.getName()) + bw.flush() + print ' break;' + +class Class(Entity): + def __init__(self, e): + Entity.__init__(self, None, e) + self.methods = [Method(self, ee) for ee in e.getElementsByTagName('method')] + + def printStructDefExtras(self): + print ' uint32_t _flags;' + +def resolveDomain(n): + while n in domainmap and domainmap[n] != n: + n = domainmap[n] + return n + +specxml = xml.dom.minidom.parse("amqp0-9-1.stripped.xml") +constants = [Constant(e) for e in specxml.getElementsByTagName('constant')] +domainmap = dict((e.getAttribute('name'), e.getAttribute('type')) \ + for e in specxml.getElementsByTagName('domain')) + +classes = [Class(e) for e in specxml.getElementsByTagName('class')] +classmap = dict((c.name, c) for c in classes) + +def header(): + print '/* TODO: put copyright etc */' + print '/* Generated from AMQP spec version %s.%s.%s */' % \ + (specxml.documentElement.getAttribute('major'), + specxml.documentElement.getAttribute('minor'), + specxml.documentElement.getAttribute('revision')) + + print + for c in constants: + print '#define %s %s' % (c.getName(), c.getValue()) + + for c in classes: + c.printStructDef('properties') + + for c in classes: + for m in c.methods: + m.printStructDef('method') + + print + print 'typedef struct cmsg_amqp_method_t_ {' + print ' uint32_t id;' + print ' union {' + for c in classes: + for m in c.methods: + if m.fields: + print ' cmsg_amqp_%s_method_t %s;' % (m.getName(), m.getName()) + print ' } body;' + print '} cmsg_amqp_method_t;' + + print + print 'int parse_amqp_method(' + print ' cmsg_bytes_t input,' + print ' cmsg_amqp_method_t *output) {' + print ' size_t offset = 0;' + print ' uint8_t bit_buffer = 0;' + print ' if (!parse_amqp_long(&output->id, &input, &offset))' + print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;' + print ' switch (output->id) {' + for c in classes: + for m in c.methods: + m.printParseClause() + print ' default:' + print ' warn("Invalid AMQP method number 0x%%08x", output->id);' + print ' return -CMSG_AMQP_ERROR_NOT_IMPLEMENTED;' + print ' }' + print '}' + + print + print 'void write_amqp_method(' + print ' IOHandle *h,' + print ' cmsg_amqp_method_t *m) {' + print ' uint8_t bit_buffer = 0;' + print ' write_amqp_long(&m->id);' + print ' switch (m->id) {' + for c in classes: + for m in c.methods: + m.printWriteClause() + print ' default:' + print ' die("Invalid AMQP method number 0x%%08x", m->id);' + print ' }' + print '}' + +header()