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; > +}; _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel