summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2016-03-10 15:18:06 +0100
committerMarc-André Lureau <marcandre.lureau@redhat.com>2016-03-10 16:01:36 +0100
commit1cd26b87c10870ea9bfb65b4640d2e318b45aafb (patch)
treed6488289140a7082bea97ea12ab2eaa551f401e1
parentf06f699d7817354a532f091f46044720f9045887 (diff)
downloadspice-common-1cd26b87c10870ea9bfb65b4640d2e318b45aafb.tar.gz
spice-common-1cd26b87c10870ea9bfb65b4640d2e318b45aafb.tar.xz
spice-common-1cd26b87c10870ea9bfb65b4640d2e318b45aafb.zip
Revert "Remove files moved to spice-protocol"
This reverts commit 7665dcf1bb2fa0b16b3d0015b28d7f5912664c3f. Also revert the related build-sys changes to fix the build. 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. *.proto files are required as well, since it generates code that parent modules depend on unconditionnaly. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
-rw-r--r--Makefile.am10
-rw-r--r--common/Makefile.am42
-rw-r--r--configure.ac3
-rw-r--r--python_modules/Makefile.am16
-rw-r--r--python_modules/__init__.py0
-rw-r--r--python_modules/codegen.py380
-rw-r--r--python_modules/demarshal.py1270
-rw-r--r--python_modules/marshal.py420
-rw-r--r--python_modules/ptypes.py1129
-rw-r--r--python_modules/spice_parser.py162
-rw-r--r--spice.proto1388
-rw-r--r--spice1.proto943
-rwxr-xr-xspice_codegen.py275
-rw-r--r--tests/Makefile.am18
14 files changed, 6023 insertions, 33 deletions
diff --git a/Makefile.am b/Makefile.am
index 4d9cf59..63d7956 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,15 @@
NULL =
ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = common tests
+SUBDIRS = python_modules common tests
+
+EXTRA_DIST = \
+ spice_codegen.py \
+ spice.proto \
+ spice1.proto \
+ $(NULL)
+
+DISTCLEANFILES = *.pyc
MAINTAINERCLEANFILES = \
$(srcdir)/INSTALL \
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..2ff985d 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])
SPICE_CHECK_PYTHON_MODULES()
@@ -50,6 +48,7 @@ AC_SUBST(SPICE_COMMON_LIBS)
AC_CONFIG_FILES([
Makefile
common/Makefile
+ python_modules/Makefile
tests/Makefile
])
diff --git a/python_modules/Makefile.am b/python_modules/Makefile.am
new file mode 100644
index 0000000..50e1a71
--- /dev/null
+++ b/python_modules/Makefile.am
@@ -0,0 +1,16 @@
+NULL =
+
+PYTHON_MODULES = \
+ __init__.py \
+ codegen.py \
+ demarshal.py \
+ marshal.py \
+ ptypes.py \
+ spice_parser.py \
+ $(NULL)
+
+EXTRA_DIST = $(PYTHON_MODULES)
+
+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
--- /dev/null
+++ b/python_modules/__init__.py
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..209eafc
--- /dev/null
+++ b/python_modules/demarshal.py
@@ -0,0 +1,1270 @@
+
+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.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..7ab2771
--- /dev/null
+++ b/python_modules/ptypes.py
@@ -0,0 +1,1129 @@
+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
+
+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)
diff --git a/python_modules/spice_parser.py b/python_modules/spice_parser.py
new file mode 100644
index 0000000..97af8b2
--- /dev/null
+++ b/python_modules/spice_parser.py
@@ -0,0 +1,162 @@
+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))
+
+ # 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_ ^
+ 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..4ea1263
--- /dev/null
+++ b/spice.proto
@@ -0,0 +1,1388 @@
+/* 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_compress {
+ 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;
+};
+
+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;
+
+ 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 {
+ uint8 image_compression;
+ } preferred_compression;
+};
+
+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) \