#!/bin/env python3 import argparse import sys import os from pyparsing import * from pyparsing import pyparsing_common as ppc typename_prefix = "" funcname_prefix = "" LBRACK, RBRACK, LBRACE, RBRACE, COLON, SEMI = map(Suppress, "[]{}:;") ident = Word(alphas + "_", alphanums + "_").setName("identifier") named_types = {} def snake_case_to_CamelCase(name): res = "" for run in name.split("_"): res = res + run[0].upper() + run[1:] return res def CamelCase_to_snake_case(name): res = "" for i, c in enumerate(name): if c.isupper(): if i > 0: res = res + "_" res = res + c.lower() else: res = res + c return res def remove_prefix(text, prefix): return text[text.startswith(prefix) and len(prefix):] output_file = None def writeC(code, continued = False): if output_file: output_file.write(code) if not continued: output_file.write('\n') else: print(code, end='' if continued else '\n') def genC(code, extra_vars = None): vars = { 'Prefix': typename_prefix, 'prefix_': funcname_prefix, 'PREFIX_': funcname_prefix.upper() } if extra_vars: vars = {**vars, **extra_vars} return code.format_map(vars) def escapeC(s): return s.replace('{', '{{').replace('}', '}}') def C(code, extra_vars = None, continued = False): writeC(genC(code, extra_vars), continued) def generate_header(filename): C( """/* generated code for {filename} */ #include #include /********** Basic types and helpers *****************/ typedef struct {{ gconstpointer base; gsize size; }} {Prefix}Ref; #define {PREFIX_}REF_READ_FRAME_OFFSET(_v, _index) {prefix_}ref_read_unaligned_le ((guchar*)((_v).base) + (_v).size - (offset_size * ((_index) + 1)), offset_size) #define {PREFIX_}REF_ALIGN(_offset, _align_to) ((_offset + _align_to - 1) & ~(gsize)(_align_to - 1)) /* Note: clz is undefinded for 0, so never call this size == 0 */ G_GNUC_CONST static inline guint {prefix_}ref_get_offset_size (gsize size) {{ #if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__) /* Instead of using a lookup table we use nibbles in a lookup word */ guint32 v = (guint32)0x88884421; return (v >> (((__builtin_clzl(size) ^ 63) / 8) * 4)) & 0xf; #else if (size > G_MAXUINT16) {{ if (size > G_MAXUINT32) return 8; else return 4; }} else {{ if (size > G_MAXUINT8) return 2; else return 1; }} #endif }} G_GNUC_PURE static inline gsize {prefix_}ref_read_unaligned_le (guchar *bytes, guint size) {{ union {{ guchar bytes[GLIB_SIZEOF_SIZE_T]; gsize integer; }} tmpvalue; tmpvalue.integer = 0; /* we unroll the size checks here so that memcpy gets constant args */ if (size >= 4) {{ if (size == 8) memcpy (&tmpvalue.bytes, bytes, 8); else memcpy (&tmpvalue.bytes, bytes, 4); }} else {{ if (size == 2) memcpy (&tmpvalue.bytes, bytes, 2); else memcpy (&tmpvalue.bytes, bytes, 1); }} return GSIZE_FROM_LE (tmpvalue.integer); }} static inline void __{prefix_}gstring_append_double (GString *string, double d) {{ gchar buffer[100]; gint i; g_ascii_dtostr (buffer, sizeof buffer, d); for (i = 0; buffer[i]; i++) if (buffer[i] == '.' || buffer[i] == 'e' || buffer[i] == 'n' || buffer[i] == 'N') break; /* if there is no '.' or 'e' in the float then add one */ if (buffer[i] == '\\0') {{ buffer[i++] = '.'; buffer[i++] = '0'; buffer[i++] = '\\0'; }} g_string_append (string, buffer); }} static inline void __{prefix_}gstring_append_string (GString *string, const char *str) {{ gunichar quote = strchr (str, '\\'') ? '"' : '\\''; g_string_append_c (string, quote); while (*str) {{ gunichar c = g_utf8_get_char (str); if (c == quote || c == '\\\\') g_string_append_c (string, '\\\\'); if (g_unichar_isprint (c)) g_string_append_unichar (string, c); else {{ g_string_append_c (string, '\\\\'); if (c < 0x10000) switch (c) {{ case '\\a': g_string_append_c (string, 'a'); break; case '\\b': g_string_append_c (string, 'b'); break; case '\\f': g_string_append_c (string, 'f'); break; case '\\n': g_string_append_c (string, 'n'); break; case '\\r': g_string_append_c (string, 'r'); break; case '\\t': g_string_append_c (string, 't'); break; case '\\v': g_string_append_c (string, 'v'); break; default: g_string_append_printf (string, "u%04x", c); break; }} else g_string_append_printf (string, "U%08x", c); }} str = g_utf8_next_char (str); }} g_string_append_c (string, quote); }} /************** {Prefix}VariantRef *******************/ typedef {Prefix}Ref {Prefix}VariantRef; static inline const GVariantType * {prefix_}variant_ref_get_type ({Prefix}VariantRef v) {{ gsize size = v.size - 1; while (((guchar *)v.base)[size] != 0) size--; return (const GVariantType *)((guchar *)v.base + size + 1); }} static inline {Prefix}Ref {prefix_}variant_ref_get_child ({Prefix}VariantRef v) {{ gsize size = v.size - 1; while (((guchar *)v.base)[size] != 0) size--; return ({Prefix}Ref) {{ v.base, size }}; }} static inline GVariant * {prefix_}variant_ref_dup_to_gvariant ({Prefix}VariantRef v) {{ return g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, g_memdup (v.base, v.size), v.size, TRUE, g_free, NULL); }} static inline GVariant * {prefix_}variant_ref_peek_as_variant ({Prefix}VariantRef v) {{ return g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, v.base, v.size, TRUE, NULL, NULL); }} static inline GVariant * {prefix_}variant_ref_dup_child_to_gvariant ({Prefix}VariantRef v) {{ const GVariantType *type = {prefix_}variant_ref_get_type (v); {Prefix}Ref child = {prefix_}variant_ref_get_child (v); return g_variant_new_from_data (type, g_memdup (child.base, child.size), child.size, TRUE, g_free, NULL); }} static inline GVariant * {prefix_}variant_ref_peek_child_as_variant ({Prefix}VariantRef v) {{ const GVariantType *type = {prefix_}variant_ref_get_type (v); {Prefix}Ref child = {prefix_}variant_ref_get_child (v); return g_variant_new_from_data (type, child.base, child.size, TRUE, NULL, NULL); }} static inline GString * {prefix_}variant_ref_format ({Prefix}VariantRef v, GString *s, gboolean type_annotate) {{ #ifdef {PREFIX_}DEEP_VARIANT_FORMAT GVariant *gv = {prefix_}variant_ref_peek_as_variant (v); return g_variant_print_string (gv, s, TRUE); #else const GVariantType *type = {prefix_}variant_ref_get_type (v); g_string_append_printf (s, "<@%.*s>", (int)g_variant_type_get_string_length (type), (const char *)type); return s; #endif }} static inline char * {prefix_}variant_ref_print ({Prefix}VariantRef v, gboolean type_annotate) {{ GString *s = g_string_new (""); {prefix_}variant_ref_format (v, s, type_annotate); return g_string_free (s, FALSE); }}""", {'filename': filename}) def generate_footer(filename): C('', {'filename': filename}) def align_down(value, alignment): return value & ~(alignment - 1) def align_up(value, alignment): return align_down(value + alignment - 1, alignment) def add_named_type(name, type): assert not name in named_types type.set_typename(name, True) named_types[name] = type def get_named_type(name): name = typename_prefix + name assert name in named_types return named_types[name] class TypeDef: def __init__(self, name, type): name = typename_prefix + name self.name = name self.type = type add_named_type(name, type) def generate(self, generated): def do_generate (type, generated): for c in type.get_children(): do_generate (c, generated) if type.typename != None and type.typename not in generated: generated[type.typename] = True type.generate() do_generate(self.type, generated) class Type: def __init__(self): self.typename = None def typestring(self): assert False def set_typename(self, name, override = False): if self.typename == None or override: self.typename = name self.propagate_typename(name) def propagate_typename(self, typename): pass def is_basic(self): return False def is_fixed(self): return False def get_fixed_size(self): return None def alignment(self): return 1 def get_children(self): return [] def add_expansion_vars(self, vars): pass def genC(self, code, extra_vars = None): vars = { 'TypeName': self.typename, 'typestring': self.typestring(), 'ctype': self.get_ctype(), 'alignment': self.alignment(), 'fixed_size': self.get_fixed_size() } if self.typename: vars['TypeNameRef'] = self.typename + 'Ref' snake = CamelCase_to_snake_case(self.typename) vars['type_name_'] = snake + '_' vars['TYPE_NAME_'] = snake.upper() + '_' vars['type_name_ref_'] = snake + '_ref_' if self.is_fixed(): vars['fixed_ctype'] = self.get_fixed_ctype() if extra_vars: vars = {**vars, **extra_vars} self.add_expansion_vars(vars) return genC(code, vars) def C(self, code, extra_vars = None, continued=False): writeC(self.genC(code, extra_vars), continued) def generate_types(self): self.C( ''' /************** {TypeName} *******************/ #define {TYPE_NAME_}TYPESTRING "{typestring}" #define {TYPE_NAME_}TYPEFORMAT G_VARIANT_TYPE ({TYPE_NAME_}TYPESTRING) typedef {Prefix}Ref {TypeNameRef};''') def generate_standard_functions(self): self.C( ''' static inline {TypeNameRef} {type_name_ref_}from_variant (GVariant *v) {{ g_assert (g_variant_type_equal (g_variant_get_type (v), {TYPE_NAME_}TYPESTRING)); return ({TypeNameRef}) {{ g_variant_get_data (v), g_variant_get_size (v) }}; }} static inline {TypeNameRef} {type_name_ref_}from_bytes (GBytes *b) {{ return ({TypeNameRef}) {{ g_bytes_get_data (b, NULL), g_bytes_get_size (b) }}; }} static inline {TypeNameRef} {type_name_ref_}from_data (gpointer data, gsize size) {{ return ({TypeNameRef}) {{ data, size }}; }} static inline GVariant * {type_name_ref_}dup_to_variant ({TypeNameRef} v) {{ return g_variant_new_from_data ({TYPE_NAME_}TYPEFORMAT, g_memdup (v.base, v.size), v.size, TRUE, g_free, NULL); }} static inline GVariant * {type_name_ref_}ref_to_variant ({TypeNameRef} v, GDestroyNotify notify, gpointer user_data) {{ return g_variant_new_from_data ({TYPE_NAME_}TYPEFORMAT, v.base, v.size, TRUE, notify, user_data); }} static inline GVariant * {type_name_ref_}peek_as_variant ({TypeNameRef} v) {{ return g_variant_new_from_data ({TYPE_NAME_}TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL); }} static inline {TypeNameRef} {type_name_ref_}from_variant_ref ({Prefix}VariantRef v) {{ g_assert (g_variant_type_equal({prefix_}variant_ref_get_type (v), {TYPE_NAME_}TYPESTRING)); return ({TypeNameRef}) {prefix_}variant_ref_get_child (v); }} ''') def generate_print(self): self.C( ''' static inline char * {type_name_ref_}print ({TypeNameRef} v, gboolean type_annotate) {{ GString *s = g_string_new (""); {type_name_ref_}format (v, s, type_annotate); return g_string_free (s, FALSE); }} ''') def get_ctype(self): return self.typename + "Ref" def get_fixed_ctype(self): return self.typename def can_printf_format(self): return False def generate_append_value(self, value, with_type_annotate): return self.genC("{type_name_ref_}format ({value}, s, {type_annotate});", {'value': value, 'type_annotate': with_type_annotate}) basic_types = { "boolean": ("b", True, 1, "gboolean", "", '%s'), "byte": ("y", True, 1, "guint8", "byte ", '0x%02x'), "int16": ("n", True, 2, "gint16", "int16 ", '%"G_GINT16_FORMAT"'), "uint16": ("q", True, 2, "guint16", "uint16 ", '%"G_GUINT16_FORMAT"'), "int32": ("i", True, 4, "gint32", "", '%"G_GINT32_FORMAT"'), "uint32": ("u", True, 4, "guint32", "uint32 ", '%"G_GUINT32_FORMAT"'), "int64": ("x", True, 8, "gint64", "int64 ", '%"G_GINT64_FORMAT"'), "uint64": ("t", True, 8, "guint64", "uint64 ", '%"G_GUINT64_FORMAT"'), "handle": ("h", True, 4, "guint32", "handle ", '%"G_GINT32_FORMAT"'), "double": ("d", True, 8, "double", "", None), # double formating is special "string": ("s", False, 1, "const char *", "", None), # String formating is special "objectpath": ("o", False, 1, "const char *", "objectpath ", "\\'%s\\'"), "signature": ("g", False, 1, "const char *", "signature ", "\\'%s\\'"), } class BasicType(Type): def __init__(self, kind): super().__init__() assert kind in basic_types self.kind = kind def __repr__(self): return "BasicType(%s)" % self.kind def typestring(self): return basic_types[self.kind][0] def set_typename(self, name): pass # No names for basic types def is_basic(self): return True def is_fixed(self): return basic_types[self.kind][1] def get_fixed_size(self): return basic_types[self.kind][2] def alignment(self): return basic_types[self.kind][2] def get_ctype(self): return basic_types[self.kind][3] def get_read_ctype(self): if self.kind == "boolean": return "guint8" return self.get_ctype() def get_fixed_ctype(self): return self.get_read_ctype() def get_type_annotation(self): return basic_types[self.kind][4] def get_format_string(self): return basic_types[self.kind][5] def convert_value_for_format(self, value): if self.kind == "boolean": value = '(%s) ? "true" : "false"' % value return value def can_printf_format(self): return self.get_format_string() != None def add_expansion_vars(self, vars): vars['readctype'] = self.get_read_ctype() def generate_append_value(self, value, with_type_annotate): # Special case some basic types genC = self.genC if self.kind == "string": return genC('__{prefix_}gstring_append_string (s, {value});', {'value': value}) elif self.kind == "double": return genC ('__{prefix_}gstring_append_double (s, {value});', {'value': value}) else: value = self.convert_value_for_format(value) if with_type_annotate != "FALSE" and self.get_type_annotation() != "": return genC('g_string_append_printf (s, "%s{format}", {type_annotate} ? "{annotate}" : "", {value});', { 'format': self.get_format_string(), 'type_annotate': with_type_annotate, 'annotate': self.get_type_annotation(), 'value': value }) else: return genC('g_string_append_printf (s, "{format}", {value});', { 'format': self.get_format_string(), 'value': value } ) def equal_code(self, val1, val2): if self.is_fixed(): return "%s == %s" % (val1, val2) else: # String type return "strcmp(%s, %s) == 0" % (val1, val2) def canonicalize_code(self, val): if self.kind == "boolean": return "!!%s" % val return val class ArrayType(Type): def __init__(self, element_type): super().__init__() self.element_type = element_type if element_type.is_basic(): self.typename = typename_prefix + "Arrayof" + self.element_type.kind elif element_type.typename: self.typename = typename_prefix + "Arrayof" + remove_prefix(element_type.typename, typename_prefix) def __repr__(self): return "ArrayType<%s>(%s)" % (self.typename, repr(self.element_type)) def typestring(self): return "a" + self.element_type.typestring() def propagate_typename(self, name): self.element_type.set_typename (name + "Element") def alignment(self): return self.element_type.alignment() def get_children(self): return [self.element_type] def add_expansion_vars(self, vars): vars['element_ctype'] = self.element_type.get_ctype() vars['ElementTypename'] = self.element_type.typename if self.element_type.typename: vars['ElementTypenameRef'] = self.element_type.typename + "Ref" vars['element_fixed_size'] = self.element_type.get_fixed_size() vars['element_alignment'] = self.element_type.alignment() if self.element_type.is_basic(): vars['element_read_ctype'] = self.element_type.get_read_ctype() def generate(self): super().generate_types() super().generate_standard_functions() C = self.C C("static inline gsize") C("{type_name_ref_}get_length ({TypeNameRef} v)") C("{{") if self.element_type.is_fixed(): C(" return v.size / {element_fixed_size};") else: C(" if (v.size == 0)"); C(" return 0;"); C(" guint offset_size = {prefix_}ref_get_offset_size (v.size);"); C(" gsize last_end = {PREFIX_}REF_READ_FRAME_OFFSET(v, 0);"); C(" return (v.size - last_end) / offset_size;") C("}}") C("static inline {element_ctype}") C("{type_name_ref_}get_at ({TypeNameRef} v, gsize index)") C("{{") if self.element_type.is_fixed(): if self.element_type.is_basic(): C(" return ({element_ctype})G_STRUCT_MEMBER({element_read_ctype}, v.base, index * {element_fixed_size});") else: C(" return ({ElementTypenameRef}) {{ G_STRUCT_MEMBER_P(v.base, index * {element_fixed_size}), {element_fixed_size}}};") else: # non-fixed size C(" guint offset_size = {prefix_}ref_get_offset_size (v.size);") C(" gsize last_end = {PREFIX_}REF_READ_FRAME_OFFSET(v, 0);"); C(" gsize len = (v.size - last_end) / offset_size;") C(" gsize start = 0;") if not self.element_type.is_basic(): C(" gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, len - index - 1);"); C(" if (index > 0)") C(" {{") C(" start = {PREFIX_}REF_READ_FRAME_OFFSET(v, len - index);") C(" start = {PREFIX_}REF_ALIGN(start, {element_alignment});") C(" }}"); if self.element_type.is_basic(): # non-fixed basic == Stringlike C(" return ((const char *)v.base) + start;") else: C(" return ({ElementTypenameRef}) {{ ((const char *)v.base) + start, end - start }};") C("}}") C( """static inline GString * {type_name_ref_}format ({TypeNameRef} v, GString *s, gboolean type_annotate) {{ gsize len = {type_name_ref_}get_length (v); gsize i; if (len == 0 && type_annotate) g_string_append_printf (s, \"@%s \", {TYPE_NAME_}TYPESTRING); g_string_append_c (s, '['); for (i = 0; i < len; i++) {{ if (i != 0) g_string_append (s, \", \"); {append_element_code} }} g_string_append_c (s, ']'); return s; }}""", { 'append_element_code': escapeC(self.element_type.generate_append_value(self.genC("{type_name_ref_}get_at (v, i)"), "((i == 0) ? type_annotate : FALSE)")) }) self.generate_print() class DictType(Type): def __init__(self, key_type, value_type): super().__init__() self.key_type = key_type self.value_type = value_type self._fixed_element = value_type.is_fixed() and key_type.is_fixed(); if self._fixed_element: fixed_pos = key_type.get_fixed_size() fixed_pos = align_up(fixed_pos, value_type.alignment()) + value_type.get_fixed_size() self._fixed_element_size = align_up(fixed_pos, self.alignment()) else: self._fixed_element_size = None def __repr__(self): return "DictType<%s>(%s, %s)" % (self.typename, repr(self.key_type), repr(self.value_type)) def typestring(self): return "a{%s%s}" % (self.key_type.typestring(), self.value_type.typestring()) def propagate_typename(self, name): self.value_type.set_typename (name + "Value") def alignment(self): return max(self.value_type.alignment(), self.key_type.alignment()) def element_is_fixed(self): return self._fixed_element def element_fixed_size(self): return self._fixed_element_size def get_children(self): return [self.key_type, self.value_type] def add_expansion_vars(self, vars): vars['element_fixed_size'] = self.element_fixed_size() vars['value_ctype'] = self.value_type.get_ctype() vars['value_typename'] = self.value_type.typename vars['value_fixed_size'] = self.value_type.get_fixed_size() vars['value_alignment'] = self.value_type.alignment() if self.value_type.is_basic(): vars['value_read_ctype'] = self.value_type.get_read_ctype() vars['key_ctype'] = self.key_type.get_ctype() if self.key_type.is_basic(): vars['key_read_ctype'] = self.key_type.get_read_ctype() def generate(self): C=self.C super().generate_types() C('typedef {Prefix}Ref {TypeName}EntryRef;') if self.element_is_fixed(): C("typedef struct {{") pad_index = 1; pos = 0 for k, t in [("key", self.key_type), ("value", self.value_type)]: old_pos = pos pos = align_up(pos, t.alignment()) if pos > old_pos: C(" guchar _padding{pad_index}[{pad_count}];", {'pad_index': pad_index , 'pad_count': pos - old_pos}) pad_index += 1 self.C(" {field_type} {fieldname};", {'field_type': t.get_fixed_ctype(), 'fieldname': k}) pos += t.get_fixed_size() old_pos = pos pos = align_up(pos, self.alignment()) if pos > old_pos: C(" guchar _padding{pad_index}[{pad_count}];", {'pad_index': pad_index , 'pad_count': pos - old_pos}) pad_index += 1 C("}} {TypeName}Entry;") super().generate_standard_functions() C('') C("static inline gsize") C("{type_name_ref_}get_length ({TypeNameRef} v)") C("{{") if self.element_is_fixed(): C(" return v.size / {element_fixed_size};") else: C(" if (v.size == 0)"); C(" return 0;"); C(" guint offset_size = {prefix_}ref_get_offset_size (v.size);"); C(" gsize last_end = {PREFIX_}REF_READ_FRAME_OFFSET(v, 0);"); C(" return (v.size - last_end) / offset_size;") C("}}") C('') C("static inline {TypeName}EntryRef") C("{type_name_ref_}get_at ({TypeNameRef} v, gsize index)") C("{{") if self.element_is_fixed(): C(" return ({TypeName}EntryRef) {{ G_STRUCT_MEMBER_P(v.base, index * {element_fixed_size}), {element_fixed_size} }};") else: # non-fixed size C(" guint offset_size = {prefix_}ref_get_offset_size (v.size);") C(" gsize last_end = {PREFIX_}REF_READ_FRAME_OFFSET(v, 0);"); C(" gsize len = (v.size - last_end) / offset_size;") C(" gsize start = 0;") C(" gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, len - index - 1);"); C(" if (index > 0)") C(" {{") C(" start = {PREFIX_}REF_READ_FRAME_OFFSET(v, len - index);") C(" start = {PREFIX_}REF_ALIGN(start, {alignment});") C(" }}"); C(" return ({TypeName}EntryRef) {{ ((const char *)v.base) + start, end - start }};") C("}}") C('') C("static inline {key_ctype}") C("{type_name_}entry_ref_get_key ({TypeName}EntryRef v)") C("{{") # Keys are always basic if self.key_type.is_fixed(): C(" return ({key_ctype})*(({key_read_ctype} *)v.base);") else: # string-style C(" return ({key_ctype})v.base;") C("}}") C('') C("static inline {value_ctype}") C("{type_name_}entry_ref_get_value ({TypeName}EntryRef v)") C("{{") if not self.key_type.is_fixed(): C(" guint offset_size = {prefix_}ref_get_offset_size (v.size);") C(" gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, 0);"); C(" gsize offset = {PREFIX_}REF_ALIGN(end, {value_alignment});") offset = "offset" end = "(v.size - offset_size)" else: # Fixed key, so known offset offset = align_up(self.key_type.get_fixed_size(), self.value_type.alignment()) end = "v.size" if self.value_type.is_basic(): if self.value_type.is_fixed(): C(" return ({value_ctype})*(({value_read_ctype} *)((char *)v.base + {offset}));", {'offset': offset}) else: # string-style C(" return ({value_ctype})v.base + {offset};", {'offset': offset}) else: C(" return ({value_typename}Ref) {{ (char *)v.base + {offset}, {end} - {offset} }};", {'offset': offset, 'end': end }) C("}}") C('') C( """static inline gboolean {type_name_ref_}lookup ({TypeNameRef} v, {key_ctype} key, {value_ctype} *out) {{ gsize len = {type_name_ref_}get_length (v); {key_ctype} canonical_key = {canonicalize}; gsize i; for (i = 0; i < len; i++) {{ {TypeName}EntryRef e = {type_name_ref_}get_at (v, i); {key_ctype} e_key = {type_name_}entry_ref_get_key (e); if ({equal}) {{ *out = {type_name_}entry_ref_get_value (e); return TRUE; }} }} return FALSE; }}""", { 'equal': self.key_type.equal_code("canonical_key", "e_key"), 'canonicalize': self.key_type.canonicalize_code("key") }) C('') C( """static inline GString * {type_name_ref_}format ({TypeNameRef} v, GString *s, gboolean type_annotate) {{ gsize len = {type_name_ref_}get_length (v); gsize i; if (len == 0 && type_annotate) g_string_append_printf (s, \"@%s \", {TYPE_NAME_}TYPESTRING); g_string_append_c (s, '{{'); for (i = 0; i < len; i++) {{ {TypeName}EntryRef entry = {type_name_ref_}get_at (v, i); if (i != 0) g_string_append (s, \", \"); {append_key_code} g_string_append (s, ": "); {append_value_code} }} g_string_append_c (s, '}}'); return s; }}""",{ 'append_key_code': escapeC(self.key_type.generate_append_value(self.genC("{type_name_}entry_ref_get_key (entry)"), "type_annotate")), 'append_value_code': escapeC(self.value_type.generate_append_value(self.genC("{type_name_}entry_ref_get_value (entry)"), "type_annotate")), }) self.generate_print() class MaybeType(Type): def __init__(self, element_type): super().__init__() self.element_type = element_type if element_type.is_basic(): self.typename = typename_prefix + "Maybe" + self.element_type.kind elif element_type.typename: self.typename = typename_prefix + "Maybe" + remove_prefix(element_type.typename, typename_prefix) def __repr__(self): return "MaybeType<%s>(%s, %s)" % (self.typename, repr(self.element_type)) def typestring(self): return "m" + self.element_type.typestring() def propagate_typename(self, name): self.element_type.set_typename (name + "Element") def alignment(self): return self.element_type.alignment() def get_children(self): return [self.element_type] def add_expansion_vars(self, vars): vars['element_ctype'] = self.element_type.get_ctype() vars['ElementTypename'] = self.element_type.typename if self.element_type.typename: vars['ElementTypenameRef'] = self.element_type.typename + "Ref" vars['element_fixed_size'] = self.element_type.get_fixed_size() vars['element_alignment'] = self.element_type.alignment() if self.element_type.is_basic(): vars['element_read_ctype'] = self.element_type.get_read_ctype() def generate(self): super().generate_types() super().generate_standard_functions() C=self.C # has_value C( """static inline gboolean {type_name_ref_}has_value({TypeNameRef} v) {{ return v.size != 0; }}""") # Getter C("static inline {element_ctype}") C("{type_name_ref_}get_value ({TypeNameRef} v)") C("{{") C(" g_assert(v.size != 0);") if self.element_type.is_basic(): if self.element_type.is_fixed(): C(" return ({element_ctype})*(({element_read_ctype} *)v.base);") else: # string C(" return ({element_ctype})v.base;") else: if self.element_type.is_fixed(): # Fixed means use whole size size = "v.size" else: # Otherwise, ignore extra zero byte size = "(v.size - 1)" C(" return ({ElementTypenameRef}) {{ v.base, {size} }};", { 'size': size }) C("}}") C("static inline GString *") C("{type_name_ref_}format ({TypeNameRef} v, GString *s, gboolean type_annotate)") C("{{") C(" if (type_annotate)") C(' g_string_append_printf (s, "@%s ", {TYPE_NAME_}TYPESTRING);') C(" if (v.size != 0)") C(" {{") if isinstance(self.element_type, MaybeType): C(' g_string_append (s, "just ");') C(' {append_element_code}', { 'append_element_code': escapeC(self.element_type.generate_append_value(self.genC("{type_name_ref_}get_value (v)"), "FALSE")) }) C(" }}") C(" else") C(" {{") C(' g_string_append (s, "nothing");') C(" }}") C(" return s;") C("}}") self.generate_print() class VariantType(Type): def __init__(self): super().__init__() self.typename = typename_prefix + "Variant" def __repr__(self): return "VariantType()" def typestring(self): return "v" def set_typename(self, name): pass # No names for variant def alignment(self): return 8 def generate(self): pass # These are hardcoded in the prefix so all types can use it class Field: def __init__(self, name, attributes, type): self.name = name self.attributes = list(attributes) self.type = type self.last = False self.struct = None self.index = None def __repr__(self): return "Field(%s, %s)" % (self.name, self.type) def propagate_typename(self, struct_name): self.type.set_typename (struct_name + snake_case_to_CamelCase (self.name)) def genC(self, code, extra_vars = None): vars = { 'fieldname': self.name, 'FIELDNAME': self.name.upper(), 'StructName': self.struct.typename, 'StructNameRef': self.struct.typename + "Ref", 'STRUCT_NAME_': CamelCase_to_snake_case(self.struct.typename).upper() + '_', 'struct_name_ref_': CamelCase_to_snake_case(self.struct.typename) + '_ref_', 'fieldindex': self.fieldindex, } if extra_vars: vars = {**vars, **extra_vars} return self.type.genC(code, vars) def C(self, code, extra_vars = None, continued = False): writeC(self.genC(code, extra_vars), continued=continued) def generate(self): # Getter C=self.C genC=self.genC C("#define {STRUCT_NAME_}INDEXOF_{FIELDNAME} {fieldindex}") C("") C("static inline {ctype}") C("{struct_name_ref_}get_{fieldname} ({StructName}Ref v)") C("{{") has_offset_size = False if self.table_i == -1: offset = "((%d) & (~(gsize)%d)) + %d" % (self.table_a + self.table_b, self.table_b, self.table_c) else: has_offset_size = True C(" guint offset_size = {prefix_}ref_get_offset_size (v.size);"); C(" gsize last_end = {PREFIX_}REF_READ_FRAME_OFFSET(v, {table_i});", {'table_i': self.table_i }); offset = "((last_end + %d) & (~(gsize)%d)) + %d" % (self.table_a + self.table_b, self.table_b, self.table_c) if self.type.is_basic(): if self.type.is_fixed(): val = genC("({ctype})G_STRUCT_MEMBER({readctype}, v.base, {offset})", {'offset': offset}) if "bigendian" in self.attributes: val = "%s_FROM_BE(%s)" % (self.type.get_ctype().upper(), val) if "littleendian" in self.attributes: val = "%s_FROM_LE(%s)" % (self.type.get_ctype().upper(), val) C(" return {val};", {"val": val}) else: # string C(" return &G_STRUCT_MEMBER(char, v.base, {offset});", {'offset': offset}) else: if self.type.is_fixed(): C(" return ({TypeNameRef}) {{ G_STRUCT_MEMBER_P(v.base, {offset}), {fixed_size} }};", {'offset': offset}) else: if not has_offset_size: has_offset_size = True C(" guint offset_size = {prefix_}ref_get_offset_size (v.size);"); C(" gsize start = {offset};", {'offset': offset}); if self.last: C(" gsize end = v.size - offset_size * {framing_offset_size};", {'framing_offset_size': self.struct.framing_offset_size }) else: C(" gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, %d);" % (self.table_i + 1)); C(" return ({TypeNameRef}) {{ G_STRUCT_MEMBER_P(v.base, start), end - start }};") C("}}") C("") if self.type.is_fixed() and not self.type.is_basic(): C( """static inline const {fixed_ctype} * {struct_name_ref_}peek_{fieldname} ({StructName}Ref v) {{ return ({fixed_ctype} *){struct_name_ref_}get_{fieldname} (v).base; }} """) elif isinstance(self.type, ArrayType) and self.type.element_type.is_fixed(): C( """static inline const {element_fixed_ctype} * {struct_name_ref_}peek_{fieldname} ({StructName}Ref v, gsize *len) {{ {ctype} a = {struct_name_ref_}get_{fieldname} (v); if (len != NULL) *len = {type_name_ref_}get_length (a); return (const {element_fixed_ctype} *)a.base; }} """, { 'element_fixed_ctype': self.type.element_type.get_fixed_ctype(), }) elif isinstance(self.type, DictType) and self.type.element_is_fixed(): C( """static inline const {element_fixed_ctype} * {struct_name_ref_}peek_{fieldname} ({StructName}Ref v, gsize *len) {{ {ctype} a = {struct_name_ref_}get_{fieldname} (v); if (len != NULL) *len = {type_name_ref_}get_length (a); return (const {element_fixed_ctype} *)a.base; }} """, { 'element_fixed_ctype': self.type.typename + "Entry", }) def generate_fixed(self): comments = [] if "bigendian" in self.attributes: comments.append("big endian") if "littleendian" in self.attributes: comments.append("little endian") self.C(" {field_type} {fieldname};{comment}", { 'field_type': self.type.get_fixed_ctype(), 'comment': "" if len(comments) == 0 else "/* " + ",".join(comments) + " */", }) class StructType(Type): def __init__(self, fields): super().__init__() self.fields = list(fields) if len(self.fields) > 0: self.fields[len(self.fields) - 1].last = True for i, f in enumerate(self.fields): f.struct = self f.fieldindex = i framing_offset_size = 0 fixed = True fixed_pos = 0 for f in fields: if f.type.is_fixed(): fixed_pos = align_up(fixed_pos, f.type.alignment()) + f.type.get_fixed_size() else: fixed = False if not f.last: framing_offset_size = framing_offset_size + 1 self.framing_offset_size = framing_offset_size self._fixed = fixed self._fixed_size = None; if fixed: if fixed_pos == 0: # Special case unit struct self._fixed_size = 1; else: # Round up to alignment self._fixed_size = align_up(fixed_pos, self.alignment()) def tuple_align(offset, alignment): return offset + ((-offset) & alignment) # This is code equivalend to tuple_generate_table() in gvariantinfo.c, see its docs i = -1 a = 0 b = 0 c = 0 for f in fields: d = f.type.alignment() - 1; e = f.type.get_fixed_size() if f.type.is_fixed() else 0 # align to 'd' if d <= b: # rule 1 c = tuple_align(c, d) else: # rule 2 a = a + tuple_align(c, b) b = d c = 0 # the start of the item is at this point (ie: right after we # have aligned for it). store this information in the table. f.table_i = i f.table_a = a f.table_b = b f.table_c = c # "move past" the item by adding in its size. if e == 0: # variable size: # # we'll have an offset stored to mark the end of this item, so # just bump the offset index to give us a new starting point # and reset all the counters. i = i + 1 a = b = c = 0 else: # fixed size c = c + e # rule 3 def __repr__(self): return "StructType<%s>(%s)" % (self.typename, ",".join(map(repr, self.fields))) def typestring(self): res = ['('] for f in self.fields: res.append(f.type.typestring()) res.append(')') return "".join(res) def get_children(self): children = [] for f in self.fields: children.append(f.type) return children def propagate_typename(self, name): for f in self.fields: f.propagate_typename(name) def alignment(self): alignment = 1; for f in self.fields: alignment = max(alignment, f.type.alignment()) return alignment def is_fixed(self): return self._fixed; def get_fixed_size(self): return self._fixed_size def generate(self): C=self.C super().generate_types() if self.is_fixed(): C("typedef struct {{") pos = 0 pad_index = 1; for f in self.fields: old_pos = pos pos = align_up(pos, f.type.alignment()) if pos > old_pos: C(" guchar _padding{pad_index}[{pad_count}];", {'pad_index': pad_index , 'pad_count': pos - old_pos}) pad_index += 1 f.generate_fixed() pos += f.type.get_fixed_size() old_pos = pos pos = align_up(pos, self.alignment()) if pos > old_pos: C(" guchar _padding{pad_index}[{pad_count}];", {'pad_index': pad_index , 'pad_count': pos - old_pos}) pad_index += 1 C("}} {TypeName};") super().generate_standard_functions() if self.is_fixed(): C( """static inline const {fixed_ctype} * {type_name_ref_}peek ({TypeNameRef} v) {{ return (const {fixed_ctype} *)v.base; }} """); for f in self.fields: f.generate() C("static inline GString *") C("{type_name_ref_}format ({TypeNameRef} v, GString *s, gboolean type_annotate)") C("{{") # Create runs of things we can combine into single printf field_runs = [] current_run = None for f in self.fields: if current_run and f.type.can_printf_format() == current_run[0].type.can_printf_format(): current_run.append(f) else: current_run = [f] field_runs.append(current_run) for i, run in enumerate(field_runs): if run[0].type.can_printf_format(): # A run of printf fields C(' g_string_append_printf (s, "%s' % ("(" if i == 0 else ""), continued=True) for f in run: if f.type.get_type_annotation() != "": C('%s', continued=True) C('%s' % (f.type.get_format_string()), continued=True) if not f.last: C(', ', continued=True) elif len(self.fields) == 1: C(',)', continued=True) else: C(')', continued=True) C('",') for j, f in enumerate(run): if f.type.get_type_annotation() != "": C(' type_annotate ? "%s" : "",' % (f.type.get_type_annotation())) value = f.type.convert_value_for_format(f.genC("{struct_name_ref_}get_{fieldname} (v)")) C(' %s%s' % (value, "," if j != len(run) - 1 else ");")) else: # A run of container fields if i == 0: C(' g_string_append (s, "(");') for f in run: C(' {append_field_code}', {'append_field_code': escapeC(f.type.generate_append_value(f.genC("{struct_name_ref_}get_{fieldname} (v)"), "type_annotate"))}) if not f.last: C(' g_string_append (s, ", ");') elif len(self.fields) == 1: C(' g_string_append (s, ",)");') else: C(' g_string_append (s, ")");') C(" return s;") C("}}") self.generate_print() typeSpec = Forward() basicType = oneOf(basic_types.keys()).setParseAction(lambda toks: BasicType(toks[0])) variantType = Keyword("variant").setParseAction(lambda toks: VariantType()) arrayType = (LBRACK + RBRACK + typeSpec).setParseAction(lambda toks: ArrayType(toks[0])) dictType = (LBRACK + basicType + RBRACK + typeSpec).setParseAction(lambda toks: DictType(toks[0], toks[1])) maybeType = (Suppress("?") + typeSpec).setParseAction(lambda toks: MaybeType(toks[0])) fieldAttribute = oneOf("bigendian littleendian") field = (ident + COLON + Group(ZeroOrMore(fieldAttribute)) + typeSpec + SEMI).setParseAction(lambda toks: Field(toks[0], toks[1], toks[2])) structType = (LBRACE + ZeroOrMore(field) + RBRACE).setParseAction(lambda toks: StructType(toks)) namedType = ident.copy().setParseAction(lambda toks: get_named_type(str(toks[0]))) def handleNameableType(toks): type = toks[-1] if len(toks) == 2: name = toks[0] add_named_type(typename_prefix + name, type) return type nameableType = (Optional((Suppress("'") + ident).leaveWhitespace()) + (arrayType ^ maybeType ^ dictType ^ structType)).setParseAction(handleNameableType) typeSpec <<= basicType ^ variantType ^ namedType ^ nameableType typeDef = (Suppress(Keyword("type")) + ident + typeSpec + SEMI).setParseAction(lambda toks: TypeDef(toks[0], toks[1])) typeDefs = ZeroOrMore(typeDef).ignore(cppStyleComment) def generate(typedefs, filename): generate_header(filename) generated = {} for td in typedefs: td.generate(generated) generate_footer(filename) if __name__ == "__main__": parser = argparse.ArgumentParser(description='Generate variant accessors.') parser.add_argument('--prefix', help='prefix') parser.add_argument('--outfile', help='output filename') parser.add_argument('file') args = parser.parse_args() if args.prefix: typename_prefix = snake_case_to_CamelCase(args.prefix) funcname_prefix = args.prefix + "_" if args.outfile: output_file = open(args.outfile, "w") with open(args.file, "r") as f: testdata = f.read() try: typedefs = typeDefs.parseString(testdata, parseAll=True) generate(typedefs, os.path.basename(args.file)) except ParseException as pe: print("Parse error:", pe) sys.exit(1)