mirror of
https://github.com/flatpak/flatpak.git
synced 2026-04-14 03:49:28 -04:00
This is not ideal, as we still don't quite separate function prefixes and type prefixes for generated types, but at least we're not crowding the global namespace.
1000 lines
37 KiB
Python
Executable File
1000 lines
37 KiB
Python
Executable File
#!/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 generate_header(filename):
|
|
print(
|
|
"""/* generated code for {filename} */
|
|
#include <string.h>
|
|
#include <glib.h>
|
|
|
|
/********** Header *****************/
|
|
|
|
typedef struct {{
|
|
gconstpointer base;
|
|
gsize size;
|
|
}} {tprefix}VariantChunk;
|
|
|
|
#define {FPREFIX}VARIANT_CHUNK_READ_FRAME_OFFSET(_v, _index) {fprefix}variant_chunk_read_unaligned_le ((guchar*)((_v).base) + (_v).size - (offset_size * ((_index) + 1)), offset_size)
|
|
#define {FPREFIX}VARIANT_CHUNK_ALIGN(_offset, _align_to) ((_offset + _align_to - 1) & ~(gsize)(_align_to - 1))
|
|
|
|
typedef {tprefix}VariantChunk {tprefix}variant;
|
|
static inline const GVariantType *
|
|
{tprefix}variant_get_type ({tprefix}variant v)
|
|
{{
|
|
gsize size = v.size - 1;
|
|
while (((guchar *)v.base)[size] != 0)
|
|
size--;
|
|
return (const GVariantType *)((guchar *)v.base + size + 1);
|
|
}}
|
|
|
|
static inline {tprefix}VariantChunk
|
|
{tprefix}variant_get_child ({tprefix}variant v)
|
|
{{
|
|
gsize size = v.size - 1;
|
|
while (((guchar *)v.base)[size] != 0)
|
|
size--;
|
|
return ({tprefix}VariantChunk) {{ v.base, size }};
|
|
}}
|
|
|
|
static inline GVariant *
|
|
{tprefix}variant_dup_to_gvariant ({tprefix}variant 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 *
|
|
{tprefix}variant_peek_as_gvariant ({tprefix}variant v)
|
|
{{
|
|
return g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, v.base, v.size, TRUE, NULL, NULL);
|
|
}}
|
|
|
|
static inline GVariant *
|
|
{tprefix}variant_dup_child_to_gvariant ({tprefix}variant v)
|
|
{{
|
|
const GVariantType *type = {tprefix}variant_get_type (v);
|
|
{tprefix}VariantChunk child = {tprefix}variant_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 *
|
|
{tprefix}variant_peek_child_as_gvariant ({tprefix}variant v)
|
|
{{
|
|
const GVariantType *type = {tprefix}variant_get_type (v);
|
|
{tprefix}VariantChunk child = {tprefix}variant_get_child (v);
|
|
return g_variant_new_from_data (type, child.base, child.size, TRUE, NULL, NULL);
|
|
}}
|
|
|
|
static inline GString *
|
|
{tprefix}variant_format ({tprefix}variant v, GString *s, gboolean type_annotate)
|
|
{{
|
|
#ifdef SHALLOW_VARIANT_FORMAT
|
|
const GVariantType *type = {tprefix}variant_get_type (v);
|
|
g_string_append_printf (s, "<@%.*s>", (int)g_variant_type_get_string_length (type), (const char *)type);
|
|
return s;
|
|
#else
|
|
GVariant *gv = {tprefix}variant_peek_as_gvariant (v);
|
|
return g_variant_print_string (gv, s, TRUE);
|
|
#endif
|
|
}}
|
|
|
|
static inline char *
|
|
{tprefix}variant_print ({tprefix}variant v, gboolean type_annotate)
|
|
{{
|
|
GString *s = g_string_new ("");
|
|
{tprefix}variant_format (v, s, type_annotate);
|
|
return g_string_free (s, FALSE);
|
|
}}
|
|
|
|
/* Note: clz is undefinded for 0, so never call this size == 0 */
|
|
G_GNUC_CONST static inline guint
|
|
{fprefix}variant_chunk_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
|
|
{fprefix}variant_chunk_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
|
|
{fprefix}__variant_string_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
|
|
{fprefix}__variant_string_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);
|
|
}}
|
|
""".format(filename=filename, tprefix=typename_prefix, fprefix=funcname_prefix, FPREFIX=funcname_prefix.upper()))
|
|
|
|
def generate_footer(filename):
|
|
print(
|
|
"""
|
|
""".format(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):
|
|
assert False # Should not be reached
|
|
|
|
def alignment(self):
|
|
return 1
|
|
|
|
def get_children(self):
|
|
return []
|
|
|
|
def generate(self):
|
|
print (
|
|
'''
|
|
/************** {typename} *******************/
|
|
|
|
typedef {tprefix}VariantChunk {typename};
|
|
#define {typename}_typestring "{typestring}"
|
|
#define {typename}_typeformat G_VARIANT_TYPE ({typename}_typestring)
|
|
static inline {typename}
|
|
{typename}_from_gvariant(GVariant *v)
|
|
{{
|
|
g_assert (g_variant_type_equal (g_variant_get_type (v), {typename}_typestring));
|
|
return ({typename}) {{ g_variant_get_data (v), g_variant_get_size (v) }};
|
|
}}
|
|
static inline GVariant *
|
|
{typename}_dup_to_gvariant ({typename} v)
|
|
{{
|
|
return g_variant_new_from_data ({typename}_typeformat, g_memdup (v.base, v.size), v.size, TRUE, g_free, NULL);
|
|
}}
|
|
static inline {typename}
|
|
{typename}_from_variant({tprefix}variant v) {{
|
|
g_assert (g_variant_type_equal({tprefix}variant_get_type (v), {typename}_typestring));
|
|
return ({typename}) {tprefix}variant_get_child (v);
|
|
}}'''.format(typename=self.typename, typestring=self.typestring(), tprefix=typename_prefix, fprefix=funcname_prefix))
|
|
|
|
def generate_print(self):
|
|
print (
|
|
'''
|
|
static inline char *
|
|
{typename}_print ({typename} v, gboolean type_annotate)
|
|
{{
|
|
GString *s = g_string_new ("");
|
|
{typename}_format (v, s, type_annotate);
|
|
return g_string_free (s, FALSE);
|
|
}}
|
|
'''.format(typename=self.typename))
|
|
|
|
def get_ctype(self):
|
|
return self.typename
|
|
|
|
def can_printf_format(self):
|
|
return False
|
|
|
|
def generate_append_value(self, value, with_type_annotate):
|
|
print (" {typename}_format ({value}, s, {ta});".format(typename=self.typename, value=value, ta=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_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 generate_append_value(self, value, with_type_annotate):
|
|
# Special case some basic types
|
|
if self.kind == "string":
|
|
print (' %s__variant_string_append_string (s, %s);' % (funcname_prefix, value))
|
|
elif self.kind == "double":
|
|
print (' %s__variant_string_append_double (s, %s);' % (funcname_prefix, value))
|
|
else:
|
|
value = self.convert_value_for_format(value)
|
|
if with_type_annotate != "FALSE" and self.get_type_annotation() != "":
|
|
print (' g_string_append_printf (s, "%s{format}", {type_annotate} ? "{annotate}" : "", {value});'
|
|
.format(format=self.get_format_string(),
|
|
type_annotate=with_type_annotate,
|
|
annotate=self.get_type_annotation(),
|
|
value=value))
|
|
else:
|
|
print (' g_string_append_printf (s, "{format}", {value});'
|
|
.format(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 = self.element_type.kind + "array"
|
|
|
|
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 generate(self):
|
|
super().generate()
|
|
print ("static inline gsize")
|
|
print ("{typename}_get_length({typename} v)".format(typename=self.typename))
|
|
print ("{")
|
|
if self.element_type.is_fixed():
|
|
print(" return v.size / %d;" % self.element_type.get_fixed_size())
|
|
else:
|
|
print(" guint offset_size = %svariant_chunk_get_offset_size (v.size);" % funcname_prefix);
|
|
print(" gsize last_end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, 0);" % funcname_prefix.upper());
|
|
print(" return (v.size - last_end) / offset_size;")
|
|
print("}")
|
|
print("static inline {ctype}".format(typename=self.typename, ctype=self.element_type.get_ctype()))
|
|
print("{typename}_get_at({typename} v, gsize index)".format(typename=self.typename, ctype=self.element_type.get_ctype()))
|
|
print("{")
|
|
if self.element_type.is_fixed():
|
|
fixed_size = self.element_type.get_fixed_size()
|
|
if self.element_type.is_basic():
|
|
print (" return (%s)G_STRUCT_MEMBER(%s, v.base, index * %d);" % (self.element_type.get_ctype(), self.element_type.get_read_ctype(), fixed_size))
|
|
else:
|
|
print (" return (%s) { G_STRUCT_MEMBER_P(v.base, index * %s), %d};" % (self.element_type.typename, fixed_size, fixed_size))
|
|
else:
|
|
# non-fixed size
|
|
print(" guint offset_size = %svariant_chunk_get_offset_size (v.size);" % funcname_prefix)
|
|
print(" gsize last_end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, 0);" % funcname_prefix.upper());
|
|
print(" gsize len = (v.size - last_end) / offset_size;")
|
|
print(" gsize start = 0;")
|
|
if not self.element_type.is_basic():
|
|
print(" gsize end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, len - index - 1);" % funcname_prefix.upper());
|
|
print(" if (index > 0) {")
|
|
print(" start = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, len - index);" % funcname_prefix.upper())
|
|
print(" start = %sVARIANT_CHUNK_ALIGN(start, %d);" % (funcname_prefix.upper(), self.element_type.alignment()))
|
|
print(" }");
|
|
if self.element_type.is_basic(): # non-fixed basic == Stringlike
|
|
print (" return ((const char *)v.base) + start;")
|
|
else:
|
|
print(" return (%s) { ((const char *)v.base) + start, end - start };" % (self.element_type.typename))
|
|
print("}")
|
|
|
|
print("static inline GString *")
|
|
print("{typename}_format ({typename} v, GString *s, gboolean type_annotate)".format(typename=self.typename))
|
|
print ("{")
|
|
print(" gsize len = %s_get_length(v);" % self.typename)
|
|
print(" gsize i;")
|
|
print(" if (len == 0 && type_annotate)")
|
|
print(' g_string_append_printf (s, "@%%s ", %s_typestring);' % (self.typename))
|
|
print(" g_string_append_c (s, '[');")
|
|
print(" for (i = 0; i < len; i++) {")
|
|
print(' if (i != 0)')
|
|
print(' g_string_append (s, ", ");')
|
|
print(' ', end='')
|
|
self.element_type.generate_append_value("%s_get_at(v, i)" % self.typename, "((i == 0) ? type_annotate : FALSE)")
|
|
print(" }")
|
|
print(" g_string_append_c (s, ']');")
|
|
print(" return s;")
|
|
print("}")
|
|
self.generate_print()
|
|
|
|
|
|
class DictType(Type):
|
|
def __init__(self, key_type, element_type):
|
|
super().__init__()
|
|
self.key_type = key_type
|
|
self.element_type = element_type
|
|
|
|
self._fixed_element = element_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, element_type.alignment()) + element_type.get_fixed_size()
|
|
self._fixed_element_size = align_up(fixed_pos, self.alignment())
|
|
|
|
def __repr__(self):
|
|
return "DictType<%s>(%s, %s)" % (self.typename, repr(self.key_type), repr(self.element_type))
|
|
def typestring(self):
|
|
return "a{%s%s}" % (self.key_type.typestring(), self.element_type.typestring())
|
|
def propagate_typename(self, name):
|
|
self.element_type.set_typename (name + "__value")
|
|
def alignment(self):
|
|
return max(self.element_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.element_type]
|
|
def generate(self):
|
|
super().generate()
|
|
print ('typedef {tprefix}VariantChunk {typename}__entry;'.format(typename=self.typename, tprefix=typename_prefix))
|
|
|
|
print ("static inline gsize")
|
|
print ("{typename}_get_length({typename} v)".format(typename=self.typename))
|
|
print ("{")
|
|
if self.element_is_fixed():
|
|
print(" return v.size / %d;" % self.element_fixed_size())
|
|
else:
|
|
print(" guint offset_size = %svariant_chunk_get_offset_size (v.size);" % funcname_prefix);
|
|
print(" gsize last_end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, 0);" % funcname_prefix.upper());
|
|
print(" return (v.size - last_end) / offset_size;")
|
|
print("}")
|
|
|
|
print("static inline {typename}__entry".format(typename=self.typename))
|
|
print("{typename}_get_at({typename} v, gsize index)".format(typename=self.typename))
|
|
print("{")
|
|
if self.element_is_fixed():
|
|
fixed_size = self.element_fixed_size()
|
|
print (" return (%s) { G_STRUCT_MEMBER_P(v.base, index * %s), %d};" % (self.typename + "__entry", fixed_size, fixed_size))
|
|
else:
|
|
# non-fixed size
|
|
print(" guint offset_size = %svariant_chunk_get_offset_size (v.size);" % funcname_prefix)
|
|
print(" gsize last_end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, 0);" % funcname_prefix.upper());
|
|
print(" gsize len = (v.size - last_end) / offset_size;")
|
|
print(" gsize start = 0;")
|
|
print(" gsize end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, len - index - 1);" % funcname_prefix.upper());
|
|
print(" if (index > 0) {")
|
|
print(" start = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, len - index);" % funcname_prefix.upper())
|
|
print(" start = %sVARIANT_CHUNK_ALIGN(start, %d);" % (funcname_prefix.upper(), self.alignment()))
|
|
print(" }");
|
|
print(" return (%s) { ((const char *)v.base) + start, end - start };" % (self.typename + "__entry"))
|
|
print("}")
|
|
|
|
print("static inline {ctype}".format(typename=self.typename, ctype=self.key_type.get_ctype()))
|
|
print("{typename}__entry_get_key({typename}__entry v)".format(typename=self.typename, ctype=self.key_type.get_ctype()))
|
|
print("{")
|
|
# Keys are always basic
|
|
if self.key_type.is_fixed():
|
|
print (" return (%s)*((%s *)v.base);" % (self.key_type.get_ctype(), self.key_type.get_read_ctype()))
|
|
else: # string-style
|
|
print (" return (%s)v.base;" % (self.key_type.get_ctype()))
|
|
print("}")
|
|
|
|
print("static inline {ctype}".format(typename=self.typename, ctype=self.element_type.get_ctype()))
|
|
print("{typename}__entry_get_value({typename}__entry v)".format(typename=self.typename, ctype=self.element_type.get_ctype()))
|
|
print("{")
|
|
if not self.key_type.is_fixed():
|
|
print(" guint offset_size = %svariant_chunk_get_offset_size (v.size);" % funcname_prefix)
|
|
print(" gsize end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, 0);" % funcname_prefix.upper());
|
|
print(" gsize offset = %sVARIANT_CHUNK_ALIGN(end, %d);" % (funcname_prefix.upper(), self.element_type.alignment()))
|
|
offset = "offset"
|
|
end = "(v.size - offset_size)"
|
|
else:
|
|
# Fixed key, so known offset
|
|
offset = align_up(self.key_type.get_fixed_size(), self.element_type.alignment())
|
|
end = "v.size"
|
|
|
|
if self.element_type.is_basic():
|
|
if self.element_type.is_fixed():
|
|
print (" return (%s)*((%s *)((char *)v.base + %s));" % (self.element_type.get_ctype(), self.element_type.get_read_ctype(), offset))
|
|
else: # string-style
|
|
print (" return (%s)v.base + %s;" % (self.element_type.get_ctype(), offset))
|
|
else:
|
|
print (" return (%s) { (char *)v.base + %s, %s - %s};" % (self.element_type.typename, offset, end, offset))
|
|
|
|
print("}")
|
|
|
|
print(
|
|
"""static inline gboolean
|
|
{typename}_lookup({typename} v, {keyctype} key, {elementctype} *out)
|
|
{{
|
|
gsize len = {typename}_get_length(v);
|
|
{keyctype} canonical_key = {canonicalize};
|
|
gsize i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
{{
|
|
{typename}__entry e = {typename}_get_at(v, i);
|
|
{keyctype} e_key = {typename}__entry_get_key(e);
|
|
if ({equal})
|
|
{{
|
|
*out = {typename}__entry_get_value (e);
|
|
return TRUE;
|
|
}}
|
|
}}
|
|
return FALSE;
|
|
}}""".format(elementctype=self.element_type.get_ctype(), keyctype=self.key_type.get_ctype(), typename=self.typename,
|
|
equal=self.key_type.equal_code("canonical_key", "e_key"),
|
|
canonicalize=self.key_type.canonicalize_code("key")))
|
|
|
|
print("static inline GString *")
|
|
print("{typename}_format ({typename} v, GString *s, gboolean type_annotate)".format(typename=self.typename))
|
|
print ("{")
|
|
print(" gsize len = %s_get_length(v);" % self.typename)
|
|
print(" gsize i;")
|
|
print(" if (len == 0 && type_annotate)")
|
|
print(' g_string_append_printf (s, "@%%s ", %s_typestring);' % (self.typename))
|
|
print(" g_string_append_c (s, '{');")
|
|
print(" for (i = 0; i < len; i++) {")
|
|
print(" {typename}__entry entry = {typename}_get_at(v, i);".format(typename=self.typename))
|
|
print(' if (i != 0)')
|
|
print(' g_string_append (s, ", ");')
|
|
print(' ', end='')
|
|
self.key_type.generate_append_value("%s__entry_get_key(entry)" % self.typename, "type_annotate")
|
|
print(' g_string_append (s, ": ");')
|
|
print(' ', end='')
|
|
self.element_type.generate_append_value("%s__entry_get_value(entry)" % self.typename, "type_annotate")
|
|
print(" }")
|
|
print(" g_string_append_c (s, '}');")
|
|
print(" return s;")
|
|
print("}")
|
|
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 = "maybe" + self.element_type.kind
|
|
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 generate(self):
|
|
super().generate()
|
|
|
|
# has_value
|
|
print ("static inline gboolean")
|
|
print ("{typename}_has_value({typename} v)".format(typename=self.typename, ctype=self.get_ctype()))
|
|
print ("{")
|
|
print(" return v.size != 0;")
|
|
print("}")
|
|
|
|
# Getter
|
|
print ("static inline {ctype}".format(typename=self.typename, ctype=self.element_type.get_ctype()))
|
|
print ("{typename}_get_value({typename} v)".format(typename=self.typename, ctype=self.element_type.get_ctype()))
|
|
print ("{")
|
|
print(" g_assert(v.size != 0);")
|
|
|
|
if self.element_type.is_basic():
|
|
if self.element_type.is_fixed():
|
|
print (" return (%s)*((%s *)v.base);" % (self.element_type.get_ctype(), self.element_type.get_read_ctype()))
|
|
else: # string
|
|
print (" return (%s)v.base;" % (self.element_type.get_ctype()))
|
|
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)"
|
|
print (" return (%s) { v.base, %s};" % (self.element_type.typename, size))
|
|
print("}")
|
|
|
|
print ("static inline GString *")
|
|
print ("{typename}_format ({typename} v, GString *s, gboolean type_annotate)".format(typename=self.typename))
|
|
print ("{")
|
|
print (" if (type_annotate)")
|
|
print (' g_string_append_printf (s, "@%%s ", %s_typestring);' % (self.typename))
|
|
print (" if (v.size != 0)")
|
|
print (" {")
|
|
if isinstance(self.element_type, MaybeType):
|
|
print (' g_string_append (s, "just ");')
|
|
print (' ', end='')
|
|
self.element_type.generate_append_value("{typename}_get_value(v)".format(typename=self.typename), "FALSE")
|
|
print (" }")
|
|
print (" else")
|
|
print (" {")
|
|
print (' g_string_append (s, "nothing");')
|
|
print (" }")
|
|
print(" return s;")
|
|
print ("}")
|
|
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 = attributes
|
|
self.type = type
|
|
self.last = False
|
|
|
|
def __repr__(self):
|
|
return "Field(%s, %s)" % (self.name, self.type)
|
|
|
|
def propagate_typename(self, struct_name):
|
|
self.type.set_typename (struct_name + "__" + self.name)
|
|
|
|
def generate(self, struct, index):
|
|
# Getter
|
|
print ("#define {structname}_indexof_{fieldname} {index}".format(structname=struct.typename, fieldname=self.name, index=index))
|
|
print ("static inline {ctype}".format(structname=struct.typename, ctype=self.type.get_ctype(), fieldname=self.name))
|
|
print ("{structname}_get_{fieldname}({structname} v)".format(structname=struct.typename, ctype=self.type.get_ctype(), fieldname=self.name))
|
|
print ("{")
|
|
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
|
|
print (" guint offset_size = %svariant_chunk_get_offset_size (v.size);" % funcname_prefix);
|
|
print (" gsize last_end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, %d);" % (funcname_prefix.upper(), 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():
|
|
print (" return (%s)G_STRUCT_MEMBER(%s, v.base, %s);" % (self.type.get_ctype(), self.type.get_read_ctype(), offset))
|
|
else: # string
|
|
print (" return &G_STRUCT_MEMBER(char, v.base, %s);" % (offset))
|
|
else:
|
|
if self.type.is_fixed():
|
|
print (" return (%s) { G_STRUCT_MEMBER_P(v.base, %s), %d };" % (self.type.typename, offset, self.type.get_fixed_size()))
|
|
else:
|
|
if not has_offset_size:
|
|
has_offset_size = True
|
|
print (" guint offset_size = %svariant_chunk_get_offset_size (v.size);" % funcname_prefix);
|
|
print (" gsize start = %s;" % offset);
|
|
if self.last:
|
|
print (" gsize end = v.size - offset_size * %d;" % (struct.framing_offset_size))
|
|
else:
|
|
print (" gsize end = %sVARIANT_CHUNK_READ_FRAME_OFFSET(v, %d);" % (funcname_prefix.upper(), self.table_i + 1));
|
|
print (" return (%s) { G_STRUCT_MEMBER_P(v.base, start), end - start };" % (self.type.typename))
|
|
print("}")
|
|
|
|
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
|
|
|
|
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
|
|
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):
|
|
super().generate()
|
|
for i, f in enumerate(self.fields):
|
|
f.generate(self, i)
|
|
print ("static inline GString *")
|
|
print ("{typename}_format ({typename} v, GString *s, gboolean type_annotate)".format(typename=self.typename))
|
|
print ("{")
|
|
|
|
# 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
|
|
print (' g_string_append_printf (s, "%s' % ("(" if i == 0 else ""), end = '')
|
|
for f in run:
|
|
if f.type.get_type_annotation() != "":
|
|
print ('%s', end = '')
|
|
print ('%s' % (f.type.get_format_string()), end = '')
|
|
if not f.last:
|
|
print (', ', end = '')
|
|
elif len(self.fields) == 1:
|
|
print (',)', end = '')
|
|
else:
|
|
print (')', end = '')
|
|
print ('",')
|
|
for j, f in enumerate(run):
|
|
if f.type.get_type_annotation() != "":
|
|
print (' type_annotate ? "%s" : "",' % (f.type.get_type_annotation()))
|
|
value = f.type.convert_value_for_format("{structname}_get_{fieldname}(v)".format(structname=self.typename, fieldname=f.name))
|
|
print (' %s%s' % (value, "," if j != len(run) - 1 else ");"))
|
|
else:
|
|
# A run of container fields
|
|
if i == 0:
|
|
print (' g_string_append (s, "(");')
|
|
for f in run:
|
|
value = "{structname}_get_{fieldname}(v)".format(structname=self.typename, fieldname=f.name)
|
|
f.type.generate_append_value(value, "type_annotate")
|
|
if not f.last:
|
|
print (' g_string_append (s, ", ");')
|
|
elif len(self.fields) == 1:
|
|
print (' g_string_append (s, ",)");')
|
|
else:
|
|
print (' g_string_append (s, ")");')
|
|
print(" return s;")
|
|
print ("}")
|
|
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 nativeendian")
|
|
|
|
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('file')
|
|
args = parser.parse_args()
|
|
if args.prefix:
|
|
typename_prefix = args.prefix[0].upper() + args.prefix[1:]
|
|
funcname_prefix = args.prefix + "_"
|
|
|
|
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)
|