mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice-common
synced 2025-12-26 14:18:36 +00:00
This option was used in protocol 1 to generate 64 bit pointers. A pointer in the protocol is an offset in the current message. This allows the possibility to have messages with pointers with more than 4GB. This feature was removed and not used in protocol 2. The reason this feature was correctly removed in protocol 2 is that having 64 bit pointers in the protocol would require messages larger than 4GB which would cause: - huge latency as a single message would take more than 4 seconds to be send in a 10Gb connection; - huge memory requirements. Signed-off-by: Frediano Ziglio <fziglio@redhat.com> Acked-by: Christophe Fergeau <cfergeau@redhat.com>
443 lines
17 KiB
Python
443 lines
17 KiB
Python
|
|
from . import ptypes
|
|
from . import codegen
|
|
import re
|
|
|
|
def write_includes(writer):
|
|
writer.header.writeln("#include <spice/protocol.h>")
|
|
writer.header.writeln('#include "common/marshaller.h"')
|
|
writer.header.newline()
|
|
if writer.header.has_option("dest_file"):
|
|
src = writer.header.options["dest_file"]
|
|
else:
|
|
src = "generated_headers.h"
|
|
src = re.sub(r'(?i)[^a-z0-9]+', '_', src)
|
|
src = src.upper()
|
|
if src.endswith("_H"):
|
|
src = "_H_"+src[:-2]
|
|
writer.header.writeln("#ifndef %s" % src)
|
|
writer.header.writeln("#define %s" % src)
|
|
writer.header.newline()
|
|
writer.header.writeln("SPICE_BEGIN_DECLS")
|
|
writer.header.newline()
|
|
|
|
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)
|
|
if bpp == 8:
|
|
return "((uint64_t) %s * %s)" % (width_v, rows_v)
|
|
elif bpp == 1:
|
|
return "((((uint64_t) %s + 7U) / 8U ) * %s)" % (width_v, rows_v)
|
|
else:
|
|
return "((((uint64_t) %s * %s + 7U) / 8U ) * %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)"
|
|
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)
|
|
|
|
first = False
|
|
|
|
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()))
|
|
|
|
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, private):
|
|
if message.has_attr("ifdef"):
|
|
writer.ifdef(message.attributes["ifdef"][0])
|
|
writer.header.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 private:
|
|
message_name = message.c_name()
|
|
if (not message_name.startswith("msgc_")):
|
|
#small bug above, checks for startswith("msg") which
|
|
#matches "msgc" and appends "msg_" if this fails causing
|
|
#inconsistencies
|
|
message_name = "msg_" + message_name
|
|
writer.header.writeln("void (*" + message_name + ")(SpiceMarshaller *m, %s *msg" % message.c_type() + names_args + ");")
|
|
else:
|
|
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.header.endif(message.attributes["ifdef"][0])
|
|
|
|
writer.newline()
|
|
return function_name
|
|
|
|
def write_protocol_marshaller(writer, proto, is_server, private_marshallers):
|
|
functions = {}
|
|
if private_marshallers:
|
|
writer.header.begin_block("typedef struct")
|
|
for c in proto.channels:
|
|
channel = c.channel_type
|
|
if channel.has_attr("ifdef"):
|
|
writer.ifdef(channel.attributes["ifdef"][0])
|
|
writer.header.ifdef(channel.attributes["ifdef"][0])
|
|
if is_server:
|
|
messages = channel.client_messages
|
|
else:
|
|
messages = channel.server_messages
|
|
for m in messages:
|
|
message = m.message_type
|
|
f = write_message_marshaller(writer, message, 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:
|
|
writer.header.end_block(newline=False)
|
|
writer.header.writeln(" SpiceMessageMarshallers;")
|
|
writer.header.newline()
|
|
writer.header.statement("SpiceMessageMarshallers *spice_message_marshallers_get" + writer.public_prefix+"(void)")
|
|
writer.header.newline()
|
|
|
|
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.newline()
|
|
writer.header.writeln("SPICE_END_DECLS")
|
|
writer.header.newline()
|
|
|
|
writer.header.writeln("#endif")
|