[Spice-devel] [PATCH spice-common] RFC: add back codegen

Frediano Ziglio fziglio at redhat.com
Fri Feb 26 17:58:08 UTC 2016


> 
> codegen generated code depends on spice-common code (marshaller, messages
> etc),
> it makes more sense to keep the generator along this. Otherwise a newer
> protocol
> release will fail to build older projects (as is the case today).
> 
> *.proto files are required as well, since it generates code that parent
> modules
> depend on unconditionnaly.
> 
> RFC because we may want to import history and discuss this with Christophe
> who
> moved codegen to protocol.
> 

Agreed with the idea. Didn't check a lot the patch.

Frediano

> Signed-off-by: Marc-André Lureau <marcandre.lureau at gmail.com>
> ---
>  common/Makefile.am             |   42 +-
>  configure.ac                   |    2 -
>  python_modules/Makefile.am     |   15 +
>  python_modules/__init__.py     |    0
>  python_modules/codegen.py      |  380 +++++++++++
>  python_modules/demarshal.py    | 1274 ++++++++++++++++++++++++++++++++++++
>  python_modules/marshal.py      |  420 ++++++++++++
>  python_modules/ptypes.py       | 1138 ++++++++++++++++++++++++++++++++
>  python_modules/spice_parser.py |  163 +++++
>  spice.proto                    | 1412
>  ++++++++++++++++++++++++++++++++++++++++
>  spice1.proto                   |  943 +++++++++++++++++++++++++++
>  spice_codegen.py               |  275 ++++++++
>  tests/Makefile.am              |   18 +-
>  13 files changed, 6050 insertions(+), 32 deletions(-)
>  create mode 100644 python_modules/Makefile.am
>  create mode 100644 python_modules/__init__.py
>  create mode 100644 python_modules/codegen.py
>  create mode 100644 python_modules/demarshal.py
>  create mode 100644 python_modules/marshal.py
>  create mode 100644 python_modules/ptypes.py
>  create mode 100644 python_modules/spice_parser.py
>  create mode 100644 spice.proto
>  create mode 100644 spice1.proto
>  create mode 100755 spice_codegen.py
> 
> diff --git a/common/Makefile.am b/common/Makefile.am
> index 7382509..89809ac 100644
> --- a/common/Makefile.am
> +++ b/common/Makefile.am
> @@ -100,38 +100,38 @@ libspice_common_la_LIBADD =				\
>  	$(NULL)
>  
>  MARSHALLERS_DEPS =							\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/__init__.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/codegen.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/demarshal.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/marshal.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/ptypes.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/spice_parser.py	\
> -	$(CODE_GENERATOR_BASEDIR)/spice_codegen.py			\
> +	$(top_srcdir)/python_modules/__init__.py		\
> +	$(top_srcdir)/python_modules/codegen.py		\
> +	$(top_srcdir)/python_modules/demarshal.py		\
> +	$(top_srcdir)/python_modules/marshal.py		\
> +	$(top_srcdir)/python_modules/ptypes.py		\
> +	$(top_srcdir)/python_modules/spice_parser.py	\
> +	$(top_srcdir)/spice_codegen.py			\
>  	$(NULL)
>  
>  # Note despite being autogenerated these are not part of CLEANFILES, they
>  are
>  # actually a part of EXTRA_DIST, to avoid the need for pyparser by end users
> -generated_client_demarshallers.c: $(CODE_GENERATOR_BASEDIR)/spice.proto
> $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-demarshallers --client --include common/messages.h $< $@
> >/dev/null
> +generated_client_demarshallers.c: $(top_srcdir)/spice.proto
> $(MARSHALLERS_DEPS)
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py
> --generate-demarshallers --client --include common/messages.h $< $@
> >/dev/null
>  
> -generated_client_demarshallers1.c: $(CODE_GENERATOR_BASEDIR)/spice1.proto
> $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-demarshallers --client --include common/messages.h --prefix 1
> --ptrsize 8 $< $@ >/dev/null
> +generated_client_demarshallers1.c: $(top_srcdir)/spice1.proto
> $(MARSHALLERS_DEPS)
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py
> --generate-demarshallers --client --include common/messages.h --prefix 1
> --ptrsize 8 $< $@ >/dev/null
>  
> -generated_client_marshallers.c: $(CODE_GENERATOR_BASEDIR)/spice.proto
> $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-marshallers -P --include common/messages.h --include
> client_marshallers.h --client $< $@ >/dev/null
> +generated_client_marshallers.c: $(top_srcdir)/spice.proto
> $(MARSHALLERS_DEPS)
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers
> -P --include common/messages.h --include client_marshallers.h --client $< $@
> >/dev/null
>  
> -generated_client_marshallers1.c: $(CODE_GENERATOR_BASEDIR)/spice1.proto
> $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-marshallers -P --include common/messages.h --include
> client_marshallers.h --client --prefix 1 --ptrsize 8 $< $@ >/dev/null
> +generated_client_marshallers1.c: $(top_srcdir)/spice1.proto
> $(MARSHALLERS_DEPS)
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers
> -P --include common/messages.h --include client_marshallers.h --client
> --prefix 1 --ptrsize 8 $< $@ >/dev/null
>  
> -generated_server_demarshallers.c: $(CODE_GENERATOR_BASEDIR)/spice.proto
> $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-demarshallers --server --include common/messages.h $< $@
> >/dev/null
> +generated_server_demarshallers.c: $(top_srcdir)/spice.proto
> $(MARSHALLERS_DEPS)
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py
> --generate-demarshallers --server --include common/messages.h $< $@
> >/dev/null
>  
>  STRUCTS = -M String -M Rect -M Point -M DisplayBase -M Fill -M Opaque -M
>  Copy -M Blend -M Blackness -M Whiteness -M Invers -M Rop3 -M Stroke -M Text
>  -M Transparent -M AlphaBlend -M Composite
> -generated_server_marshallers.c: $(CODE_GENERATOR_BASEDIR)/spice.proto
> $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-marshallers $(STRUCTS) --server --include common/messages.h $< $@
> >/dev/null
> +generated_server_marshallers.c: $(top_srcdir)/spice.proto
> $(MARSHALLERS_DEPS)
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers
> $(STRUCTS) --server --include common/messages.h $< $@ >/dev/null
>  
> -generated_server_marshallers.h: $(CODE_GENERATOR_BASEDIR)/spice.proto
> $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-marshallers $(STRUCTS) --server --include common/messages.h -H $<
> $@ >/dev/null
> +generated_server_marshallers.h: $(top_srcdir)/spice.proto
> $(MARSHALLERS_DEPS)
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers
> $(STRUCTS) --server --include common/messages.h -H $< $@ >/dev/null
>  
>  EXTRA_DIST =				\
>  	$(CLIENT_MARSHALLERS)		\
> diff --git a/configure.ac b/configure.ac
> index fde03e7..4025746 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -29,8 +29,6 @@ SPICE_CHECK_SYSDEPS
>  
>  # Checks for libraries
>  PKG_CHECK_MODULES([PROTOCOL], [spice-protocol >= 0.12.10])
> -CODE_GENERATOR_BASEDIR=`${PKG_CONFIG} --variable=codegendir spice-protocol`
> -AC_SUBST([CODE_GENERATOR_BASEDIR])
>  

This could be used to reduce the diff

>  SPICE_CHECK_PYTHON_MODULES()
>  
> diff --git a/python_modules/Makefile.am b/python_modules/Makefile.am
> new file mode 100644
> index 0000000..98ab41f
> --- /dev/null
> +++ b/python_modules/Makefile.am
> @@ -0,0 +1,15 @@
> +NULL =
> +
> +python_codegendir = $(datadir)/spice-protocol/python_modules

at this point I don't see the point of installing these files.

