special case 'intermixed variable and fixed size fields': fixed reply side, needs testing

This commit is contained in:
Christoph Reimann 2010-08-08 21:25:13 +02:00
parent 77b594f958
commit fe0e32b5fa

View File

@ -292,12 +292,10 @@ def _c_type_setup(self, name, postfix):
self.c_aux_unchecked_name = _n(name + ('aux', 'unchecked'))
self.c_serialize_name = _n(name + ('serialize',))
self.c_unserialize_name = _n(name + ('unserialize',))
self.c_unpack_name = _n(name + ('unpack',))
self.c_sizeof_name = _n(name + ('sizeof',))
# if hasattr(self, 'reply'):
# if self.reply is not None:
# self.c_serialize_name = _n(name + ('reply', 'serialize'))
# self.c_unserialize_name = _n(name + ('reply', 'unserialize'))
# indicates rare structs where variable size fields are followed fixed size fields
# special case: structs where variable size fields are followed by fixed size fields
self.var_followed_by_fixed_fields = False
# whether a request or reply has a switch field
@ -323,18 +321,19 @@ def _c_type_setup(self, name, postfix):
# if field.type.member.need_serialize:
# self.need_serialize = True
self.need_sizeof = True
# print "-> variable size list elements: %s (%s %s)" % (self.c_type, field.field_type, field.field_name)
field.c_field_type = _t(field.field_type)
field.c_field_const_type = ('' if field.type.nmemb == 1 else 'const ') + field.c_field_type
field.c_field_name = _cpp(field.field_name)
field.c_subscript = '[%d]' % field.type.nmemb if (field.type.nmemb > 1) else ''
field.c_pointer = ' ' if field.type.nmemb == 1 else '*'
# correct the c_pointer field for variable size non-list types
if not field.type.fixed_size() and field.c_pointer == ' ':
field.c_pointer = '*'
if field.type.is_list and not field.type.member.fixed_size():
field.c_pointer = '*'
if field.type.is_switch:
field.c_pointer = '*'
field.c_field_const_type = 'const ' + field.c_field_type
@ -369,16 +368,10 @@ def _c_type_setup(self, name, postfix):
prev_varsized_field = field
prev_varsized_offset = 0
# very special case - if self.var_followed_by_fixed_fields==True,
# we have to generate accessor functions also for fixed size fields
# now there might a naming conflict if the length field ends with _length
if self.var_followed_by_fixed_fields:
if field.type.is_list:
if field.type.expr.lenfield_name is not None:
full_lenfield_name = _n(name + (field.type.expr.lenfield_name,))
if full_lenfield_name == field.c_length_name:
field.c_length_name += '_'
if field.type.fixed_size():
field.prev_varsized_field = None
if self.need_serialize:
# when _unserialize() is wanted, create _sizeof() as well for consistency reasons
self.need_sizeof = True
@ -390,16 +383,18 @@ def _c_type_setup(self, name, postfix):
finished_switch.append(self.c_type)
# special: switch C structs get pointer fields for variable-sized members
_c_complex(self)
# FIXME: declare switch (un)packing functions
_c_accessors(self, name, name)
# FIXME: what about accessors & switch (un)packing functions
# _c_iterator() does not seem to be the right place to do it,
# as the accessors would need any parameters to pass to _unpack() as well
# FIXME - in case of request/reply, serialize() is not always needed
if not self.is_bitcase:
if self.need_serialize:
if self.c_serialize_name not in finished_serializers:
finished_serializers.append(self.c_serialize_name)
_c_serialize(self)
_c_unserialize(self)
# _unpack() and _unserialize() are only needed for special cases
if self.is_switch or self.var_followed_by_fixed_fields:
_c_unserialize(self)
if self.need_sizeof:
if self.c_sizeof_name not in finished_sizeof:
finished_sizeof.append(self.c_sizeof_name)
@ -500,7 +495,7 @@ def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'):
params = []
if 'serialize' == context:
params.append(('void', '**', buffer_var))
elif context in ('unserialize', 'sizeof'):
elif context in ('unserialize', 'unpack', 'sizeof'):
params.append(('const void', '*', buffer_var))
# look for special cases
@ -526,10 +521,10 @@ def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'):
if 'serialize' == context:
add_param(params, ('const %s' % self.c_type, '*', aux_var))
elif 'unserialize' == context:
if self.is_switch:
add_param(params, ('%s' % self.c_type, '*', aux_var))
else:
add_param(params, ('%s' % self.c_type, '**', aux_var))
add_param(params, ('%s' % self.c_type, '**', aux_var))
elif 'unpack' == context:
add_param(params, ('%s' % self.c_type, '*', aux_var))
if not self.is_switch and 'serialize' == context:
for p in param_fields:
if not p.type.fixed_size():
@ -573,8 +568,6 @@ def _c_field_mapping(context, complex_type, prefix):
fname = "%s%s" % (prefix_str, f.c_field_name)
if fields.has_key(f.field_name):
continue
# FIXME
raise Exception("field name %s has been registered before" % f.field_name)
fields[f.field_name] = (fname, f)
if f.type.is_container:
@ -604,7 +597,7 @@ def _c_serialize_helper_insert_padding(context, code_lines, space):
code_lines.append('%s xcb_parts[xcb_parts_idx].iov_base = xcb_pad0;' % space)
code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len = xcb_pad;' % space)
code_lines.append('%s xcb_parts_idx++;' % space)
elif 'unserialize' == context:
elif context in ('unserialize', 'unpack', 'sizeof'):
code_lines.append('%s xcb_tmp += xcb_pad;' % space)
code_lines.append('%s xcb_buffer_len += xcb_pad;' % space)
@ -626,6 +619,7 @@ def _c_serialize_helper_switch(context, self, complex_name,
for b in self.bitcases:
bitcase_expr = _c_accessor_get_expr(b.type.expr, prefix_str)
code_lines.append(' if(%s & %s) {' % (switch_expr, bitcase_expr))
code_lines.append(' printf("entering bitcase section for %%%%d...\\n", %s);' % bitcase_expr)
b_prefix = switch_prefix
if b.type.has_name:
b_prefix = switch_prefix + [(b.c_field_name, '.', b.type)]
@ -638,7 +632,7 @@ def _c_serialize_helper_switch(context, self, complex_name,
if 'serialize' == context:
count += _c_serialize_helper_insert_padding(context, code_lines, space)
if 'unserialize' == context:
if context in ('unserialize', 'unpack', 'sizeof'):
# padding
code_lines.append('%s xcb_pad = -xcb_block_len & 3;' % space)
code_lines.append('%s xcb_buffer_len += xcb_block_len + xcb_pad;' % space)
@ -651,6 +645,7 @@ def _c_serialize_helper_switch_field(context, self, field, c_switch_variable, pr
param_fields, wire_fields, params = get_serialize_params(context, self)
args = get_expr_fields(field.type)
field_mapping = _c_field_mapping(context, self, prefix)
prefix_str, lenfield_prefix = _c_serialize_helper_prefix(prefix)
# determine which params to pass to _unserialize() and their prefixes
switch_len_fields = resolve_fields(self, field.type)
@ -662,10 +657,14 @@ def _c_serialize_helper_switch_field(context, self, field, c_switch_variable, pr
c_field_names += "%s, " % field_mapping[a.c_field_name][0]
for a in args:
c_field_names += "%s, " % field_mapping[a.c_field_name][0]
# switch_field_name = field_mapping[field.field_name][0]
# call _unserialize() to determine the actual size
length = "%s(xcb_tmp, %s&%s)" % (field.type.c_unserialize_name,
c_field_names, c_switch_variable) #switch_field_name)
# call _serialize()/_unpack() to determine the actual size
if 'serialize' == context:
length = "%s(&%s, %s&%s%s)" % (field.type.c_serialize_name, c_switch_variable,
c_field_names, prefix_str, field.c_field_name)
elif context in ('unserialize', 'unpack'):
length = "%s(xcb_tmp, %s&%s%s)" % (field.type.c_unpack_name,
c_field_names, prefix_str, field.c_field_name)
return length
# _c_serialize_helper_switch_field()
@ -704,7 +703,7 @@ def _c_serialize_helper_list_field(context, self, field,
# list with variable-sized elements
if not field.type.member.fixed_size():
length = ''
if context in ('unserialize', 'sizeof'):
if context in ('unserialize', 'sizeof', 'unpack'):
int_i = ' unsigned int i;'
xcb_tmp_len = ' unsigned int xcb_tmp_len;'
if int_i not in temp_vars:
@ -743,7 +742,7 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
length = "sizeof(%s)" % field.c_field_type
if context in ('unserialize', 'sizeof'):
if context in ('unserialize', 'unpack', 'sizeof'):
value = ' %s%s = *(%s *)xcb_tmp;' % (prefix_str, field.c_field_name, field.c_field_type)
if field.type.is_pad and field.type.nmemb > 1:
value = ''
@ -751,7 +750,7 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
code_lines.append('%s %s%s[%d] = *(%s *)xcb_tmp;' %
(space, prefix_str, field.c_field_name, i, field.c_field_type))
length += " * %d" % field.type.nmemb
# FIXME? - lists
if field.type.is_list:
raise Exception('list with fixed number of elemens unhandled in _unserialize()')
elif 'serialize' == context:
@ -791,30 +790,37 @@ def _c_serialize_helper_fields_variable_size(context, self, field,
space, prefix):
prefix_str, lenfield_prefix = _c_serialize_helper_prefix(prefix)
if context in ('unserialize', 'sizeof'):
if context in ('unserialize', 'unpack', 'sizeof'):
value = ''
var_field_name = 'xcb_tmp'
if self.var_followed_by_fixed_fields and 'unserialize' == context:
value = ' %s = xcb_tmp;' % field.c_field_name
temp_vars.append(' %s *%s;' % (field.type.c_type, field.c_field_name))
if 'unpack' == context:
value = ' %s%s = (%s *)xcb_tmp;' % (prefix_str, field.c_field_name, field.c_field_type)
elif 'serialize' == context:
address_of = '&' if (self.is_bitcase and self.has_name) else ''
value = ' xcb_parts[xcb_parts_idx].iov_base = (char *) %s%s%s;' % (address_of, prefix_str, field.c_field_name)
var_field_name = "%s%s%s" % (address_of, prefix_str, field.c_field_name)
value = ' xcb_parts[xcb_parts_idx].iov_base = (char *) %s;' % var_field_name
length = ''
prefix_str, lenfield_prefix = _c_serialize_helper_prefix(prefix)
code_lines.append('%s /* %s */' % (space, field.c_field_name))
if field.type.is_list:
if value != '':
code_lines.append("%s%s" % (space, value))
value = ''
length = _c_serialize_helper_list_field(context, self, field,
code_lines, temp_vars,
space, prefix)
elif field.type.is_switch:
prev = filter(lambda x: x.find('xcb_switch_field'), temp_vars)
var_name = 'xcb_switch_field%d' % len(prev)
temp_vars.append(' %s %s;' % (field.type.c_type, var_name))
length = _c_serialize_helper_switch_field(context, self, field, var_name, prefix)
value = ''
if context == 'serialize':
value = ' xcb_parts[xcb_parts_idx].iov_base = (char *)0;'
length = _c_serialize_helper_switch_field(context, self, field, 'xcb_parts[xcb_parts_idx].iov_base', prefix)
else:
length = "%s(xcb_tmp)" % (field.type.c_sizeof_name)
length = "%s(%s)" % (field.type.c_sizeof_name, var_field_name)
return (value, length)
# _c_serialize_helper_fields_variable_size
@ -866,18 +872,17 @@ def _c_serialize_helper_fields(context, self,
code_lines.append('%s%s' % (space, value))
if field.type.fixed_size() and is_bitcase:
code_lines.append('%s xcb_block_len += %s;' % (space, length))
if context in ('unserialize', 'sizeof'):
if context in ('unserialize', 'unpack', 'sizeof'):
code_lines.append('%s xcb_tmp += %s;' % (space, length))
else:
# padding
if '' != length:
code_lines.append('%s xcb_block_len = %s;' % (space, length))
if (not field.type.fixed_size() and
self.var_followed_by_fixed_fields and
'unserialize' == context):
self.var_followed_by_fixed_fields and 'unserialize' == context):
temp_vars.append(' int %s_len;' % field.c_field_name)
code_lines.append(' %s_len = xcb_block_len;' % field.c_field_name)
if context in ('unserialize', 'sizeof'):
if context in ('unserialize', 'sizeof', 'unpack'):
code_lines.append('%s xcb_tmp += xcb_block_len;' % space)
if 'serialize' == context:
if '' != length:
@ -912,7 +917,7 @@ def _c_serialize_helper(context, complex_type,
# all other data types can be evaluated one field a time
else:
# unserialize & fixed size fields: simply cast the buffer to the respective xcb_out type
if context in ('unserialize', 'sizeof') and not self.var_followed_by_fixed_fields:
if context in ('unserialize', 'unpack', 'sizeof') and not self.var_followed_by_fixed_fields:
code_lines.append('%s xcb_block_len += sizeof(%s);' % (space, self.c_type))
code_lines.append('%s xcb_tmp += xcb_block_len;' % space)
_c_serialize_helper_insert_padding(context, code_lines, space)
@ -1043,7 +1048,13 @@ def _c_unserialize(self):
variable_size_fields = 0
# maximum space required for type definition of function arguments
maxtypelen = 0
param_fields, wire_fields, params = get_serialize_params('unserialize', self)
if self.is_switch:
context = 'unpack'
func_name = self.c_unpack_name
else:
context = 'unserialize'
func_name = self.c_unserialize_name
param_fields, wire_fields, params = get_serialize_params(context, self)
# determine N(variable_fields)
for field in param_fields:
@ -1058,10 +1069,10 @@ def _c_unserialize(self):
for idx, p in enumerate(params):
line = ""
typespec, pointerspec, field_name = p
indent = ' '*(len(self.c_unserialize_name)+2)
indent = ' '*(len(func_name)+2)
# p==0: function declaration
if 0==idx:
line = "%s (" % self.c_unserialize_name
line = "%s (" % func_name
indent = ''
spacing = ' '*(maxtypelen-len(typespec)-len(pointerspec))
line += "%s%s%s %s%s /**< */" % (indent, typespec, spacing, pointerspec, field_name)
@ -1084,7 +1095,7 @@ def _c_unserialize(self):
code_lines = []
temp_vars = []
_c_serialize_helper('unserialize', self,
_c_serialize_helper(context, self,
code_lines, temp_vars)
for t in temp_vars:
_c(t)
@ -1093,9 +1104,21 @@ def _c_unserialize(self):
for l in code_lines:
_c(l)
_c('')
if not self.is_switch:
if self.var_followed_by_fixed_fields:
_c(' free(_aux);')
if 'unserialize' == context:
# allocate memory automatically
_c(' if (NULL == *_aux) {')
_c(' /* allocate memory */')
_c(' *_aux = malloc(xcb_buffer_len);')
_c(' }')
_c('')
_c(' **_aux = xcb_out;')
_c(' xcb_tmp = (char *)++(*_aux);')
for field in param_fields:
if not field.type.fixed_size():
_c(' memcpy(xcb_tmp, %s, %s_len);', field.c_field_name, field.c_field_name)
_c(' xcb_tmp += %s_len;', field.c_field_name)
_c('')
_c(' return xcb_buffer_len;')
_c('}')
@ -1142,25 +1165,31 @@ def _c_sizeof_helper(self):
_c("%s)" % line)
_c('{')
# if self.is_switch: call serialize
param_names = [p[2] for p in params]
if self.is_switch:
# switch: call _unpack()
_c(' %s _aux;', self.c_type)
param_names = [p[2] for p in params]
_c(' return %s(%s, &_aux);', self.c_unpack_name, reduce(lambda x,y: "%s, %s" % (x, y), param_names))
elif self.var_followed_by_fixed_fields:
# special case: call _unserialize()
_c(' %s *_aux = NULL;', self.c_type)
_c(' return %s(%s, &_aux);', self.c_unserialize_name, reduce(lambda x,y: "%s, %s" % (x, y), param_names))
# otherwise: evaluate parameters directly
else:
_c(' char *xcb_tmp = (char *)_buffer;')
if not self.var_followed_by_fixed_fields:
_c(' const %s *_aux = (%s *)_buffer;', self.c_type, self.c_type)
else:
_c(' %s *_aux = malloc(sizeof(%s));', self.c_type, self.c_type)
_c(' unsigned int xcb_buffer_len = 0;')
_c(' unsigned int xcb_block_len = 0;')
_c(' unsigned int xcb_pad = 0;')
code_lines = []
temp_vars = []
_c_serialize_helper('sizeof', self,
code_lines, temp_vars)
_c(' char *xcb_tmp = (char *)_buffer;')
if len(filter(lambda x: x.find('_aux')!=-1, code_lines))>0:
if not self.var_followed_by_fixed_fields:
_c(' const %s *_aux = (%s *)_buffer;', self.c_type, self.c_type)
else:
_c(' %s *_aux = malloc(sizeof(%s));', self.c_type, self.c_type)
_c(' unsigned int xcb_buffer_len = 0;')
_c(' unsigned int xcb_block_len = 0;')
_c(' unsigned int xcb_pad = 0;')
for t in temp_vars:
_c(t)
_c('')
@ -1168,11 +1197,7 @@ def _c_sizeof_helper(self):
for l in code_lines:
_c(l)
_c('')
if not self.is_switch:
if self.var_followed_by_fixed_fields:
_c(' free(_aux);')
_c(' return xcb_buffer_len;')
_c('}')
# _c_sizeof_helper()
@ -1350,7 +1375,7 @@ def _c_accessor_get_expr(expr, prefix='', sep='->'):
prefix += sep
list_name = "%s%s" % (prefix, field.c_field_name)
c_length_func = "%s(%s%s)" % (field.c_length_name, prefix, field.c_field_name)
# FIXME
# FIXME - works only for integers this way!!
c_length_func = _c_accessor_get_expr(field.type.expr, prefix='', sep='')
return 'xcb_sumof(%s, %s)' % (list_name, c_length_func)
elif expr.op != None:
@ -1399,16 +1424,24 @@ def _c_accessors_field(self, field):
_hc(' **')
_hc(' *****************************************************************************/')
_hc(' ')
_hc('%s *', field.c_field_type)
if field.type.is_switch:
return_type = 'void *'
else:
return_type = '%s *' % field.c_field_type
_hc(return_type)
_h('%s (const %s *R /**< */);', field.c_accessor_name, self.c_type)
_c('%s (const %s *R /**< */)', field.c_accessor_name, self.c_type)
_c('{')
if field.prev_varsized_field is None:
_c(' return (%s *) (R + 1);', field.c_field_type)
_c(' return (%s) (R + 1);', return_type)
# note: the special case 'variable fields followed by fixed size fields'
# is not of any consequence here, since the ordering gets
# 'corrected' in the reply function
else:
_c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R'))
_c(' return (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);',
field.c_field_type, field.first_field_after_varsized.type.c_type, field.prev_varsized_offset)
_c(' return (%s) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);',
return_type, field.first_field_after_varsized.type.c_type, field.prev_varsized_offset)
_c('}')
def _c_accessors_list(self, field):
@ -1526,15 +1559,14 @@ def _c_accessors(self, name, base):
'''
Declares the accessor functions for the fields of a structure.
'''
for field in self.fields:
# no accessors for switch -
# switch always needs to be unserialized explicitly
if self.is_switch:
continue
if field.type.is_list and not field.type.fixed_size():
_c_accessors_list(self, field)
elif field.prev_varsized_field != None or not field.type.fixed_size():
_c_accessors_field(self, field)
# no accessors for switch -
# switch always needs to be unpacked explicitly
if not self.is_switch:
for field in self.fields:
if field.type.is_list and not field.type.fixed_size():
_c_accessors_list(self, field)
elif field.prev_varsized_field is not None or not field.type.fixed_size():
_c_accessors_field(self, field)
def c_simple(self, name):
'''
@ -1925,7 +1957,29 @@ def _c_reply(self, name):
spacing1 = ' ' * (len(self.c_cookie_type) - len('xcb_connection_t'))
spacing2 = ' ' * (len(self.c_cookie_type) - len('xcb_generic_error_t'))
spacing3 = ' ' * (len(self.c_reply_name) + 2)
# check if _unserialize() has to be called for any field
def look_for_special_cases(complex_obj):
unserialize_fields = []
# no unserialize call in case of switch
if not complex_obj.is_switch:
for field in complex_obj.fields:
# three cases: 1. field with special case
# 2. container that contains special case field
# 3. list with special case elements
if field.type.var_followed_by_fixed_fields:
unserialize_fields.append(field)
elif field.type.is_container:
unserialize_fields += look_for_special_cases(field.type)
elif field.type.is_list:
if field.type.member.var_followed_by_fixed_fields:
unserialize_fields.append(field)
if field.type.member.is_container:
unserialize_fields += look_for_special_cases(field.type.member)
return unserialize_fields
unserialize_fields = look_for_special_cases(self.reply)
_h('')
_h('/**')
_h(' * Return the reply')
@ -1960,7 +2014,35 @@ def _c_reply(self, name):
_h('%sxcb_generic_error_t%s **e /**< */);', spacing3, spacing2)
_c('%sxcb_generic_error_t%s **e /**< */)', spacing3, spacing2)
_c('{')
_c(' return (%s *) xcb_wait_for_reply(c, cookie.sequence, e);', self.c_reply_type)
if len(unserialize_fields)>0:
# certain variable size fields need to be unserialized explicitly
_c(' %s *reply = (%s *) xcb_wait_for_reply(c, cookie.sequence, e);',
self.c_reply_type, self.c_reply_type)
_c(' int i;')
for field in unserialize_fields:
if field.type.is_list:
_c(' %s %s_iter = %s(reply);', field.c_iterator_type, field.c_field_name, field.c_iterator_name)
_c(' int %s_len = %s(reply);', field.c_field_name, field.c_length_name)
_c(' %s *%s_data;', field.c_field_type, field.c_field_name)
else:
raise Exception('not implemented: call _unserialize() in reply for non-list type %s', field.c_field_type)
# call _unserialize(), using the reply as source and target buffer
_c(' /* special cases: transform parts of the reply to match XCB data structures */')
for field in unserialize_fields:
if field.type.is_list:
_c(' for(i=0; i<%s_len; i++) {', field.c_field_name)
_c(' %s_data = %s_iter.data;', field.c_field_name, field.c_field_name)
_c(' %s((const void *)%s_data, &%s_data);', field.type.c_unserialize_name,
field.c_field_name, field.c_field_name)
_c(' %s(&%s_iter);', field.type.c_next_name, field.c_field_name)
_c(' }')
# return the transformed reply
_c(' return reply;')
else:
_c(' return (%s *) xcb_wait_for_reply(c, cookie.sequence, e);', self.c_reply_type)
_c('}')
def _c_opcode(name, opcode):