[Spice-devel] [spice-protocol 1/4] build-sys: Import code generation files from spice-common
Pavel Grunt
pgrunt at redhat.com
Mon Jul 20 02:51:11 PDT 2015
Hey Christophe,
there are some mixed spaces & tabs:
Applying: build-sys: Import code generation files from spice-common
/home/pgrunt/RH/Spice/spice-protocol/.git/rebase-apply/patch:3568: space before
tab in indent.
uint64 time_stamp;
/home/pgrunt/RH/Spice/spice-protocol/.git/rebase-apply/patch:4270: space before
tab in indent.
DisplayBase base;
/home/pgrunt/RH/Spice/spice-protocol/.git/rebase-apply/patch:4928: space before
tab in indent.
uint64 time_stamp;
warning: 3 lines add whitespace errors.
Pavel
On Mon, 2015-07-06 at 18:33 +0200, Christophe Fergeau wrote:
> This is in preparation for shipping these files as part of
> spice-protocol, and allow spice-common to stop using spice-protocol as a
> submodule. The reason for moving them there is in order to be able to
> automatically regenerate enums.h when spice.proto changes.
>
> This commit is just a placeholder so that a patch series can be sent.
> The actual commit is a merge commit of a repository created with git
> filter-branch on spice-common to only keep the relevant files:
>
> git filter-branch \
> --prune-empty \
> --index-filter '
> git ls-tree -z -r --name-only --full-tree $GIT_COMMIT \
> | grep -z -v "^python_modules/" \
> | grep -z -v "^spice.proto" \
> | grep -z -v "^spice1.proto" \
> | grep -z -v "^spice_codegen.py" \
> | xargs -0 -r git rm -f --cached -r
> ' \
> -- \
> --all
>
> git filter-branch --force \
> --prune-empty
> --index-filter '
> git rm -f spice-protocol --cached --ignore-unmatch
> ' \
> -- \
> --all
> ---
> python_modules/Makefile.am | 16 +
> python_modules/__init__.py | 0
> python_modules/codegen.py | 378 +++++++++++
> python_modules/demarshal.py | 1270 ++++++++++++++++++++++++++++++++++++
> python_modules/marshal.py | 427 ++++++++++++
> python_modules/ptypes.py | 1061 ++++++++++++++++++++++++++++++
> python_modules/spice_parser.py | 162 +++++
> spice.proto | 1388
> ++++++++++++++++++++++++++++++++++++++++
> spice1.proto | 943 +++++++++++++++++++++++++++
> 9 files changed, 5645 insertions(+)
> create mode 100644 python_modules/Makefile.am
> create mode 100644 python_modules/__init__.py
> create mode 100644 python_modules/codegen.py
> create mode 100644 python_modules/demarshal.py
> create mode 100644 python_modules/marshal.py
> create mode 100644 python_modules/ptypes.py
> create mode 100644 python_modules/spice_parser.py
> create mode 100644 spice.proto
> create mode 100644 spice1.proto
>
> 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
> diff --git a/python_modules/codegen.py b/python_modules/codegen.py
> new file mode 100644
> index 0000000..f324498
> --- /dev/null
> +++ b/python_modules/codegen.py
> @@ -0,0 +1,378 @@
> +
> +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
> +
> + 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.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:
> + for i in range(self.indentation):
> + self.out.write(u" ")
> + 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.indenttation = 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("uint32_t", index)
> + return index
> +
> + def push_index(self):
> + 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..b77b910
> --- /dev/null
> +++ b/python_modules/marshal.py
> @@ -0,0 +1,427 @@
> +
> +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:
> + for m in channel.client_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
> + else:
> + for m in channel.server_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..d031d09
> --- /dev/null
> +++ b/python_modules/ptypes.py
> @@ -0,0 +1,1061 @@
> +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"]
> +
> +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):
> + 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
> + for attr in attribute_list:
> + self.attributes[attr[0][1:]] = attr[1:]
> +
> + 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
> +
> + for attr in attribute_list:
> + self.attributes[attr[0][1:]] = attr[1:]
> +
> + 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
> +
> + for attr in attribute_list:
> + self.attributes[attr[0][1:]] = attr[1:]
> +
> + 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):
> + 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
> + for attr in attribute_list:
> + self.attributes[attr[0][1:]] = attr[1:]
> +
> + def resolve(self, container):
> + self.container = container
> + self.member_type = self.member_type.resolve()
> + self.member_type.register()
> + if self.has_attr("ptr32") and self.member_type.is_pointer():
> + self.member_type.set_ptr_size(4)
> + 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
> + for attr in attribute_list:
> + self.attributes[attr[0][1:]] = attr[1:]
> +
> + 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
> + for attr in attribute_list:
> + self.attributes[attr[0][1:]] = attr[1:]
> +
> + 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
> + for attr in attribute_list:
> + self.attributes[attr[0][1:]] = attr[1:]
> +
> + 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
> + for attr in attribute_list:
> + self.attributes[attr[0][1:]] = attr[1:]
> +
> + 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..d60bb10
> --- /dev/null
> +++ b/python_modules/spice_parser.py
> @@ -0,0 +1,162 @@
> +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 six
> +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
> + channel_ = Keyword("channel")
> + 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..2889802
> --- /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..67eb0e6
> --- /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;
> +};
More information about the Spice-devel
mailing list