> +dist_python_codegen_DATA =		\
> +	__init__.py			\
> +	codegen.py			\
> +	demarshal.py			\
> +	marshal.py			\
> +	ptypes.py			\
> +	spice_parser.py			\
> +	$(NULL)
> +
> +DISTCLEANFILES = *.pyc
> +
> +-include $(top_srcdir)/git.mk
> diff --git a/python_modules/__init__.py b/python_modules/__init__.py
> new file mode 100644
> index 0000000..e69de29
> diff --git a/python_modules/codegen.py b/python_modules/codegen.py
> new file mode 100644
> index 0000000..f7a2048
> --- /dev/null
> +++ b/python_modules/codegen.py
> @@ -0,0 +1,380 @@
> +
> +import six
> +from io import StringIO
> +
> +def camel_to_underscores(s, upper = False):
> +    res = ""
> +    for i in range(len(s)):
> +        c = s[i]
> +        if i > 0 and c.isupper():
> +            res = res + "_"
> +        if upper:
> +            res = res + c.upper()
> +        else:
> +            res = res + c.lower()
> +    return res
> +
> +def underscores_to_camel(s):
> +    res = ""
> +    do_upper = True
> +    for i in range(len(s)):
> +        c = s[i]
> +        if c == "_":
> +            do_upper = True
> +        else:
> +            if do_upper:
> +                res = res + c.upper()
> +            else:
> +                res = res + c
> +            do_upper = False
> +    return res
> +
> +proto_prefix = "Temp"
> +
> +def set_prefix(prefix):
> +    global proto_prefix
> +    global proto_prefix_upper
> +    global proto_prefix_lower
> +    proto_prefix = prefix
> +    proto_prefix_upper = prefix.upper()
> +    proto_prefix_lower = prefix.lower()
> +
> +def prefix_underscore_upper(*args):
> +    s = proto_prefix_upper
> +    for arg in args:
> +        s = s + "_" + arg
> +    return s
> +
> +def prefix_underscore_lower(*args):
> +    s = proto_prefix_lower
> +    for arg in args:
> +        s = s + "_" + arg
> +    return s
> +
> +def prefix_camel(*args):
> +    s = proto_prefix
> +    for arg in args:
> +        s = s + underscores_to_camel(arg)
> +    return s
> +
> +def increment_identifier(idf):
> +    v = idf[-1:]
> +    if v.isdigit():
> +        return idf[:-1] + str(int(v) + 1)
> +    return idf + "2"
> +
> +def sum_array(array):
> +    if len(array) == 0:
> +        return 0
> +    return " + ".join(array)
> +
> +class CodeWriter:
> +    def __init__(self):
> +        self.out = StringIO()
> +        self.contents = [self.out]
> +        self.indentation = 0
> +        self.at_line_start = True
> +        self.indexes = ["i", "j", "k", "ii", "jj", "kk"]
> +        self.current_index = 0
> +        self.generated = {}
> +        self.vars = []
> +        self.has_error_check = False
> +        self.options = {}
> +        self.function_helper_writer = None
> +        self.index_type = 'uint32_t'
> +
> +    def set_option(self, opt, value = True):
> +        self.options[opt] = value
> +
> +    def has_option(self, opt):
> +        return opt in self.options
> +
> +    def set_is_generated(self, kind, name):
> +        if kind not in self.generated:
> +            v = {}
> +            self.generated[kind] = v
> +        else:
> +            v = self.generated[kind]
> +        v[name] = 1
> +
> +    def is_generated(self, kind, name):
> +        if kind not in self.generated:
> +            return False
> +        v = self.generated[kind]
> +        return name in v
> +
> +    def getvalue(self):
> +        strs = [writer.getvalue() for writer in self.contents]
> +        return "".join(strs)
> +
> +    def get_subwriter(self):
> +        writer = CodeWriter()
> +        self.contents.append(writer)
> +        self.out = StringIO()
> +        self.contents.append(self.out)
> +        writer.indentation = self.indentation
> +        writer.at_line_start = self.at_line_start
> +        writer.index_type = self.index_type
> +        writer.generated = self.generated
> +        writer.options = self.options
> +        writer.public_prefix = self.public_prefix
> +
> +        return writer
> +
> +    def write(self, s):
> +        # Ensure its a unicode string
> +        if six.PY3:
> +            s = str(s)
> +        else:
> +            s = unicode(s)
> +
> +        if len(s) == 0:
> +            return
> +
> +        if self.at_line_start:
> +            self.out.write(u" " * self.indentation)
> +            self.at_line_start = False
> +        self.out.write(s)
> +        return self
> +
> +    def newline(self):
> +        self.out.write(u"\n")
> +        self.at_line_start = True
> +        return self
> +
> +    def writeln(self, s):
> +        self.write(s)
> +        self.newline()
> +        return self
> +
> +    def label(self, s):
> +        self.indentation = self.indentation - 1
> +        self.write(s + ":")
> +        self.indentation = self.indentation + 1
> +        self.newline()
> +
> +    def statement(self, s):
> +        self.write(s)
> +        self.write(";")
> +        self.newline()
> +        return self
> +
> +    def assign(self, var, val):
> +        self.write("%s = %s" % (var, val))
> +        self.write(";")
> +        self.newline()
> +        return self
> +
> +    def increment(self, var, val):
> +        self.write("%s += %s" % (var, val))
> +        self.write(";")
> +        self.newline()
> +        return self
> +
> +    def comment(self, str):
> +        self.write("/* " + str + " */")
> +        return self
> +
> +    def todo(self, str):
> +        self.comment("TODO: *** %s ***" % str).newline()
> +        return self
> +
> +    def error_check(self, check, label = "error"):
> +        self.has_error_check = True
> +        with self.block("if (SPICE_UNLIKELY(%s))" % check):
> +            if self.has_option("print_error"):
> +                self.statement('printf("%%s: Caught error - %s",
> __PRETTY_FUNCTION__)' % check)
> +            if self.has_option("assert_on_error"):
> +                self.statement("assert(0)")
> +            self.statement("goto %s" % label)
> +
> +    def indent(self):
> +        self.indentation += 4
> +
> +    def unindent(self):
> +        self.indentation -= 4
> +        if self.indentation < 0:
> +            self.indentation = 0
> +
> +    def begin_block(self, prefix= "", comment = ""):
> +        if len(prefix) > 0:
> +            self.write(prefix)
> +        if self.at_line_start:
> +            self.write("{")
> +        else:
> +            self.write(" {")
> +        if len(comment) > 0:
> +            self.write(" ")
> +            self.comment(comment)
> +        self.newline()
> +        self.indent()
> +
> +    def end_block(self, semicolon=False, newline=True):
> +        self.unindent()
> +        if self.at_line_start:
> +            self.write("}")
> +        else:
> +            self.write(" }")
> +        if semicolon:
> +            self.write(";")
> +        if newline:
> +            self.newline()
> +
> +    class Block:
> +        def __init__(self, writer, semicolon, newline):
> +            self.writer = writer
> +            self.semicolon = semicolon
> +            self.newline = newline
> +
> +        def __enter__(self):
> +            return self.writer.get_subwriter()
> +
> +        def __exit__(self, exc_type, exc_value, traceback):
> +            self.writer.end_block(self.semicolon, self.newline)
> +
> +    class PartialBlock:
> +        def __init__(self, writer, scope, semicolon, newline):
> +            self.writer = writer
> +            self.scope = scope
> +            self.semicolon = semicolon
> +            self.newline = newline
> +
> +        def __enter__(self):
> +            return self.scope
> +
> +        def __exit__(self, exc_type, exc_value, traceback):
> +            self.writer.end_block(self.semicolon, self.newline)
> +
> +    class NoBlock:
> +        def __init__(self, scope):
> +            self.scope = scope
> +
> +        def __enter__(self):
> +            return self.scope
> +
> +        def __exit__(self, exc_type, exc_value, traceback):
> +            pass
> +
> +    def block(self, prefix= "", comment = "", semicolon=False,
> newline=True):
> +        self.begin_block(prefix, comment)
> +        return self.Block(self, semicolon, newline)
> +
> +    def partial_block(self, scope, semicolon=False, newline=True):
> +        return self.PartialBlock(self, scope, semicolon, newline)
> +
> +    def no_block(self, scope):
> +        return self.NoBlock(scope)
> +
> +    def optional_block(self, scope):
> +        if scope != None:
> +            return self.NoBlock(scope)
> +        return self.block()
> +
> +    def for_loop(self, index, limit):
> +        return self.block("for (%s = 0; %s < %s; %s++)" % (index, index,
> limit, index))
> +
> +    def while_loop(self, expr):
> +        return self.block("while (%s)" % (expr))
> +
> +    def if_block(self, check, elseif=False, newline=True):
> +        s = "if (%s)" % (check)
> +        if elseif:
> +            s = " else " + s
> +        self.begin_block(s, "")
> +        return self.Block(self, False, newline)
> +
> +    def variable_defined(self, name):
> +        for n in self.vars:
> +            if n == name:
> +                return True
> +        return False
> +
> +    def variable_def(self, ctype, *names):
> +        for n in names:
> +            # Strip away initialization
> +            i = n.find("=")
> +            if i != -1:
> +                n = n[0:i]
> +            self.vars.append(n.strip())
> +        # only add space for non-pointer types
> +        if ctype[-1] == "*":
> +            ctype = ctype[:-1].rstrip()
> +            self.writeln("%s *%s;"%(ctype, ", *".join(names)))
> +        else:
> +            self.writeln("%s %s;"%(ctype, ", ".join(names)))
> +        return self
> +
> +    def function_helper(self):
> +        if self.function_helper_writer != None:
> +            writer = self.function_helper_writer.get_subwriter()
> +            self.function_helper_writer.newline()
> +        else:
> +            writer = self.get_subwriter()
> +        return writer
> +
> +    def function(self, name, return_type, args, static = False):
> +        self.has_error_check = False
> +        self.function_helper_writer = self.get_subwriter()
> +        if static:
> +            self.write("static ")
> +        self.write(return_type)
> +        self.write(" %s(%s)"% (name, args)).newline()
> +        self.begin_block()
> +        self.function_variables_writer = self.get_subwriter()
> +        self.function_variables = {}
> +        return self.function_variables_writer
> +
> +    def macro(self, name, args, define):
> +        self.write("#define %s(%s) %s" % (name, args, define)).newline()
> +
> +    def ifdef(self, name):
> +        indentation = self.indentation
> +        self.indentation = 0;
> +        self.write("#ifdef %s" % (name)).newline()
> +        self.indentation = indentation
> +
> +    def ifdef_else(self, name):
> +        indentation = self.indentation
> +        self.indentation = 0;
> +        self.write("#else /* %s */" % (name)).newline()
> +        self.indentation = indentation
> +
> +    def endif(self, name):
> +        indentation = self.indentation
> +        self.indentation = 0;
> +        self.write("#endif /* %s */" % (name)).newline()
> +        self.indentation = indentation
> +
> +    def add_function_variable(self, ctype, name):
> +        if name in self.function_variables:
> +            assert(self.function_variables[name] == ctype)
> +        else:
> +            self.function_variables[name] = ctype
> +            self.function_variables_writer.variable_def(ctype, name)
> +
> +    def pop_index(self):
> +        index = self.indexes[self.current_index]
> +        self.current_index = self.current_index + 1
> +        self.add_function_variable(self.index_type, index)
> +        return index
> +
> +    def push_index(self):
> +        assert self.current_index > 0
> +        self.current_index = self.current_index - 1
> +
> +    class Index:
> +        def __init__(self, writer, val):
> +            self.writer = writer
> +            self.val = val
> +
> +        def __enter__(self):
> +            return self.val
> +
> +        def __exit__(self, exc_type, exc_value, traceback):
> +            self.writer.push_index()
> +
> +    def index(self, no_block = False):
> +        if no_block:
> +            return self.no_block(None)
> +        val = self.pop_index()
> +        return self.Index(self, val)
> diff --git a/python_modules/demarshal.py b/python_modules/demarshal.py
> new file mode 100644
> index 0000000..2252f37
> --- /dev/null
> +++ b/python_modules/demarshal.py
> @@ -0,0 +1,1274 @@
> +
> +from . import ptypes
> +from . import codegen
> +
> +# The handling of sizes is somewhat complex, as there are several types of
> size:
> +# * nw_size
> +#   This is the network size, i.e. the number of bytes on the network
> +#
> +# * mem_size
> +#   The total amount of memory used for the representation of something
> inside
> +#   spice. This is generally sizeof(C struct), but can be larger if for
> instance
> +#   the type has a variable size array at the end or has a pointer in it
> that
> +#   points to another data chunk (which will be allocated after the main
> +#   data chunk). This is essentially how much memory you need to allocate to
> +#   contain the data type.
> +#
> +# * extra_size
> +#   This is the size of anything that is not part of the containing
> structure.
> +#   For instance, a primitive (say uint32_t) member has no extra size,
> because
> +#   when allocating its part of the sizeof(MessageStructType) struct.
> However
> +#   a variable array can be places at the end of a structure (@end) and its
> +#   size is then extra_size. Note that this extra_size is included in the
> +#   mem_size of the enclosing struct, and even if you request the mem_size
> +#   of the array itself. However, extra_size is typically not requested
> +#   when the full mem_size is also requested.
> +#
> +#   extra sizes come in two flavours. contains_extra_size means that the
> item
> +#   has a normal presence in the parent container, but has some additional
> +#   extra_size it references. For instance via a pointer somewhere in it.
> +#   There is also is_extra_size(). This indicates that the whole elements
> +#   "normal" mem size should be considered extra size for the container, so
> +#   when computing the parent mem_size you should add the mem_size of this
> +#   part as extra_size
> +
> +def write_parser_helpers(writer):
> +    if writer.is_generated("helper", "demarshaller"):
> +        return
> +
> +    writer.set_is_generated("helper", "demarshaller")
> +
> +    writer = writer.function_helper()
> +
> +    writer.writeln("#ifdef WORDS_BIGENDIAN")
> +    for size in [8, 16, 32, 64]:
> +        for sign in ["", "u"]:
> +            utype = "uint%d" % (size)
> +            type = "%sint%d" % (sign, size)
> +            swap = "SPICE_BYTESWAP%d" % size
> +            if size == 8:
> +                writer.macro("read_%s" % type, "ptr", "(*((%s_t *)(ptr)))" %
> type)
> +                writer.macro("write_%s" % type, "ptr, val", "*(%s_t *)(ptr)
> = val" % (type))
> +            else:
> +                writer.macro("read_%s" % type, "ptr", "((%s_t)%s(*((%s_t
> *)(ptr))))" % (type, swap, utype))
> +                writer.macro("write_%s" % type, "ptr, val", "*(%s_t *)(ptr)
> = %s((%s_t)val)" % (utype, swap, utype))
> +    writer.writeln("#else")
> +    for size in [8, 16, 32, 64]:
> +        for sign in ["", "u"]:
> +            type = "%sint%d" % (sign, size)
> +            writer.macro("read_%s" % type, "ptr", "(*((%s_t *)(ptr)))" %
> type)
> +            writer.macro("write_%s" % type, "ptr, val", "(*((%s_t *)(ptr)))
> = val" % type)
> +    writer.writeln("#endif")
> +
> +    for size in [8, 16, 32, 64]:
> +        for sign in ["", "u"]:
> +            writer.newline()
> +            type = "%sint%d" % (sign, size)
> +            ctype = "%s_t" % type
> +            scope = writer.function("SPICE_GNUC_UNUSED consume_%s" % type,
> ctype, "uint8_t **ptr", True)
> +            scope.variable_def(ctype, "val")
> +            writer.assign("val", "read_%s(*ptr)" % type)
> +            writer.increment("*ptr", size // 8)
> +            writer.statement("return val")
> +            writer.end_block()
> +
> +    writer.function("SPICE_GNUC_UNUSED consume_fd", "int", "uint8_t **ptr",
> True)
> +    writer.statement("return -1")
> +    writer.end_block()
> +
> +    writer.newline()
> +    writer.statement("typedef struct PointerInfo PointerInfo")
> +    writer.statement("typedef void (*message_destructor_t)(uint8_t
> *message)")
> +    writer.statement("typedef uint8_t * (*parse_func_t)(uint8_t
> *message_start, uint8_t *message_end, uint8_t *struct_data, PointerInfo
> *ptr_info, int minor)")
> +    writer.statement("typedef uint8_t * (*parse_msg_func_t)(uint8_t
> *message_start, uint8_t *message_end, int minor, size_t *size_out,
> message_destructor_t *free_message)")
> +    writer.statement("typedef uint8_t *
> (*spice_parse_channel_func_t)(uint8_t *message_start, uint8_t *message_end,
> uint16_t message_type, int minor, size_t *size_out, message_destructor_t
> *free_message)")
> +
> +    writer.newline()
> +    writer.begin_block("struct PointerInfo")
> +    writer.variable_def("uint64_t", "offset")
> +    writer.variable_def("parse_func_t", "parse")
> +    writer.variable_def("void **", "dest")
> +    writer.variable_def("uint32_t", "nelements")
> +    writer.end_block(semicolon=True)
> +
> +def write_read_primitive(writer, start, container, name, scope):
> +    m = container.lookup_member(name)
> +    assert(m.is_primitive())
> +    writer.assign("pos", start + " + " + container.get_nw_offset(m, "",
> "__nw_size"))
> +    writer.error_check("pos + %s > message_end" %
> m.member_type.get_fixed_nw_size())
> +
> +    var = "%s__value" % (name.replace(".", "_"))
> +    if not scope.variable_defined(var):
> +        scope.variable_def(m.member_type.c_type(), var)
> +    writer.assign(var, "read_%s(pos)" % (m.member_type.primitive_type()))
> +    return var
> +
> +def write_write_primitive(writer, start, container, name, val):
> +    m = container.lookup_member(name)
> +    assert(m.is_primitive())
> +    writer.assign("pos", start + " + " + container.get_nw_offset(m, "",
> "__nw_size"))
> +
> +    var = "%s__value" % (name)
> +    writer.statement("write_%s(pos, %s)" % (m.member_type.primitive_type(),
> val))
> +    return var
> +
> +def write_read_primitive_item(writer, item, scope):
> +    assert(item.type.is_primitive())
> +    writer.assign("pos", item.get_position())
> +    writer.error_check("pos + %s > message_end" %
> item.type.get_fixed_nw_size())
> +    var = "%s__value" % (item.subprefix.replace(".", "_"))
> +    scope.variable_def(item.type.c_type(), var)
> +    writer.assign(var, "read_%s(pos)" % (item.type.primitive_type()))
> +    return var
> +
> +class ItemInfo:
> +    def __init__(self, type, prefix, position):
> +        self.type = type
> +        self.prefix = prefix
> +        self.subprefix = prefix
> +        self.position = position
> +        self.member = None
> +
> +    def nw_size(self):
> +        return self.prefix + "__nw_size"
> +
> +    def mem_size(self):
> +        return self.prefix + "__mem_size"
> +
> +    def extra_size(self):
> +        return self.prefix + "__extra_size"
> +
> +    def get_position(self):
> +        return self.position
> +
> +class MemberItemInfo(ItemInfo):
> +    def __init__(self, member, mprefix, container, start):
> +        if not member.is_switch():
> +            self.type = member.member_type
> +        self.prefix = member.name
> +        if mprefix:
> +            mprefix = mprefix + "_"
> +            self.prefix = mprefix + self.prefix
> +        self.subprefix = member.name
> +        self.position = "(%s + %s)" % (start,
> container.get_nw_offset(member, mprefix or "", "__nw_size"))
> +        self.member = member
> +
> +def write_validate_switch_member(writer, mprefix, container, switch_member,
> scope, parent_scope, start,
> +                                 want_nw_size, want_mem_size,
> want_extra_size):
> +    var = container.lookup_member(switch_member.variable)
> +    var_type = var.member_type
> +
> +    v = write_read_primitive(writer, start, container,
> switch_member.variable, parent_scope)
> +
> +    item = MemberItemInfo(switch_member, mprefix, container, start)
> +
> +    first = True
> +    for c in switch_member.cases:
> +        check = c.get_check(v, var_type)
> +        m = c.member
> +        with writer.if_block(check, not first, False) as if_scope:
> +            item.type = c.member.member_type
> +            item.subprefix = item.prefix + "_" + m.name
> +            item.member = c.member
> +
> +            all_as_extra_size = m.is_extra_size() and want_extra_size
> +            if not want_mem_size and all_as_extra_size and not
> scope.variable_defined(item.mem_size()):
> +                scope.variable_def("uint32_t", item.mem_size())
> +
> +            sub_want_mem_size = want_mem_size or all_as_extra_size
> +            sub_want_extra_size = want_extra_size and not all_as_extra_size
> +
> +            write_validate_item(writer, container, item, if_scope, scope,
> start,
> +                                want_nw_size, sub_want_mem_size,
> sub_want_extra_size)
> +
> +            if all_as_extra_size:
> +                writer.assign(item.extra_size(), item.mem_size())
> +
> +        first = False
> +
> +    with writer.block(" else"):
> +        if want_nw_size:
> +            writer.assign(item.nw_size(), 0)
> +        if want_mem_size:
> +            writer.assign(item.mem_size(), 0)
> +        if want_extra_size:
> +            writer.assign(item.extra_size(), 0)
> +
> +    writer.newline()
> +
> +def write_validate_struct_function(writer, struct):
> +    validate_function = "validate_%s" % struct.c_type()
> +    if writer.is_generated("validator", validate_function):
> +        return validate_function
> +
> +    writer.set_is_generated("validator", validate_function)
> +    writer = writer.function_helper()
> +    scope = writer.function(validate_function, "static intptr_t", "uint8_t
> *message_start, uint8_t *message_end, uint64_t offset, SPICE_GNUC_UNUSED int
> minor")
> +    scope.variable_def("uint8_t *", "start = message_start + offset")
> +    scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", "pos")
> +    scope.variable_def("size_t", "mem_size", "nw_size")
> +    num_pointers = struct.get_num_pointers()
> +    if  num_pointers != 0:
> +        scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size")
> +
> +    writer.newline()
> +    with writer.if_block("offset == 0"):
> +        writer.statement("return 0")
> +
> +    writer.newline()
> +    writer.error_check("start >= message_end")
> +
> +    writer.newline()
> +    write_validate_container(writer, None, struct, "start", scope, True,
> True, False)
> +
> +    writer.newline()
> +    writer.comment("Check if struct fits in reported side").newline()
> +    writer.error_check("start + nw_size > message_end")
> +
> +    writer.statement("return mem_size")
> +
> +    writer.newline()
> +    writer.label("error")
> +    writer.statement("return -1")
> +
> +    writer.end_block()
> +
> +    return validate_function
> +
> +def write_validate_pointer_item(writer, container, item, scope,
> parent_scope, start,
> +                                want_nw_size, want_mem_size,
> want_extra_size):
> +    if want_nw_size:
> +        writer.assign(item.nw_size(), item.type.get_fixed_nw_size())
> +
> +    if want_mem_size or want_extra_size:
> +        target_type = item.type.target_type
> +
> +        v = write_read_primitive_item(writer, item, scope)
> +        if item.type.has_attr("nonnull"):
> +            writer.error_check("%s == 0" % v)
> +
> +        # pointer target is struct, or array of primitives
> +        # if array, need no function check
> +
> +        if target_type.is_array():
> +            writer.error_check("message_start + %s >= message_end" % v)
> +
> +
> +            assert target_type.element_type.is_primitive()
> +
> +            array_item = ItemInfo(target_type, "%s__array" % item.prefix,
> start)
> +            scope.variable_def("uint32_t", array_item.nw_size())
> +            # don't create a variable that isn't used, fixes
> -Werror=unused-but-set-variable
> +            need_mem_size = want_mem_size or (
> +                want_extra_size and not item.member.has_attr("chunk")
> +                and not target_type.is_cstring_length())
> +            if need_mem_size:
> +                scope.variable_def("uint32_t", array_item.mem_size())
> +            if target_type.is_cstring_length():
> +                writer.assign(array_item.nw_size(), "spice_strnlen((char
> *)message_start + %s, message_end - (message_start + %s))" % (v, v))
> +                writer.error_check("*(message_start + %s + %s) != 0" % (v,
> array_item.nw_size()))
> +            else:
> +                write_validate_array_item(writer, container, array_item,
> scope, parent_scope, start,
> +                                          True, want_mem_size=need_mem_size,
> want_extra_size=False)
> +                writer.error_check("message_start + %s + %s > message_end" %
> (v, array_item.nw_size()))
> +
> +            if want_extra_size:
> +                if item.member and item.member.has_attr("chunk"):
> +                    writer.assign(item.extra_size(), "sizeof(SpiceChunks) +
> sizeof(SpiceChunk)")
> +                elif item.member and item.member.has_attr("nocopy"):
> +                    writer.comment("@nocopy, so no extra size").newline()
> +                    writer.assign(item.extra_size(), 0)
> +                elif target_type.element_type.get_fixed_nw_size == 1:
> +                    writer.assign(item.extra_size(), array_item.mem_size())
> +                # If not bytes or zero, add padding needed for alignment
> +                else:
> +                    writer.assign(item.extra_size(), "%s + /* for alignment
> */ 3" % array_item.mem_size())
> +            if want_mem_size:
> +                writer.assign(item.mem_size(), "sizeof(void *) + %s" %
> array_item.mem_size())
> +
> +        elif target_type.is_struct():
> +            validate_function = write_validate_struct_function(writer,
> target_type)
> +            writer.assign("ptr_size", "%s(message_start, message_end, %s,
> minor)" % (validate_function, v))
> +            writer.error_check("ptr_size < 0")
> +
> +            if want_extra_size:
> +                writer.assign(item.extra_size(), "ptr_size + /* for
> alignment */ 3")
> +            if want_mem_size:
> +                writer.assign(item.mem_size(), "sizeof(void *) + ptr_size")
> +        else:
> +            raise NotImplementedError("pointer to unsupported type %s" %
> target_type)
> +
> +
> +def write_validate_array_item(writer, container, item, scope, parent_scope,
> start,
> +                              want_nw_size, want_mem_size, want_extra_size):
> +    array = item.type
> +    is_byte_size = False
> +    element_type = array.element_type
> +    if array.is_bytes_length():
> +        nelements = "%s__nbytes" %(item.prefix)
> +        real_nelements = "%s__nelements" %(item.prefix)
> +        if not parent_scope.variable_defined(real_nelements):
> +            parent_scope.variable_def("uint32_t", real_nelements)
> +    else:
> +        nelements = "%s__nelements" %(item.prefix)
> +    if not parent_scope.variable_defined(nelements):
> +        parent_scope.variable_def("uint32_t", nelements)
> +
> +    if array.is_constant_length():
> +        writer.assign(nelements, array.size)
> +    elif array.is_remaining_length():
> +        if element_type.is_fixed_nw_size():
> +            if element_type.get_fixed_nw_size() == 1:
> +                writer.assign(nelements, "message_end - %s" %
> item.get_position())
> +            else:
> +                writer.assign(nelements, "(message_end - %s) / (%s)"
> %(item.get_position(), element_type.get_fixed_nw_size()))
> +        else:
> +            raise NotImplementedError("TODO array[] of dynamic element size
> not done yet")
> +    elif array.is_identifier_length():
> +        v = write_read_primitive(writer, start, container, array.size,
> scope)
> +        writer.assign(nelements, v)
> +    elif array.is_image_size_length():
> +        bpp = array.size[1]
> +        width = array.size[2]
> +        rows = array.size[3]
> +        width_v = write_read_primitive(writer, start, container, width,
> scope)
> +        rows_v = write_read_primitive(writer, start, container, rows, scope)
> +        # TODO: Handle multiplication overflow
> +        if bpp == 8:
> +            writer.assign(nelements, "%s * %s" % (width_v, rows_v))
> +        elif bpp == 1:
> +            writer.assign(nelements, "((%s + 7) / 8 ) * %s" % (width_v,
> rows_v))
> +        else:
> +            writer.assign(nelements, "((%s * %s + 7) / 8 ) * %s" % (bpp,
> width_v, rows_v))
> +    elif array.is_bytes_length():
> +        is_byte_size = True
> +        v = write_read_primitive(writer, start, container, array.size[1],
> scope)
> +        writer.assign(nelements, v)
> +        writer.assign(real_nelements, 0)
> +    elif array.is_cstring_length():
> +        writer.todo("cstring array size type not handled yet")
> +    else:
> +        writer.todo("array size type not handled yet")
> +
> +    writer.newline()
> +
> +    nw_size = item.nw_size()
> +    mem_size = item.mem_size()
> +    extra_size = item.extra_size()
> +
> +    if is_byte_size and want_nw_size:
> +        writer.assign(nw_size, nelements)
> +        want_nw_size = False
> +
> +    if element_type.is_fixed_nw_size() and want_nw_size:
> +        element_size = element_type.get_fixed_nw_size()
> +        # TODO: Overflow check the multiplication
> +        if element_size == 1:
> +            writer.assign(nw_size, nelements)
> +        else:
> +            writer.assign(nw_size, "(%s) * %s" % (element_size, nelements))
> +        want_nw_size = False
> +
> +    if array.has_attr("as_ptr") and want_mem_size:
> +        writer.assign(mem_size, "sizeof(void *)")
> +        want_mem_size = False
> +
> +    if array.has_attr("chunk"):
> +        if want_mem_size:
> +            writer.assign(extra_size, "sizeof(SpiceChunks *)")
> +            want_mem_size = False
> +        if want_extra_size:
> +            writer.assign(extra_size, "sizeof(SpiceChunks) +
> sizeof(SpiceChunk)")
> +            want_extra_size = False
> +
> +    if element_type.is_fixed_sizeof() and want_mem_size and not
> is_byte_size:
> +        # TODO: Overflow check the multiplication
> +        if array.has_attr("ptr_array"):
> +            writer.assign(mem_size, "sizeof(void *) + SPICE_ALIGN(%s * %s,
> 4)" % (element_type.sizeof(), nelements))
> +        else:
> +            writer.assign(mem_size, "%s * %s" % (element_type.sizeof(),
> nelements))
> +        want_mem_size = False
> +
> +    if not element_type.contains_extra_size() and want_extra_size:
> +        writer.assign(extra_size, 0)
> +        want_extra_size = False
> +
> +    if not (want_mem_size or want_nw_size or want_extra_size):
> +        return
> +
> +    start2 = codegen.increment_identifier(start)
> +    scope.variable_def("uint8_t *", "%s = %s" % (start2,
> item.get_position()))
> +    if is_byte_size:
> +        start2_end = "%s_array_end" % start2
> +        scope.variable_def("uint8_t *", start2_end)
> +
> +    element_item = ItemInfo(element_type, "%s__element" % item.prefix,
> start2)
> +
> +    element_nw_size = element_item.nw_size()
> +    element_mem_size = element_item.mem_size()
> +    element_extra_size = element_item.extra_size()
> +    scope.variable_def("uint32_t", element_nw_size)
> +    scope.variable_def("uint32_t", element_mem_size)
> +    if want_extra_size:
> +        scope.variable_def("uint32_t", element_extra_size)
> +
> +    if want_nw_size:
> +        writer.assign(nw_size, 0)
> +    if want_mem_size:
> +        writer.assign(mem_size, 0)
> +    if want_extra_size:
> +        writer.assign(extra_size, 0)
> +
> +    want_element_nw_size = want_nw_size
> +    if element_type.is_fixed_nw_size():
> +        start_increment = element_type.get_fixed_nw_size()
> +    else:
> +        want_element_nw_size = True
> +        start_increment = element_nw_size
> +
> +    if is_byte_size:
> +        writer.assign(start2_end, "%s + %s" % (start2, nelements))
> +
> +    with writer.index(no_block = is_byte_size) as index:
> +        with writer.while_loop("%s < %s" % (start2, start2_end) ) if
> is_byte_size else writer.for_loop(index, nelements) as scope:
> +            if is_byte_size:
> +                writer.increment(real_nelements, 1)
> +            write_validate_item(writer, container, element_item, scope,
> parent_scope, start2,
> +                                want_element_nw_size, want_mem_size,
> want_extra_size)
> +
> +            if want_nw_size:
> +                writer.increment(nw_size, element_nw_size)
> +            if want_mem_size:
> +                if array.has_attr("ptr_array"):
> +                    writer.increment(mem_size, "sizeof(void *) +
> SPICE_ALIGN(%s, 4)" % element_mem_size)
> +                else:
> +                    writer.increment(mem_size, element_mem_size)
> +            if want_extra_size:
> +                writer.increment(extra_size, element_extra_size)
> +
> +            writer.increment(start2, start_increment)
> +    if is_byte_size:
> +        writer.error_check("%s != %s" % (start2, start2_end))
> +        write_write_primitive(writer, start, container, array.size[1],
> real_nelements)
> +
> +def write_validate_struct_item(writer, container, item, scope, parent_scope,
> start,
> +                               want_nw_size, want_mem_size,
> want_extra_size):
> +    struct = item.type
> +    start2 = codegen.increment_identifier(start)
> +    scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", start2 + " = %s" %
> (item.get_position()))
> +
> +    write_validate_container(writer, item.prefix, struct, start2, scope,
> want_nw_size, want_mem_size, want_extra_size)
> +
> +def write_validate_primitive_item(writer, container, item, scope,
> parent_scope, start,
> +                                  want_nw_size, want_mem_size,
> want_extra_size):
> +    if want_nw_size:
> +        nw_size = item.nw_size()
> +        writer.assign(nw_size, item.type.get_fixed_nw_size())
> +    if want_mem_size:
> +        mem_size = item.mem_size()
> +        writer.assign(mem_size, item.type.sizeof())
> +    if want_extra_size:
> +        writer.assign(item.extra_size(), 0)
> +
> +def write_validate_item(writer, container, item, scope, parent_scope, start,
> +                        want_nw_size, want_mem_size, want_extra_size):
> +    if item.member and item.member.has_attr("to_ptr"):
> +        want_nw_size = True
> +    if item.type.is_pointer():
> +        write_validate_pointer_item(writer, container, item, scope,
> parent_scope, start,
> +                                    want_nw_size, want_mem_size,
> want_extra_size)
> +    elif item.type.is_array():
> +        write_validate_array_item(writer, container, item, scope,
> parent_scope, start,
> +                                  want_nw_size, want_mem_size,
> want_extra_size)
> +    elif item.type.is_struct():
> +        write_validate_struct_item(writer, container, item, scope,
> parent_scope, start,
> +                                   want_nw_size, want_mem_size,
> want_extra_size)
> +    elif item.type.is_primitive():
> +        write_validate_primitive_item(writer, container, item, scope,
> parent_scope, start,
> +                                      want_nw_size, want_mem_size,
> want_extra_size)
> +    else:
> +        writer.todo("Implement validation of %s" % item.type)
> +
> +    if item.member and item.member.has_attr("to_ptr"):
> +        saved_size = "%s__saved_size" % item.member.name
> +        writer.add_function_variable("uint32_t", saved_size + " = 0")
> +        writer.assign(saved_size, item.nw_size())
> +
> +def write_validate_member(writer, mprefix, container, member, parent_scope,
> start,
> +                          want_nw_size, want_mem_size, want_extra_size):
> +    if member.has_attr("virtual"):
> +        return
> +
> +    if member.has_minor_attr():
> +        prefix = "if (minor >= %s)" % (member.get_minor_attr())
> +        newline = False
> +    else:
> +        prefix = ""
> +        newline = True
> +    item = MemberItemInfo(member, mprefix, container, start)
> +    with writer.block(prefix, newline=newline, comment=member.name) as
> scope:
> +        if member.is_switch():
> +            write_validate_switch_member(writer, mprefix, container, member,
> scope, parent_scope, start,
> +                                         want_nw_size, want_mem_size,
> want_extra_size)
> +        else:
> +            write_validate_item(writer, container, item, scope,
> parent_scope, start,
> +                                want_nw_size, want_mem_size,
> want_extra_size)
> +
> +    if member.has_minor_attr():
> +        with writer.block(" else", comment = "minor < %s" %
> (member.get_minor_attr())):
> +            if member.is_array():
> +                nelements = "%s__nelements" %(item.prefix)
> +                writer.assign(nelements, 0)
> +            if want_nw_size:
> +                writer.assign(item.nw_size(), 0)
> +
> +            if want_mem_size:
> +                if member.is_fixed_sizeof():
> +                    writer.assign(item.mem_size(), member.sizeof())
> +                elif member.is_array():
> +                    writer.assign(item.mem_size(), 0)
> +                else:
> +                    raise NotImplementedError("TODO minor check for
> non-constant items")
> +
> +            assert not want_extra_size
> +
> +def write_validate_container(writer, prefix, container, start, parent_scope,
> want_nw_size, want_mem_size, want_extra_size):
> +    def prefix_m(prefix, m):
> +        name = m.name
> +        if prefix:
> +            name = prefix + "_" + name
> +        return name
> +
> +    for m in container.members:
> +        sub_want_nw_size = want_nw_size and not m.is_fixed_nw_size()
> +        sub_want_mem_size = m.is_extra_size() and want_mem_size
> +        sub_want_extra_size = not m.is_extra_size() and
> m.contains_extra_size()
> +        defs = ["size_t"]
> +        name = prefix_m(prefix, m)
> +        if sub_want_nw_size:
> +
> +            defs.append (name + "__nw_size")
> +        if sub_want_mem_size:
> +            defs.append (name + "__mem_size")
> +        if sub_want_extra_size:
> +            defs.append (name + "__extra_size")
> +
> +        if sub_want_nw_size or sub_want_mem_size or sub_want_extra_size:
> +            parent_scope.variable_def(*defs)
> +            write_validate_member(writer, prefix, container, m,
> parent_scope, start,
> +                                  sub_want_nw_size, sub_want_mem_size,
> sub_want_extra_size)
> +            writer.newline()
> +
> +    if want_nw_size:
> +        if prefix:
> +            nw_size = prefix + "__nw_size"
> +        else:
> +            nw_size = "nw_size"
> +
> +        size = 0
> +        for m in container.members:
> +            if m.is_fixed_nw_size():
> +                size = size + m.get_fixed_nw_size()
> +
> +        nm_sum = str(size)
> +        for m in container.members:
> +            name = prefix_m(prefix, m)
> +            if not m.is_fixed_nw_size():
> +                nm_sum = nm_sum + " + " + name + "__nw_size"
> +
> +        writer.assign(nw_size, nm_sum)
> +
> +    if want_mem_size:
> +        if prefix:
> +            mem_size = prefix + "__mem_size"
> +        else:
> +            mem_size = "mem_size"
> +
> +        mem_sum = container.sizeof()
> +        for m in container.members:
> +            name = prefix_m(prefix, m)
> +            if m.is_extra_size():
> +                mem_sum = mem_sum + " + " + name + "__mem_size"
> +            elif m.contains_extra_size():
> +                mem_sum = mem_sum + " + " + name + "__extra_size"
> +
> +        writer.assign(mem_size, mem_sum)
> +
> +    if want_extra_size:
> +        if prefix:
> +            extra_size = prefix + "__extra_size"
> +        else:
> +            extra_size = "extra_size"
> +
> +        extra_sum = []
> +        for m in container.members:
> +            name = prefix_m(prefix, m)
> +            if m.is_extra_size():
> +                extra_sum.append(name + "__mem_size")
> +            elif m.contains_extra_size():
> +                extra_sum.append(name + "__extra_size")
> +        writer.assign(extra_size, codegen.sum_array(extra_sum))
> +
> +class DemarshallingDestination:
> +    def __init__(self):
> +        pass
> +
> +    def child_at_end(self, writer, t):
> +        return RootDemarshallingDestination(self, t.c_type(), t.sizeof())
> +
> +    def child_sub(self, member):
> +        return SubDemarshallingDestination(self, member)
> +
> +    def declare(self, writer):
> +        return writer.optional_block(self.reuse_scope)
> +
> +    def is_toplevel(self):
> +        return self.parent_dest == None and not self.is_helper
> +
> +class RootDemarshallingDestination(DemarshallingDestination):
> +    def __init__(self, parent_dest, c_type, sizeof, pointer = None):
> +        self.is_helper = False
> +        self.reuse_scope = None
> +        self.parent_dest = parent_dest
> +        if parent_dest:
> +            self.base_var =
> codegen.increment_identifier(parent_dest.base_var)
> +        else:
> +            self.base_var = "out"
> +        self.c_type = c_type
> +        self.sizeof = sizeof
> +        self.pointer = pointer # None == at "end"
> +
> +    def get_ref(self, member):
> +        return self.base_var + "->" + member
> +
> +    def declare(self, writer):
> +        if self.reuse_scope:
> +            scope = self.reuse_scope
> +        else:
> +            writer.begin_block()
> +            scope = writer.get_subwriter()
> +
> +        scope.variable_def(self.c_type + " *", self.base_var)
> +        if not self.reuse_scope:
> +            scope.newline()
> +
> +        if self.pointer:
> +            writer.assign(self.base_var, "(%s *)%s" % (self.c_type,
> self.pointer))
> +        else:
> +            writer.assign(self.base_var, "(%s *)end" % (self.c_type))
> +            writer.increment("end", self.sizeof)
> +        writer.newline()
> +
> +        if self.reuse_scope:
> +            return writer.no_block(self.reuse_scope)
> +        else:
> +            return writer.partial_block(scope)
> +
> +class SubDemarshallingDestination(DemarshallingDestination):
> +    def __init__(self, parent_dest, member):
> +        self.reuse_scope = None
> +        self.parent_dest = parent_dest
> +        self.base_var = parent_dest.base_var
> +        self.member = member
> +        self.is_helper = False
> +
> +    def get_ref(self, member):
> +        return self.parent_dest.get_ref(self.member) + "." + member
> +
> +# Note: during parsing, byte_size types have been converted to count during
> validation
> +def read_array_len(writer, prefix, array, dest, scope, is_ptr):
> +    if is_ptr:
> +        nelements = "%s__array__nelements" % prefix
> +    else:
> +        nelements = "%s__nelements" % prefix
> +    if dest.is_toplevel() and scope.variable_defined(nelements):
> +        return nelements # Already there for toplevel, need not recalculate
> +    element_type = array.element_type
> +    scope.variable_def("uint32_t", nelements)
> +    if array.is_constant_length():
> +        writer.assign(nelements, array.size)
> +    elif array.is_identifier_length():
> +        writer.assign(nelements, dest.get_ref(array.size))
> +    elif array.is_remaining_length():
> +        if element_type.is_fixed_nw_size():
> +            writer.assign(nelements, "(message_end - in) / (%s)"
> %(element_type.get_fixed_nw_size()))
> +        else:
> +            raise NotImplementedError("TODO array[] of dynamic element size
> not done yet")
> +    elif array.is_image_size_length():
> +        bpp = array.size[1]
> +        width = array.size[2]
> +        rows = array.size[3]
> +        width_v = dest.get_ref(width)
> +        rows_v = dest.get_ref(rows)
> +        # TODO: Handle multiplication overflow
> +        if bpp == 8:
> +            writer.assign(nelements, "%s * %s" % (width_v, rows_v))
> +        elif bpp == 1:
> +            writer.assign(nelements, "((%s + 7) / 8 ) * %s" % (width_v,
> rows_v))
> +        else:
> +            writer.assign(nelements, "((%s * %s + 7) / 8 ) * %s" % (bpp,
> width_v, rows_v))
> +    elif array.is_bytes_length():
> +        writer.assign(nelements, dest.get_ref(array.size[2]))
> +    else:
> +        raise NotImplementedError("TODO array size type not handled yet")
> +    return nelements
> +
> +def write_switch_parser(writer, container, switch, dest, scope):
> +    var = container.lookup_member(switch.variable)
> +    var_type = var.member_type
> +
> +    if switch.has_attr("fixedsize"):
> +        scope.variable_def("uint8_t *", "in_save")
> +        writer.assign("in_save", "in")
> +
> +    first = True
> +    for c in switch.cases:
> +        check = c.get_check(dest.get_ref(switch.variable), var_type)
> +        m = c.member
> +        with writer.if_block(check, not first, False) as block:
> +            t = m.member_type
> +            if switch.has_end_attr():
> +                dest2 = dest.child_at_end(writer, m.member_type)
> +            elif switch.has_attr("anon"):
> +                if t.is_struct() and not m.has_attr("to_ptr"):
> +                    dest2 = dest.child_sub(m.name)
> +                else:
> +                    dest2 = dest
> +            else:
> +                if t.is_struct():
> +                    dest2 = dest.child_sub(switch.name + "." + m.name)
> +                else:
> +                    dest2 = dest.child_sub(switch.name)
> +            dest2.reuse_scope = block
> +
> +            if m.has_attr("to_ptr"):
> +                write_parse_to_pointer(writer, t, False, dest2, m.name,
> block)
> +            elif t.is_pointer():
> +                write_parse_pointer(writer, t, False, dest2, m.name, block)
> +            elif t.is_struct():
> +                write_container_parser(writer, t, dest2)
> +            elif t.is_primitive():
> +                if m.has_attr("zero"):
> +                    writer.statement("consume_%s(&in)" %
> (t.primitive_type()))
> +                else:
> +                    writer.assign(dest2.get_ref(m.name), "consume_%s(&in)" %
> (t.primitive_type()))
> +                #TODO validate e.g. flags and enums
> +            elif t.is_array():
> +                nelements = read_array_len(writer, m.name, t, dest, block,
> False)
> +                write_array_parser(writer, m, nelements, t, dest2, block)
> +            else:
> +                writer.todo("Can't handle type %s" % m.member_type)
> +
> +        first = False
> +
> +    writer.newline()
> +
> +    if switch.has_attr("fixedsize"):
> +        writer.assign("in", "in_save + %s" % switch.get_fixed_nw_size())
> +
> +def write_parse_ptr_function(writer, target_type):
> +    if target_type.is_array():
> +        parse_function = "parse_array_%s" %
> target_type.element_type.primitive_type()
> +    else:
> +        parse_function = "parse_struct_%s" % target_type.c_type()
> +    if writer.is_generated("parser", parse_function):
> +        return parse_function
> +
> +    writer.set_is_generated("parser", parse_function)
> +
> +    writer = writer.function_helper()
> +    scope = writer.function(parse_function, "static uint8_t *", "uint8_t
> *message_start, SPICE_GNUC_UNUSED uint8_t *message_end, uint8_t
> *struct_data, PointerInfo *this_ptr_info, SPICE_GNUC_UNUSED int minor")
> +    scope.variable_def("uint8_t *", "in = message_start +
> this_ptr_info->offset")
> +    scope.variable_def("uint8_t *", "end")
> +
> +    num_pointers = target_type.get_num_pointers()
> +    if  num_pointers != 0:
> +        scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size")
> +        scope.variable_def("uint32_t", "n_ptr=0")
> +        scope.variable_def("PointerInfo", "ptr_info[%s]" % num_pointers)
> +
> +    writer.newline()
> +    if target_type.is_array():
> +        writer.assign("end", "struct_data")
> +    else:
> +        writer.assign("end", "struct_data + %s" % (target_type.sizeof()))
> +
> +    dest = RootDemarshallingDestination(None, target_type.c_type(),
> target_type.sizeof(), "struct_data")
> +    dest.is_helper = True
> +    dest.reuse_scope = scope
> +    if target_type.is_array():
> +        write_array_parser(writer, None, "this_ptr_info->nelements",
> target_type, dest, scope)
> +    else:
> +        write_container_parser(writer, target_type, dest)
> +
> +    if num_pointers != 0:
> +        write_ptr_info_check(writer)
> +
> +    writer.statement("return end")
> +
> +    if writer.has_error_check:
> +        writer.newline()
> +        writer.label("error")
> +        writer.statement("return NULL")
> +
> +    writer.end_block()
> +
> +    return parse_function
> +
> +def write_array_parser(writer, member, nelements, array, dest, scope):
> +    is_byte_size = array.is_bytes_length()
> +
> +    element_type = array.element_type
> +    if member:
> +        array_start = dest.get_ref(member.name)
> +        at_end = member.has_attr("end")
> +    else:
> +        array_start = "end"
> +        at_end = True
> +
> +    if element_type == ptypes.uint8 or element_type == ptypes.int8:
> +        writer.statement("memcpy(%s, in, %s)" % (array_start, nelements))
> +        writer.increment("in", nelements)
> +        if at_end:
> +            writer.increment("end", nelements)
> +    else:
> +        with writer.index() as index:
> +            if member:
> +                array_pos = "%s[%s]" % (array_start, index)
> +            else:
> +                array_pos = "*(%s *)end" % (element_type.c_type())
> +
> +            if array.has_attr("ptr_array"):
> +                scope.variable_def("void **", "ptr_array")
> +                scope.variable_def("int", "ptr_array_index")
> +                writer.assign("ptr_array_index", 0)
> +                writer.assign("ptr_array", "(void **)%s" % array_start)
> +                writer.increment("end", "sizeof(void *) * %s" % nelements)
> +                array_start = "end"
> +                array_pos = "*(%s *)end" % (element_type.c_type())
> +                at_end = True
> +
> +            with writer.for_loop(index, nelements) as array_scope:
> +                if array.has_attr("ptr_array"):
> +                    writer.statement("ptr_array[ptr_array_index++] = end")
> +                if element_type.is_primitive():
> +                    writer.statement("%s = consume_%s(&in)" % (array_pos,
> element_type.primitive_type()))
> +                    if at_end:
> +                        writer.increment("end", element_type.sizeof())
> +                else:
> +                    if at_end:
> +                        dest2 = dest.child_at_end(writer, element_type)
> +                    else:
> +                        dest2 = RootDemarshallingDestination(dest,
> element_type.c_type(), element_type.c_type(), array_pos)
> +                    dest2.reuse_scope = array_scope
> +                    write_container_parser(writer, element_type, dest2)
> +                if array.has_attr("ptr_array"):
> +                    writer.comment("Align ptr_array element to 4
> bytes").newline()
> +                    writer.assign("end", "(uint8_t
> *)SPICE_ALIGN((size_t)end, 4)")
> +
> +def write_parse_pointer_core(writer, target_type, offset, at_end, dest,
> member_name, scope):
> +    writer.assign("ptr_info[n_ptr].offset", offset)
> +    writer.assign("ptr_info[n_ptr].parse", write_parse_ptr_function(writer,
> target_type))
> +    if at_end:
> +        writer.assign("ptr_info[n_ptr].dest", "(void **)end")
> +        writer.increment("end", "sizeof(void *)")
> +    else:
> +        writer.assign("ptr_info[n_ptr].dest", "(void **)&%s" %
> dest.get_ref(member_name))
> +    if target_type.is_array():
> +        nelements = read_array_len(writer, member_name, target_type, dest,
> scope, True)
> +        writer.assign("ptr_info[n_ptr].nelements", nelements)
> +
> +    writer.statement("n_ptr++")
> +
> +def write_parse_pointer(writer, t, at_end, dest, member_name, scope):
> +    write_parse_pointer_core(writer, t.target_type, "consume_%s(&in)" %
> t.primitive_type(),
> +                             at_end, dest, member_name, scope)
> +
> +def write_parse_to_pointer(writer, t, at_end, dest, member_name, scope):
> +    write_parse_pointer_core(writer, t, "in - start",
> +                             at_end, dest, member_name, scope)
> +    writer.increment("in", "%s__saved_size" % member_name)
> +
> +def write_member_parser(writer, container, member, dest, scope):
> +    if member.has_attr("virtual"):
> +        writer.assign(dest.get_ref(member.name),
> member.attributes["virtual"][0])
> +        return
> +
> +    if member.is_switch():
> +        write_switch_parser(writer, container, member, dest, scope)
> +        return
> +
> +    t = member.member_type
> +
> +    if member.has_attr("to_ptr"):
> +        write_parse_to_pointer(writer, t, member.has_end_attr(), dest,
> member.name, scope)
> +    elif t.is_pointer():
> +        if member.has_attr("chunk"):
> +            assert(t.target_type.is_array())
> +            nelements = read_array_len(writer, member.name, t.target_type,
> dest, scope, True)
> +            writer.comment("Reuse data from network message as
> chunk").newline()
> +            scope.variable_def("SpiceChunks *", "chunks")
> +            writer.assign("chunks", "(SpiceChunks *)end")
> +            writer.increment("end", "sizeof(SpiceChunks) +
> sizeof(SpiceChunk)")
> +            writer.assign(dest.get_ref(member.name), "chunks")
> +            writer.assign("chunks->data_size", nelements)
> +            writer.assign("chunks->flags", 0)
> +            writer.assign("chunks->num_chunks", 1)
> +            writer.assign("chunks->chunk[0].len", nelements)
> +            writer.assign("chunks->chunk[0].data", "message_start +
> consume_%s(&in)" % t.primitive_type())
> +        elif member.has_attr("nocopy"):
> +            writer.comment("Reuse data from network message").newline()
> +            writer.assign(dest.get_ref(member.name), "(size_t)(message_start
> + consume_%s(&in))" % t.primitive_type())
> +        else:
> +            write_parse_pointer(writer, t, member.has_end_attr(), dest,
> member.name, scope)
> +    elif t.is_primitive():
> +        if member.has_attr("zero"):
> +            writer.statement("consume_%s(&in)" % t.primitive_type())
> +        elif member.has_end_attr():
> +            writer.statement("*(%s *)end = consume_%s(&in)" % (t.c_type(),
> t.primitive_type()))
> +            writer.increment("end", t.sizeof())
> +        else:
> +            if member.has_attr("bytes_count"):
> +                dest_var = dest.get_ref(member.attributes["bytes_count"][0])
> +            else:
> +                dest_var = dest.get_ref(member.name)
> +            writer.assign(dest_var, "consume_%s(&in)" %
> (t.primitive_type()))
> +        #TODO validate e.g. flags and enums
> +    elif t.is_array():
> +        nelements = read_array_len(writer, member.name, t, dest, scope,
> False)
> +        if member.has_attr("chunk") and t.element_type.is_fixed_nw_size()
> and t.element_type.get_fixed_nw_size() == 1:
> +            writer.comment("use array as chunk").newline()
> +
> +            scope.variable_def("SpiceChunks *", "chunks")
> +            writer.assign("chunks", "(SpiceChunks *)end")
> +            writer.increment("end", "sizeof(SpiceChunks) +
> sizeof(SpiceChunk)")
> +            writer.assign(dest.get_ref(member.name), "chunks")
> +            writer.assign("chunks->data_size", nelements)
> +            writer.assign("chunks->flags", 0)
> +            writer.assign("chunks->num_chunks", 1)
> +            writer.assign("chunks->chunk[0].len", nelements)
> +            writer.assign("chunks->chunk[0].data", "in")
> +            writer.increment("in", "%s" % (nelements))
> +        elif member.has_attr("as_ptr") and
> t.element_type.is_fixed_nw_size():
> +            writer.comment("use array as pointer").newline()
> +            writer.assign(dest.get_ref(member.name), "(%s *)in" %
> t.element_type.c_type())
> +            len_var = member.attributes["as_ptr"]
> +            if len(len_var) > 0:
> +                writer.assign(dest.get_ref(len_var[0]), nelements)
> +            el_size = t.element_type.get_fixed_nw_size()
> +            if el_size != 1:
> +                writer.increment("in", "%s * %s" % (nelements, el_size))
> +            else:
> +                writer.increment("in", "%s" % (nelements))
> +        else:
> +            write_array_parser(writer, member, nelements, t, dest, scope)
> +    elif t.is_struct():
> +        if member.has_end_attr():
> +            dest2 = dest.child_at_end(writer, t)
> +        else:
> +            dest2 = dest.child_sub(member.name)
> +        writer.comment(member.name)
> +        write_container_parser(writer, t, dest2)
> +    else:
> +        raise NotImplementedError("TODO can't handle parsing of %s" % t)
> +
> +def write_container_parser(writer, container, dest):
> +    with dest.declare(writer) as scope:
> +        for m in container.members:
> +            if m.has_minor_attr():
> +                writer.begin_block("if (minor >= %s)" % m.get_minor_attr())
> +            write_member_parser(writer, container, m, dest, scope)
> +            if m.has_minor_attr():
> +                # We need to zero out the fixed part of all optional fields
> +                if not m.member_type.is_array():
> +                    writer.end_block(newline=False)
> +                    writer.begin_block(" else")
> +                    # TODO: This is not right for fields that don't exist in
> the struct
> +                    if m.has_attr("zero"):
> +                        pass
> +                    elif m.member_type.is_primitive():
> +                        writer.assign(dest.get_ref(m.name), "0")
> +                    elif m.is_fixed_sizeof():
> +                        writer.statement("memset ((char *)&%s, 0, %s)" %
> (dest.get_ref(m.name), m.sizeof()))
> +                    else:
> +                        raise NotImplementedError("TODO Clear optional
> dynamic fields")
> +                writer.end_block()
> +
> +def write_ptr_info_check(writer):
> +    writer.newline()
> +    with writer.index() as index:
> +        with writer.for_loop(index, "n_ptr") as scope:
> +            offset = "ptr_info[%s].offset" % index
> +            function = "ptr_info[%s].parse" % index
> +            dest = "ptr_info[%s].dest" % index
> +            with writer.if_block("%s == 0" % offset, newline=False):
> +                writer.assign("*%s" % dest, "NULL")
> +            with writer.block(" else"):
> +                writer.comment("Align to 32 bit").newline()
> +                writer.assign("end", "(uint8_t *)SPICE_ALIGN((size_t)end,
> 4)")
> +                writer.assign("*%s" % dest, "(void *)end")
> +                writer.assign("end", "%s(message_start, message_end, end,
> &ptr_info[%s], minor)" % (function, index))
> +                writer.error_check("end == NULL")
> +    writer.newline()
> +
> +def write_nofree(writer):
> +    if writer.is_generated("helper", "nofree"):
> +        return
> +    writer = writer.function_helper()
> +    scope = writer.function("nofree", "static void", "SPICE_GNUC_UNUSED
> uint8_t *data")
> +    writer.end_block()
> +
> +def write_msg_parser(writer, message):
> +    msg_name = message.c_name()
> +    function_name = "parse_%s" % msg_name
> +    if writer.is_generated("demarshaller", function_name):
> +        return function_name
> +    writer.set_is_generated("demarshaller", function_name)
> +
> +    msg_type = message.c_type()
> +    msg_sizeof = message.sizeof()
> +
> +    want_mem_size = (len(message.members) != 1 or
> message.members[0].is_fixed_nw_size()
> +                         or not message.members[0].is_array())
> +
> +    writer.newline()
> +    if message.has_attr("ifdef"):
> +        writer.ifdef(message.attributes["ifdef"][0])
> +    parent_scope = writer.function(function_name,
> +                                   "uint8_t *",
> +                                   "uint8_t *message_start, uint8_t
> *message_end, SPICE_GNUC_UNUSED int minor, size_t *size,
> message_destructor_t *free_message", True)
> +    parent_scope.variable_def("SPICE_GNUC_UNUSED uint8_t *", "pos")
> +    parent_scope.variable_def("uint8_t *", "start = message_start")
> +    parent_scope.variable_def("uint8_t *", "data = NULL")
> +    parent_scope.variable_def("size_t", "nw_size")
> +    if want_mem_size:
> +        parent_scope.variable_def("size_t", "mem_size")
> +    if not message.has_attr("nocopy"):
> +        parent_scope.variable_def("uint8_t *", "in", "end")
> +    num_pointers = message.get_num_pointers()
> +    if  num_pointers != 0:
> +        parent_scope.variable_def("SPICE_GNUC_UNUSED intptr_t", "ptr_size")
> +        parent_scope.variable_def("uint32_t", "n_ptr=0")
> +        parent_scope.variable_def("PointerInfo", "ptr_info[%s]" %
> num_pointers)
> +    writer.newline()
> +
> +    write_parser_helpers(writer)
> +
> +    write_validate_container(writer, None, message, "start", parent_scope,
> True,
> +                             want_mem_size=want_mem_size,
> want_extra_size=False)
> +
> +    writer.newline()
> +
> +    writer.comment("Check if message fits in reported side").newline()
> +    with writer.block("if (start + nw_size > message_end)"):
> +        writer.statement("return NULL")
> +
> +    writer.newline().comment("Validated extents and calculated
> size").newline()
> +
> +    if message.has_attr("nocopy"):
> +        write_nofree(writer)
> +        writer.assign("data", "message_start")
> +        writer.assign("*size", "message_end - message_start")
> +        writer.assign("*free_message", "nofree")
> +    else:
> +        writer.assign("data", "(uint8_t *)malloc(mem_size)")
> +        writer.error_check("data == NULL")
> +        writer.assign("end", "data + %s" % (msg_sizeof))
> +        writer.assign("in", "start").newline()
> +
> +        # avoid defined and assigned but not used warnings of gcc 4.6.0+
> +        if message.is_extra_size() or not message.is_fixed_nw_size() or
> message.get_fixed_nw_size() > 0:
> +            dest = RootDemarshallingDestination(None, msg_type, msg_sizeof,
> "data")
> +            dest.reuse_scope = parent_scope
> +            write_container_parser(writer, message, dest)
> +
> +        writer.newline()
> +        writer.statement("assert(in <= message_end)")
> +
> +        if num_pointers != 0:
> +            write_ptr_info_check(writer)
> +
> +        writer.statement("assert(end <= data + mem_size)")
> +
> +        writer.newline()
> +        writer.assign("*size", "end - data")
> +        writer.assign("*free_message", "(message_destructor_t) free")
> +
> +    writer.statement("return data")
> +    writer.newline()
> +    if writer.has_error_check:
> +        writer.label("error")
> +        with writer.block("if (data != NULL)"):
> +            writer.statement("free(data)")
> +        writer.statement("return NULL")
> +    writer.end_block()
> +
> +    if message.has_attr("ifdef"):
> +        writer.endif(message.attributes["ifdef"][0])
> +
> +    return function_name
> +
> +def write_channel_parser(writer, channel, server):
> +    writer.newline()
> +    ids = {}
> +    min_id = 1000000
> +    if server:
> +        messages = channel.server_messages
> +    else:
> +        messages = channel.client_messages
> +    for m in messages:
> +        ids[m.value] = m
> +
> +    ranges = []
> +    ids2 = ids.copy()
> +    while len(ids2) > 0:
> +        end = start = min(ids2.keys())
> +        while end in ids2:
> +            del ids2[end]
> +            end = end + 1
> +
> +        ranges.append( (start, end) )
> +
> +    if server:
> +        function_name = "parse_%s_msg" % channel.name
> +    else:
> +        function_name = "parse_%s_msgc" % channel.name
> +    writer.newline()
> +    if channel.has_attr("ifdef"):
> +        writer.ifdef(channel.attributes["ifdef"][0])
> +    scope = writer.function(function_name,
> +                            "static uint8_t *",
> +                            "uint8_t *message_start, uint8_t *message_end,
> uint16_t message_type, SPICE_GNUC_UNUSED int minor, size_t *size_out,
> message_destructor_t *free_message")
> +
> +    helpers = writer.function_helper()
> +
> +    d = 0
> +    for r in ranges:
> +        d = d + 1
> +        writer.write("static parse_msg_func_t funcs%d[%d] = " % (d, r[1] -
> r[0]))
> +        writer.begin_block()
> +        for i in range(r[0], r[1]):
> +            func = write_msg_parser(helpers, ids[i].message_type)
> +            writer.write(func)
> +            if i != r[1] -1:
> +                writer.write(",")
> +            writer.newline()
> +
> +        writer.end_block(semicolon = True)
> +
> +    d = 0
> +    for r in ranges:
> +        d = d + 1
> +        with writer.if_block("message_type >= %d && message_type < %d" %
> (r[0], r[1]), d > 1, False):
> +            writer.statement("return funcs%d[message_type-%d](message_start,
> message_end, minor, size_out, free_message)" % (d, r[0]))
> +    writer.newline()
> +
> +    writer.statement("return NULL")
> +    writer.end_block()
> +    if channel.has_attr("ifdef"):
> +        writer.endif(channel.attributes["ifdef"][0])
> +
> +    return function_name
> +
> +def write_get_channel_parser(writer, channel_parsers, max_channel,
> is_server):
> +    writer.newline()
> +    if is_server:
> +        function_name = "spice_get_server_channel_parser" +
> writer.public_prefix
> +    else:
> +        function_name = "spice_get_client_channel_parser" +
> writer.public_prefix
> +
> +    scope = writer.function(function_name,
> +                            "spice_parse_channel_func_t",
> +                            "uint32_t channel, unsigned int
> *max_message_type")
> +
> +    writer.write("static struct {spice_parse_channel_func_t func; unsigned
> int max_messages; } channels[%d] = " % (max_channel+1))
> +    writer.begin_block()
> +    channel = None
> +    for i in range(0, max_channel + 1):
> +        if i in channel_parsers:
> +            channel = channel_parsers[i][0]
> +            if channel.has_attr("ifdef"):
> +                writer.ifdef(channel.attributes["ifdef"][0])
> +            writer.write("{ ")
> +            writer.write(channel_parsers[i][1])
> +            writer.write(", ")
> +
> +            max_msg = 0
> +            if is_server:
> +                messages = channel.server_messages
> +            else:
> +                messages = channel.client_messages
> +            for m in messages:
> +                max_msg = max(max_msg, m.value)
> +            writer.write(max_msg)
> +            writer.write("}")
> +        else:
> +            writer.write("{ NULL, 0 }")
> +
> +        if i != max_channel:
> +            writer.write(",")
> +        writer.newline()
> +        if channel and channel.has_attr("ifdef"):
> +            writer.ifdef_else(channel.attributes["ifdef"][0])
> +            writer.write("{ NULL, 0 }")
> +            if i != max_channel:
> +                writer.write(",")
> +            writer.newline()
> +            writer.endif(channel.attributes["ifdef"][0])
> +    writer.end_block(semicolon = True)
> +
> +    with writer.if_block("channel < %d" % (max_channel + 1)):
> +        with writer.if_block("max_message_type != NULL"):
> +            writer.assign("*max_message_type",
> "channels[channel].max_messages")
> +        writer.statement("return channels[channel].func")
> +
> +    writer.statement("return NULL")
> +    writer.end_block()
> +
> +
> +def write_full_protocol_parser(writer, is_server):
> +    writer.newline()
> +    if is_server:
> +        function_name = "spice_parse_msg"
> +    else:
> +        function_name = "spice_parse_reply"
> +    scope = writer.function(function_name + writer.public_prefix,
> +                            "uint8_t *",
> +                            "uint8_t *message_start, uint8_t *message_end,
> uint32_t channel, uint16_t message_type, SPICE_GNUC_UNUSED int minor, size_t
> *size_out, message_destructor_t *free_message")
> +    scope.variable_def("spice_parse_channel_func_t", "func" )
> +
> +    if is_server:
> +        writer.assign("func", "spice_get_server_channel_parser%s(channel,
> NULL)" % writer.public_prefix)
> +    else:
> +        writer.assign("func", "spice_get_client_channel_parser%s(channel,
> NULL)" % writer.public_prefix)
> +
> +    with writer.if_block("func != NULL"):
> +        writer.statement("return func(message_start, message_end,
> message_type, minor, size_out, free_message)")
> +
> +    writer.statement("return NULL")
> +    writer.end_block()
> +
> +def write_protocol_parser(writer, proto, is_server):
> +    max_channel = 0
> +    parsers = {}
> +
> +    for channel in proto.channels:
> +        max_channel = max(max_channel, channel.value)
> +
> +        parsers[channel.value] = (channel.channel_type,
> write_channel_parser(writer, channel.channel_type, is_server))
> +
> +    write_get_channel_parser(writer, parsers, max_channel, is_server)
> +    write_full_protocol_parser(writer, is_server)
> +
> +def write_includes(writer):
> +    writer.writeln("#include <string.h>")
> +    writer.writeln("#include <assert.h>")
> +    writer.writeln("#include <stdlib.h>")
> +    writer.writeln("#include <stdio.h>")
> +    writer.writeln("#include <spice/protocol.h>")
> +    writer.writeln("#include <spice/macros.h>")
> +    writer.writeln('#include <common/mem.h>')
> +    writer.newline()
> +    writer.writeln("#ifdef _MSC_VER")
> +    writer.writeln("#pragma warning(disable:4101)")
> +    writer.writeln("#endif")
> diff --git a/python_modules/marshal.py b/python_modules/marshal.py
> new file mode 100644
> index 0000000..1d38d3d
> --- /dev/null
> +++ b/python_modules/marshal.py
> @@ -0,0 +1,420 @@
> +
> +from . import ptypes
> +from . import codegen
> +
> +def write_includes(writer):
> +    writer.header.writeln("#include <spice/protocol.h>")
> +    writer.header.writeln('#include "common/marshaller.h"')
> +    writer.header.newline()
> +    writer.header.writeln("#ifndef _GENERATED_HEADERS_H")
> +    writer.header.writeln("#define _GENERATED_HEADERS_H")
> +
> +    writer.writeln("#include <string.h>")
> +    writer.writeln("#include <assert.h>")
> +    writer.writeln("#include <stdlib.h>")
> +    writer.writeln("#include <stdio.h>")
> +    writer.writeln("#include <spice/protocol.h>")
> +    writer.writeln("#include <spice/macros.h>")
> +    writer.writeln('#include "common/marshaller.h"')
> +    writer.newline()
> +    writer.writeln("#ifdef _MSC_VER")
> +    writer.writeln("#pragma warning(disable:4101)")
> +    writer.writeln("#pragma warning(disable:4018)")
> +    writer.writeln("#endif")
> +    writer.newline()
> +
> +class MarshallingSource:
> +    def __init__(self):
> +        pass
> +
> +    def child_at_end(self, t):
> +        return RootMarshallingSource(self, t.c_type(), t.sizeof())
> +
> +    def child_sub(self, containee):
> +        return SubMarshallingSource(self, containee)
> +
> +    def declare(self, writer):
> +        return writer.optional_block(self.reuse_scope)
> +
> +    def is_toplevel(self):
> +        return self.parent_src == None and not self.is_helper
> +
> +class RootMarshallingSource(MarshallingSource):
> +    def __init__(self, parent_src, c_type, sizeof, pointer = None):
> +        self.is_helper = False
> +        self.reuse_scope = None
> +        self.parent_src = parent_src
> +        if parent_src:
> +            self.base_var =
> codegen.increment_identifier(parent_src.base_var)
> +        else:
> +            self.base_var = "src"
> +        self.c_type = c_type
> +        self.sizeof = sizeof
> +        self.pointer = pointer
> +        assert pointer != None
> +
> +    def get_self_ref(self):
> +        return self.base_var
> +
> +    def get_ref(self, member):
> +        return self.base_var + "->" + member
> +
> +    def declare(self, writer):
> +        if self.reuse_scope:
> +            scope = self.reuse_scope
> +        else:
> +            writer.begin_block()
> +            scope = writer.get_subwriter()
> +
> +        scope.variable_def(self.c_type + " *", self.base_var)
> +        if not self.reuse_scope:
> +            scope.newline()
> +
> +        writer.assign(self.base_var, "(%s *)%s" % (self.c_type,
> self.pointer))
> +        writer.newline()
> +
> +        if self.reuse_scope:
> +            return writer.no_block(self.reuse_scope)
> +        else:
> +            return writer.partial_block(scope)
> +
> +class SubMarshallingSource(MarshallingSource):
> +    def __init__(self, parent_src, containee):
> +        self.reuse_scope = None
> +        self.parent_src = parent_src
> +        self.base_var = parent_src.base_var
> +        self.containee = containee
> +        self.name = containee.name
> +        self.is_helper = False
> +
> +    def get_self_ref(self):
> +        if self.containee.has_attr("to_ptr"):
> +            return "%s" % self.parent_src.get_ref(self.name)
> +        else:
> +            return "&%s" % self.parent_src.get_ref(self.name)
> +
> +    def get_ref(self, member):
> +        if self.containee.has_attr("to_ptr"):
> +            return self.parent_src.get_ref(self.name) + "->" + member
> +        else:
> +            return self.parent_src.get_ref(self.name) + "." + member
> +
> +def write_marshal_ptr_function(writer, target_type, is_helper=True):
> +    if target_type.is_array():
> +        marshal_function = "spice_marshall_array_%s" %
> target_type.element_type.primitive_type()
> +    else:
> +        marshal_function = "spice_marshall_%s" % target_type.name
> +    if writer.is_generated("marshaller", marshal_function):
> +        return marshal_function
> +
> +    writer.set_is_generated("marshaller", marshal_function)
> +
> +    names = target_type.get_pointer_names(False)
> +    names_args = ""
> +    if len(names) > 0:
> +        n = [", SpiceMarshaller **%s_out" % name for name in names]
> +        names_args = "".join(n)
> +
> +    header = writer.header
> +    if is_helper:
> +        writer = writer.function_helper()
> +    writer.header = header
> +    writer.out_prefix = ""
> +    if target_type.is_array():
> +        scope = writer.function(marshal_function, "SPICE_GNUC_UNUSED static
> void", "SpiceMarshaller *m, %s_t *ptr, unsigned count" %
> target_type.element_type.primitive_type() + names_args)
> +    else:
> +        scope = writer.function(marshal_function, "void", "SpiceMarshaller
> *m, %s *ptr" % target_type.c_type() + names_args)
> +        header.writeln("void " + marshal_function + "(SpiceMarshaller *m, %s
> *msg" % target_type.c_type() + names_args + ");")
> +    scope.variable_def("SPICE_GNUC_UNUSED SpiceMarshaller *", "m2")
> +
> +    for n in names:
> +        writer.assign("*%s_out" % n, "NULL")
> +
> +    writer.newline()
> +
> +    if target_type.is_struct():
> +        src = RootMarshallingSource(None, target_type.c_type(),
> target_type.sizeof(), "ptr")
> +        src.reuse_scope = scope
> +        write_container_marshaller(writer, target_type, src)
> +    elif target_type.is_array() and target_type.element_type.is_primitive():
> +        with writer.index() as index:
> +            with writer.for_loop(index, "count") as array_scope:
> +                writer.statement("spice_marshaller_add_%s(m, *ptr++)" %
> (target_type.element_type.primitive_type()))
> +    else:
> +        writer.todo("Unsuppored pointer marshaller type")
> +
> +    writer.end_block()
> +
> +    return marshal_function
> +
> +def get_array_size(array, container_src):
> +    if array.is_constant_length():
> +        return array.size
> +    elif array.is_identifier_length():
> +        return container_src.get_ref(array.size)
> +    elif array.is_remaining_length():
> +        raise NotImplementedError("remaining size array sizes marshalling
> not supported")
> +    elif array.is_image_size_length():
> +        bpp = array.size[1]
> +        width = array.size[2]
> +        rows = array.size[3]
> +        width_v = container_src.get_ref(width)
> +        rows_v = container_src.get_ref(rows)
> +        # TODO: Handle multiplication overflow
> +        if bpp == 8:
> +            return "(unsigned) (%s * %s)" % (width_v, rows_v)
> +        elif bpp == 1:
> +            return "(unsigned) (((%s + 7) / 8 ) * %s)" % (width_v, rows_v)
> +        else:
> +            return "(unsigned) (((%s * %s + 7) / 8 ) * %s)" % (bpp, width_v,
> rows_v)
> +    elif array.is_bytes_length():
> +        return container_src.get_ref(array.size[2])
> +    else:
> +        raise NotImplementedError("TODO array size type not handled yet: %s"
> % array)
> +
> +def write_array_marshaller(writer, member, array, container_src, scope):
> +    element_type = array.element_type
> +
> +    if array.is_remaining_length():
> +        writer.comment("Remaining data must be appended manually").newline()
> +        return
> +
> +    nelements = get_array_size(array, container_src)
> +    is_byte_size = array.is_bytes_length()
> +
> +    element = "%s__element" % member.name
> +
> +    if not scope.variable_defined(element):
> +        if array.has_attr("ptr_array"):
> +            stars = " **"
> +        else:
> +            stars = " *"
> +        scope.variable_def(element_type.c_type() + stars, element)
> +    element_array = element
> +    if array.has_attr("ptr_array"):
> +        element = "*" + element
> +
> +    writer.assign(element_array, container_src.get_ref(member.name))
> +
> +    if is_byte_size:
> +        size_start_var = "%s__size_start" % member.name
> +        scope.variable_def("size_t", size_start_var)
> +        writer.assign(size_start_var, "spice_marshaller_get_size(m)")
> +
> +    with writer.index() as index:
> +        with writer.for_loop(index, nelements) as array_scope:
> +            if element_type.is_primitive():
> +                writer.statement("spice_marshaller_add_%s(m, *%s)" %
> (element_type.primitive_type(), element))
> +            elif element_type.is_struct():
> +                src2 = RootMarshallingSource(container_src,
> element_type.c_type(), element_type.sizeof(), element)
> +                src2.reuse_scope = array_scope
> +                write_container_marshaller(writer, element_type, src2)
> +            else:
> +                writer.todo("array element unhandled type").newline()
> +
> +            writer.statement("%s++" % element_array)
> +
> +    if is_byte_size:
> +        size_var = member.container.lookup_member(array.size[1])
> +        size_var_type = size_var.member_type
> +        var = "%s__ref" % array.size[1]
> +        writer.statement("spice_marshaller_set_%s(m, %s,
> spice_marshaller_get_size(m) - %s)" % (size_var_type.primitive_type(), var,
> size_start_var))
> +
> +def write_pointer_marshaller(writer, member, src):
> +    t = member.member_type
> +    ptr_func = write_marshal_ptr_function(writer, t.target_type)
> +    submarshaller = "spice_marshaller_get_ptr_submarshaller(m, %d)" % (1 if
> member.get_fixed_nw_size() == 8 else 0)
> +    if member.has_attr("marshall"):
> +        rest_args = ""
> +        if t.target_type.is_array():
> +            rest_args = ", %s" % get_array_size(t.target_type, src)
> +        writer.assign("m2", submarshaller)
> +        if t.has_attr("nonnull"):
> +            writer.statement("%s(m2, %s%s)" % (ptr_func,
> src.get_ref(member.name), rest_args))
> +        else:
> +            with writer.if_block("%s != NULL" % src.get_ref(member.name)) as
> block:
> +                writer.statement("%s(m2, %s%s)" % (ptr_func,
> src.get_ref(member.name), rest_args))
> +    else:
> +        writer.assign("*%s_out" % (writer.out_prefix + member.name),
> submarshaller)
> +
> +def write_switch_marshaller(writer, container, switch, src, scope):
> +    var = container.lookup_member(switch.variable)
> +    var_type = var.member_type
> +
> +    saved_out_prefix = writer.out_prefix
> +    first = True
> +    for c in switch.cases:
> +        check = c.get_check(src.get_ref(switch.variable), var_type)
> +        m = c.member
> +        writer.out_prefix = saved_out_prefix
> +        if m.has_attr("outvar"):
> +            writer.out_prefix = "%s_%s" % (m.attributes["outvar"][0],
> writer.out_prefix)
> +        with writer.if_block(check, not first, False) as block:
> +            t = m.member_type
> +            if switch.has_attr("anon"):
> +                if t.is_struct():
> +                    src2 = src.child_sub(m)
> +                else:
> +                    src2 = src
> +            else:
> +                if t.is_struct():
> +                    src2 = src.child_sub(switch).child_sub(m)
> +                else:
> +                    src2 = src.child_sub(switch)
> +            src2.reuse_scope = block
> +
> +            if t.is_struct():
> +                write_container_marshaller(writer, t, src2)
> +            elif t.is_pointer():
> +                write_pointer_marshaller(writer, m, src2)
> +            elif t.is_primitive():
> +                if m.has_attr("zero"):
> +                    writer.statement("spice_marshaller_add_%s(m, 0)" %
> (t.primitive_type()))
> +                else:
> +                    writer.statement("spice_marshaller_add_%s(m, %s)" %
> (t.primitive_type(), src2.get_ref(m.name)))
> +                #TODO validate e.g. flags and enums
> +            elif t.is_array():
> +                write_array_marshaller(writer, m, t, src2, scope)
> +            else:
> +                writer.todo("Can't handle type %s" % m.member_type)
> +
> +            if switch.has_attr("fixedsize"):
> +                remaining = switch.get_fixed_nw_size() -
> t.get_fixed_nw_size()
> +                if remaining != 0:
> +                    writer.statement("spice_marshaller_reserve_space(m, %s)"
> % remaining)
> +
> +        first = False
> +    if switch.has_attr("fixedsize"):
> +        with writer.block(" else"):
> +            writer.statement("spice_marshaller_reserve_space(m, %s)" %
> switch.get_fixed_nw_size())
> +
> +    writer.newline()
> +
> +def write_member_marshaller(writer, container, member, src, scope):
> +    if member.has_attr("outvar"):
> +        writer.out_prefix = "%s_%s" % (member.attributes["outvar"][0],
> writer.out_prefix)
> +    if member.has_attr("virtual"):
> +        writer.comment("Don't marshall @virtual %s" % member.name).newline()
> +        return
> +    if member.has_attr("nomarshal"):
> +        writer.comment("Don't marshall @nomarshal %s" %
> member.name).newline()
> +        return
> +    if member.is_switch():
> +        write_switch_marshaller(writer, container, member, src, scope)
> +        return
> +
> +    t = member.member_type
> +
> +    if t.is_pointer():
> +        write_pointer_marshaller(writer, member, src)
> +    elif t.is_primitive():
> +        if member.has_attr("zero"):
> +            writer.statement("spice_marshaller_add_%s(m, 0)" %
> (t.primitive_type()))
> +        if member.has_attr("bytes_count"):
> +            var = "%s__ref" % member.name
> +            scope.variable_def("void *", var)
> +            writer.statement("%s = spice_marshaller_add_%s(m, %s)" % (var,
> t.primitive_type(), 0))
> +
> +        else:
> +            writer.statement("spice_marshaller_add_%s(m, %s)" %
> (t.primitive_type(), src.get_ref(member.name)))
> +    elif t.is_array():
> +        write_array_marshaller(writer, member, t, src, scope)
> +    elif t.is_struct():
> +        src2 = src.child_sub(member)
> +        writer.comment(member.name)
> +        write_container_marshaller(writer, t, src2)
> +    else:
> +        raise NotImplementedError("TODO can't handle parsing of %s" % t)
> +
> +def write_container_marshaller(writer, container, src):
> +    saved_out_prefix = writer.out_prefix
> +    with src.declare(writer) as scope:
> +        for m in container.members:
> +            writer.out_prefix = saved_out_prefix
> +            write_member_marshaller(writer, container, m, src, scope)
> +
> +def write_message_marshaller(writer, message, is_server, private):
> +    if message.has_attr("ifdef"):
> +        writer.ifdef(message.attributes["ifdef"][0])
> +    writer.out_prefix = ""
> +    function_name = "spice_marshall_" + message.c_name()
> +    if writer.is_generated("marshaller", function_name):
> +        return function_name
> +    writer.set_is_generated("marshaller", function_name)
> +
> +    names = message.get_pointer_names(False)
> +    names_args = ""
> +    if len(names) > 0:
> +        n = [", SpiceMarshaller **%s_out" % name for name in names]
> +        names_args = "".join(n)
> +
> +    if not private:
> +        writer.header.writeln("void " + function_name + "(SpiceMarshaller
> *m, %s *msg" % message.c_type() + names_args + ");")
> +
> +    scope = writer.function(function_name,
> +                            "static void" if private else "void",
> +                            "SPICE_GNUC_UNUSED SpiceMarshaller *m,
> SPICE_GNUC_UNUSED %s *msg" % message.c_type() + names_args)
> +    scope.variable_def("SPICE_GNUC_UNUSED SpiceMarshaller *", "m2")
> +
> +    for n in names:
> +        writer.assign("*%s_out" % n, "NULL")
> +
> +    # fix warnings about unused variables by not creating body if no members
> to parse
> +    if any(x.is_fixed_nw_size() for x in message.members):
> +        src = RootMarshallingSource(None, message.c_type(),
> message.sizeof(), "msg")
> +        src.reuse_scope = scope
> +
> +        write_container_marshaller(writer, message, src)
> +
> +    writer.end_block()
> +    if message.has_attr("ifdef"):
> +        writer.endif(message.attributes["ifdef"][0])
> +    writer.newline()
> +    return function_name
> +
> +def write_protocol_marshaller(writer, proto, is_server,
> private_marshallers):
> +    functions = {}
> +    for c in proto.channels:
> +        channel = c.channel_type
> +        if channel.has_attr("ifdef"):
> +            writer.ifdef(channel.attributes["ifdef"][0])
> +            writer.header.ifdef(channel.attributes["ifdef"][0])
> +        if is_server:
> +            messages = channel.client_messages
> +        else:
> +            messages = channel.server_messages
> +        for m in messages:
> +            message = m.message_type
> +            f = write_message_marshaller(writer, message, is_server,
> private_marshallers)
> +            if channel.has_attr("ifdef") and f not in functions:
> +                functions[f] = channel.attributes["ifdef"][0]
> +            elif message.has_attr("ifdef") and f not in functions:
> +                functions[f] = message.attributes["ifdef"][0]
> +            else:
> +                functions[f] = True
> +        if channel.has_attr("ifdef"):
> +            writer.endif(channel.attributes["ifdef"][0])
> +            writer.header.endif(channel.attributes["ifdef"][0])
> +
> +    if private_marshallers:
> +        scope = writer.function("spice_message_marshallers_get" +
> writer.public_prefix,
> +                                "SpiceMessageMarshallers *",
> +                                "void")
> +        writer.writeln("static SpiceMessageMarshallers marshallers =
> {NULL};").newline()
> +        for f in sorted(functions.keys()):
> +            member = f[len("spice_marshall_"):]
> +            if not member.startswith("msg"):
> +                member = "msg_" + member
> +            if functions[f] != True:
> +                writer.ifdef(functions[f])
> +            writer.assign("marshallers.%s" % member, f)
> +            if functions[f] != True:
> +                writer.endif(functions[f])
> +
> +        writer.newline()
> +        writer.statement("return &marshallers")
> +        writer.end_block()
> +        writer.newline()
> +
> +def write_trailer(writer):
> +    writer.header.writeln("#endif")
> diff --git a/python_modules/ptypes.py b/python_modules/ptypes.py
> new file mode 100644
> index 0000000..753d363
> --- /dev/null
> +++ b/python_modules/ptypes.py
> @@ -0,0 +1,1138 @@
> +from . import codegen
> +import types
> +
> +_types_by_name = {}
> +_types = []
> +
> +default_pointer_size = 4
> +
> +def type_exists(name):
> +    return name in _types_by_name
> +
> +def lookup_type(name):
> +    return _types_by_name[name]
> +
> +def get_named_types():
> +    return _types
> +
> +class FixedSize:
> +    def __init__(self, val = 0, minor = 0):
> +        if isinstance(val, FixedSize):
> +            self.vals = val.vals
> +        else:
> +            self.vals = [0] * (minor + 1)
> +            self.vals[minor] = val
> +
> +    def __add__(self, other):
> +        if isinstance(other, int):
> +            other = FixedSize(other)
> +
> +        new = FixedSize()
> +        l = max(len(self.vals), len(other.vals))
> +        shared = min(len(self.vals), len(other.vals))
> +
> +        new.vals = [0] * l
> +
> +        for i in range(shared):
> +            new.vals[i] = self.vals[i] + other.vals[i]
> +
> +        for i in range(shared,len(self.vals)):
> +            new.vals[i] = self.vals[i]
> +
> +        for i in range(shared,len(other.vals)):
> +            new.vals[i] = new.vals[i] + other.vals[i]
> +
> +        return new
> +
> +    def __radd__(self, other):
> +        return self.__add__(other)
> +
> +    def __str__(self):
> +        s = "%d" % (self.vals[0])
> +
> +        for i in range(1,len(self.vals)):
> +            if self.vals[i] > 0:
> +                s = s + " + ((minor >= %d)?%d:0)" % (i, self.vals[i])
> +        return s
> +
> +# Some attribute are propagated from member to the type as they really
> +# are part of the type definition, rather than the member. This applies
> +# only to attributes that affect pointer or array attributes, as these
> +# are member local types, unlike e.g. a Struct that may be used by
> +# other members
> +propagated_attributes=["ptr_array", "nonnull", "chunk"]
> +
> +valid_attributes=set([
> +    # embedded/appended at the end of the structure
> +    'end',
> +    # the C structure contains a pointer to data
> +    # for instance we want to write an array to an allocated array
> +    'to_ptr',
> +    # write output to this C structure
> +    'ctype',
> +    # prefix for flags/values enumerations
> +    'prefix',
> +    # used in demarshaller to use directly data from message without a copy
> +    'nocopy',
> +    # store member array in a pointer
> +    # similar to to_ptr but has an additional argument which is the name of
> a C
> +    # field which will store the array length
> +    'as_ptr',
> +    # do not generate marshall code
> +    # used for last members to be able to marshall them manually
> +    'nomarshal',
> +    # ??? not used by python code
> +    'zero_terminated',
> +    'marshall',
> +    # this pointer member cannot be null
> +    'nonnull',
> +    # this flag member contains only a single flag
> +    'unique_flag',
> +    'ptr_array',
> +    'outvar',
> +    # C structure has an anonymous member (used in switch)
> +    'anon',
> +    'chunk',
> +    # this channel is contained in an #ifdef section
> +    # the argument specifies the preprocessor define to check
> +    'ifdef',
> +    # write this member as zero on network
> +    'zero',
> +    # specify minor version required for these members
> +    'minor',
> +    # this member contains the byte count for an array.
> +    # the argument is the member name for item count (not bytes)
> +    'bytes_count',
> +    # this attribute does not exist on the network, fill just structure with
> the value
> +    'virtual',
> +    # for a switch this indicates that on network
> +    # it will occupy always the same size (maximum size required for all
> members)
> +    'fixedsize',
> +])
> +
> +attributes_with_arguments=set([
> +    'ctype',
> +    'prefix',
> +    'as_ptr',
> +    'outvar',
> +    'ifdef',
> +    'minor',
> +    'bytes_count',
> +    'virtual',
> +])
> +
> +def fix_attributes(attribute_list):
> +    attrs = {}
> +    for attr in attribute_list:
> +        name = attr[0][1:]
> +        lst = attr[1:]
> +        if not name in valid_attributes:
> +            raise Exception("Attribute %s not recognized" % name)
> +        if not name in attributes_with_arguments:
> +            if len(lst) > 0:
> +                raise Exception("Attribute %s specified with options" %
> name)
> +        elif len(lst) > 1:
> +            raise Exception("Attribute %s has more than 1 argument" % name)
> +        attrs[name] = lst
> +    return attrs
> +
> +class Type:
> +    def __init__(self):
> +        self.attributes = {}
> +        self.registred = False
> +        self.name = None
> +
> +    def has_name(self):
> +        return self.name != None
> +
> +    def get_type(self, recursive=False):
> +        return self
> +
> +    def is_primitive(self):
> +        return False
> +
> +    def is_fixed_sizeof(self):
> +        return True
> +
> +    def is_extra_size(self):
> +        return False
> +
> +    def contains_extra_size(self):
> +        return False
> +
> +    def is_fixed_nw_size(self):
> +        return True
> +
> +    def is_array(self):
> +        return isinstance(self, ArrayType)
> +
> +    def contains_member(self, member):
> +        return False
> +
> +    def is_struct(self):
> +        return isinstance(self, StructType)
> +
> +    def is_pointer(self):
> +        return isinstance(self, PointerType)
> +
> +    def get_num_pointers(self):
> +        return 0
> +
> +    def get_pointer_names(self, marshalled):
> +        return []
> +
> +    def sizeof(self):
> +        return "sizeof(%s)" % (self.c_type())
> +
> +    def __repr__(self):
> +        return self.__str__()
> +
> +    def __str__(self):
> +        if self.name != None:
> +            return self.name
> +        return "anonymous type"
> +
> +    def resolve(self):
> +        return self
> +
> +    def register(self):
> +        if self.registred or self.name == None:
> +            return
> +        self.registred = True
> +        if self.name in _types_by_name:
> +            raise Exception("Type %s already defined" % self.name)
> +        _types.append(self)
> +        _types_by_name[self.name] = self
> +
> +    def has_attr(self, name):
> +        if not name in valid_attributes:
> +            raise Exception('attribute %s not expected' % name)
> +        return name in self.attributes
> +
> +class TypeRef(Type):
> +    def __init__(self, name):
> +        Type.__init__(self)
> +        self.name = name
> +
> +    def __str__(self):
> +        return "ref to %s" % (self.name)
> +
> +    def resolve(self):
> +        if self.name not in _types_by_name:
> +            raise Exception("Unknown type %s" % self.name)
> +        return _types_by_name[self.name]
> +
> +    def register(self):
> +        assert True, "Can't register TypeRef!"
> +
> +
> +class IntegerType(Type):
> +    def __init__(self, bits, signed):
> +        Type.__init__(self)
> +        self.bits = bits
> +        self.signed = signed
> +
> +        if signed:
> +            self.name = "int%d" % bits
> +        else:
> +            self.name = "uint%d" % bits
> +
> +    def primitive_type(self):
> +        return self.name
> +
> +    def c_type(self):
> +        return self.name + "_t"
> +
> +    def get_fixed_nw_size(self):
> +        return self.bits // 8
> +
> +    def is_primitive(self):
> +        return True
> +
> +class TypeAlias(Type):
> +    def __init__(self, name, the_type, attribute_list):
> +        Type.__init__(self)
> +        self.name = name
> +        self.the_type = the_type
> +        self.attributes = fix_attributes(attribute_list)
> +
> +    def get_type(self, recursive=False):
> +        if recursive:
> +            return self.the_type.get_type(True)
> +        else:
> +            return self.the_type
> +
> +    def primitive_type(self):
> +        return self.the_type.primitive_type()
> +
> +    def resolve(self):
> +        self.the_type = self.the_type.resolve()
> +        return self
> +
> +    def __str__(self):
> +        return "alias %s" % self.name
> +
> +    def is_primitive(self):
> +        return self.the_type.is_primitive()
> +
> +    def is_fixed_sizeof(self):
> +        return self.the_type.is_fixed_sizeof()
> +
> +    def is_fixed_nw_size(self):
> +        return self.the_type.is_fixed_nw_size()
> +
> +    def get_fixed_nw_size(self):
> +        return self.the_type.get_fixed_nw_size()
> +
> +    def get_num_pointers(self):
> +        return self.the_type.get_num_pointers()
> +
> +    def get_pointer_names(self, marshalled):
> +        return self.the_type.get_pointer_names(marshalled)
> +
> +    def c_type(self):
> +        if self.has_attr("ctype"):
> +            return self.attributes["ctype"][0]
> +        return self.name
> +
> +class EnumBaseType(Type):
> +    def is_enum(self):
> +        return isinstance(self, EnumType)
> +
> +    def primitive_type(self):
> +        return "uint%d" % (self.bits)
> +
> +    def c_type(self):
> +        return "uint%d_t" % (self.bits)
> +
> +    def c_name(self):
> +        return codegen.prefix_camel(self.name)
> +
> +    def c_enumname(self, value):
> +        return self.c_enumname_by_name(self.names[value])
> +
> +    def c_enumname_by_name(self, name):
> +        if self.has_attr("prefix"):
> +            return self.attributes["prefix"][0] + name
> +        return codegen.prefix_underscore_upper(self.name.upper(), name)
> +
> +    def is_primitive(self):
> +        return True
> +
> +    def get_fixed_nw_size(self):
> +        return self.bits // 8
> +
> +    # generates a value-name table suitable for use with the wireshark
> protocol
> +    # dissector
> +    def c_describe(self, writer):
> +        writer.write("static const value_string %s_vs[] = " %
> codegen.prefix_underscore_lower(self.name))
> +        writer.begin_block()
> +        values = list(self.names.keys())
> +        values.sort()
> +        for i in values:
> +            writer.write("{ ")
> +            writer.write(self.c_enumname(i))
> +            writer.write(", \"%s\" }," % self.names[i])
> +            writer.newline()
> +        writer.write("{ 0, NULL }")
> +        writer.end_block(semicolon=True)
> +        writer.newline()
> +
> +
> +class EnumType(EnumBaseType):
> +    def __init__(self, bits, name, enums, attribute_list):
> +        Type.__init__(self)
> +        self.bits = bits
> +        self.name = name
> +
> +        last = -1
> +        names = {}
> +        values = {}
> +        for v in enums:
> +            name = v[0]
> +            if len(v) > 1:
> +                value = v[1]
> +            else:
> +                value = last + 1
> +            last = value
> +
> +            assert value not in names
> +            names[value] = name
> +            values[name] = value
> +
> +        self.names = names
> +        self.values = values
> +
> +        self.attributes = fix_attributes(attribute_list)
> +
> +    def __str__(self):
> +        return "enum %s" % self.name
> +
> +    def c_define(self, writer):
> +        writer.write("typedef enum ")
> +        writer.write(self.c_name())
> +        writer.begin_block()
> +        values = list(self.names.keys())
> +        values.sort()
> +        current_default = 0
> +        for i in values:
> +            writer.write(self.c_enumname(i))
> +            if i != current_default:
> +                writer.write(" = %d" % (i))
> +            writer.write(",")
> +            writer.newline()
> +            current_default = i + 1
> +        writer.newline()
> +        writer.write(codegen.prefix_underscore_upper(self.name.upper(),
> "ENUM_END"))
> +        writer.newline()
> +        writer.end_block(newline=False)
> +        writer.write(" ")
> +        writer.write(self.c_name())
> +        writer.write(";")
> +        writer.newline()
> +        writer.newline()
> +
> +class FlagsType(EnumBaseType):
> +    def __init__(self, bits, name, flags, attribute_list):
> +        Type.__init__(self)
> +        self.bits = bits
> +        self.name = name
> +
> +        last = -1
> +        names = {}
> +        values = {}
> +        for v in flags:
> +            name = v[0]
> +            if len(v) > 1:
> +                value = v[1]
> +            else:
> +                value = last + 1
> +            last = value
> +
> +            assert value not in names
> +            names[value] = name
> +            values[name] = value
> +
> +        self.names = names
> +        self.values = values
> +
> +        self.attributes = fix_attributes(attribute_list)
> +
> +    def __str__(self):
> +        return "flags %s" % self.name
> +
> +    def c_define(self, writer):
> +        writer.write("typedef enum ")
> +        writer.write(self.c_name())
> +        writer.begin_block()
> +        values = list(self.names.keys())
> +        values.sort()
> +        mask = 0
> +        for i in values:
> +            writer.write(self.c_enumname(i))
> +            mask = mask |  (1<<i)
> +            writer.write(" = (1 << %d)" % (i))
> +            writer.write(",")
> +            writer.newline()
> +            current_default = i + 1
> +        writer.newline()
> +        writer.write(codegen.prefix_underscore_upper(self.name.upper(),
> "MASK"))
> +        writer.write(" = 0x%x" % (mask))
> +        writer.newline()
> +        writer.end_block(newline=False)
> +        writer.write(" ")
> +        writer.write(self.c_name())
> +        writer.write(";")
> +        writer.newline()
> +        writer.newline()
> +
> +class ArrayType(Type):
> +    def __init__(self, element_type, size):
> +        Type.__init__(self)
> +        self.name = None
> +
> +        self.element_type = element_type
> +        self.size = size
> +
> +    def __str__(self):
> +        if self.size == None:
> +            return "%s[]" % (str(self.element_type))
> +        else:
> +            return "%s[%s]" % (str(self.element_type), str(self.size))
> +
> +    def resolve(self):
> +        self.element_type = self.element_type.resolve()
> +        return self
> +
> +    def is_constant_length(self):
> +        return isinstance(self.size, int)
> +
> +    def is_remaining_length(self):
> +        return isinstance(self.size, str) and len(self.size) == 0
> +
> +    def is_identifier_length(self):
> +        return isinstance(self.size, str) and len(self.size) > 0
> +
> +    def is_image_size_length(self):
> +        if isinstance(self.size, int) or isinstance(self.size, str):
> +            return False
> +        return self.size[0] == "image_size"
> +
> +    def is_bytes_length(self):
> +        if isinstance(self.size, int) or isinstance(self.size, str):
> +            return False
> +        return self.size[0] == "bytes"
> +
> +    def is_cstring_length(self):
> +        if isinstance(self.size, int) or isinstance(self.size, str):
> +            return False
> +        return self.size[0] == "cstring"
> +
> +    def is_fixed_sizeof(self):
> +        return self.is_constant_length() and
> self.element_type.is_fixed_sizeof()
> +
> +    def is_fixed_nw_size(self):
> +        return self.is_constant_length() and
> self.element_type.is_fixed_nw_size()
> +
> +    def get_fixed_nw_size(self):
> +        if not self.is_fixed_nw_size():
> +            raise Exception("Not a fixed size type")
> +
> +        return self.element_type.get_fixed_nw_size() * self.size
> +
> +    def get_num_pointers(self):
> +        element_count = self.element_type.get_num_pointers()
> +        if element_count  == 0:
> +            return 0
> +        if self.is_constant_length(self):
> +            return element_count * self.size
> +        raise Exception("Pointers in dynamic arrays not supported")
> +
> +    def get_pointer_names(self, marshalled):
> +        element_count = self.element_type.get_num_pointers()
> +        if element_count  == 0:
> +            return []
> +        raise Exception("Pointer names in arrays not supported")
> +
> +    def is_extra_size(self):
> +        return self.has_attr("ptr_array")
> +
> +    def contains_extra_size(self):
> +        return self.element_type.contains_extra_size() or
> self.has_attr("chunk")
> +
> +    def sizeof(self):
> +        return "%s * %s" % (self.element_type.sizeof(), self.size)
> +
> +    def c_type(self):
> +        return self.element_type.c_type()
> +
> +class PointerType(Type):
> +    def __init__(self, target_type):
> +        Type.__init__(self)
> +        self.name = None
> +        self.target_type = target_type
> +        self.pointer_size = default_pointer_size
> +
> +    def __str__(self):
> +        return "%s*" % (str(self.target_type))
> +
> +    def resolve(self):
> +        self.target_type = self.target_type.resolve()
> +        return self
> +
> +    def set_ptr_size(self, new_size):
> +        self.pointer_size = new_size
> +
> +    def is_fixed_nw_size(self):
> +        return True
> +
> +    def is_primitive(self):
> +        return True
> +
> +    def primitive_type(self):
> +        if self.pointer_size == 4:
> +            return "uint32"
> +        else:
> +            return "uint64"
> +
> +    def get_fixed_nw_size(self):
> +        return self.pointer_size
> +
> +    def c_type(self):
> +        if self.pointer_size == 4:
> +            return "uint32_t"
> +        else:
> +            return "uint64_t"
> +
> +    def contains_extra_size(self):
> +        return True
> +
> +    def get_num_pointers(self):
> +        return 1
> +
> +class Containee:
> +    def __init__(self):
> +        self.attributes = {}
> +
> +    def is_switch(self):
> +        return False
> +
> +    def is_pointer(self):
> +        return not self.is_switch() and self.member_type.is_pointer()
> +
> +    def is_array(self):
> +        return not self.is_switch() and self.member_type.is_array()
> +
> +    def is_struct(self):
> +        return not self.is_switch() and self.member_type.is_struct()
> +
> +    def is_primitive(self):
> +        return not self.is_switch() and self.member_type.is_primitive()
> +
> +    def has_attr(self, name):
> +        if not name in valid_attributes:
> +            raise Exception('attribute %s not expected' % name)
> +        return name in self.attributes
> +
> +    def has_minor_attr(self):
> +        return self.has_attr("minor")
> +
> +    def has_end_attr(self):
> +        return self.has_attr("end")
> +
> +    def get_minor_attr(self):
> +        return self.attributes["minor"][0]
> +
> +class Member(Containee):
> +    def __init__(self, name, member_type, attribute_list):
> +        Containee.__init__(self)
> +        self.name = name
> +        self.member_type = member_type
> +        self.attributes = fix_attributes(attribute_list)
> +
> +    def resolve(self, container):
> +        self.container = container
> +        self.member_type = self.member_type.resolve()
> +        self.member_type.register()
> +        for i in propagated_attributes:
> +            if self.has_attr(i):
> +                self.member_type.attributes[i] = self.attributes[i]
> +        return self
> +
> +    def contains_member(self, member):
> +        return self.member_type.contains_member(member)
> +
> +    def is_primitive(self):
> +        return self.member_type.is_primitive()
> +
> +    def is_fixed_sizeof(self):
> +        if self.has_end_attr():
> +            return False
> +        return self.member_type.is_fixed_sizeof()
> +
> +    def is_extra_size(self):
> +        return self.has_end_attr() or self.has_attr("to_ptr") or
> self.member_type.is_extra_size()
> +
> +    def is_fixed_nw_size(self):
> +        if self.has_attr("virtual"):
> +            return True
> +        return self.member_type.is_fixed_nw_size()
> +
> +    def get_fixed_nw_size(self):
> +        if self.has_attr("virtual"):
> +            return 0
> +        size = self.member_type.get_fixed_nw_size()
> +        if self.has_minor_attr():
> +            minor = self.get_minor_attr()
> +            size = FixedSize(size, minor)
> +        return size
> +
> +    def contains_extra_size(self):
> +        return self.member_type.contains_extra_size()
> +
> +    def sizeof(self):
> +        return self.member_type.sizeof()
> +
> +    def __repr__(self):
> +        return "%s (%s)" % (str(self.name), str(self.member_type))
> +
> +    def get_num_pointers(self):
> +        if self.has_attr("to_ptr"):
> +            return 1
> +        return self.member_type.get_num_pointers()
> +
> +    def get_pointer_names(self, marshalled):
> +        if self.member_type.is_pointer():
> +            if self.has_attr("marshall") == marshalled:
> +                names = [self.name]
> +            else:
> +                names = []
> +        else:
> +            names = self.member_type.get_pointer_names(marshalled)
> +        if self.has_attr("outvar"):
> +            prefix = self.attributes["outvar"][0]
> +            names = [prefix + "_" + name for name in names]
> +        return names
> +
> +class SwitchCase:
> +    def __init__(self, values, member):
> +        self.values = values
> +        self.member = member
> +        self.members = [member]
> +
> +    def get_check(self, var_cname, var_type):
> +        checks = []
> +        for v in self.values:
> +            if v == None:
> +                return "1"
> +            elif var_type.is_enum():
> +                checks.append("%s == %s" % (var_cname,
> var_type.c_enumname_by_name(v[1])))
> +            else:
> +                checks.append("%s(%s & %s)" % (v[0], var_cname,
> var_type.c_enumname_by_name(v[1])))
> +        return " || ".join(checks)
> +
> +    def resolve(self, container):
> +        self.switch = container
> +        self.member = self.member.resolve(self)
> +        return self
> +
> +    def get_num_pointers(self):
> +        return self.member.get_num_pointers()
> +
> +    def get_pointer_names(self, marshalled):
> +        return self.member.get_pointer_names(marshalled)
> +
> +class Switch(Containee):
> +    def __init__(self, variable, cases, name, attribute_list):
> +        Containee.__init__(self)
> +        self.variable = variable
> +        self.name = name
> +        self.cases = cases
> +        self.attributes = fix_attributes(attribute_list)
> +
> +    def is_switch(self):
> +        return True
> +
> +    def lookup_case_member(self, name):
> +        for c in self.cases:
> +            if c.member.name == name:
> +                return c.member
> +        return None
> +
> +    def has_switch_member(self, member):
> +        for c in self.cases:
> +            if c.member == member:
> +                return True
> +        return False
> +
> +    def resolve(self, container):
> +        self.container = container
> +        self.cases = [c.resolve(self) for c in self.cases]
> +        return self
> +
> +    def __repr__(self):
> +        return "switch on %s %s" % (str(self.variable),str(self.name))
> +
> +    def is_fixed_sizeof(self):
> +        # Kinda weird, but we're unlikely to have a real struct if there is
> an @end
> +        if self.has_end_attr():
> +            return False
> +        return True
> +
> +    def is_fixed_nw_size(self):
> +        if self.has_attr("fixedsize"):
> +            return True
> +
> +        size = None
> +        has_default = False
> +        for c in self.cases:
> +            for v in c.values:
> +                if v == None:
> +                    has_default = True
> +            if not c.member.is_fixed_nw_size():
> +                return False
> +            if size == None:
> +                size = c.member.get_fixed_nw_size()
> +            elif size != c.member.get_fixed_nw_size():
> +                return False
> +        # Fixed size if all elements listed, or has default
> +        if has_default:
> +            return True
> +        key = self.container.lookup_member(self.variable)
> +        return len(self.cases) == len(key.member_type.values)
> +
> +    def is_extra_size(self):
> +        return self.has_end_attr()
> +
> +    def contains_extra_size(self):
> +        for c in self.cases:
> +            if c.member.is_extra_size():
> +                return True
> +            if c.member.contains_extra_size():
> +                return True
> +        return False
> +
> +    def get_fixed_nw_size(self):
> +        if not self.is_fixed_nw_size():
> +            raise Exception("Not a fixed size type")
> +        size = 0
> +        for c in self.cases:
> +            size = max(size, c.member.get_fixed_nw_size())
> +        return size
> +
> +    def sizeof(self):
> +        return "sizeof(((%s *)NULL)->%s)" % (self.container.c_type(),
> +                                             self.name)
> +
> +    def contains_member(self, member):
> +        return False # TODO: Don't support switch deep member lookup yet
> +
> +    def get_num_pointers(self):
> +        count = 0
> +        for c in self.cases:
> +            count = max(count, c.get_num_pointers())
> +        return count
> +
> +    def get_pointer_names(self, marshalled):
> +        names = []
> +        for c in self.cases:
> +            names = names + c.get_pointer_names(marshalled)
> +        return names
> +
> +class ContainerType(Type):
> +    def is_fixed_sizeof(self):
> +        for m in self.members:
> +            if not m.is_fixed_sizeof():
> +                return False
> +        return True
> +
> +    def contains_extra_size(self):
> +        for m in self.members:
> +            if m.is_extra_size():
> +                return True
> +            if m.contains_extra_size():
> +                return True
> +        return False
> +
> +    def is_fixed_nw_size(self):
> +        for i in self.members:
> +            if not i.is_fixed_nw_size():
> +                return False
> +        return True
> +
> +    def get_fixed_nw_size(self):
> +        size = 0
> +        for i in self.members:
> +            size = size + i.get_fixed_nw_size()
> +        return size
> +
> +    def contains_member(self, member):
> +        for m in self.members:
> +            if m == member or m.contains_member(member):
> +                return True
> +        return False
> +
> +    def get_fixed_nw_offset(self, member):
> +        size = 0
> +        for i in self.members:
> +            if i == member:
> +                break
> +            if i.contains_member(member):
> +                size = size  + i.member_type.get_fixed_nw_offset(member)
> +                break
> +            if i.is_fixed_nw_size():
> +                size = size + i.get_fixed_nw_size()
> +        return size
> +
> +    def resolve(self):
> +        self.members = [m.resolve(self) for m in self.members]
> +        return self
> +
> +    def get_num_pointers(self):
> +        count = 0
> +        for m in self.members:
> +            count = count + m.get_num_pointers()
> +        return count
> +
> +    def get_pointer_names(self, marshalled):
> +        names = []
> +        for m in self.members:
> +            names = names + m.get_pointer_names(marshalled)
> +        return names
> +
> +    def get_nw_offset(self, member, prefix = "", postfix = ""):
> +        fixed = self.get_fixed_nw_offset(member)
> +        v = []
> +        container = self
> +        while container != None:
> +            members = container.members
> +            container = None
> +            for m in members:
> +                if m == member:
> +                    break
> +                if m.contains_member(member):
> +                    container = m.member_type
> +                    break
> +                if m.is_switch() and m.has_switch_member(member):
> +                    break
> +                if not m.is_fixed_nw_size():
> +                    v.append(prefix + m.name + postfix)
> +        if len(v) > 0:
> +            return str(fixed) + " + " + (" + ".join(v))
> +        else:
> +            return str(fixed)
> +
> +    def lookup_member(self, name):
> +        dot = name.find('.')
> +        rest = None
> +        if dot >= 0:
> +            rest = name[dot+1:]
> +            name = name[:dot]
> +
> +        member = None
> +        if name in self.members_by_name:
> +            member = self.members_by_name[name]
> +        else:
> +            for m in self.members:
> +                if m.is_switch():
> +                    member = m.lookup_case_member(name)
> +                    if member != None:
> +                        break
> +                if member != None:
> +                    break
> +
> +        if member == None:
> +            raise Exception("No member called %s found" % name)
> +
> +        if rest != None:
> +            return member.member_type.lookup_member(rest)
> +
> +        return member
> +
> +class StructType(ContainerType):
> +    def __init__(self, name, members, attribute_list):
> +        Type.__init__(self)
> +        self.name = name
> +        self.members = members
> +        self.members_by_name = {}
> +        for m in members:
> +            self.members_by_name[m.name] = m
> +        self.attributes = fix_attributes(attribute_list)
> +
> +    def __str__(self):
> +        if self.name == None:
> +            return "anonymous struct"
> +        else:
> +            return "struct %s" % self.name
> +
> +    def c_type(self):
> +        if self.has_attr("ctype"):
> +            return self.attributes["ctype"][0]
> +        return codegen.prefix_camel(self.name)
> +
> +class MessageType(ContainerType):
> +    def __init__(self, name, members, attribute_list):
> +        Type.__init__(self)
> +        self.name = name
> +        self.members = members
> +        self.members_by_name = {}
> +        for m in members:
> +            self.members_by_name[m.name] = m
> +        self.reverse_members = {} # ChannelMembers referencing this message
> +        self.attributes = fix_attributes(attribute_list)
> +
> +    def __str__(self):
> +        if self.name == None:
> +            return "anonymous message"
> +        else:
> +            return "message %s" % self.name
> +
> +    def c_name(self):
> +        if self.name == None:
> +            cms = list(self.reverse_members.keys())
> +            if len(cms) != 1:
> +                raise "Unknown typename for message"
> +            cm = cms[0]
> +            channelname = cm.channel.member_name
> +            if channelname == None:
> +                channelname = ""
> +            else:
> +                channelname = channelname + "_"
> +            if cm.is_server:
> +                return "msg_" + channelname +  cm.name
> +            else:
> +                return "msgc_" + channelname +  cm.name
> +        else:
> +            return codegen.prefix_camel("Msg", self.name)
> +
> +    def c_type(self):
> +        if self.has_attr("ctype"):
> +            return self.attributes["ctype"][0]
> +        if self.name == None:
> +            cms = list(self.reverse_members.keys())
> +            if len(cms) != 1:
> +                raise "Unknown typename for message"
> +            cm = cms[0]
> +            channelname = cm.channel.member_name
> +            if channelname == None:
> +                channelname = ""
> +            if cm.is_server:
> +                return codegen.prefix_camel("Msg", channelname, cm.name)
> +            else:
> +                return codegen.prefix_camel("Msgc", channelname, cm.name)
> +        else:
> +            return codegen.prefix_camel("Msg", self.name)
> +
> +class ChannelMember(Containee):
> +    def __init__(self, name, message_type, value):
> +        Containee.__init__(self)
> +        self.name = name
> +        self.message_type = message_type
> +        self.value = value
> +
> +    def resolve(self, channel):
> +        self.channel = channel
> +        self.message_type = self.message_type.resolve()
> +        self.message_type.reverse_members[self] = 1
> +
> +        return self
> +
> +    def __repr__(self):
> +        return "%s (%s)" % (str(self.name), str(self.message_type))
> +
> +class ChannelType(Type):
> +    def __init__(self, name, base, members, attribute_list):
> +        Type.__init__(self)
> +        self.name = name
> +        self.base = base
> +        self.member_name = None
> +        self.members = members
> +        self.attributes = fix_attributes(attribute_list)
> +
> +    def __str__(self):
> +        if self.name == None:
> +            return "anonymous channel"
> +        else:
> +            return "channel %s" % self.name
> +
> +    def is_fixed_nw_size(self):
> +        return False
> +
> +    def get_client_message(self, name):
> +        return self.client_messages_byname[name]
> +
> +    def get_server_message(self, name):
> +        return self.server_messages_byname[name]
> +
> +    def resolve(self):
> +        if self.base != None:
> +            self.base = self.base.resolve()
> +
> +            server_messages = self.base.server_messages[:]
> +            server_messages_byname = self.base.server_messages_byname.copy()
> +            client_messages = self.base.client_messages[:]
> +            client_messages_byname = self.base.client_messages_byname.copy()
> +
> +            # Set default member_name, FooChannel -> foo
> +            self.member_name = self.name[:-7].lower()
> +        else:
> +            server_messages = []
> +            server_messages_byname = {}
> +            client_messages = []
> +            client_messages_byname = {}
> +
> +        server_count = 1
> +        client_count = 1
> +
> +        server = True
> +        for m in self.members:
> +            if m == "server":
> +                server = True
> +            elif m == "client":
> +                server = False
> +            elif server:
> +                m.is_server = True
> +                m = m.resolve(self)
> +                if m.value:
> +                    server_count = m.value + 1
> +                else:
> +                    m.value = server_count
> +                    server_count = server_count + 1
> +                server_messages.append(m)
> +                server_messages_byname[m.name] = m
> +            else:
> +                m.is_server = False
> +                m = m.resolve(self)
> +                if m.value:
> +                    client_count = m.value + 1
> +                else:
> +                    m.value = client_count
> +                    client_count = client_count + 1
> +                client_messages.append(m)
> +                client_messages_byname[m.name] = m
> +
> +        self.server_messages = server_messages
> +        self.server_messages_byname = server_messages_byname
> +        self.client_messages = client_messages
> +        self.client_messages_byname = client_messages_byname
> +
> +        return self
> +
> +class ProtocolMember:
> +    def __init__(self, name, channel_type, value):
> +        self.name = name
> +        self.channel_type = channel_type
> +        self.value = value
> +
> +    def resolve(self, protocol):
> +        self.channel_type = self.channel_type.resolve()
> +        self.channel_type.member_name = self.name
> +        return self
> +
> +    def __repr__(self):
> +        return "%s (%s)" % (str(self.name), str(self.channel_type))
> +
> +class ProtocolType(Type):
> +    def __init__(self, name, channels):
> +        Type.__init__(self)
> +        self.name = name
> +        self.channels = channels
> +
> +    def __str__(self):
> +        if self.name == None:
> +            return "anonymous protocol"
> +        else:
> +            return "protocol %s" % self.name
> +
> +    def is_fixed_nw_size(self):
> +        return False
> +
> +    def resolve(self):
> +        count = 1
> +        for m in self.channels:
> +            m = m.resolve(self)
> +            if m.value:
> +                count = m.value + 1
> +            else:
> +                m.value = count
> +                count = count + 1
> +
> +        return self
> +
> +class FdType(IntegerType):
> +    def __init__(self):
> +        IntegerType.__init__(self, 0, False)
> +        self.name = "fd"
> +
> +    def c_type(self):
> +        return "int"
> +
> +int8 = IntegerType(8, True)
> +uint8 = IntegerType(8, False)
> +int16 = IntegerType(16, True)
> +uint16 = IntegerType(16, False)
> +int32 = IntegerType(32, True)
> +uint32 = IntegerType(32, False)
> +int64 = IntegerType(64, True)
> +uint64 = IntegerType(64, False)
> +unix_fd = FdType()
> diff --git a/python_modules/spice_parser.py b/python_modules/spice_parser.py
> new file mode 100644
> index 0000000..db3cc8d
> --- /dev/null
> +++ b/python_modules/spice_parser.py
> @@ -0,0 +1,163 @@
> +import six
> +
> +try:
> +    from pyparsing import Literal, CaselessLiteral, Word, OneOrMore,
> ZeroOrMore, \
> +            Forward, delimitedList, Group, Optional, Combine, alphas, nums,
> restOfLine, cStyleComment, \
> +            alphanums, ParseException, ParseResults, Keyword, StringEnd,
> replaceWith
> +except ImportError:
> +    six.print_("Module pyparsing not found.")
> +    exit(1)
> +
> +
> +from . import ptypes
> +import sys
> +
> +cvtInt = lambda toks: int(toks[0])
> +
> +def parseVariableDef(toks):
> +    t = toks[0][0]
> +    pointer = toks[0][1]
> +    name = toks[0][2]
> +    array_size = toks[0][3]
> +    attributes = toks[0][4]
> +
> +    if array_size != None:
> +        t = ptypes.ArrayType(t, array_size)
> +
> +    if pointer != None:
> +        t = ptypes.PointerType(t)
> +
> +    return ptypes.Member(name, t, attributes)
> +
> +bnf = None
> +def SPICE_BNF():
> +    global bnf
> +
> +    if not bnf:
> +
> +        # punctuation
> +        colon  = Literal(":").suppress()
> +        lbrace = Literal("{").suppress()
> +        rbrace = Literal("}").suppress()
> +        lbrack = Literal("[").suppress()
> +        rbrack = Literal("]").suppress()
> +        lparen = Literal("(").suppress()
> +        rparen = Literal(")").suppress()
> +        equals = Literal("=").suppress()
> +        comma  = Literal(",").suppress()
> +        semi   = Literal(";").suppress()
> +
> +        # primitive types
> +        int8_      =
> Keyword("int8").setParseAction(replaceWith(ptypes.int8))
> +        uint8_     =
> Keyword("uint8").setParseAction(replaceWith(ptypes.uint8))
> +        int16_     =
> Keyword("int16").setParseAction(replaceWith(ptypes.int16))
> +        uint16_    =
> Keyword("uint16").setParseAction(replaceWith(ptypes.uint16))
> +        int32_     =
> Keyword("int32").setParseAction(replaceWith(ptypes.int32))
> +        uint32_    =
> Keyword("uint32").setParseAction(replaceWith(ptypes.uint32))
> +        int64_     =
> Keyword("int64").setParseAction(replaceWith(ptypes.int64))
> +        uint64_    =
> Keyword("uint64").setParseAction(replaceWith(ptypes.uint64))
> +        unix_fd_   =
> Keyword("unix_fd").setParseAction(replaceWith(ptypes.unix_fd))
> +
> +        # keywords
> +        enum32_    = Keyword("enum32").setParseAction(replaceWith(32))
> +        enum16_    = Keyword("enum16").setParseAction(replaceWith(16))
> +        enum8_     = Keyword("enum8").setParseAction(replaceWith(8))
> +        flags32_   = Keyword("flags32").setParseAction(replaceWith(32))
> +        flags16_   = Keyword("flags16").setParseAction(replaceWith(16))
> +        flags8_    = Keyword("flags8").setParseAction(replaceWith(8))
> +        channel_   = Keyword("channel")
> +        server_    = Keyword("server")
> +        client_    = Keyword("client")
> +        protocol_  = Keyword("protocol")
> +        typedef_   = Keyword("typedef")
> +        struct_    = Keyword("struct")
> +        message_   = Keyword("message")
> +        image_size_ = Keyword("image_size")
> +        bytes_     = Keyword("bytes")
> +        cstring_   = Keyword("cstring")
> +        switch_    = Keyword("switch")
> +        default_   = Keyword("default")
> +        case_      = Keyword("case")
> +
> +        identifier = Word( alphas, alphanums + "_" )
> +        enumname = Word( alphanums + "_" )
> +
> +        integer = ( Combine( CaselessLiteral("0x") + Word(
> nums+"abcdefABCDEF" ) ) |
> +                    Word( nums+"+-", nums )
> ).setName("int").setParseAction(cvtInt)
> +
> +        typename = identifier.copy().setParseAction(lambda toks :
> ptypes.TypeRef(str(toks[0])))
> +
> +        # This is just normal "types", i.e. not channels or messages
> +        typeSpec = Forward()
> +
> +        attributeValue = integer ^ identifier
> +        attribute = Group(Combine ("@" + identifier) + Optional(lparen +
> delimitedList(attributeValue) + rparen))
> +        attributes = Group(ZeroOrMore(attribute))
> +        arraySizeSpecImage = Group(image_size_ + lparen + integer + comma +
> identifier + comma + identifier + rparen)
> +        arraySizeSpecBytes = Group(bytes_ + lparen + identifier + comma +
> identifier + rparen)
> +        arraySizeSpecCString = Group(cstring_ + lparen + rparen)
> +        arraySizeSpec = lbrack + Optional(identifier ^ integer ^
> arraySizeSpecImage ^ arraySizeSpecBytes ^arraySizeSpecCString, default="") +
> rbrack
> +        variableDef = Group(typeSpec + Optional("*", default=None) +
> identifier + Optional(arraySizeSpec, default=None) + attributes - semi) \
> +            .setParseAction(parseVariableDef)
> +
> +        switchCase =
> Group(Group(OneOrMore(default_.setParseAction(replaceWith(None)) + colon |
> Group(case_.suppress() + Optional("!", default="") + identifier) + colon)) +
> variableDef) \
> +            .setParseAction(lambda toks: ptypes.SwitchCase(toks[0][0],
> toks[0][1]))
> +        switchBody = Group(switch_ + lparen +
> delimitedList(identifier,delim='.', combine=True) + rparen + lbrace +
> Group(OneOrMore(switchCase)) + rbrace + identifier + attributes - semi) \
> +            .setParseAction(lambda toks: ptypes.Switch(toks[0][1],
> toks[0][2], toks[0][3], toks[0][4]))
> +        messageBody = structBody = Group(lbrace + ZeroOrMore(variableDef |
> switchBody)  + rbrace)
> +        structSpec = Group(struct_ + identifier + structBody +
> attributes).setParseAction(lambda toks: ptypes.StructType(toks[0][1],
> toks[0][2], toks[0][3]))
> +
> +        # have to use longest match for type, in case a user-defined type
> name starts with a keyword type, like "channel_type"
> +        typeSpec << ( structSpec ^ int8_ ^ uint8_ ^ int16_ ^ uint16_ ^
> +                     int32_ ^ uint32_ ^ int64_ ^ uint64_ ^ unix_fd_ ^
> +                     typename).setName("type")
> +
> +        flagsBody = enumBody = Group(lbrace + delimitedList(Group (enumname
> + Optional(equals + integer))) + Optional(comma) + rbrace)
> +
> +        messageSpec = Group(message_ + messageBody +
> attributes).setParseAction(lambda toks: ptypes.MessageType(None, toks[0][1],
> toks[0][2])) | typename
> +
> +        channelParent = Optional(colon + typename, default=None)
> +        channelMessage = Group(messageSpec + identifier + Optional(equals +
> integer, default=None) + semi) \
> +            .setParseAction(lambda toks: ptypes.ChannelMember(toks[0][1],
> toks[0][0], toks[0][2]))
> +        channelBody = channelParent + Group(lbrace + ZeroOrMore( server_ +
> colon | client_ + colon | channelMessage)  + rbrace)
> +
> +        enum_ = (enum32_ | enum16_ | enum8_)
> +        flags_ = (flags32_ | flags16_ | flags8_)
> +        enumDef = Group(enum_ + identifier + enumBody + attributes -
> semi).setParseAction(lambda toks: ptypes.EnumType(toks[0][0], toks[0][1],
> toks[0][2], toks[0][3]))
> +        flagsDef = Group(flags_ + identifier + flagsBody + attributes  -
> semi).setParseAction(lambda toks: ptypes.FlagsType(toks[0][0], toks[0][1],
> toks[0][2], toks[0][3]))
> +        messageDef = Group(message_ + identifier + messageBody + attributes
> - semi).setParseAction(lambda toks: ptypes.MessageType(toks[0][1],
> toks[0][2], toks[0][3]))
> +        channelDef = Group(channel_ + identifier + channelBody + attributes
> - semi).setParseAction(lambda toks: ptypes.ChannelType(toks[0][1],
> toks[0][2], toks[0][3], toks[0][4]))
> +        structDef = Group(struct_ + identifier + structBody + attributes -
> semi).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2],
> toks[0][3]))
> +        typedefDef = Group(typedef_ + identifier  + typeSpec + attributes -
> semi).setParseAction(lambda toks: ptypes.TypeAlias(toks[0][1], toks[0][2],
> toks[0][3]))
> +
> +        definitions = typedefDef | structDef | enumDef | flagsDef |
> messageDef | channelDef
> +
> +        protocolChannel = Group(typename + identifier +  Optional(equals +
> integer, default=None) + semi) \
> +            .setParseAction(lambda toks: ptypes.ProtocolMember(toks[0][1],
> toks[0][0], toks[0][2]))
> +        protocolDef = Group(protocol_ + identifier + Group(lbrace +
> ZeroOrMore(protocolChannel) + rbrace) + semi) \
> +            .setParseAction(lambda toks: ptypes.ProtocolType(toks[0][1],
> toks[0][2]))
> +
> +        bnf = ZeroOrMore (definitions) +  protocolDef + StringEnd()
> +
> +        singleLineComment = "//" + restOfLine
> +        bnf.ignore( singleLineComment )
> +        bnf.ignore( cStyleComment )
> +
> +    return bnf
> +
> +
> +def parse(filename):
> +    try:
> +        bnf = SPICE_BNF()
> +        types = bnf.parseFile(filename)
> +    except ParseException as err:
> +        six.print_(err.line, file=sys.stderr)
> +        six.print_(" "*(err.column-1) + "^", file=sys.stderr)
> +        six.print_(err, file=sys.stderr)
> +        return None
> +
> +    for t in types:
> +        t.resolve()
> +        t.register()
> +    protocol = types[-1]
> +    return protocol
> diff --git a/spice.proto b/spice.proto
> new file mode 100644
> index 0000000..af970f2
> --- /dev/null
> +++ b/spice.proto
> @@ -0,0 +1,1412 @@
> +/* built in types:
> +   int8, uint8, 16, 32, 64
> +*/
> +
> +typedef fixed28_4 int32 @ctype(SPICE_FIXED28_4);
> +
> +struct Point {
> +    int32 x;
> +    int32 y;
> +};
> +
> +struct Point16 {
> +    int16 x;
> +    int16 y;
> +};
> +
> +struct PointFix {
> +    fixed28_4 x;
> +    fixed28_4 y;
> +};
> +
> +struct Rect {
> +    int32 top;
> +    int32 left;
> +    int32 bottom;
> +    int32 right;
> +};
> +
> +struct Transform {
> +    uint32 t00;
> +    uint32 t01;
> +    uint32 t02;
> +    uint32 t10;
> +    uint32 t11;
> +    uint32 t12;
> +};
> +
> +enum32 link_err {
> +    OK,
> +    ERROR,
> +    INVALID_MAGIC,
> +    INVALID_DATA,
> +    VERSION_MISMATCH,
> +    NEED_SECURED,
> +    NEED_UNSECURED,
> +    PERMISSION_DENIED,
> +    BAD_CONNECTION_ID,
> +    CHANNEL_NOT_AVAILABLE
> +};
> +
> +enum32 warn_code {
> +    WARN_GENERAL
> +} @prefix(SPICE_);
> +
> +enum32 info_code {
> +    INFO_GENERAL
> +} @prefix(SPICE_);
> +
> +flags32 migrate_flags {
> +    NEED_FLUSH,
> +    NEED_DATA_TRANSFER
> +} @prefix(SPICE_MIGRATE_);
> +
> +flags32 composite_flags {
> +    OP0, OP1, OP2, OP3, OP4, OP5, OP6, OP7,
> +    SRC_FILTER0, SRC_FILTER1, SRC_FILTER2,
> +    MASK_FILTER0, MASK_FITLER1, MASK_FILTER2,
> +
> +    SRC_REPEAT0, SRC_REPEAT1,
> +    MASK_REPEAT0, MASK_REPEAT1,
> +    COMPONENT_ALPHA,
> +
> +    HAS_MASK,
> +    HAS_SRC_TRANSFORM,
> +    HAS_MASK_TRANSFORM,
> +
> +    /* These are used to override the formats given in the images. For
> +     * example, if the mask image has format a8r8g8b8, but MASK_OPAQUE
> +     * is set, the image should be treated as if it were x8r8g8b8
> +     */
> +    SOURCE_OPAQUE,
> +    MASK_OPAQUE,
> +    DEST_OPAQUE,
> +} @prefix(SPICE_COMPOSITE_);
> +
> +enum32 notify_severity {
> +    INFO,
> +    WARN,
> +    ERROR,
> +};
> +
> +enum32 notify_visibility {
> +    LOW,
> +    MEDIUM,
> +    HIGH,
> +};
> +
> +flags16 mouse_mode {
> +    SERVER,
> +    CLIENT,
> +};
> +
> +enum16 pubkey_type {
> +    INVALID,
> +    RSA,
> +    RSA2,
> +    DSA,
> +    DSA1,
> +    DSA2,
> +    DSA3,
> +    DSA4,
> +    DH,
> +    EC,
> +};
> +
> +message Empty {
> +};
> +
> +message Data {
> +    uint8 data[] @end @ctype(uint8_t);
> +} @nocopy;
> +
> +struct ChannelWait {
> +    uint8 channel_type;
> +    uint8 channel_id;
> +    uint64 message_serial;
> +} @ctype(SpiceWaitForChannel);
> +
> +channel BaseChannel {
> + server:
> +    message {
> +	migrate_flags flags;
> +    } migrate;
> +
> +    Data migrate_data;
> +
> +    message {
> +	uint32 generation;
> +	uint32 window;
> +    } set_ack;
> +
> +    message {
> +	uint32 id;
> +	uint64 timestamp;
> +	uint8 data[] @ctype(uint8_t) @as_ptr(data_len);
> +    } ping;
> +
> +    message {
> +	uint8 wait_count;
> +	ChannelWait wait_list[wait_count] @end;
> +    } wait_for_channels;
> +
> +    message {
> +	uint64 time_stamp;
> +	link_err reason;
> +    } @ctype(SpiceMsgDisconnect) disconnecting;
> +
> +    message {
> +	uint64 time_stamp;
> +	notify_severity severity;
> +	notify_visibility visibilty;
> +	uint32 what; /* error_code/warn_code/info_code */
> +	uint32 message_len;
> +	uint8 message[message_len] @end @nomarshal;
> +    } notify;
> +
> +    Data list; /* the msg body is SpiceSubMessageList */
> +
> +    Empty base_last = 100;
> +
> + client:
> +    message {
> +	uint32 generation;
> +    } ack_sync;
> +
> +    Empty ack;
> +
> +    message {
> +	uint32 id;
> +	uint64 timestamp;
> +    } @ctype(SpiceMsgPing) pong;
> +
> +    Empty migrate_flush_mark;
> +
> +    Data migrate_data;
> +
> +    message {
> +	uint64 time_stamp;
> +	link_err reason;
> +    } @ctype(SpiceMsgDisconnect) disconnecting;
> +};
> +
> +struct ChannelId {
> +    uint8 type;
> +    uint8 id;
> +};
> +
> +struct DstInfo {
> +	uint16 port;
> +	uint16 sport;
> +	uint32 host_size;
> +	uint8 *host_data[host_size] @zero_terminated @marshall @nonnull;
> +	uint32 cert_subject_size;
> +	uint8 *cert_subject_data[cert_subject_size] @zero_terminated @marshall;
> +} @ctype(SpiceMigrationDstInfo);
> +
> +channel MainChannel : BaseChannel {
> + server:
> +     message {
> +        DstInfo dst_info;
> +    } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
> +
> +    Empty migrate_cancel;
> +
> +    message {
> +	uint32 session_id;
> +	uint32 display_channels_hint;
> +	uint32 supported_mouse_modes;
> +	uint32 current_mouse_mode;
> +	uint32 agent_connected;
> +	uint32 agent_tokens;
> +	uint32 multi_media_time;
> +	uint32 ram_hint;
> +    } init;
> +
> +    message {
> +	uint32 num_of_channels;
> +	ChannelId channels[num_of_channels] @end;
> +    } @ctype(SpiceMsgChannels) channels_list;
> +
> +    message {
> +	mouse_mode supported_modes;
> +	mouse_mode current_mode @unique_flag;
> +    } mouse_mode;
> +
> +    message {
> +	uint32 time;
> +    } @ctype(SpiceMsgMainMultiMediaTime) multi_media_time;
> +
> +    Empty agent_connected;
> +
> +    message {
> +	link_err error_code;
> +    } @ctype(SpiceMsgMainAgentDisconnect) agent_disconnected;
> +
> +    Data agent_data;
> +
> +    message {
> +	uint32 num_tokens;
> +    } @ctype(SpiceMsgMainAgentTokens) agent_token;
> +
> +    message {
> +      uint16 port;
> +      uint16 sport;
> +      uint32 host_size;
> +      uint8 *host_data[host_size] @zero_terminated @marshall;
> +      uint32 cert_subject_size;
> +      uint8 *cert_subject_data[cert_subject_size] @zero_terminated
> @marshall;
> +    } @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host;
> +
> +    Empty migrate_end;
> +
> +    message {
> +       uint32 name_len;
> +       uint8 name[name_len] @end;
> +    } name;
> +
> +    message {
> +       uint8 uuid[16];
> +    } uuid;
> +
> +    message {
> +        uint32 num_tokens;
> +    } agent_connected_tokens;
> +
> +    message {
> +        DstInfo dst_info;
> +        uint32 src_mig_version;
> +    } migrate_begin_seamless;
> +
> +    Empty migrate_dst_seamless_ack;
> +    Empty migrate_dst_seamless_nack;
> +
> + client:
> +    message {
> +	uint64 cache_size;
> +    } @ctype(SpiceMsgcClientInfo) client_info = 101;
> +
> +    Empty migrate_connected;
> +
> +    Empty migrate_connect_error;
> +
> +    Empty attach_channels;
> +
> +    message {
> +	mouse_mode mode;
> +    } mouse_mode_request;
> +
> +    message {
> +	uint32 num_tokens;
> +    } agent_start;
> +
> +    Data agent_data;
> +
> +    message {
> +        uint32 num_tokens;
> +    } @ctype(SpiceMsgcMainAgentTokens) agent_token;
> +
> +    Empty migrate_end;
> +
> +    message {
> +        uint32 src_version;
> +    } migrate_dst_do_seamless;
> +
> +    Empty migrate_connected_seamless;
> +};
> +
> +enum8 clip_type {
> +    NONE,
> +    RECTS
> +};
> +
> +flags8 path_flags { /* TODO: C enum names changes */
> +    BEGIN = 0,
> +    END = 1,
> +    CLOSE = 3,
> +    BEZIER = 4,
> +} @prefix(SPICE_PATH_);
> +
> +enum8 video_codec_type {
> +    MJPEG = 1,
> +};
> +
> +flags8 stream_flags {
> +    TOP_DOWN = 0,
> +};
> +
> +enum8 brush_type {
> +    NONE,
> +    SOLID,
> +    PATTERN,
> +};
> +
> +flags8 mask_flags {
> +    INVERS,
> +};
> +
> +enum8 image_type {
> +    BITMAP,
> +    QUIC,
> +    RESERVED,
> +    LZ_PLT = 100,
> +    LZ_RGB,
> +    GLZ_RGB,
> +    FROM_CACHE,
> +    SURFACE,
> +    JPEG,
> +    FROM_CACHE_LOSSLESS,
> +    ZLIB_GLZ_RGB,
> +    JPEG_ALPHA,
> +    LZ4,
> +};
> +
> +enum8 image_compression {
> +    INVALID  = 0,
> +    OFF,
> +    AUTO_GLZ,
> +    AUTO_LZ,
> +    QUIC,
> +    GLZ,
> +    LZ,
> +    LZ4,
> +};
> +
> +flags8 image_flags {
> +    CACHE_ME,
> +    HIGH_BITS_SET,
> +    CACHE_REPLACE_ME,
> +};
> +
> +enum8 bitmap_fmt {
> +    INVALID,
> +    1BIT_LE,
> +    1BIT_BE,
> +    4BIT_LE,
> +    4BIT_BE,
> +    8BIT /* 8bit indexed mode */,
> +    16BIT, /* 0555 mode */
> +    24BIT /* 3 byte, brg */,
> +    32BIT /* 4 byte, xrgb in little endian format */,
> +    RGBA /* 4 byte, argb in little endian format */,
> +    8BIT_A /* 1 byte, alpha */
> +};
> +
> +flags8 bitmap_flags {
> +    PAL_CACHE_ME,
> +    PAL_FROM_CACHE,
> +    TOP_DOWN,
> +};
> +
> +flags8 jpeg_alpha_flags {
> +    TOP_DOWN,
> +};
> +
> +enum8 image_scale_mode {
> +    INTERPOLATE,
> +    NEAREST,
> +};
> +
> +flags16 ropd {
> +    INVERS_SRC,
> +    INVERS_BRUSH,
> +    INVERS_DEST,
> +    OP_PUT,
> +    OP_OR,
> +    OP_AND,
> +    OP_XOR,
> +    OP_BLACKNESS,
> +    OP_WHITENESS,
> +    OP_INVERS,
> +    INVERS_RES,
> +};
> +
> +/* This *must* remain with values identical to api/winddi.h
> +   LA_STYLED == 0x8 (log_2)=> 3
> +   LA_STARTGAP == 0x4 (log_2)=> 2
> +   This is used by the windows driver.
> + */
> +flags8 line_flags {
> +    STYLED = 3,
> +    START_WITH_GAP = 2,
> +};
> +
> +flags8 string_flags {
> +    RASTER_A1,
> +    RASTER_A4,
> +    RASTER_A8,
> +    RASTER_TOP_DOWN,
> +};
> +
> +flags32 surface_flags {
> +    /* Adding flags requires some caps check, since old clients only
> +       treat the value as an enum and not as a flag (flag == PRIMARY) */
> +    PRIMARY
> +};
> +
> +enum32 surface_fmt {
> +    INVALID,
> +    1_A     = 1,
> +    8_A     = 8,
> +    16_555  = 16 ,
> +    16_565  = 80,
> +    32_xRGB = 32,
> +    32_ARGB = 96
> +};
> +
> +flags8 alpha_flags {
> +    DEST_HAS_ALPHA,
> +    SRC_SURFACE_HAS_ALPHA
> +};
> +
> +enum8 resource_type {
> +      INVALID,
> +      PIXMAP
> +} @prefix(SPICE_RES_TYPE_);
> +
> +struct ClipRects {
> +    uint32 num_rects;
> +    Rect rects[num_rects] @end;
> +};
> +
> +struct PathSegment {
> +    path_flags flags;
> +    uint32 count;
> +    PointFix points[count] @end;
> +}  @ctype(SpicePathSeg);
> +
> +struct Path {
> +    uint32 num_segments;
> +    PathSegment segments[num_segments] @ptr_array;
> +};
> +
> +struct Clip {
> +    clip_type type;
> +    switch (type) {
> +    case RECTS:
> +        ClipRects rects @outvar(cliprects) @to_ptr;
> +    } u @anon;
> +};
> +
> +struct DisplayBase {
> +    uint32 surface_id;
> +    Rect box;
> +    Clip clip;
> +} @ctype(SpiceMsgDisplayBase);
> +
> +struct ResourceID {
> +    uint8 type;
> +    uint64 id;
> +};
> +
> +struct WaitForChannel {
> +    uint8 channel_type;
> +    uint8 channel_id;
> +    uint64 message_serial;
> +};
> +
> +struct Palette {
> +    uint64 unique;
> +    uint16 num_ents;
> +    uint32 ents[num_ents] @end;
> +};
> +
> +struct BitmapData {
> +    bitmap_fmt format;
> +    bitmap_flags flags;
> +    uint32 x;
> +    uint32 y;
> +    uint32 stride;
> +    switch (flags) {
> +    case PAL_FROM_CACHE:
> +	uint64 palette_id;
> +    default:
> +	Palette *palette @outvar(bitmap);
> +    } pal @anon;
> +    uint8 data[image_size(8, stride, y)] @chunk @nomarshal;
> +} @ctype(SpiceBitmap);
> +
> +struct BinaryData {
> +    uint32 data_size;
> +    uint8 data[data_size] @nomarshal @chunk;
> +} @ctype(SpiceQUICData);
> +
> +struct LZPLTData {
> +    bitmap_flags flags;
> +    uint32 data_size;
> +    switch (flags) {
> +    case PAL_FROM_CACHE:
> +	uint64 palette_id;
> +    default:
> +	Palette *palette @nonnull @outvar(lzplt);
> +    } pal @anon;
> +    uint8 data[data_size] @nomarshal @chunk;
> +};
> +
> +struct ZlibGlzRGBData {
> +    uint32 glz_data_size;
> +    uint32 data_size;
> +    uint8 data[data_size] @nomarshal @chunk;
> +} @ctype(SpiceZlibGlzRGBData);
> +
> +struct JPEGAlphaData {
> +    jpeg_alpha_flags flags;
> +    uint32 jpeg_size;
> +    uint32 data_size;
> +    uint8 data[data_size] @nomarshal @chunk;
> +} @ctype(SpiceJPEGAlphaData);
> +
> +struct Surface {
> +    uint32 surface_id;
> +};
> +
> +
> +struct Image {
> +    struct ImageDescriptor {
> +        uint64 id;
> +        image_type type;
> +        image_flags flags;
> +        uint32 width;
> +        uint32 height;
> +    } descriptor;
> +
> +    switch (descriptor.type) {
> +    case BITMAP:
> +        BitmapData bitmap;
> +    case QUIC:
> +        BinaryData quic;
> +    case LZ_RGB:
> +    case GLZ_RGB:
> +        BinaryData lz_rgb;
> +    case JPEG:
> +        BinaryData jpeg;
> +    case LZ4:
> +        BinaryData lz4;
> +    case LZ_PLT:
> +        LZPLTData lz_plt;
> +    case ZLIB_GLZ_RGB:
> +        ZlibGlzRGBData zlib_glz;
> +    case JPEG_ALPHA:
> +        JPEGAlphaData jpeg_alpha;
> +    case SURFACE:
> +        Surface surface;
> +    } u;
> +};
> +
> +struct Pattern {
> +    Image *pat @nonnull;
> +    Point pos;
> +};
> +
> +struct Brush {
> +    brush_type type;
> +    switch (type) {
> +    case SOLID:
> +        uint32 color;
> +    case PATTERN:
> +        Pattern pattern;
> +    } u;
> +};
> +
> +struct QMask {
> +    mask_flags flags;
> +    Point pos;
> +    Image *bitmap;
> +};
> +
> +struct LineAttr {
> +    line_flags flags;
> +    switch (flags) {
> +    case STYLED:
> +        uint8 style_nseg;
> +   } u1 @anon;
> +   switch (flags) {
> +   case STYLED:
> +        fixed28_4 *style[style_nseg];
> +   } u2 @anon;
> +};
> +
> +struct RasterGlyphA1 {
> +    Point render_pos;
> +    Point glyph_origin;
> +    uint16 width;
> +    uint16 height;
> +    uint8 data[image_size(1, width, height)] @end;
> +} @ctype(SpiceRasterGlyph);
> +
> +struct RasterGlyphA4 {
> +    Point render_pos;
> +    Point glyph_origin;
> +    uint16 width;
> +    uint16 height;
> +    uint8 data[image_size(4, width, height)] @end;
> +} @ctype(SpiceRasterGlyph);
> +
> +struct RasterGlyphA8 {
> +    Point render_pos;
> +    Point glyph_origin;
> +    uint16 width;
> +    uint16 height;
> +    uint8 data[image_size(8, width, height)] @end;
> +} @ctype(SpiceRasterGlyph);
> +
> +struct String {
> +    uint16 length;
> +    string_flags flags; /* Special: Only one of a1/a4/a8 set */
> +    switch (flags) {
> +    case RASTER_A1:
> +	RasterGlyphA1 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
> +    case RASTER_A4:
> +	RasterGlyphA4 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
> +    case RASTER_A8:
> +	RasterGlyphA8 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
> +    } u @anon;
> +};
> +
> +struct StreamDataHeader {
> +	uint32 id;
> +	uint32 multi_media_time;
> +};
> +
> +struct Head {
> +    uint32 id;
> +    uint32 surface_id;
> +    uint32 width;
> +    uint32 height;
> +    uint32 x;
> +    uint32 y;
> +    uint32 flags;
> +};
> +
> +flags32 gl_scanout_flags {
> +    Y0TOP
> +};
> +
> +channel DisplayChannel : BaseChannel {
> + server:
> +    message {
> +	uint32 x_res;
> +	uint32 y_res;
> +	uint32 bits;
> +    } mode = 101;
> +
> +    Empty mark;
> +    Empty reset;
> +    message {
> +	DisplayBase base;
> +	Point src_pos;
> +    } copy_bits;
> +
> +    message {
> +	uint16 count;
> +	ResourceID resources[count] @end;
> +    } @ctype(SpiceResourceList) inval_list;
> +
> +    message {
> +	uint8 wait_count;
> +	WaitForChannel wait_list[wait_count] @end;
> +    } @ctype(SpiceMsgWaitForChannels) inval_all_pixmaps;
> +
> +    message {
> +	uint64 id;
> +    } @ctype(SpiceMsgDisplayInvalOne) inval_palette;
> +
> +    Empty inval_all_palettes;
> +
> +    message {
> +	uint32 surface_id;
> +	uint32 id;
> +	stream_flags flags;
> +	video_codec_type codec_type;
> +	uint64 stamp;
> +	uint32 stream_width;
> +	uint32 stream_height;
> +	uint32 src_width;
> +	uint32 src_height;
> +	Rect dest;
> +	Clip clip;
> +    } stream_create = 122;
> +
> +    message {
> +	StreamDataHeader base;
> +	uint32 data_size;
> +	uint8 data[data_size] @end @nomarshal;
> +    } stream_data;
> +
> +    message {
> +	uint32 id;
> +	Clip clip;
> +    } stream_clip;
> +
> +    message {
> +	uint32 id;
> +    } stream_destroy;
> +
> +    Empty stream_destroy_all;
> +
> +    message {
> +	DisplayBase base;
> +	struct Fill {
> +	    Brush brush @outvar(brush);
> +	    ropd rop_descriptor;
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_fill = 302;
> +
> +    message {
> +	DisplayBase base;
> +	struct Opaque {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    Brush brush;
> +	    ropd rop_descriptor;
> +	    image_scale_mode scale_mode;
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_opaque;
> +
> +    message {
> +	DisplayBase base;
> +	struct Copy {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    ropd rop_descriptor;
> +	    image_scale_mode scale_mode;
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_copy;
> +
> +    message {
> +	DisplayBase base;
> +	struct Blend {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    ropd rop_descriptor;
> +	    image_scale_mode scale_mode;
> +	    QMask mask @outvar(mask);
> +	} @ctype(SpiceCopy) data;
> +    } draw_blend;
> +
> +    message {
> +	DisplayBase base;
> +	struct Blackness {
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_blackness;
> +
> +    message {
> +	DisplayBase base;
> +	struct Whiteness {
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_whiteness;
> +
> +    message {
> +	DisplayBase base;
> +	struct Invers {
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_invers;
> +
> +    message {
> +	DisplayBase base;
> +	struct Rop3 {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    Brush brush;
> +	    uint8 rop3;
> +	    image_scale_mode scale_mode;
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_rop3;
> +
> +    message {
> +	DisplayBase base;
> +	struct Stroke {
> +	    Path *path @marshall @nonnull;
> +	    LineAttr attr;
> +	    Brush brush;
> +	    uint16 fore_mode;
> +	    uint16 back_mode;
> +	} data;
> +    } draw_stroke;
> +
> +    message {
> +	DisplayBase base;
> +	struct Text {
> +	    String *str @marshall @nonnull;
> +	    Rect back_area;
> +	    Brush fore_brush @outvar(fore_brush);
> +	    Brush back_brush @outvar(back_brush);
> +	    uint16 fore_mode;
> +	    uint16 back_mode;
> +	} data;
> +    } draw_text;
> +
> +    message {
> +	DisplayBase base;
> +	struct Transparent {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    uint32 src_color;
> +	    uint32 true_color;
> +	} data;
> +    } draw_transparent;
> +
> +    message {
> +	DisplayBase base;
> +	struct AlphaBlend {
> +	    alpha_flags alpha_flags;
> +	    uint8 alpha;
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	} data;
> +    } draw_alpha_blend;
> +
> +    message {
> +	uint32 surface_id;
> +	uint32 width;
> +	uint32 height;
> +	surface_fmt format;
> +	surface_flags flags;
> +    } @ctype(SpiceMsgSurfaceCreate) surface_create;
> +
> +    message {
> +	uint32 surface_id;
> +    } @ctype(SpiceMsgSurfaceDestroy) surface_destroy;
> +
> +    message {
> +	StreamDataHeader base;
> +	uint32 width;
> +	uint32 height;
> +	Rect dest;
> +	uint32 data_size;
> +	uint8 data[data_size] @end @nomarshal;
> +    } stream_data_sized;
> +
> +    message {
> +	uint16 count;
> +    uint16 max_allowed;
> +	Head heads[count] @end;
> +    } monitors_config;
> +
> +    message {
> +	DisplayBase base;
> +	struct Composite {
> +	    composite_flags flags;
> +	    Image *src_bitmap;
> +	    switch (flags) {
> +	    case HAS_MASK:
> +	        Image *mask_bitmap;
> +            } a @anon;
> +	    switch (flags) {
> +	    case HAS_SRC_TRANSFORM:
> +	        Transform src_transform;
> +            } b @anon;
> +	    switch (flags) {
> +	    case HAS_MASK_TRANSFORM:
> +	        Transform mask_transform;
> +            } c @anon;
> +	    Point16 src_origin;
> +	    Point16 mask_origin;
> +	} data;
> +    } draw_composite;
> +
> +    message {
> +        uint32 stream_id;
> +        uint32 unique_id;
> +        uint32 max_window_size;
> +        uint32 timeout_ms;
> +    } stream_activate_report;
> +
> +    message {
> +        unix_fd drm_dma_buf_fd;
> +        uint32 width;
> +        uint32 height;
> +        uint32 stride;
> +        /* specifies the format of drm_dma_buf_fd defined in drm_fourcc.h */
> +        uint32 drm_fourcc_format;
> +        gl_scanout_flags flags;
> +    } gl_scanout_unix;
> +
> +    message {
> +        uint32 x;
> +        uint32 y;
> +        uint32 w;
> +        uint32 h;
> +    } gl_draw;
> +
> + client:
> +    message {
> +	uint8 pixmap_cache_id;
> +	int64 pixmap_cache_size; //in pixels
> +	uint8 glz_dictionary_id;
> +	int32 glz_dictionary_window_size;  // in pixels
> +    } init = 101;
> +
> +    message {
> +        uint32 stream_id;
> +        uint32 unique_id;
> +        uint32 start_frame_mm_time;
> +        uint32 end_frame_mm_time;
> +        uint32 num_frames;
> +        uint32 num_drops;
> +        int32 last_frame_delay;
> +        uint32 audio_delay;
> +    } stream_report;
> +
> +    message {
> +        image_compression image_compression;
> +    } preferred_compression;
> +
> +    message {
> +    } gl_draw_done;
> +};
> +
> +flags16 keyboard_modifier_flags {
> +    SCROLL_LOCK,
> +    NUM_LOCK,
> +    CAPS_LOCK
> +};
> +
> +enum8 mouse_button {
> +    INVALID,
> +    LEFT,
> +    MIDDLE,
> +    RIGHT,
> +    UP,
> +    DOWN,
> +};
> +
> +flags16 mouse_button_mask {
> +    LEFT,
> +    MIDDLE,
> +    RIGHT
> +};
> +
> +channel InputsChannel : BaseChannel {
> + client:
> +    message {
> +	uint32 code;
> +    } @ctype(SpiceMsgcKeyDown) key_down = 101;
> +
> +    message {
> +	uint32 code;
> +    } @ctype(SpiceMsgcKeyUp) key_up;
> +
> +    message {
> +	keyboard_modifier_flags modifiers;
> +    } @ctype(SpiceMsgcKeyModifiers) key_modifiers;
> +
> +    Data key_scancode;
> +
> +    message {
> +	int32 dx;
> +	int32 dy;
> +	mouse_button_mask buttons_state;
> +    } @ctype(SpiceMsgcMouseMotion) mouse_motion = 111;
> +
> +    message {
> +	uint32 x;
> +	uint32 y;
> +	mouse_button_mask buttons_state;
> +	uint8 display_id;
> +    } @ctype(SpiceMsgcMousePosition) mouse_position;
> +
> +    message {
> +	mouse_button button;
> +	mouse_button_mask buttons_state;
> +    } @ctype(SpiceMsgcMousePress) mouse_press;
> +
> +    message {
> +	mouse_button button;
> +	mouse_button_mask buttons_state;
> +    } @ctype(SpiceMsgcMouseRelease) mouse_release;
> +
> + server:
> +    message {
> +	keyboard_modifier_flags keyboard_modifiers;
> +    } init = 101;
> +
> +    message {
> +	keyboard_modifier_flags modifiers;
> +    } key_modifiers;
> +
> +    Empty mouse_motion_ack = 111;
> +};
> +
> +enum8 cursor_type {
> +    ALPHA,
> +    MONO,
> +    COLOR4,
> +    COLOR8,
> +    COLOR16,
> +    COLOR24,
> +    COLOR32,
> +};
> +
> +flags16 cursor_flags {
> +    NONE, /* Means no cursor */
> +    CACHE_ME,
> +    FROM_CACHE,
> +};
> +
> +struct CursorHeader {
> +    uint64 unique;
> +    cursor_type type;
> +    uint16 width;
> +    uint16 height;
> +    uint16 hot_spot_x;
> +    uint16 hot_spot_y;
> +};
> +
> +struct Cursor {
> +    cursor_flags flags;
> +    switch (flags) {
> +    case !NONE:
> +        CursorHeader header;
> +    } u @anon;
> +    uint8 data[] @as_ptr(data_size);
> +};
> +
> +channel CursorChannel : BaseChannel {
> + server:
> +    message {
> +	Point16 position;
> +	uint16 trail_length;
> +	uint16 trail_frequency;
> +	uint8 visible;
> +	Cursor cursor;
> +    } init = 101;
> +
> +    Empty reset;
> +
> +    message {
> +	Point16 position;
> +	uint8 visible;
> +	Cursor cursor;
> +    } set;
> +
> +    message {
> +	Point16 position;
> +    } move;
> +
> +    Empty hide;
> +
> +    message {
> +	uint16 length;
> +	uint16 frequency;
> +    } trail;
> +
> +    message {
> +	uint64 id;
> +    } @ctype(SpiceMsgDisplayInvalOne) inval_one;
> +
> +    Empty inval_all;
> +};
> +
> +enum16 audio_data_mode {
> +    INVALID,
> +    RAW,
> +    CELT_0_5_1,
> +    OPUS,
> +};
> +
> +enum16 audio_fmt {
> +    INVALID,
> +    S16,
> +};
> +
> +message AudioVolume {
> +    uint8 nchannels;
> +    uint16 volume[nchannels] @end;
> +};
> +
> +message AudioMute {
> +    uint8 mute;
> +};
> +
> +channel PlaybackChannel : BaseChannel {
> + server:
> +    message {
> +	uint32 time;
> +	uint8 data[] @as_ptr(data_size);
> +    } @ctype(SpiceMsgPlaybackPacket) data = 101;
> +
> +    message {
> +	uint32 time;
> +	audio_data_mode mode;
> +	uint8 data[] @as_ptr(data_size);
> +    } mode;
> +
> +    message {
> +       uint32 channels;
> +       audio_fmt format;
> +       uint32 frequency;
> +       uint32 time;
> +    } start;
> +
> +    Empty stop;
> +    AudioVolume volume;
> +    AudioMute mute;
> +
> +    message {
> +        uint32 latency_ms;
> +    } latency;
> +};
> +
> +channel RecordChannel : BaseChannel {
> + server:
> +    message {
> +	uint32 channels;
> +	audio_fmt format;
> +	uint32 frequency;
> +    } start = 101;
> +
> +    Empty stop;
> +    AudioVolume volume;
> +    AudioMute mute;
> + client:
> +    message {
> +	uint32 time;
> +	uint8 data[] @nomarshal @as_ptr(data_size);
> +    } @ctype(SpiceMsgcRecordPacket) data = 101;
> +
> +    message {
> +	uint32 time;
> +	audio_data_mode mode;
> +	uint8 data[] @as_ptr(data_size);
> +    } mode;
> +
> +    message {
> +	uint32 time;
> +    } start_mark;
> +};
> +
> +enum16 tunnel_service_type {
> +    INVALID,
> +    GENERIC,
> +    IPP,
> +};
> +
> +enum16 tunnel_ip_type {
> +    INVALID,
> +    IPv4,
> +};
> +
> +struct TunnelIpInfo {
> +    tunnel_ip_type type;
> +    switch (type) {
> +    case IPv4:
> +	uint8 ipv4[4];
> +    } u;
> +} @ctype(SpiceMsgTunnelIpInfo);
> +
> +channel TunnelChannel : BaseChannel {
> + server:
> +    message {
> +	uint16 max_num_of_sockets;
> +	uint32 max_socket_data_size;
> +    } init = 101;
> +
> +    message {
> +	uint32 service_id;
> +	TunnelIpInfo virtual_ip;
> +    } service_ip_map;
> +
> +    message {
> +	uint16 connection_id;
> +	uint32 service_id;
> +	uint32 tokens;
> +    } socket_open;
> +
> +    message {
> +	uint16 connection_id;
> +    } socket_fin;
> +
> +    message {
> +	uint16 connection_id;
> +    } socket_close;
> +
> +    message {
> +	uint16 connection_id;
> +	uint8 data[] @end;
> +    } socket_data;
> +
> +    message {
> +	uint16 connection_id;
> +    } socket_closed_ack;
> +
> +    message {
> +	uint16 connection_id;
> +	uint32 num_tokens;
> +    } @ctype(SpiceMsgTunnelSocketTokens) socket_token;
> +
> + client:
> +    message {
> +	tunnel_service_type type;
> +	uint32 id;
> +	uint32 group;
> +	uint32 port;
> +	uint8 *name[cstring()] @nocopy;
> +	uint8 *description[cstring()] @nocopy;
> +	switch (type) {
> +	case IPP:
> +	    TunnelIpInfo ip @ctype(SpiceMsgTunnelIpInfo);
> +	} u;
> +    } @ctype(SpiceMsgcTunnelAddGenericService) service_add = 101;
> +
> +    message {
> +	uint32 id;
> +    } @ctype(SpiceMsgcTunnelRemoveService) service_remove;
> +
> +    message {
> +	uint16 connection_id;
> +	uint32 tokens;
> +    } socket_open_ack;
> +
> +    message {
> +	uint16 connection_id;
> +    } socket_open_nack;
> +
> +    message {
> +	uint16 connection_id;
> +    } socket_fin;
> +
> +    message {
> +	uint16 connection_id;
> +    } socket_closed;
> +
> +    message {
> +	uint16 connection_id;
> +    } socket_closed_ack;
> +
> +    message {
> +	uint16 connection_id;
> +	uint8 data[] @end;
> +    } socket_data;
> +
> +    message {
> +	uint16 connection_id;
> +	uint32 num_tokens;
> +    } @ctype(SpiceMsgcTunnelSocketTokens) socket_token;
> +};
> +
> +enum32 vsc_message_type {
> +    Init = 1,
> +    Error,
> +    ReaderAdd,
> +    ReaderRemove,
> +    ATR,
> +    CardRemove,
> +    APDU,
> +    Flush,
> +    FlushComplete
> +};
> +
> +struct VscMessageHeader {
> +    vsc_message_type type;
> +    uint32 reader_id;
> +    uint32 length;
> +} @ctype(VSCMsgHeader);
> +
> +struct VscMessageError {
> +    uint32 code;
> +} @ctype(VSCMsgError);
> +
> +struct VscMessageAPDU {
> +    uint8 data[];
> +} @ctype(VSCMsgAPDU);
> +
> +struct VscMessageATR {
> +    uint8 data[];
> +} @ctype(VSCMsgATR);
> +
> +struct VscMessageReaderAdd {
> +    int8 *reader_name[] @zero_terminated @nonnull @end @nomarshal;
> +} @ctype(VSCMsgReaderAdd);
> +
> +channel SmartcardChannel : BaseChannel {
> + server:
> +    message {
> +	vsc_message_type type;
> +	uint32 reader_id;
> +	uint32 length;
> +	uint8 data[] @end;
> +    } @ctype(SpiceMsgSmartcard) data = 101;
> +
> + client:
> +    message {
> +	VscMessageHeader header;
> +	switch (header.type) {
> +	case ReaderAdd:
> +	    VscMessageReaderAdd add;
> +	case ATR:
> +	case APDU:
> +	    VscMessageATR atr_data;
> +	case Error:
> +	    VscMessageError error;
> +	} u @anon;
> +    } @ctype(SpiceMsgcSmartcard) data = 101;
> +
> +    message {
> +	vsc_message_type type;
> +	uint32 reader_id;
> +	uint32 length;
> +    } @ctype(VSCMsgHeader) header = 101;
> +
> +    message {
> +	uint32 code;
> +    } @ctype(VSCMsgError) error = 101;
> +
> +    message {
> +	uint8 data[];
> +    } @ctype(VSCMsgATR) atr = 101;
> +
> +    message {
> +	int8 reader_name[] @zero_terminated @nonnull;
> +    } @ctype(VSCMsgReaderAdd) reader_add = 101;
> +} @ifdef(USE_SMARTCARD);
> +
> +channel SpicevmcChannel : BaseChannel {
> +server:
> +    Data data = 101;
> +client:
> +    Data data = 101;
> +};
> +
> +channel UsbredirChannel : SpicevmcChannel {
> +};
> +
> +channel PortChannel : SpicevmcChannel {
> + client:
> +    message {
> +	uint8 event;
> +    } event = 201;
> + server:
> +    message {
> +	uint32 name_size;
> +	uint8 *name[name_size] @zero_terminated @marshall @nonnull;
> +	uint8 opened;
> +    } init = 201;
> +    message {
> +	uint8 event;
> +    } event;
> +};
> +
> +channel WebDAVChannel : PortChannel {
> +};
> +
> +protocol Spice {
> +    MainChannel main = 1;
> +    DisplayChannel display;
> +    InputsChannel inputs;
> +    CursorChannel cursor;
> +    PlaybackChannel playback;
> +    RecordChannel record;
> +    TunnelChannel tunnel;
> +    SmartcardChannel smartcard;
> +    UsbredirChannel usbredir;
> +    PortChannel port;
> +    WebDAVChannel webdav;
> +};
> diff --git a/spice1.proto b/spice1.proto
> new file mode 100644
> index 0000000..6adf312
> --- /dev/null
> +++ b/spice1.proto
> @@ -0,0 +1,943 @@
> +/* built in types:
> +   int8, uint8, 16, 32, 64
> +*/
> +
> +typedef fixed28_4 int32 @ctype(SPICE_FIXED28_4);
> +
> +struct Point {
> +    int32 x;
> +    int32 y;
> +};
> +
> +struct Point16 {
> +    int16 x;
> +    int16 y;
> +};
> +
> +struct PointFix {
> +    fixed28_4 x;
> +    fixed28_4 y;
> +};
> +
> +struct Rect {
> +    int32 top;
> +    int32 left;
> +    int32 bottom;
> +    int32 right;
> +};
> +
> +enum32 link_err {
> +    OK,
> +    ERROR,
> +    INVALID_MAGIC,
> +    INVALID_DATA,
> +    VERSION_MISMATCH,
> +    NEED_SECURED,
> +    NEED_UNSECURED,
> +    PERMISSION_DENIED,
> +    BAD_CONNECTION_ID,
> +    CHANNEL_NOT_AVAILABLE
> +};
> +
> +enum32 warn_code {
> +    WARN_GENERAL
> +} @prefix(SPICE_);
> +
> +enum32 info_code {
> +    INFO_GENERAL
> +} @prefix(SPICE_);
> +
> +flags32 migrate_flags {
> +    NEED_FLUSH,
> +    NEED_DATA_TRANSFER
> +} @prefix(SPICE_MIGRATE_);
> +
> +enum32 notify_severity {
> +    INFO,
> +    WARN,
> +    ERROR,
> +};
> +
> +enum32 notify_visibility {
> +    LOW,
> +    MEDIUM,
> +    HIGH,
> +};
> +
> +flags32 mouse_mode {
> +    SERVER,
> +    CLIENT,
> +};
> +
> +enum16 pubkey_type {
> +    INVALID,
> +    RSA,
> +    RSA2,
> +    DSA,
> +    DSA1,
> +    DSA2,
> +    DSA3,
> +    DSA4,
> +    DH,
> +    EC,
> +};
> +
> +message Empty {
> +};
> +
> +message Data {
> +    uint8 data[] @end @ctype(uint8_t);
> +} @nocopy;
> +
> +struct ChannelWait {
> +    uint8 channel_type;
> +    uint8 channel_id;
> +    uint64 message_serial;
> +} @ctype(SpiceWaitForChannel);
> +
> +channel BaseChannel {
> + server:
> +    message {
> +	migrate_flags flags;
> +    } migrate;
> +
> +    Data migrate_data;
> +
> +    message {
> +	uint32 generation;
> +	uint32 window;
> +    } set_ack;
> +
> +    message {
> +	uint32 id;
> +	uint64 timestamp;
> +	uint8 data[] @ctype(uint8_t) @as_ptr(data_len);
> +    } ping;
> +
> +    message {
> +	uint8 wait_count;
> +	ChannelWait wait_list[wait_count] @end;
> +    } wait_for_channels;
> +
> +    message {
> +	uint64 time_stamp;
> +	link_err reason;
> +    } @ctype(SpiceMsgDisconnect) disconnecting;
> +
> +    message {
> +	uint64 time_stamp;
> +	notify_severity severity;
> +	notify_visibility visibilty;
> +	uint32 what; /* error_code/warn_code/info_code */
> +	uint32 message_len;
> +	uint8 message[message_len] @end @nomarshal;
> +	uint8 zero @end @ctype(uint8_t) @nomarshal;
> +    } notify;
> +
> + client:
> +    message {
> +	uint32 generation;
> +    } ack_sync;
> +
> +    Empty ack;
> +
> +    message {
> +	uint32 id;
> +	uint64 timestamp;
> +    } @ctype(SpiceMsgPing) pong;
> +
> +    Empty migrate_flush_mark;
> +
> +    Data migrate_data;
> +
> +    message {
> +	uint64 time_stamp;
> +	link_err reason;
> +    } @ctype(SpiceMsgDisconnect) disconnecting;
> +};
> +
> +struct ChannelId {
> +    uint8 type;
> +    uint8 id;
> +};
> +
> +struct DstInfo {
> +	uint16 port;
> +	uint16 sport;
> +	uint32 host_offset @zero;
> +	uint32 host_size;
> +	pubkey_type pub_key_type @minor(1);
> +	uint32 pub_key_offset @minor(1) @zero;
> +	uint32 pub_key_size @minor(1);
> +	uint8 host_data[host_size] @as_ptr @zero_terminated;
> +	uint8 pub_key_data[pub_key_size] @minor(1) @as_ptr @zero_terminated;
> +} @ctype(SpiceMigrationDstInfo);
> +
> +channel MainChannel : BaseChannel {
> + server:
> +     message {
> +        DstInfo dst_info;
> +    } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
> +
> +    Empty migrate_cancel;
> +
> +    message {
> +	uint32 session_id;
> +	uint32 display_channels_hint;
> +	uint32 supported_mouse_modes;
> +	uint32 current_mouse_mode;
> +	uint32 agent_connected;
> +	uint32 agent_tokens;
> +	uint32 multi_media_time;
> +	uint32 ram_hint;
> +    } init;
> +
> +    message {
> +	uint32 num_of_channels;
> +	ChannelId channels[num_of_channels] @end;
> +    } @ctype(SpiceMsgChannels) channels_list;
> +
> +    message {
> +	mouse_mode supported_modes;
> +	mouse_mode current_mode @unique_flag;
> +    } mouse_mode;
> +
> +    message {
> +	uint32 time;
> +    } @ctype(SpiceMsgMainMultiMediaTime) multi_media_time;
> +
> +    Empty agent_connected;
> +
> +    message {
> +	link_err error_code;
> +    } @ctype(SpiceMsgMainAgentDisconnect) agent_disconnected;
> +
> +    Data agent_data;
> +
> +    message {
> +	uint32 num_tokens;
> +    } @ctype(SpiceMsgMainAgentTokens) agent_token;
> +
> +    message {
> +      uint16 port;
> +      uint16 sport;
> +      uint32 host_offset @zero;
> +      uint32 host_size;
> +      uint32 cert_subject_offset @zero;
> +      uint32 cert_subject_size;
> +      uint8 host_data[host_size] @as_ptr @zero_terminated;
> +      uint8 cert_subject_data[cert_subject_size] @as_ptr @zero_terminated;
> +    } @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host;
> +
> + client:
> +    message {
> +	uint64 cache_size;
> +    } @ctype(SpiceMsgcClientInfo) client_info = 101;
> +
> +    Empty migrate_connected;
> +
> +    Empty migrate_connect_error;
> +
> +    Empty attach_channels;
> +
> +    message {
> +	mouse_mode mode;
> +    } mouse_mode_request;
> +
> +    message {
> +	uint32 num_tokens;
> +    } agent_start;
> +
> +    Data agent_data;
> +
> +    message {
> +        uint32 num_tokens;
> +    } @ctype(SpiceMsgcMainAgentTokens) agent_token;
> +};
> +
> +enum32 clip_type {
> +    NONE,
> +    RECTS
> +};
> +
> +flags32 path_flags { /* TODO: C enum names changes */
> +    BEGIN = 0,
> +    END = 1,
> +    CLOSE = 3,
> +    BEZIER = 4,
> +} @prefix(SPICE_PATH_);
> +
> +enum32 video_codec_type {
> +    MJPEG = 1,
> +};
> +
> +flags32 stream_flags {
> +    TOP_DOWN = 0,
> +};
> +
> +enum32 brush_type {
> +    NONE,
> +    SOLID,
> +    PATTERN,
> +};
> +
> +flags8 mask_flags {
> +    INVERS,
> +};
> +
> +enum8 image_type {
> +    BITMAP,
> +    QUIC,
> +    RESERVED,
> +    LZ_PLT = 100,
> +    LZ_RGB,
> +    GLZ_RGB,
> +    FROM_CACHE,
> +};
> +
> +flags8 image_flags {
> +    CACHE_ME,
> +};
> +
> +enum8 bitmap_fmt {
> +    INVALID,
> +    1BIT_LE,
> +    1BIT_BE,
> +    4BIT_LE,
> +    4BIT_BE,
> +    8BIT /* 8bit indexed mode */,
> +    16BIT, /* 0555 mode */
> +    24BIT /* 3 byte, brg */,
> +    32BIT /* 4 byte, xrgb in little endian format */,
> +    RGBA /* 4 byte, argb in little endian format */
> +};
> +
> +flags8 bitmap_flags {
> +    PAL_CACHE_ME,
> +    PAL_FROM_CACHE,
> +    TOP_DOWN,
> +};
> +
> +enum8 image_scale_mode {
> +    INTERPOLATE,
> +    NEAREST,
> +};
> +
> +flags16 ropd {
> +    INVERS_SRC,
> +    INVERS_BRUSH,
> +    INVERS_DEST,
> +    OP_PUT,
> +    OP_OR,
> +    OP_AND,
> +    OP_XOR,
> +    OP_BLACKNESS,
> +    OP_WHITENESS,
> +    OP_INVERS,
> +    INVERS_RES,
> +};
> +
> +flags8 line_flags {
> +    STYLED = 3,
> +    START_WITH_GAP = 2,
> +};
> +
> +enum8 line_cap {
> +    ROUND,
> +    SQUARE,
> +    BUTT,
> +};
> +
> +enum8 line_join {
> +    ROUND,
> +    BEVEL,
> +    MITER,
> +};
> +
> +flags16 string_flags {
> +    RASTER_A1,
> +    RASTER_A4,
> +    RASTER_A8,
> +    RASTER_TOP_DOWN,
> +};
> +
> +enum8 resource_type {
> +      INVALID,
> +      PIXMAP
> +} @prefix(SPICE_RES_TYPE_);
> +
> +struct ClipRects {
> +    uint32 num_rects;
> +    Rect rects[num_rects] @end;
> +};
> +
> +struct PathSegment {
> +    path_flags flags;
> +    uint32 count;
> +    PointFix points[count] @end;
> +}  @ctype(SpicePathSeg);
> +
> +struct Path {
> +    uint32 segments_size @bytes_count(num_segments);
> +    PathSegment segments[bytes(segments_size, num_segments)] @ptr_array;
> +};
> +
> +struct Clip {
> +    clip_type type;
> +    switch (type) {
> +    case RECTS:
> +        ClipRects *rects @outvar(cliprects);
> +    default:
> +        uint64 data @zero;
> +    } u @anon;
> +};
> +
> +struct DisplayBase {
> +    uint32 surface_id @virtual(0);
> +    Rect box;
> +    Clip clip;
> +} @ctype(SpiceMsgDisplayBase);
> +
> +struct ResourceID {
> +    uint8 type;
> +    uint64 id;
> +};
> +
> +struct WaitForChannel {
> +    uint8 channel_type;
> +    uint8 channel_id;
> +    uint64 message_serial;
> +};
> +
> +struct Palette {
> +    uint64 unique;
> +    uint16 num_ents;
> +    uint32 ents[num_ents] @end;
> +};
> +
> +struct BitmapData {
> +    bitmap_fmt format;
> +    bitmap_flags flags;
> +    uint32 x;
> +    uint32 y;
> +    uint32 stride;
> +    switch (flags) {
> +    case PAL_FROM_CACHE:
> +	uint64 palette_id;
> +    default:
> +	Palette *palette @outvar(bitmap);
> +    } pal @anon;
> +    uint8 *data[image_size(8, stride, y)] @chunk; /* pointer to array, not
> array of pointers as in C */
> +} @ctype(SpiceBitmap);
> +
> +struct BinaryData {
> +    uint32 data_size;
> +    uint8 data[data_size] @nomarshal @chunk;
> +} @ctype(SpiceQUICData);
> +
> +struct LZPLTData {
> +    bitmap_flags flags;
> +    uint32 data_size;
> +    switch (flags) {
> +    case PAL_FROM_CACHE:
> +	uint64 palette_id;
> +    default:
> +	Palette *palette @nonnull @outvar(lzplt);
> +    } pal @anon;
> +    uint8 data[data_size] @nomarshal @chunk;
> +};
> +
> +struct Image {
> +    struct ImageDescriptor {
> +        uint64 id;
> +        image_type type;
> +        image_flags flags;
> +        uint32 width;
> +        uint32 height;
> +    } descriptor;
> +
> +    switch (descriptor.type) {
> +    case BITMAP:
> +        BitmapData bitmap;
> +    case QUIC:
> +        BinaryData quic;
> +    case LZ_RGB:
> +    case GLZ_RGB:
> +        BinaryData lz_rgb;
> +    case LZ_PLT:
> +        LZPLTData lz_plt;
> +    } u;
> +};
> +
> +struct Pattern {
> +    Image *pat @nonnull;
> +    Point pos;
> +};
> +
> +struct Brush {
> +    brush_type type;
> +    switch (type) {
> +    case SOLID:
> +        uint32 color;
> +    case PATTERN:
> +        Pattern pattern;
> +    } u @fixedsize;
> +};
> +
> +struct QMask {
> +    mask_flags flags;
> +    Point pos;
> +    Image *bitmap;
> +};
> +
> +struct LineAttr {
> +    line_flags flags;
> +    line_join join_style @zero;
> +    line_cap end_style @zero;
> +    uint8 style_nseg;
> +    fixed28_4 width @zero;
> +    fixed28_4 miter_limit @zero;
> +    fixed28_4 *style[style_nseg];
> +};
> +
> +struct RasterGlyphA1 {
> +    Point render_pos;
> +    Point glyph_origin;
> +    uint16 width;
> +    uint16 height;
> +    uint8 data[image_size(1, width, height)] @end;
> +} @ctype(SpiceRasterGlyph);
> +
> +struct RasterGlyphA4 {
> +    Point render_pos;
> +    Point glyph_origin;
> +    uint16 width;
> +    uint16 height;
> +    uint8 data[image_size(4, width, height)] @end;
> +} @ctype(SpiceRasterGlyph);
> +
> +struct RasterGlyphA8 {
> +    Point render_pos;
> +    Point glyph_origin;
> +    uint16 width;
> +    uint16 height;
> +    uint8 data[image_size(8, width, height)] @end;
> +} @ctype(SpiceRasterGlyph);
> +
> +struct String {
> +    uint16 length;
> +    string_flags flags; /* Special: Only one of a1/a4/a8 set */
> +    switch (flags) {
> +    case RASTER_A1:
> +	RasterGlyphA1 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
> +    case RASTER_A4:
> +	RasterGlyphA4 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
> +    case RASTER_A8:
> +	RasterGlyphA8 glyphs[length] @ctype(SpiceRasterGlyph) @ptr_array;
> +    } u @anon;
> +};
> +
> +struct StreamDataHeader {
> +	uint32 id;
> +	uint32 multi_media_time;
> +};
> +
> +channel DisplayChannel : BaseChannel {
> + server:
> +    message {
> +	uint32 x_res;
> +	uint32 y_res;
> +	uint32 bits;
> +    } mode = 101;
> +
> +    Empty mark;
> +    Empty reset;
> +
> +    message {
> +	DisplayBase base;
> +	Point src_pos;
> +    } copy_bits;
> +
> +    message {
> +	uint16 count;
> +	ResourceID resources[count] @end;
> +    } @ctype(SpiceResourceList) inval_list;
> +
> +    message {
> +	uint8 wait_count;
> +	WaitForChannel wait_list[wait_count] @end;
> +    } @ctype(SpiceMsgWaitForChannels) inval_all_pixmaps;
> +
> +    message {
> +	uint64 id;
> +    } @ctype(SpiceMsgDisplayInvalOne) inval_palette;
> +
> +    Empty inval_all_palettes;
> +
> +    message {
> +	uint32 surface_id @virtual(0);
> +	uint32 id;
> +	stream_flags flags;
> +	video_codec_type codec_type;
> +	uint64 stamp;
> +	uint32 stream_width;
> +	uint32 stream_height;
> +	uint32 src_width;
> +	uint32 src_height;
> +	Rect dest;
> +	Clip clip;
> +    } stream_create = 122;
> +
> +    message {
> +	StreamDataHeader base;
> +	uint32 data_size;
> +	uint32 pad_size @zero;
> +	uint8 data[data_size] @end  @nomarshal;
> +	/* Ignore: uint8 padding[pad_size] */
> +    } stream_data;
> +
> +    message {
> +	uint32 id;
> +	Clip clip;
> +    } stream_clip;
> +
> +    message {
> +	uint32 id;
> +    } stream_destroy;
> +
> +    Empty stream_destroy_all;
> +
> +    message {
> +	DisplayBase base;
> +	struct Fill {
> +	    Brush brush @outvar(brush);
> +	    uint16 rop_descriptor;
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_fill = 302;
> +
> +    message {
> +	DisplayBase base;
> +	struct Opaque {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    Brush brush;
> +	    ropd rop_descriptor;
> +	    image_scale_mode scale_mode;
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_opaque;
> +
> +    message {
> +	DisplayBase base;
> +	struct Copy {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    ropd rop_descriptor;
> +	    image_scale_mode scale_mode;
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_copy;
> +
> +    message {
> +	DisplayBase base;
> +	struct Blend {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    ropd rop_descriptor;
> +	    image_scale_mode scale_mode;
> +	    QMask mask @outvar(mask);
> +	} @ctype(SpiceCopy) data;
> +    } draw_blend;
> +
> +    message {
> +	DisplayBase base;
> +	struct Blackness {
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_blackness;
> +
> +    message {
> +	DisplayBase base;
> +	struct Whiteness {
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_whiteness;
> +
> +    message {
> +	DisplayBase base;
> +	struct Invers {
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_invers;
> +
> +    message {
> +	DisplayBase base;
> +	struct Rop3 {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    Brush brush;
> +	    uint8 rop3;
> +	    image_scale_mode scale_mode;
> +	    QMask mask @outvar(mask);
> +	} data;
> +    } draw_rop3;
> +
> +    message {
> +	DisplayBase base;
> +	struct Stroke {
> +	    Path *path;
> +	    LineAttr attr;
> +	    Brush brush;
> +	    uint16 fore_mode;
> +	    uint16 back_mode;
> +	} data;
> +    } draw_stroke;
> +
> +    message {
> +	DisplayBase base;
> +	struct Text {
> +	    String *str;
> +	    Rect back_area;
> +	    Brush fore_brush @outvar(fore_brush);
> +	    Brush back_brush @outvar(back_brush);
> +	    uint16 fore_mode;
> +	    uint16 back_mode;
> +	} data;
> +    } draw_text;
> +
> +    message {
> +	DisplayBase base;
> +	struct Transparent {
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	    uint32 src_color;
> +	    uint32 true_color;
> +	} data;
> +    } draw_transparent;
> +
> +    message {
> +	DisplayBase base;
> +	struct AlphaBlend {
> +	    int8 alpha_flags @virtual(0);
> +	    uint8 alpha;
> +	    Image *src_bitmap;
> +	    Rect src_area;
> +	} data;
> +    } draw_alpha_blend;
> +
> + client:
> +    message {
> +	uint8 pixmap_cache_id;
> +	int64 pixmap_cache_size; //in pixels
> +	uint8 glz_dictionary_id;
> +	int32 glz_dictionary_window_size;  // in pixels
> +    } init = 101;
> +};
> +
> +flags32 keyboard_modifier_flags {
> +    SCROLL_LOCK,
> +    NUM_LOCK,
> +    CAPS_LOCK
> +};
> +
> +enum32 mouse_button {
> +    INVALID,
> +    LEFT,
> +    MIDDLE,
> +    RIGHT,
> +    UP,
> +    DOWN,
> +};
> +
> +flags32 mouse_button_mask {
> +    LEFT,
> +    MIDDLE,
> +    RIGHT
> +};
> +
> +channel InputsChannel : BaseChannel {
> + client:
> +    message {
> +	uint32 code;
> +    } @ctype(SpiceMsgcKeyDown) key_down = 101;
> +
> +    message {
> +	uint32 code;
> +    } @ctype(SpiceMsgcKeyUp) key_up;
> +
> +    message {
> +	keyboard_modifier_flags modifiers;
> +    } @ctype(SpiceMsgcKeyModifiers) key_modifiers;
> +
> +    message {
> +	int32 dx;
> +	int32 dy;
> +	mouse_button_mask buttons_state;
> +    } @ctype(SpiceMsgcMouseMotion) mouse_motion = 111;
> +
> +    message {
> +	uint32 x;
> +	uint32 y;
> +	mouse_button_mask buttons_state;
> +	uint8 display_id;
> +    } @ctype(SpiceMsgcMousePosition) mouse_position;
> +
> +    message {
> +	mouse_button button;
> +	mouse_button_mask buttons_state;
> +    } @ctype(SpiceMsgcMousePress) mouse_press;
> +
> +    message {
> +	mouse_button button;
> +	mouse_button_mask buttons_state;
> +    } @ctype(SpiceMsgcMouseRelease) mouse_release;
> +
> + server:
> +    message {
> +	keyboard_modifier_flags keyboard_modifiers;
> +    } init = 101;
> +
> +    message {
> +	keyboard_modifier_flags modifiers;
> +    } key_modifiers;
> +
> +    Empty mouse_motion_ack = 111;
> +};
> +
> +enum16 cursor_type {
> +    ALPHA,
> +    MONO,
> +    COLOR4,
> +    COLOR8,
> +    COLOR16,
> +    COLOR24,
> +    COLOR32,
> +};
> +
> +flags32 cursor_flags {
> +    NONE, /* Means no cursor */
> +    CACHE_ME,
> +    FROM_CACHE,
> +};
> +
> +struct CursorHeader {
> +    uint64 unique;
> +    cursor_type type;
> +    uint16 width;
> +    uint16 height;
> +    uint16 hot_spot_x;
> +    uint16 hot_spot_y;
> +};
> +
> +struct Cursor {
> +    cursor_flags flags;
> +    CursorHeader header;
> +    uint8 data[] @as_ptr(data_size);
> +};
> +
> +channel CursorChannel : BaseChannel {
> + server:
> +    message {
> +	Point16 position;
> +	uint16 trail_length;
> +	uint16 trail_frequency;
> +	uint8 visible;
> +	Cursor cursor;
> +    } init = 101;
> +
> +    Empty reset;
> +
> +    message {
> +	Point16 position;
> +	uint8 visible;
> +	Cursor cursor;
> +    } set;
> +
> +    message {
> +	Point16 position;
> +    } move;
> +
> +    Empty hide;
> +
> +    message {
> +	uint16 length;
> +	uint16 frequency;
> +    } trail;
> +
> +    message {
> +	uint64 id;
> +    } @ctype(SpiceMsgDisplayInvalOne) inval_one;
> +
> +    Empty inval_all;
> +};
> +
> +enum32 audio_data_mode {
> +    INVALID,
> +    RAW,
> +    CELT_0_5_1,
> +    OPUS,
> +};
> +
> +enum32 audio_fmt {
> +    INVALID,
> +    S16,
> +};
> +
> +channel PlaybackChannel : BaseChannel {
> + server:
> +    message {
> +	uint32 time;
> +	uint8 data[] @as_ptr(data_size);
> +    } @ctype(SpiceMsgPlaybackPacket) data = 101;
> +
> +    message {
> +	uint32 time;
> +	audio_data_mode mode;
> +	uint8 data[] @as_ptr(data_size);
> +    } mode;
> +
> +    message {
> +       uint32 channels;
> +       audio_fmt format;
> +       uint32 frequency;
> +       uint32 time;
> +    } start;
> +
> +    Empty stop;
> +};
> +
> +channel RecordChannel : BaseChannel {
> + server:
> +    message {
> +	uint32 channels;
> +	audio_fmt format;
> +	uint32 frequency;
> +    } start = 101;
> +
> +    Empty stop;
> + client:
> +    message {
> +	uint32 time;
> +	uint8 data[] @nomarshal @as_ptr(data_size);
> +    } @ctype(SpiceMsgcRecordPacket) data = 101;
> +
> +    message {
> +	uint32 time;
> +	audio_data_mode mode;
> +	uint8 data[] @as_ptr(data_size);
> +    } mode;
> +
> +    message {
> +	uint32 time;
> +    } start_mark;
> +};
> +
> +protocol Spice {
> +    MainChannel main = 1;
> +    DisplayChannel display;
> +    InputsChannel inputs;
> +    CursorChannel cursor;
> +    PlaybackChannel playback;
> +    RecordChannel record;
> +};
> diff --git a/spice_codegen.py b/spice_codegen.py
> new file mode 100755
> index 0000000..569cccc
> --- /dev/null
> +++ b/spice_codegen.py
> @@ -0,0 +1,275 @@
> +#!/usr/bin/env python
> +
> +import os
> +import sys
> +from optparse import OptionParser
> +import traceback
> +from python_modules import spice_parser
> +from python_modules import ptypes
> +from python_modules import codegen
> +from python_modules import demarshal
> +from python_modules import marshal
> +import six
> +
> +def write_channel_enums(writer, channel, client, describe):
> +    messages = list(filter(lambda m : m.channel == channel, \
> +                               channel.client_messages if client else
> channel.server_messages))
> +    if len(messages) == 0:
> +        return
> +    if client:
> +        prefix = [ "MSGC" ]
> +    else:
> +        prefix = [ "MSG" ]
> +    if channel.member_name:
> +        prefix.append(channel.member_name.upper())
> +    if not describe:
> +        writer.begin_block("enum")
> +    else:
> +        writer.begin_block("static const value_string %s_vs[] = " %
> (codegen.prefix_underscore_lower(*[x.lower() for x in prefix])))
> +    i = 0
> +    prefix.append(None) # To be replaced with name
> +    for m in messages:
> +        prefix[-1] = m.name.upper()
> +        enum = codegen.prefix_underscore_upper(*prefix)
> +        if describe:
> +            writer.writeln("{ %s, \"%s %s\" }," % (enum, "Client" if client
> else "Server", m.name.upper()))
> +        else:
> +            if m.value == i:
> +                writer.writeln("%s," % enum)
> +                i = i + 1
> +            else:
> +                writer.writeln("%s = %s," % (enum, m.value))
> +                i = m.value + 1
> +    if describe:
> +        writer.writeln("{ 0, NULL }");
> +    else:
> +        if channel.member_name:
> +            prefix[-1] = prefix[-2]
> +            prefix[-2] = "END"
> +            writer.newline()
> +            writer.writeln("%s" %
> (codegen.prefix_underscore_upper(*prefix)))
> +    writer.end_block(semicolon=True)
> +    writer.newline()
> +
> +def write_channel_type_enum(writer, describe=False):
> +    i = 0
> +    if describe:
> +        writer.begin_block("static const value_string channel_types_vs[] =")
> +    else:
> +        writer.begin_block("enum")
> +    for c in proto.channels:
> +        enum = codegen.prefix_underscore_upper("CHANNEL", c.name.upper())
> +        if describe:
> +            writer.writeln("{ %s, \"%s\" }," % (enum, c.name.upper()))
> +        else:
> +            if c.value == i:
> +                writer.writeln("%s," % enum)
> +                i = i + 1
> +            else:
> +                writer.writeln("%s = %s," % (enum, c.value))
> +                i = c.value + 1
> +    writer.newline()
> +    if describe:
> +        writer.writeln("{ 0, NULL }")
> +    else:
> +        writer.writeln("SPICE_END_CHANNEL")
> +    writer.end_block(semicolon=True)
> +    writer.newline()
> +
> +
> +def write_enums(writer, describe=False):
> +    writer.writeln("#ifndef _H_SPICE_ENUMS")
> +    writer.writeln("#define _H_SPICE_ENUMS")
> +    writer.newline()
> +
> +    # Define enums
> +    for t in ptypes.get_named_types():
> +        if isinstance(t, ptypes.EnumBaseType):
> +            t.c_define(writer)
> +            if describe:
> +                t.c_describe(writer)
> +
> +    write_channel_type_enum(writer)
> +    if (describe):
> +        write_channel_type_enum(writer, True)
> +
> +    for c in ptypes.get_named_types():
> +        if not isinstance(c, ptypes.ChannelType):
> +            continue
> +        write_channel_enums(writer, c, False, False)
> +        if describe:
> +            write_channel_enums(writer, c, False, describe)
> +        write_channel_enums(writer, c, True, False)
> +        if describe:
> +            write_channel_enums(writer, c, True, describe)
> +
> +    writer.writeln("#endif /* _H_SPICE_ENUMS */")
> +
> +parser = OptionParser(usage="usage: %prog [options] <protocol_file>
> <destination file>")
> +parser.add_option("-e", "--generate-enums",
> +                  action="store_true", dest="generate_enums", default=False,
> +                  help="Generate enums")
> +parser.add_option("-w", "--generate-wireshark-dissector",
> +                  action="store_true", dest="generate_dissector",
> default=False,
> +                  help="Generate Wireshark dissector definitions")
> +parser.add_option("-d", "--generate-demarshallers",
> +                  action="store_true", dest="generate_demarshallers",
> default=False,
> +                  help="Generate demarshallers")
> +parser.add_option("-m", "--generate-marshallers",
> +                  action="store_true", dest="generate_marshallers",
> default=False,
> +                  help="Generate message marshallers")
> +parser.add_option("-P", "--private-marshallers",
> +                  action="store_true", dest="private_marshallers",
> default=False,
> +                  help="Generate private message marshallers")
> +parser.add_option("-M", "--generate-struct-marshaller",
> +                  action="append", dest="struct_marshallers",
> +                  help="Generate struct marshallers")
> +parser.add_option("-a", "--assert-on-error",
> +                  action="store_true", dest="assert_on_error",
> default=False,
> +                  help="Assert on error")
> +parser.add_option("-H", "--header",
> +                  action="store_true", dest="header", default=False,
> +                  help="Generate header")
> +parser.add_option("-p", "--print-error",
> +                  action="store_true", dest="print_error", default=False,
> +                  help="Print errors")
> +parser.add_option("-s", "--server",
> +                  action="store_true", dest="server", default=False,
> +                  help="Print errors")
> +parser.add_option("-c", "--client",
> +                  action="store_true", dest="client", default=False,
> +                  help="Print errors")
> +parser.add_option("-k", "--keep-identical-file",
> +                  action="store_true", dest="keep_identical_file",
> default=False,
> +                  help="Print errors")
> +parser.add_option("-i", "--include",
> +                  action="append", dest="includes", metavar="FILE",
> +                  help="Include FILE in generated code")
> +parser.add_option("--prefix", dest="prefix",
> +                  help="set public symbol prefix", default="")
> +parser.add_option("--ptrsize", dest="ptrsize",
> +                  help="set default pointer size", default="4")
> +
> +(options, args) = parser.parse_args()
> +
> +if len(args) == 0:
> +    parser.error("No protocol file specified")
> +
> +if len(args) == 1:
> +    parser.error("No destination file specified")
> +
> +ptypes.default_pointer_size = int(options.ptrsize)
> +
> +proto_file = args[0]
> +dest_file = args[1]
> +proto = spice_parser.parse(proto_file)
> +
> +if proto == None:
> +    exit(1)
> +
> +codegen.set_prefix(proto.name)
> +writer = codegen.CodeWriter()
> +writer.header = codegen.CodeWriter()
> +writer.set_option("source", os.path.basename(proto_file))
> +
> +license = """/*
> +  Copyright (C) 2013 Red Hat, Inc.
> +
> +  This library is free software; you can redistribute it and/or
> +  modify it under the terms of the GNU Lesser General Public
> +  License as published by the Free Software Foundation; either
> +  version 2.1 of the License, or (at your option) any later version.
> +
> +  This library is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +  Lesser General Public License for more details.
> +
> +  You should have received a copy of the GNU Lesser General Public
> +  License along with this library; if not, see
> <http://www.gnu.org/licenses/>.
> +*/
> +
> +"""
> +
> +writer.public_prefix = options.prefix
> +
> +writer.writeln("/* this is a file autogenerated by spice_codegen.py */")
> +writer.write(license)
> +writer.header.writeln("/* this is a file autogenerated by spice_codegen.py
> */")
> +writer.header.write(license)
> +if not options.header and not options.generate_enums:
> +    writer.writeln("#ifdef HAVE_CONFIG_H")
> +    writer.writeln("#include <config.h>")
> +    writer.writeln("#endif")
> +
> +if options.assert_on_error:
> +    writer.set_option("assert_on_error")
> +
> +if options.print_error:
> +    writer.set_option("print_error")
> +
> +if options.includes:
> +    for i in options.includes:
> +        writer.header.writeln('#include <%s>' % i)
> +        writer.writeln('#include <%s>' % i)
> +
> +if options.generate_enums or options.generate_dissector:
> +    write_enums(writer, options.generate_dissector)
> +
> +if options.generate_demarshallers:
> +    if not options.server and not options.client:
> +        print >> sys.stderr, "Must specify client and/or server"
> +        sys.exit(1)
> +    demarshal.write_includes(writer)
> +
> +    if options.server:
> +        demarshal.write_protocol_parser(writer, proto, False)
> +    if options.client:
> +        demarshal.write_protocol_parser(writer, proto, True)
> +
> +if options.generate_marshallers or (options.struct_marshallers and
> len(options.struct_marshallers) > 0):
> +    marshal.write_includes(writer)
> +
> +if options.generate_marshallers:
> +    if not options.server and not options.client:
> +        print >> sys.stderr, "Must specify client and/or server"
> +        sys.exit(1)
> +    if options.server:
> +        marshal.write_protocol_marshaller(writer, proto, False,
> options.private_marshallers)
> +    if options.client:
> +        marshal.write_protocol_marshaller(writer, proto, True,
> options.private_marshallers)
> +
> +if options.struct_marshallers:
> +    for structname in options.struct_marshallers:
> +        t = ptypes.lookup_type(structname)
> +        marshal.write_marshal_ptr_function(writer, t, False)
> +
> +if options.generate_marshallers or (options.struct_marshallers and
> len(options.struct_marshallers) > 0):
> +    marshal.write_trailer(writer)
> +
> +if options.header:
> +    content = writer.header.getvalue()
> +else:
> +    content = writer.getvalue()
> +if options.keep_identical_file:
> +    try:
> +        f = open(dest_file, 'rb')
> +        old_content = f.read()
> +        f.close()
> +
> +        if content == old_content:
> +            six.print_("No changes to %s" % dest_file)
> +            sys.exit(0)
> +
> +    except IOError:
> +        pass
> +
> +f = open(dest_file, 'wb')
> +if six.PY3:
> +    f.write(bytes(content, 'UTF-8'))
> +else:
> +    f.write(content)
> +f.close()
> +
> +six.print_("Wrote %s" % dest_file)
> +sys.exit(0)
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 248f467..10033c0 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -42,21 +42,21 @@ TEST_MARSHALLERS =				\
>  BUILT_SOURCES = $(TEST_MARSHALLERS)
>  
>  MARSHALLERS_DEPS =							\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/__init__.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/codegen.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/demarshal.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/marshal.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/ptypes.py		\
> -	$(CODE_GENERATOR_BASEDIR)/python_modules/spice_parser.py	\
> -	$(CODE_GENERATOR_BASEDIR)/spice_codegen.py			\
> +	$(top_srcdir)/python_modules/__init__.py		\
> +	$(top_srcdir)/python_modules/codegen.py		\
> +	$(top_srcdir)/python_modules/demarshal.py		\
> +	$(top_srcdir)/python_modules/marshal.py		\
> +	$(top_srcdir)/python_modules/ptypes.py		\
> +	$(top_srcdir)/python_modules/spice_parser.py	\
> +	$(top_srcdir)/spice_codegen.py			\
>  	$(NULL)
>  
>  # Note despite being autogenerated these are not part of CLEANFILES, they
>  are
>  # actually a part of EXTRA_DIST, to avoid the need for pyparser by end users
>  generated_test_marshallers.c: $(srcdir)/test-marshallers.proto
>  $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-marshallers --server --include test-marshallers.h $< $@
> >/dev/null
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers
> --server --include test-marshallers.h $< $@ >/dev/null
>  generated_test_marshallers.h: $(srcdir)/test-marshallers.proto
>  $(MARSHALLERS_DEPS)
> -	$(AM_V_GEN)$(PYTHON) $(CODE_GENERATOR_BASEDIR)/spice_codegen.py
> --generate-marshallers --server --include test-marshallers.h -H $< $@
> >/dev/null
> +	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers
> --server --include test-marshallers.h -H $< $@ >/dev/null
>  
>  EXTRA_DIST =				\
>  	$(TEST_MARSHALLERS)		\
> --
> 2.5.0
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
> 


More information about the Spice-devel mailing list