[gnome-builder] build-aux: add variant-schema-compiler



commit 66209526f8ac20fcd7b5b18e27ab9f4f1caf69c7
Author: Christian Hergert <chergert redhat com>
Date:   Fri Sep 2 17:16:30 2022 -0700

    build-aux: add variant-schema-compiler
    
    This is handy for us to start using when we pass large variants around
    and crack them open as arrays. We can probably start using it for our
    search indexes eventually, but for now, I'm going to use it to get our
    overhead down when filtering Clang completion results client-side.

 build-aux/variant-schema-compiler | 1830 +++++++++++++++++++++++++++++++++++++
 1 file changed, 1830 insertions(+)
---
diff --git a/build-aux/variant-schema-compiler b/build-aux/variant-schema-compiler
new file mode 100755
index 000000000..9fdd7efdb
--- /dev/null
+++ b/build-aux/variant-schema-compiler
@@ -0,0 +1,1830 @@
+#!/usr/bin/env python3
+
+# Copyright © 2020-2021 Red Hat
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+# This license also covers the generated code created by the app
+
+import argparse
+import sys
+import os
+from pyparsing import *
+
+typename_prefix = ""
+funcname_prefix = ""
+internal_validation = False
+
+LBRACK, RBRACK, LBRACE, RBRACE, COLON, SEMI = map(Suppress, "[]{}:;")
+
+ident = Word(alphas + "_", alphanums + "_").setName("identifier")
+
+named_types = {}
+
+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\\'"),
+}
+
+
+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_c_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')
+
+output_h_file = None
+def writeH(code, continued = False):
+    if output_h_file:
+        output_h_file.write(code)
+        if not continued:
+            output_h_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}
+    res = code.format_map(vars)
+    if res[:1] == '\n':
+        res = res[1:]
+    return res
+def escapeC(s):
+    return s.replace('{', '{{').replace('}', '}}')
+
+def C(code, extra_vars = None, continued = False):
+    writeC(genC(code, extra_vars), continued)
+
+def H(code, extra_vars = None, continued = False):
+    writeH(genC(code, extra_vars), continued)
+
+def generate_header(filename):
+    H("""
+#ifndef __{PREFIX_}__{FILENAME}__H__
+#define __{PREFIX_}__{FILENAME}__H__
+/* generated code for {filename} */
+#include <string.h>
+#include <glib.h>
+
+/********** Basic types *****************/
+
+typedef struct {{
+ gconstpointer base;
+ gsize size;
+}} {Prefix}Ref;
+""", {
+    "filename": filename,
+    "FILENAME": filename.upper().replace(".", "_").replace(" ", "_").replace("-", "_"),
+} )
+
+    C("""
+/* Make sure generated code works with older glib versions without g_memdup2 */
+static inline gpointer
+_{prefix_}memdup2(gconstpointer mem, gsize byte_size)
+{{
+#if GLIB_CHECK_VERSION(2, 68, 0)
+    return g_memdup2(mem, byte_size);
+#else
+    gpointer new_mem;
+
+    if (mem && byte_size != 0) {{
+        new_mem = g_malloc(byte_size);
+        memcpy(new_mem, mem, byte_size);
+    }} else {{
+        new_mem = NULL;
+    }}
+
+    return new_mem;
+#endif
+}}
+
+#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__) && defined(__LP64__)
+  /* 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 guint64
+{prefix_}ref_read_unaligned_le (guchar *bytes, guint   size)
+{{
+  union
+  {{
+    guchar bytes[8];
+    guint64 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 GUINT64_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);
+}}
+""")
+
+    C("""
+/************** {Prefix}VariantRef *******************/
+""")
+
+    H("""
+typedef struct {{
+ gconstpointer base;
+ gsize size;
+}} {Prefix}VariantRef;
+""")
+
+    C("""
+static inline {Prefix}Ref
+{prefix_}variant_get_child ({Prefix}VariantRef v, const GVariantType **out_type)
+{{
+  if (v.size)
+    {{
+      guchar *base = (guchar *)v.base;
+      gsize size = v.size - 1;
+
+      /* find '\\0' character */
+      while (size > 0 && base[size] != 0)
+        size--;
+
+      /* ensure we didn't just hit the start of the string */
+      if (base[size] == 0)
+       {{
+          const char *type_string = (char *) base + size + 1;
+          const char *limit = (char *)base + v.size;
+          const char *end;
+
+          if (g_variant_type_string_scan (type_string, limit, &end) && end == limit)
+            {{
+              if (out_type)
+                *out_type = (const GVariantType *)type_string;
+              return ({Prefix}Ref) {{ v.base, size }};
+            }}
+       }}
+    }}
+  if (out_type)
+    *out_type = G_VARIANT_TYPE_UNIT;
+  return  ({Prefix}Ref) {{ "\\0", 1 }};
+}}
+
+static inline const GVariantType *
+{prefix_}variant_get_type ({Prefix}VariantRef v)
+{{
+  if (v.size)
+    {{
+      guchar *base = (guchar *)v.base;
+      gsize size = v.size - 1;
+
+      /* find '\\0' character */
+      while (size > 0 && base[size] != 0)
+        size--;
+
+      /* ensure we didn't just hit the start of the string */
+      if (base[size] == 0)
+       {{
+          const char *type_string = (char *) base + size + 1;
+          const char *limit = (char *)base + v.size;
+          const char *end;
+
+          if (g_variant_type_string_scan (type_string, limit, &end) && end == limit)
+             return (const GVariantType *)type_string;
+       }}
+    }}
+  return  G_VARIANT_TYPE_UNIT;
+}}
+
+static inline gboolean
+{prefix_}variant_is_type ({Prefix}VariantRef v, const GVariantType *type)
+{{
+   return g_variant_type_equal ({prefix_}variant_get_type (v), type);
+}}
+
+static inline {Prefix}VariantRef
+{prefix_}variant_from_gvariant (GVariant *v)
+{{
+  g_assert (g_variant_type_equal (g_variant_get_type (v), G_VARIANT_TYPE_VARIANT));
+  return ({Prefix}VariantRef) {{ g_variant_get_data (v), g_variant_get_size (v) }};
+}}
+
+static inline {Prefix}VariantRef
+{prefix_}variant_from_bytes (GBytes *b)
+{{
+  return ({Prefix}VariantRef) {{ g_bytes_get_data (b, NULL), g_bytes_get_size (b) }};
+}}
+
+static inline {Prefix}VariantRef
+{prefix_}variant_from_data (gconstpointer data, gsize size)
+{{
+  return ({Prefix}VariantRef) {{ data, size }};
+}}
+
+static inline GVariant *
+{prefix_}variant_dup_to_gvariant ({Prefix}VariantRef v)
+{{
+  guint8 *duped = _{prefix_}memdup2 (v.base, v.size);
+  return g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, duped, v.size, TRUE, g_free, duped);
+}}
+
+static inline GVariant *
+{prefix_}variant_to_gvariant ({Prefix}VariantRef v,
+                              GDestroyNotify      notify,
+                              gpointer            user_data)
+{{
+  return g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, v.base, v.size, TRUE, notify, user_data);
+}}
+
+static inline GVariant *
+{prefix_}variant_to_owned_gvariant ({Prefix}VariantRef v,
+                                     GVariant *base)
+{{
+  return {prefix_}variant_to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}}
+
+static inline GVariant *
+{prefix_}variant_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 {Prefix}VariantRef
+{prefix_}variant_from_variant ({Prefix}VariantRef v)
+{{
+  const GVariantType  *type;
+  {Prefix}Ref child = {prefix_}variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, G_VARIANT_TYPE_VARIANT));
+  return {prefix_}variant_from_data (child.base, child.size);
+}}
+
+static inline GVariant *
+{prefix_}variant_dup_child_to_gvariant ({Prefix}VariantRef v)
+{{
+  const GVariantType  *type;
+  {Prefix}Ref child = {prefix_}variant_get_child (v, &type);
+  guint8 *duped = _{prefix_}memdup2 (child.base, child.size);
+  return g_variant_new_from_data (type, duped, child.size, TRUE, g_free, duped);
+}}
+
+static inline GVariant *
+{prefix_}variant_peek_child_as_variant ({Prefix}VariantRef v)
+{{
+  const GVariantType  *type;
+  {Prefix}Ref child = {prefix_}variant_get_child (v, &type);
+  return g_variant_new_from_data (type, child.base, child.size, TRUE, NULL, NULL);
+}}
+
+static inline GString *
+{prefix_}variant_format ({Prefix}VariantRef v, GString *s, gboolean type_annotate)
+{{
+#ifdef {PREFIX_}DEEP_VARIANT_FORMAT
+  GVariant *gv = {prefix_}variant_peek_as_variant (v);
+  return g_variant_print_string (gv, s, TRUE);
+#else
+  const GVariantType  *type = {prefix_}variant_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_print ({Prefix}VariantRef v, gboolean type_annotate)
+{{
+  GString *s = g_string_new ("");
+  {prefix_}variant_format (v, s, type_annotate);
+  return g_string_free (s, FALSE);
+}}""", {'filename': filename})
+
+    for kind in basic_types.keys():
+        basic_type = basic_types[kind]
+        ctype = basic_type[3]
+        if kind == "boolean":
+            read_ctype = "guint8"
+        else:
+            read_ctype = ctype
+        fixed = basic_type[1]
+        C("""
+static inline {ctype}
+{prefix_}variant_get_{kind} ({Prefix}VariantRef v)
+{{""", {"kind": kind, 'ctype': ctype})
+        if fixed:
+            C("  return ({ctype})*(({read_ctype} *)v.base);", {'ctype': ctype, 'read_ctype': read_ctype})
+        else:
+            C("  return ({ctype})v.base;", {'ctype': ctype, 'read_ctype': read_ctype})
+        C("}}")
+
+    if internal_validation:
+        C("""
+static inline void
+{prefix_}validate_length (const GVariantType *type, gconstpointer base, gsize size, gsize length)
+{{
+  GVariant *v = g_variant_ref_sink (g_variant_new_from_data (type, base, size, FALSE, NULL, NULL));
+  g_assert (length == g_variant_n_children (v));
+  g_variant_unref (v);
+}}
+
+static inline void
+{prefix_}validate_child (const GVariantType *type, gconstpointer base, gsize size, gsize index, 
gconstpointer child_base, gsize child_size)
+{{
+  GVariant *v = g_variant_ref_sink (g_variant_new_from_data (type, base, size, FALSE, NULL, NULL));
+  GVariant *child = g_variant_get_child_value (v, index);
+  g_assert (child != NULL);
+  g_assert (child_size == g_variant_get_size (child));
+  gconstpointer child_data = g_variant_get_data (child);
+  g_assert (child_base == child_data || (child_data == NULL && child_size == 0));
+  g_variant_unref (child);
+  g_variant_unref (v);
+}}""")
+
+def generate_footer(filename):
+    H("#endif")
+
+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
+    if not name in named_types:
+        print ("Unknown type %s" % remove_prefix(name, typename_prefix))
+        sys.exit(1)
+    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() + '_'
+        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 H(self, code, extra_vars = None, continued=False):
+        writeH(self.genC(code, extra_vars), continued)
+
+    def generate_types(self):
+        self.C('''
+
+/************** {TypeName} *******************/''')
+
+        self.H('''
+#define {TYPE_NAME_}TYPESTRING "{typestring}"
+#define {TYPE_NAME_}TYPEFORMAT ((const GVariantType *) {TYPE_NAME_}TYPESTRING)
+
+typedef struct {{
+ gconstpointer base;
+ gsize size;
+}} {TypeNameRef};
+''')
+
+    def generate_standard_functions(self):
+        C=self.C
+        C('''
+
+static inline {TypeNameRef}
+{type_name_}from_gvariant (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_}from_bytes (GBytes *b)
+{{''')
+        if self.is_fixed():
+            C('''
+  g_assert (g_bytes_get_size (b) == {fixed_size});
+''')
+        C('''
+  return ({TypeNameRef}) {{ g_bytes_get_data (b, NULL), g_bytes_get_size (b) }};
+}}
+
+static inline {TypeNameRef}
+{type_name_}from_data (gconstpointer data, gsize size)
+{{''')
+        if self.is_fixed():
+            C('''
+  g_assert (size == {fixed_size});
+''')
+        C('''
+  return ({TypeNameRef}) {{ data, size }};
+}}
+
+static inline GVariant *
+{type_name_}dup_to_gvariant ({TypeNameRef} v)
+{{
+  guint8 *duped = _{prefix_}memdup2 (v.base, v.size);
+  return g_variant_new_from_data ({TYPE_NAME_}TYPEFORMAT, duped, v.size, TRUE, g_free, duped);
+}}
+
+static inline GVariant *
+{type_name_}to_gvariant ({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_}to_owned_gvariant ({TypeNameRef} v, GVariant *base)
+{{
+  return {type_name_}to_gvariant (v, (GDestroyNotify)g_variant_unref, g_variant_ref (base));
+}}
+
+static inline GVariant *
+{type_name_}peek_as_gvariant ({TypeNameRef} v)
+{{
+  return g_variant_new_from_data ({TYPE_NAME_}TYPEFORMAT, v.base, v.size, TRUE, NULL, NULL);
+}}
+
+static inline {TypeNameRef}
+{type_name_}from_variant ({Prefix}VariantRef v)
+{{
+  const GVariantType  *type;
+  {Prefix}Ref child = {prefix_}variant_get_child (v, &type);
+  g_assert (g_variant_type_equal(type, {TYPE_NAME_}TYPESTRING));
+  return {type_name_}from_data (child.base, child.size);
+}}
+''')
+
+    def generate_print(self):
+        self.C('''
+
+static inline char *
+{type_name_}print ({TypeNameRef} v, gboolean type_annotate)
+{{
+  GString *s = g_string_new ("");
+  {type_name_}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 can_compare(self):
+         return False
+
+    def generate_append_value(self, value, with_type_annotate):
+        return self.genC("{type_name_}format ({value}, s, {type_annotate});", {'value': value, 
'type_annotate': with_type_annotate})
+
+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 can_compare(self):
+        return True
+    def compare_code(self, val1, val2):
+        if self.is_fixed():
+            if self.kind == "uint64" or self.kind == "int64":
+                return "((%s) < (%s) ? -1 : (((%s) == (%s) ? 0 : 1))" % (val1, val2, val1, val2)
+            else:
+                return "((%s) - (%s))" % (val1, val2)
+        else: # String type
+            return "strcmp(%s, %s)" % (val1, val2)
+    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()
+        if self.element_type.is_fixed():
+            vars['element_fixed_ctype'] = self.element_type.get_fixed_ctype()
+        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_}get_length ({TypeNameRef} v)")
+        C("{{")
+        if self.element_type.is_fixed():
+            C("  gsize length = 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("  gsize offsets_array_size;")
+            C("  if (last_end > v.size)")
+            C("    return 0;")
+            C("  offsets_array_size = v.size - last_end;")
+            C("  if (offsets_array_size % offset_size != 0)")
+            C("    return 0;")
+            C("  gsize length  = offsets_array_size / offset_size;")
+        if internal_validation:
+            C("  {prefix_}validate_length ({TYPE_NAME_}TYPEFORMAT, v.base, v.size, length);")
+        C("  return length;")
+        C("}}")
+        C("")
+        C("static inline {element_ctype}")
+        C("{type_name_}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;")
+            # Here we assume last_end is verified in get_length(), its not safe anyway to call get_at() 
without checking the length first
+            C("  gsize start = (index > 0) ? {PREFIX_}REF_ALIGN({PREFIX_}REF_READ_FRAME_OFFSET(v, len - 
index), {element_alignment}) : 0;")
+            C("  G_GNUC_UNUSED gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, len - index - 1);")
+            C("  g_assert (start <= end);")
+            C("  g_assert (end <= last_end);")
+
+            if internal_validation:
+                C("  {prefix_}validate_child ({TYPE_NAME_}TYPEFORMAT, v.base, v.size, index, ((const char 
*)v.base) + start, end - start);");
+
+            if self.element_type.is_basic(): # non-fixed basic == Stringlike
+                C("  const char *base = (const char *)v.base;")
+                C("  g_assert (base[end-1] == 0);")
+                C("  return base + start;")
+            else:
+                C("  return ({ElementTypenameRef}) {{ ((const char *)v.base) + start, end - start }};")
+        C("}}")
+
+        if self.element_type.is_fixed():
+            C("""
+
+static inline const {element_fixed_ctype} *
+{type_name_}peek ({TypeNameRef} v)
+{{
+  return (const {element_fixed_ctype} *)v.base;
+}}""");
+        elif self.element_type.is_basic(): # Array of string type
+            C("""
+
+static inline {element_ctype}*
+{type_name_}to_strv ({TypeNameRef} v, gsize *length_out)
+{{
+  gsize length = {type_name_}get_length (v);
+  gsize i;
+  const char **resv = g_new (const char *, length + 1);
+
+  for (i = 0; i < length; i++)
+    resv[i] = {type_name_}get_at (v, i);
+  resv[i] = NULL;
+
+  if (length_out)
+    *length_out = length;
+
+  return resv;
+}}""");
+
+        C("""
+
+static inline GString *
+{type_name_}format ({TypeNameRef} v, GString *s, gboolean type_annotate)
+{{
+  gsize len = {type_name_}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_}get_at (v, 
i)"), "((i == 0) ? type_annotate : FALSE)"))
+})
+
+        self.generate_print()
+
+class DictType(Type):
+    def __init__(self, attributes, key_type, value_type):
+        super().__init__()
+        self.attributes = list(attributes)
+        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['element_typeformat'] = '((const GVariantType *) "' + self.typestring()[1:] + '")'
+        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_fixed_size'] = self.key_type.get_fixed_size()
+        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
+        H=self.H
+        super().generate_types()
+        H('''
+typedef struct {{
+ gconstpointer base;
+ gsize size;
+}} {TypeName}EntryRef;
+''')
+
+        if self.element_is_fixed():
+            H("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:
+                    H("  guchar _padding{pad_index}[{pad_count}];", {'pad_index': pad_index , 'pad_count': 
pos - old_pos})
+                    pad_index += 1
+                H("  {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:
+                H("  guchar _padding{pad_index}[{pad_count}];", {'pad_index': pad_index , 'pad_count': pos - 
old_pos})
+                pad_index += 1
+            H("}} {TypeName}Entry;")
+            H("")
+
+        super().generate_standard_functions()
+        C('')
+
+        C("static inline gsize")
+        C("{type_name_}get_length ({TypeNameRef} v)")
+        C("{{")
+        if self.element_is_fixed():
+            C("  gsize length = 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("  gsize offsets_array_size;")
+            C("  if (last_end > v.size)")
+            C("    return 0;")
+            C("  offsets_array_size = v.size - last_end;")
+            C("  if (offsets_array_size % offset_size != 0)")
+            C("    return 0;")
+            C("  gsize length = offsets_array_size / offset_size;")
+        if internal_validation:
+            C("  {prefix_}validate_length ({TYPE_NAME_}TYPEFORMAT, v.base, v.size, length);")
+        C("  return length;")
+        C("}}")
+
+        C('')
+
+        C("static inline {TypeName}EntryRef")
+        C("{type_name_}get_at ({TypeNameRef} v, gsize index)")
+        C("{{")
+        C("  {TypeName}EntryRef res;");
+        if self.element_is_fixed():
+            C("  res = ({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;")
+            # Here we assume last_end is verified in get_length(), its not safe anyway to call get_at() 
without checking the length first
+            C("  gsize start = (index > 0) ? {PREFIX_}REF_ALIGN({PREFIX_}REF_READ_FRAME_OFFSET(v, len - 
index), {alignment}) : 0;")
+            C("  gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, len - index - 1);");
+            C("  g_assert (start <= end);")
+            C("  g_assert (end <= last_end);")
+            C("  res = ({TypeName}EntryRef) {{ ((const char *)v.base) + start, end - start }};")
+        if internal_validation:
+            C("  {prefix_}validate_child ({TYPE_NAME_}TYPEFORMAT, v.base, v.size, index, res.base, 
res.size);")
+        C("  return res;")
+        C("}}")
+
+        C('')
+
+        if self.element_is_fixed():
+            C("""
+static inline const {TypeName}Entry *
+{type_name_}peek ({TypeNameRef} v) {{
+  return (const {TypeName}Entry *)v.base;
+}}
+""");
+
+
+        C("static inline {key_ctype}")
+        C("{type_name_}entry_get_key ({TypeName}EntryRef v)")
+        C("{{")
+        # Keys are always basic
+        if self.key_type.is_fixed():
+            if not self.element_is_fixed(): # No need to verify size if the entire element is fixed
+                C("  g_assert (v.size >= {key_fixed_size});")
+            if internal_validation:
+                C("  {prefix_}validate_child ({element_typeformat}, v.base, v.size, 0, v.base, 
{key_fixed_size});")
+            C("  return ({key_ctype})*(({key_read_ctype} *)v.base);")
+        else: # string-style
+            C("  guint offset_size = {prefix_}ref_get_offset_size (v.size);")
+            C("  G_GNUC_UNUSED gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, 0);");
+            C("  const char *base = (const char *)v.base;")
+            C("  g_assert (end < v.size);")
+            C("  g_assert (base[end-1] == 0);")
+            if internal_validation:
+                C("  {prefix_}validate_child ({element_typeformat}, v.base, v.size, 0, v.base, end);")
+            C("  return base;")
+        C("}}")
+
+        C('')
+
+        C("static inline {value_ctype}")
+        C("{type_name_}entry_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});")
+            if self.value_type.is_fixed():
+                C("  g_assert (offset == v.size - offset_size - {value_fixed_size});");
+            else:
+                C("  g_assert (offset <= v.size);")
+            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 not self.value_type.is_fixed():
+                C("  g_assert (v.size >= {offset});", {'offset': offset})
+
+        if internal_validation:
+            C("  {prefix_}validate_child ({element_typeformat}, v.base, v.size, 1, (char *)v.base + 
{offset},  {end} - {offset});", {'offset': offset, 'end': end })
+
+        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("  g_assert (((char *)v.base)[{end} - 1] == 0);", {'end': end })
+                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_}lookup ({TypeNameRef} v, {key_ctype} key, gsize *index_out, {value_ctype} *out)
+{{
+  {key_ctype} canonical_key = {canonicalize};""", { 'canonicalize': self.key_type.canonicalize_code("key") })
+        if self.element_is_fixed():
+            if "sorted" in self.attributes and self.key_type.can_compare(): # Sorted, fixed
+                C("""
+  gsize len = v.size / {element_fixed_size};
+  gsize start = 0;
+  gsize end = len;
+
+  while (start < end)
+    {{
+      gsize mid = (end + start) / 2;
+      {TypeName}EntryRef e = {{ ((const char *)v.base) + mid * {element_fixed_size}, {element_fixed_size} }};
+      {key_ctype} e_key = {type_name_}entry_get_key (e);
+      gint32 cmp = {compare};
+      if (cmp == 0)
+        {{
+           if (index_out)
+             *index_out = mid;
+           if (out)
+             *out = {type_name_}entry_get_value (e);
+           return TRUE;
+        }}
+      if (cmp < 0)
+        end = mid; /* canonical_key < e_key */
+      else
+        start = mid + 1; /* canonical_key > e_key */
+    }}
+    return FALSE;
+}}""", {'compare': self.key_type.compare_code("canonical_key", "e_key")})
+            else: # Unsorted, fixed size
+                C("""
+  const guchar *p = v.base;
+  const guchar *end = p + v.size;
+  gsize i = 0;
+
+  while (p < end)
+    {{
+        {TypeName}EntryRef e = {{ p, {element_fixed_size} }};
+        {key_ctype} e_key = {type_name_}entry_get_key (e);
+        if ({equal})
+          {{
+             if (index_out)
+               *index_out = i;
+             if (out)
+               *out = {type_name_}entry_get_value (e);
+             return TRUE;
+          }}
+         i++;
+         p += {element_fixed_size};
+    }}
+    return FALSE;
+}}""", {
+    'equal': self.key_type.equal_code("canonical_key", "e_key"),
+    'canonicalize': self.key_type.canonicalize_code("key")
+})
+        else:
+            C("""
+  if (v.size == 0)
+    return FALSE;
+  guint offset_size = {prefix_}ref_get_offset_size (v.size);
+  gsize last_end = {PREFIX_}REF_READ_FRAME_OFFSET(v, 0);
+  if (last_end > v.size)
+    return FALSE;
+  gsize offsets_array_size = v.size - last_end;
+  if (offsets_array_size % offset_size != 0)
+    return FALSE;
+  gsize len = offsets_array_size / offset_size;""")
+            if "sorted" in self.attributes and self.key_type.can_compare(): # Sorted, non-fixed size
+                C("""
+  gsize start = 0;
+  gsize end = len;
+
+  while (start < end)
+    {{
+      gsize mid = (end + start) / 2;
+      gsize mid_end = {PREFIX_}REF_READ_FRAME_OFFSET(v, len - mid - 1);
+      gsize mid_start = mid == 0 ? 0 : {PREFIX_}REF_ALIGN({PREFIX_}REF_READ_FRAME_OFFSET(v, len - mid), 
{alignment});
+      g_assert (mid_start <= mid_end);
+      g_assert (mid_end <= last_end);
+      {TypeName}EntryRef e = {{ ((const char *)v.base) + mid_start, mid_end - mid_start }};
+      {key_ctype} e_key = {type_name_}entry_get_key (e);
+      gint32 cmp = {compare};
+      if (cmp == 0)
+        {{
+           if (index_out)
+             *index_out = mid;
+           if (out)
+             *out = {type_name_}entry_get_value (e);
+           return TRUE;
+        }}
+      if (cmp < 0)
+        end = mid; /* canonical_key < e_key */
+      else
+        start = mid + 1; /* canonical_key > e_key */
+    }}
+    return FALSE;
+}}""", { 'compare': self.key_type.compare_code("canonical_key", "e_key")})
+            else: # Unsorted, non-fixed size
+                C("""
+  gsize start = 0;
+  gsize i;
+
+  for (i = 0; i < len; i++)
+    {{
+      gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, len - i - 1);
+      {TypeName}EntryRef e = {{ ((const guchar *)v.base) + start, end - start }};
+      g_assert (start <= end);
+      g_assert (end <= last_end);
+      {key_ctype} e_key = {type_name_}entry_get_key (e);
+      if ({equal})
+        {{
+           if (index_out)
+             *index_out = i;
+           if (out)
+             *out = {type_name_}entry_get_value (e);
+           return TRUE;
+        }}
+      start = {PREFIX_}REF_ALIGN(end, {alignment});
+    }}
+    return FALSE;
+}}""", { 'equal': self.key_type.equal_code("canonical_key", "e_key") })
+
+            C('')
+
+            if isinstance(self.value_type, VariantType):
+                for kind in basic_types.keys():
+                    basic_type = basic_types[kind]
+                    ctype = basic_type[3]
+                    typechar = basic_type[0]
+                    C("""
+static inline {ctype}
+{type_name_}lookup_{kind} ({TypeNameRef} v, {key_ctype} key, {ctype} default_value)
+{{
+   {Prefix}VariantRef value_v;
+
+  if ({type_name_}lookup (v, key, NULL, &value_v) &&
+      *(const char *){prefix_}variant_get_type (value_v) == '{typechar}')
+    return {prefix_}variant_get_{kind} (value_v);
+  return default_value;
+}}
+""", {
+    'ctype': ctype,
+    'typechar': typechar,
+    "kind": kind
+})
+
+
+        C(
+"""static inline GString *
+{type_name_}format ({TypeNameRef} v, GString *s, gboolean type_annotate)
+{{
+  gsize len = {type_name_}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_}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_get_key 
(entry)"), "type_annotate")),
+    'append_value_code': 
escapeC(self.value_type.generate_append_value(self.genC("{type_name_}entry_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_}has_value({TypeNameRef} v)
+{{
+  return v.size != 0;
+}}""")
+
+        # Getter
+        C("static inline {element_ctype}")
+        C("{type_name_}get_value ({TypeNameRef} v)")
+        C("{{")
+        if self.element_type.is_fixed():
+            C("  g_assert (v.size == {element_fixed_size});")
+        else:
+            if self.element_type.is_basic(): # string type
+                C("  g_assert (v.size >= 2);") # Must be at least extra zero byte plus a terminating zero
+            else:
+                C("  g_assert (v.size >= 1);") # Must be at least extra zero byte
+
+        if self.element_type.is_basic():
+            if self.element_type.is_fixed():
+                C("  return ({element_ctype})*(({element_read_ctype} *)v.base);")
+            else: # string
+                C("  const char *base = (const char *)v.base;")
+                C("  g_assert (base[v.size - 2] == 0);")
+                C("  return 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)"
+            if internal_validation:
+                C("  {prefix_}validate_child ({TYPE_NAME_}TYPEFORMAT, v.base, v.size, 0, ((const char 
*)v.base), {size});", { 'size': size })
+            C("  return ({ElementTypenameRef}) {{ v.base, {size} }};", { 'size': size })
+        C("}}")
+
+        C("static inline GString *")
+        C("{type_name_}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_}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_': CamelCase_to_snake_case(self.struct.typename) + '_',
+            '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 H(self, code, extra_vars = None, continued = False):
+        writeH(self.genC(code, extra_vars), continued=continued)
+
+    def generate(self):
+        # Getter
+        C=self.C
+        H=self.H
+        genC=self.genC
+        H("#define {STRUCT_NAME_}INDEXOF_{FIELDNAME} {fieldindex}")
+        H("")
+        C("static inline {ctype}")
+        C("{struct_name_}get_{fieldname} ({StructName}Ref v)")
+        C("{{")
+
+        if not self.type.is_fixed() or self.table_i >= 0:
+            C("  guint offset_size = {prefix_}ref_get_offset_size (v.size);");
+
+        if self.table_i == -1:
+            offset = "((%d) & (~(gsize)%d)) + %d" % (self.table_a + self.table_b, self.table_b, self.table_c)
+        else:
+            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)
+        C("  guint offset = {offset};", {'offset': offset});
+
+        if self.type.is_basic():
+            if self.type.is_fixed():
+                if not self.struct.is_fixed():
+                    C("  g_assert (offset + {fixed_size} <= v.size);");
+                val = genC("({ctype})G_STRUCT_MEMBER({readctype}, v.base, 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("  const char *base = (const char *)v.base;")
+                C("  gsize start = offset;");
+                if self.last:
+                    C("  G_GNUC_UNUSED gsize end = v.size - offset_size * {framing_offset_size};", 
{'framing_offset_size': self.struct.framing_offset_size })
+                else:
+                    C("  G_GNUC_UNUSED gsize end = {PREFIX_}REF_READ_FRAME_OFFSET(v, %d);" % (self.table_i + 
1));
+                C("  g_assert (start <= end);");
+                C("  g_assert (end <= v.size);");
+                C("  g_assert (base[end-1] == 0);");
+                if internal_validation:
+                    C("  {prefix_}validate_child ({STRUCT_NAME_}TYPEFORMAT, v.base, v.size, {fieldindex}, 
(const char *)v.base + start, end - start);")
+                C("  return &G_STRUCT_MEMBER(const char, v.base, start);")
+        else:
+            if self.type.is_fixed():
+                if not self.struct.is_fixed():
+                    C("  g_assert (offset + {fixed_size} <= v.size);");
+                C("  return ({TypeNameRef}) {{ G_STRUCT_MEMBER_P(v.base, offset), {fixed_size} }};")
+            else:
+                C("  gsize start = 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("  g_assert (start <= end);");
+                C("  g_assert (end <= v.size);");
+                if internal_validation:
+                    C("  {prefix_}validate_child ({STRUCT_NAME_}TYPEFORMAT, v.base, v.size, {fieldindex}, 
(const char *)v.base + start, end - start);")
+                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_}peek_{fieldname} ({StructName}Ref v) {{
+  return ({fixed_ctype} *){struct_name_}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_}peek_{fieldname} ({StructName}Ref v, gsize *len) {{
+  {ctype} a = {struct_name_}get_{fieldname} (v);
+  if (len != NULL)
+    *len = {type_name_}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_}peek_{fieldname} ({StructName}Ref v, gsize *len) {{
+  {ctype} a = {struct_name_}get_{fieldname} (v);
+  if (len != NULL)
+    *len = {type_name_}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.H("  {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
+        H=self.H
+        super().generate_types()
+
+        if self.is_fixed():
+            H("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:
+                    H("  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:
+                H("  guchar _padding{pad_index}[{pad_count}];", {'pad_index': pad_index , 'pad_count': pos - 
old_pos})
+                pad_index += 1
+            H("}} {TypeName};")
+
+
+        super().generate_standard_functions()
+
+        if self.is_fixed():
+            C(
+"""static inline const {fixed_ctype} *
+{type_name_}peek ({TypeNameRef} v) {{
+  return (const {fixed_ctype} *)v.base;
+}}
+""");
+
+        for f in self.fields:
+            f.generate()
+
+        C("static inline GString *")
+        C("{type_name_}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_}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_}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(list(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]))
+
+indexAttribute = oneOf("sorted")
+
+dictType = (LBRACK + Group(ZeroOrMore(indexAttribute)) + basicType + RBRACK + 
typeSpec).setParseAction(lambda toks: DictType(toks[0], toks[1], toks[2]))
+
+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(Combine(Suppress("'") + ident)) + (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('--outfile-header', help='output filename')
+    parser.add_argument('--internal-validation', help='enable internal validation', action='store_true')
+    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")
+        output_h_file = output_file
+    if args.outfile_header:
+        output_h_file = open(args.outfile_header, "w")
+
+    internal_validation = args.internal_validation
+
+    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)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]