| #!/usr/bin/python |
| |
| license = '''/** |
| * 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 |
| * |
| * https://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. |
| */ |
| ''' |
| |
| headers = ''' |
| #include <stdint.h> |
| #include <string> |
| #include <vector> |
| #include <map> |
| #include "Boost.hh" |
| #include "Exception.hh" |
| #include "AvroSerialize.hh" |
| #include "AvroParse.hh" |
| #include "Layout.hh" |
| ''' |
| |
| done = False |
| |
| typeToC= { 'int' : 'int32_t', 'long' :'int64_t', 'float' : 'float', 'double' : 'double', |
| 'boolean' : 'bool', 'null': 'avro::Null', 'string' : 'std::string', 'bytes' : 'std::vector<uint8_t>'} |
| |
| structList = [] |
| structNames = {} |
| forwardDeclareList = [] |
| |
| def addStruct(name, declaration) : |
| if not structNames.has_key(name) : |
| structNames[name] = True |
| structList.append(declaration) |
| |
| def addForwardDeclare(declaration) : |
| code = 'struct ' + declaration + ';' |
| forwardDeclareList.append(code) |
| |
| def doPrimitive(type): |
| return (typeToC[type], type) |
| |
| def doSymbolic(args): |
| addForwardDeclare(args[1]) |
| return (args[1], args[1]) |
| |
| def addLayout(name, type, var) : |
| result = ' add(new $offsetType$(offset + offsetof($name$, $var$)));\n' |
| result = result.replace('$name$', name) |
| if typeToC.has_key(type) : |
| offsetType = 'avro::PrimitiveLayout' |
| else : |
| offsetType = type+ '_Layout' |
| result = result.replace('$offsetType$', offsetType) |
| result = result.replace('$var$', var) |
| return result; |
| |
| def addSimpleLayout(type) : |
| result = ' add(new $offsetType$);\n' |
| if typeToC.has_key(type) : |
| offsetType = 'avro::PrimitiveLayout' |
| else : |
| offsetType = type+ '_Layout' |
| return result.replace('$offsetType$', offsetType) |
| |
| recordfieldTemplate = '$type$ $name$\n' |
| recordTemplate = '''struct $name$ { |
| |
| $name$ () : |
| $initializers$ |
| { } |
| |
| $recordfields$}; |
| |
| template <typename Serializer> |
| inline void serialize(Serializer &s, const $name$ &val, const boost::true_type &) { |
| s.writeRecord(); |
| $serializefields$ s.writeRecordEnd(); |
| } |
| |
| template <typename Parser> |
| inline void parse(Parser &p, $name$ &val, const boost::true_type &) { |
| p.readRecord(); |
| $parsefields$ p.readRecordEnd(); |
| } |
| |
| class $name$_Layout : public avro::CompoundLayout { |
| public: |
| $name$_Layout(size_t offset = 0) : |
| CompoundLayout(offset) |
| { |
| $offsetlist$ } |
| }; |
| ''' |
| |
| def doRecord(args): |
| structDef = recordTemplate; |
| typename = args[1]; |
| structDef = structDef.replace('$name$', typename); |
| fields = '' |
| serializefields = '' |
| parsefields = '' |
| initlist = '' |
| offsetlist = '' |
| end = False |
| while not end: |
| line = getNextLine() |
| if line[0] == 'end': |
| end = True |
| initlist = initlist.rstrip(',\n') |
| elif line[0] == 'name': |
| fieldname = line[1] |
| fieldline = getNextLine() |
| fieldtypename, fieldtype = processType(fieldline) |
| fields += ' ' + fieldtypename + ' ' + fieldname + ';\n' |
| serializefields += ' serialize(s, val.' + fieldname + ');\n' |
| initlist += ' ' + fieldname + '(),\n' |
| parsefields += ' parse(p, val.' + fieldname + ');\n' |
| offsetlist += addLayout(typename, fieldtype, fieldname) |
| structDef = structDef.replace('$initializers$', initlist) |
| structDef = structDef.replace('$recordfields$', fields) |
| structDef = structDef.replace('$serializefields$', serializefields) |
| structDef = structDef.replace('$parsefields$', parsefields) |
| structDef = structDef.replace('$offsetlist$', offsetlist) |
| addStruct(typename, structDef) |
| return (typename,typename) |
| |
| uniontypestemplate = 'typedef $type$ Choice$N$Type' |
| unionTemplate = '''struct $name$ { |
| |
| $typedeflist$ |
| typedef void* (*GenericSetter)($name$ *, int64_t); |
| |
| $name$() : |
| choice(0), |
| value(T0()), |
| genericSetter(&$name$::genericSet) |
| { } |
| |
| $setfuncs$ |
| #ifdef AVRO_BOOST_NO_ANYREF |
| template<typename T> |
| const T &getValue() const { |
| const T *ptr = boost::any_cast<T>(&value); |
| return *ptr; |
| } |
| #else |
| template<typename T> |
| const T &getValue() const { |
| return boost::any_cast<const T&>(value); |
| } |
| #endif |
| |
| static void *genericSet($name$ *u, int64_t choice) { |
| boost::any *val = &(u->value); |
| void *data = NULL; |
| switch (choice) {$switch$ |
| } |
| return data; |
| } |
| |
| int64_t choice; |
| boost::any value; |
| GenericSetter genericSetter; |
| }; |
| |
| template <typename Serializer> |
| inline void serialize(Serializer &s, const $name$ &val, const boost::true_type &) { |
| s.writeUnion(val.choice); |
| switch(val.choice) { |
| $switchserialize$ default : |
| throw avro::Exception("Unrecognized union choice"); |
| } |
| } |
| |
| template <typename Parser> |
| inline void parse(Parser &p, $name$ &val, const boost::true_type &) { |
| val.choice = p.readUnion(); |
| switch(val.choice) { |
| $switchparse$ default : |
| throw avro::Exception("Unrecognized union choice"); |
| } |
| } |
| |
| class $name$_Layout : public avro::CompoundLayout { |
| public: |
| $name$_Layout(size_t offset = 0) : |
| CompoundLayout(offset) |
| { |
| add(new avro::PrimitiveLayout(offset + offsetof($name$, choice))); |
| add(new avro::PrimitiveLayout(offset + offsetof($name$, genericSetter))); |
| $offsetlist$ } |
| }; |
| ''' |
| |
| unionser = ' case $choice$:\n serialize(s, val.getValue< $type$ >());\n break;\n' |
| unionpar = ' case $choice$:\n { $type$ chosenVal; parse(p, chosenVal); val.value = chosenVal; }\n break;\n' |
| |
| setfunc = ''' void set_$name$(const $type$ &val) { |
| choice = $N$; |
| value = val; |
| };\n''' |
| |
| switcher = '''\n case $N$: |
| *val = T$N$(); |
| data = boost::any_cast<T$N$>(val); |
| break;''' |
| |
| |
| def doUnion(args): |
| structDef = unionTemplate |
| uniontypes = '' |
| switchserialize= '' |
| switchparse= '' |
| typename = 'Union_of' |
| setters = '' |
| switches = '' |
| offsetlist = '' |
| i = 0 |
| end = False |
| while not end: |
| line = getNextLine() |
| if line[0] == 'end': end = True |
| else : |
| uniontype, name = processType(line) |
| typename += '_' + name |
| uniontypes += ' ' + 'typedef ' + uniontype + ' T' + str(i) + ';\n' |
| switch = unionser |
| switch = switch.replace('$choice$', str(i)) |
| switch = switch.replace('$type$', uniontype) |
| switchserialize += switch |
| switch = unionpar |
| switch = switch.replace('$choice$', str(i)) |
| switch = switch.replace('$type$', uniontype) |
| switchparse += switch |
| setter = setfunc |
| setter = setter.replace('$name$', name) |
| setter = setter.replace('$type$', uniontype) |
| setter = setter.replace('$N$', str(i)) |
| setters += setter |
| switch = switcher |
| switches += switch.replace('$N$', str(i)) |
| offsetlist += addSimpleLayout(name) |
| i+= 1 |
| structDef = structDef.replace('$name$', typename) |
| structDef = structDef.replace('$typedeflist$', uniontypes) |
| structDef = structDef.replace('$switchserialize$', switchserialize) |
| structDef = structDef.replace('$switchparse$', switchparse) |
| structDef = structDef.replace('$setfuncs$', setters) |
| structDef = structDef.replace('$switch$', switches) |
| structDef = structDef.replace('$offsetlist$', offsetlist) |
| addStruct(typename, structDef) |
| return (typename,typename) |
| |
| enumTemplate = '''struct $name$ { |
| |
| enum EnumSymbols { |
| $enumsymbols$ |
| }; |
| |
| $name$() : |
| value($firstsymbol$) |
| { } |
| |
| EnumSymbols value; |
| }; |
| |
| template <typename Serializer> |
| inline void serialize(Serializer &s, const $name$ &val, const boost::true_type &) { |
| s.writeEnum(val.value); |
| } |
| |
| template <typename Parser> |
| inline void parse(Parser &p, $name$ &val, const boost::true_type &) { |
| val.value = static_cast<$name$::EnumSymbols>(p.readEnum()); |
| } |
| |
| class $name$_Layout : public avro::CompoundLayout { |
| public: |
| $name$_Layout(size_t offset = 0) : |
| CompoundLayout(offset) |
| { |
| add(new avro::PrimitiveLayout(offset + offsetof($name$, value))); |
| } |
| }; |
| ''' |
| |
| def doEnum(args): |
| structDef = enumTemplate; |
| typename = args[1] |
| structDef = structDef.replace('$name$', typename) |
| end = False |
| symbols = ''; |
| firstsymbol = ''; |
| while not end: |
| line = getNextLine() |
| if line[0] == 'end': end = True |
| elif line[0] == 'name': |
| if symbols== '' : |
| firstsymbol = line[1] |
| else : |
| symbols += ', ' |
| symbols += line[1] |
| else: print "error" |
| structDef = structDef.replace('$enumsymbols$', symbols); |
| structDef = structDef.replace('$firstsymbol$', firstsymbol); |
| addStruct(typename, structDef) |
| return (typename,typename) |
| |
| arrayTemplate = '''struct $name$ { |
| typedef $valuetype$ ValueType; |
| typedef std::vector<ValueType> ArrayType; |
| typedef ValueType* (*GenericSetter)($name$ *); |
| |
| $name$() : |
| value(), |
| genericSetter(&$name$::genericSet) |
| { } |
| |
| static ValueType *genericSet($name$ *array) { |
| array->value.push_back(ValueType()); |
| return &array->value.back(); |
| } |
| |
| void addValue(const ValueType &val) { |
| value.push_back(val); |
| } |
| |
| ArrayType value; |
| GenericSetter genericSetter; |
| |
| }; |
| |
| template <typename Serializer> |
| inline void serialize(Serializer &s, const $name$ &val, const boost::true_type &) { |
| const size_t size = val.value.size(); |
| if(size) { |
| s.writeArrayBlock(size); |
| for(size_t i = 0; i < size; ++i) { |
| serialize(s, val.value[i]); |
| } |
| } |
| s.writeArrayEnd(); |
| } |
| |
| template <typename Parser> |
| inline void parse(Parser &p, $name$ &val, const boost::true_type &) { |
| val.value.clear(); |
| while(1) { |
| int size = p.readArrayBlockSize(); |
| if(size > 0) { |
| val.value.reserve(val.value.size() + size); |
| while (size-- > 0) { |
| val.value.push_back($name$::ValueType()); |
| parse(p, val.value.back()); |
| } |
| } |
| else { |
| break; |
| } |
| } |
| } |
| |
| class $name$_Layout : public avro::CompoundLayout { |
| public: |
| $name$_Layout(size_t offset = 0) : |
| CompoundLayout(offset) |
| { |
| add(new avro::PrimitiveLayout(offset + offsetof($name$, genericSetter))); |
| $offsetlist$ } |
| }; |
| ''' |
| |
| def doArray(args): |
| structDef = arrayTemplate |
| line = getNextLine() |
| arraytype, typename = processType(line) |
| offsetlist = addSimpleLayout(typename) |
| typename = 'Array_of_' + typename |
| |
| structDef = structDef.replace('$name$', typename) |
| structDef = structDef.replace('$valuetype$', arraytype) |
| structDef = structDef.replace('$offsetlist$', offsetlist) |
| |
| line = getNextLine() |
| if line[0] != 'end': print 'error' |
| |
| addStruct(typename, structDef) |
| return (typename,typename) |
| |
| mapTemplate = '''struct $name$ { |
| typedef $valuetype$ ValueType; |
| typedef std::map<std::string, ValueType> MapType; |
| typedef ValueType* (*GenericSetter)($name$ *, const std::string &); |
| |
| $name$() : |
| value(), |
| genericSetter(&$name$::genericSet) |
| { } |
| |
| void addValue(const std::string &key, const ValueType &val) { |
| value.insert(MapType::value_type(key, val)); |
| } |
| |
| static ValueType *genericSet($name$ *map, const std::string &key) { |
| map->value[key] = ValueType(); |
| return &(map->value[key]); |
| } |
| |
| MapType value; |
| GenericSetter genericSetter; |
| |
| }; |
| |
| template <typename Serializer> |
| inline void serialize(Serializer &s, const $name$ &val, const boost::true_type &) { |
| if(val.value.size()) { |
| s.writeMapBlock(val.value.size()); |
| $name$::MapType::const_iterator iter = val.value.begin(); |
| $name$::MapType::const_iterator end = val.value.end(); |
| while(iter!=end) { |
| serialize(s, iter->first); |
| serialize(s, iter->second); |
| ++iter; |
| } |
| } |
| s.writeMapEnd(); |
| } |
| |
| template <typename Parser> |
| inline void parse(Parser &p, $name$ &val, const boost::true_type &) { |
| val.value.clear(); |
| while(1) { |
| int size = p.readMapBlockSize(); |
| if(size > 0) { |
| while (size-- > 0) { |
| std::string key; |
| parse(p, key); |
| $name$::ValueType m; |
| parse(p, m); |
| val.value.insert($name$::MapType::value_type(key, m)); |
| } |
| } |
| else { |
| break; |
| } |
| } |
| } |
| |
| class $name$_Layout : public avro::CompoundLayout { |
| public: |
| $name$_Layout(size_t offset = 0) : |
| CompoundLayout(offset) |
| { |
| add(new avro::PrimitiveLayout(offset + offsetof($name$, genericSetter))); |
| $offsetlist$ } |
| }; |
| ''' |
| |
| def doMap(args): |
| structDef = mapTemplate |
| line = getNextLine() # must be string |
| line = getNextLine() |
| maptype, typename = processType(line); |
| |
| offsetlist = addSimpleLayout(typename) |
| typename = 'Map_of_' + typename |
| |
| structDef = structDef.replace('$name$', typename) |
| structDef = structDef.replace('$valuetype$', maptype) |
| structDef = structDef.replace('$offsetlist$', offsetlist) |
| |
| line = getNextLine() |
| if line[0] != 'end': print 'error' |
| addStruct(typename, structDef) |
| return (typename,typename) |
| |
| fixedTemplate = '''struct $name$ { |
| enum { |
| fixedSize = $N$ |
| }; |
| |
| $name$() { |
| memset(value, 0, sizeof(value)); |
| } |
| |
| uint8_t value[fixedSize]; |
| }; |
| |
| template <typename Serializer> |
| inline void serialize(Serializer &s, const $name$ &val, const boost::true_type &) { |
| s.writeFixed(val.value); |
| } |
| |
| template <typename Parser> |
| inline void parse(Parser &p, $name$ &val, const boost::true_type &) { |
| p.readFixed(val.value); |
| } |
| |
| class $name$_Layout : public avro::CompoundLayout { |
| public: |
| $name$_Layout(size_t offset = 0) : |
| CompoundLayout(offset) |
| { |
| add(new avro::PrimitiveLayout(offset + offsetof($name$, value))); |
| } |
| }; |
| ''' |
| |
| def doFixed(args): |
| structDef = fixedTemplate |
| typename = args[1] |
| size = args[2] |
| |
| line = getNextLine() |
| if line[0] != 'end': print 'error' |
| |
| structDef = structDef.replace('$name$', typename) |
| structDef = structDef.replace('$N$', size) |
| addStruct(typename, structDef) |
| return (typename,typename) |
| |
| primitiveTemplate = '''struct $name$ { |
| $type$ value; |
| }; |
| |
| template <typename Serializer> |
| inline void serialize(Serializer &s, const $name$ &val, const boost::true_type &) { |
| s.writeValue(val.value); |
| } |
| |
| template <typename Parser> |
| inline void parse(Parser &p, $name$ &val, const boost::true_type &) { |
| p.readValue(val.value); |
| } |
| |
| class $name$_Layout : public avro::CompoundLayout { |
| public: |
| $name$_Layout(size_t offset = 0) : |
| CompoundLayout(offset) |
| { |
| add(new avro::PrimitiveLayout(offset + offsetof($name$, value))); |
| } |
| }; |
| ''' |
| |
| def doPrimitiveStruct(type): |
| structDef = primitiveTemplate |
| name = type.capitalize() |
| structDef = structDef.replace('$name$', name); |
| structDef = structDef.replace('$type$', typeToC[type]); |
| addStruct(name, structDef) |
| |
| compoundBuilder= { 'record' : doRecord, 'union' : doUnion, 'enum' : doEnum, |
| 'map' : doMap, 'array' : doArray, 'fixed' : doFixed, 'symbolic' : doSymbolic } |
| |
| def processType(inputs) : |
| type = inputs[0] |
| if typeToC.has_key(type) : |
| result = doPrimitive(type) |
| else : |
| func = compoundBuilder[type] |
| result = func(inputs) |
| return result |
| |
| def generateCode() : |
| inputs = getNextLine() |
| type = inputs[0] |
| if typeToC.has_key(type) : |
| doPrimitiveStruct(type) |
| else : |
| func = compoundBuilder[type] |
| func(inputs) |
| |
| def getNextLine(): |
| try: |
| line = raw_input() |
| except: |
| line = ''; |
| globals()["done"] = True |
| |
| if line == '': |
| globals()["done"] = True |
| return line.split(' ') |
| |
| def writeHeader(filebase, namespace): |
| headerstring = "%s_%s_hh__" % (namespace, filebase) |
| |
| print license |
| print "#ifndef %s" % headerstring |
| print "#define %s" % headerstring |
| print headers |
| print "namespace %s {\n" % namespace |
| |
| for x in forwardDeclareList: |
| print "%s\n" % x |
| |
| for x in structList: |
| print "/*----------------------------------------------------------------------------------*/\n" |
| print "%s\n" % x |
| |
| print "\n} // namespace %s\n" % namespace |
| |
| print "namespace avro {\n" |
| for x in structNames: |
| print 'template <> struct is_serializable<%s::%s> : public boost::true_type{};' % (namespace, x) |
| |
| print "\n} // namespace avro\n" |
| |
| print "#endif // %s" % headerstring |
| |
| |
| def usage(): |
| print "-h, --help print this helpful message" |
| print "-i, --input=FILE input file to read (default is stdin)" |
| print "-o, --output=PATH output file to generate (default is stdout)" |
| print "-n, --namespace=LABEL namespace for schema (default is avrouser)" |
| |
| if __name__ == "__main__": |
| from sys import argv |
| import getopt,sys |
| |
| try: |
| opts, args = getopt.getopt(argv[1:], "hi:o:n:", ["help", "input=", "output=", "namespace="]) |
| |
| except getopt.GetoptError, err: |
| print str(err) |
| usage() |
| sys.exit(2) |
| |
| namespace = 'avrouser' |
| |
| savein = sys.stdin |
| saveout = sys.stdout |
| inputFile = False |
| outputFile = False |
| outputFileBase = 'AvroGenerated' |
| |
| for o, a in opts: |
| if o in ("-i", "--input"): |
| try: |
| inputFile = open(a, 'r') |
| sys.stdin = inputFile |
| except: |
| print "Could not open file " + a |
| sys.exit() |
| elif o in ("-o", "--output"): |
| try: |
| outputFile = open(a, 'w') |
| sys.stdout = outputFile |
| except: |
| print "Could not open file " + a |
| outputFileBase = a.rstrip('.hp') # strip for .h, .hh, .hpp |
| elif o in ("-n", "--namespace"): |
| namespace = a |
| elif o in ("-h", "--help"): |
| usage() |
| sys.exit() |
| else: |
| print "Unhandled option: " + o |
| usage() |
| sys.exit() |
| |
| generateCode() |
| writeHeader(outputFileBase, namespace) |
| |
| sys.stdin = savein |
| sys.stdout = saveout |
| if inputFile: |
| inputFile.close() |
| if outputFile: |
| outputFile.close() |
| |