X-Git-Url: http://git.demorecorder.com/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fc_client.py;h=bd502b3dbe5d0d914ae4f1f9a1f9b1959d1c2541;hb=a700eeb502b5fe902e4c93cc707100ec868ce946;hp=db974e09f13464e75be8f36b3291db8b20ab2981;hpb=7e0674e76186ee4491a089350511fc0d22fb3af3;p=free-sw%2Fxcb%2Flibxcb diff --git a/src/c_client.py b/src/c_client.py old mode 100755 new mode 100644 index db974e0..bd502b3 --- a/src/c_client.py +++ b/src/c_client.py @@ -16,25 +16,19 @@ _extension_special_cases = ['XPrint', 'XCMisc', 'BigRequests'] _cplusplus_annoyances = {'class' : '_class', 'new' : '_new', 'delete': '_delete'} +_c_keywords = {'default' : '_default'} -_cardinal_types = ['CARD8', 'uint8_t', - 'CARD16','uint16_t', - 'CARD32','uint32_t', - 'INT8', 'int8_t', - 'INT16', 'int16_t', - 'INT32', 'int32_t', - 'BYTE', - 'BOOL', - 'char', - 'void', - 'float', - 'double'] _hlines = [] _hlevel = 0 _clines = [] _clevel = 0 _ns = None +# global variable to keep track of serializers and switch data types +# due to weird dependencies, I see no way to do this more elegant at the moment +finished_serializers = [] +finished_switch = [] + def _h(fmt, *args): ''' Writes the given line to the header file. @@ -93,6 +87,8 @@ def _cpp(str): ''' if str in _cplusplus_annoyances: return _cplusplus_annoyances[str] + elif str in _c_keywords: + return _c_keywords[str] else: return str @@ -147,6 +143,9 @@ def c_open(self): _ns = self.namespace _ns.c_ext_global_name = _n(_ns.prefix + ('id',)) + # Build the type-name collision avoidance table used by c_enum + build_collision_table() + _h_setlevel(0) _c_setlevel(0) @@ -167,6 +166,7 @@ def c_open(self): _h('') _h('#include "xcb.h"') + _c('#include ') _c('#include ') _c('#include ') _c('#include "xcbext.h"') @@ -176,6 +176,12 @@ def c_open(self): for (n, h) in self.imports: _hc('#include "%s.h"', h) + _h('') + _h('#ifdef __cplusplus') + _h('extern "C" {') + _h('#endif') + + if _ns.is_ext: _h('') _h('#define XCB_%s_MAJOR_VERSION %s', _ns.ext_name.upper(), _ns.major_version) _h('#define XCB_%s_MINOR_VERSION %s', _ns.ext_name.upper(), _ns.minor_version) @@ -193,6 +199,12 @@ def c_close(self): _h_setlevel(2) _c_setlevel(2) _hc('') + + _h('') + _h('#ifdef __cplusplus') + _h('}') + _h('#endif') + _h('') _h('#endif') _h('') @@ -216,13 +228,26 @@ def c_close(self): cfile.write('\n') cfile.close() +def build_collision_table(): + global namecount + namecount = {} + + for v in module.types.values(): + name = _t(v[0]) + namecount[name] = (namecount.get(name) or 0) + 1 + def c_enum(self, name): ''' Exported function that handles enum declarations. ''' + + tname = _t(name) + if namecount[tname] > 1: + tname = _t(name + ('enum',)) + _h_setlevel(0) _h('') - _h('typedef enum %s {', _t(name)) + _h('typedef enum %s {', tname) count = len(self.values) @@ -232,7 +257,7 @@ def c_enum(self, name): comma = ',' if count > 0 else '' _h(' %s%s%s%s', _n(name + (enam,)).upper(), equals, eval, comma) - _h('} %s;', _t(name)) + _h('} %s;', tname) def _c_type_setup(self, name, postfix): ''' @@ -257,7 +282,29 @@ def _c_type_setup(self, name, postfix): self.c_reply_type = _t(name + ('reply',)) self.c_cookie_type = _t(name + ('cookie',)) - if self.is_container: + self.c_aux_name = _n(name + ('aux',)) + self.c_aux_checked_name = _n(name + ('aux', 'checked')) + self.c_aux_unchecked_name = _n(name + ('aux', 'unchecked')) + self.c_serialize_name = _n(name + ('serialize',)) + self.c_unserialize_name = _n(name + ('unserialize',)) + 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 + self.var_followed_by_fixed_fields = False + + # whether a request or reply has a switch field + self.need_aux = False + self.need_serialize = False + if self.is_switch: + self.need_serialize = True + self.c_container = 'struct' + for bitcase in self.bitcases: + bitcase.c_field_name = _cpp(bitcase.field_name) + _c_type_setup(bitcase.type, bitcase.field_type, ()) + + elif self.is_container: self.c_container = 'union' if self.is_union else 'struct' prev_varsized_field = None @@ -268,17 +315,30 @@ def _c_type_setup(self, name, postfix): _c_type_setup(field.type, field.field_type, ()) if field.type.is_list: _c_type_setup(field.type.member, field.field_type, ()) + # FIXME - structures with variable sized members, sort out when serialize() is needed + if (field.type.nmemb is None): # and not field.type.member.fixed_size(): + self.need_serialize = True 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 '*' + if field.type.is_switch: + field.c_pointer = '*' + field.c_field_const_type = 'const ' + field.c_field_type + self.need_aux = True + elif not field.type.fixed_size() and not field.type.is_bitcase: + self.need_serialize = True field.c_iterator_type = _t(field.field_type + ('iterator',)) # xcb_fieldtype_iterator_t field.c_iterator_name = _n(name + (field.field_name, 'iterator')) # xcb_container_field_iterator field.c_accessor_name = _n(name + (field.field_name,)) # xcb_container_field field.c_length_name = _n(name + (field.field_name, 'length')) # xcb_container_field_length + # special case - c_field_name ends with _length as well + #if field.c_field_name.endswith('length'): + # c_field_name = field.c_field_name.rsplit('length', 1) + # field.c_field_name = c_field_name[0] + "_length" field.c_end_name = _n(name + (field.field_name, 'end')) # xcb_container_field_end field.prev_varsized_field = prev_varsized_field @@ -290,10 +350,677 @@ def _c_type_setup(self, name, postfix): if field.type.fixed_size(): prev_varsized_offset += field.type.size + if prev_varsized_field is not None and not field.type.is_pad and field.wire: + if not self.is_union: + self.need_serialize = True + self.var_followed_by_fixed_fields = True +# print "WARNING (%s): variable size field %s followed by fixed size field %s" % ( +# self.c_type, prev_varsized_field.field_name, field.field_name) else: self.last_varsized_field = field prev_varsized_field = field - prev_varsized_offset = 0 + prev_varsized_offset = 0 + + # as switch does never appear at toplevel, + # continue here with type construction + if self.is_switch: + if self.c_type not in finished_switch: + 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 - in case of request/reply, serialize() is not always needed + if self.need_serialize and not self.is_bitcase: + if self.c_serialize_name not in finished_serializers: + finished_serializers.append(self.c_serialize_name) + _c_serialize(self) + _c_unserialize(self) +# _c_type_setup() + +def get_request_fields(self): + param_fields = [] + wire_fields = [] + + for field in self.fields: + if field.visible: + # the field should appear as a parameter in the function call + param_fields.append(field) + if field.wire and not field.auto: + if field.type.fixed_size() and not self.is_switch: + # field in the xcb_out structure + wire_fields.append(field) + # fields like 'pad0' are skipped! + + return (param_fields, wire_fields) +# get_request_fields() + +def get_expr_fields(self): + # get the fields referenced by switch or list expression + def get_expr_field_names(expr): + if expr.op is None: + if expr.lenfield_name is not None: + return [expr.lenfield_name] + else: + # constant value expr + return [] + else: + if expr.op == '~': + return get_expr_field_names(expr.rhs) + elif expr.op == 'popcount': + return get_expr_field_names(expr.rhs) + elif expr.op == 'sumof': + return [expr.lenfield_name] + elif expr.op == 'enumref': + return [] + else: + return get_expr_field_names(expr.lhs) + get_expr_field_names(expr.rhs) + # get_expr_field_names() + + # resolve the field names with the parent structure(s) + unresolved_fields = get_expr_field_names(self.expr) + if unresolved_fields is None: + return [] + expr_fields = dict.fromkeys(unresolved_fields) + for p in reversed(self.parent): + parent_fields = dict((f.field_name, f) for f in p.fields) + for f in parent_fields.keys(): + if f in unresolved_fields: + expr_fields[f] = parent_fields[f] + unresolved_fields.remove(f) + if len(unresolved_fields) == 0: + break + + if None in expr_fields.values(): + raise Exception("could not resolve all fields for %s" % self.name) + + params = expr_fields.values() + return params +# get_expr_fields() + +def resolve_fields(anchestor, complex_obj=None): + """find fields referenced by anchestor or descendents with external scope""" + expr_fields = [] + unresolved = [] + all_fields = [] + if complex_obj is None: + complex_obj = anchestor + for field in complex_obj.fields: + all_fields.append(field) + if field.type.is_switch or field.type.is_list: + expr_fields += get_expr_fields(field.type) + if field.type.is_container: + expr_fields += resolve_fields(anchestor, field.type) + # try to resolve expr fields + for e in expr_fields: + if e not in all_fields: + unresolved.append(e) + return unresolved + +def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'): + def add_param(params, param): + if param not in params: + params.append(param) + + param_fields, wire_fields = get_request_fields(self) + if self.is_switch: + param_fields = get_expr_fields(self) + + # _serialize function parameters + # cannot use set() for params, as series is important + params = [] + if 'serialize' == context: + params.append(('void', '**', buffer_var)) + elif 'unserialize' == context: + params.append(('const void', '*', buffer_var)) + + # look for special cases + unresolved_fields = resolve_fields(self) + for f in unresolved_fields: + add_param(params, (f.c_field_type, '', f.c_field_name)) + + # make sure all required length fields are present + for p in param_fields: + if p.visible and not p.wire and not p.auto: + typespec = p.c_field_type + pointerspec = '' + add_param(params, (typespec, pointerspec, p.c_field_name)) + + # parameter fields if any + if self.is_switch: + for p in get_expr_fields(self): + typespec = p.c_field_const_type + pointerspec = p.c_pointer + add_param(params, (typespec, pointerspec, p.c_field_name)) + + # aux argument - structure to be serialized + if 'serialize' == context: + add_param(params, ('const %s' % self.c_type, '*', aux_var)) + elif 'unserialize' == context and self.is_switch: + 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(): + add_param(params, (p.c_field_const_type, '*', p.c_field_name)) + return (param_fields, wire_fields, params) +# get_serialize_params() + +def _c_serialize_helper_prefix(prefix): + # prefix is a list of (field_name, anchestor object) tuples + # concatenate field names + prefix_str = '' + for name, obj in prefix: + prefix_str += name + prefix_str += '.' if (obj.is_bitcase and obj.has_name) else '->' + lenfield_prefix = '' if prefix_str.find('_aux')==0 else "_aux" + if prefix_str != '': + if lenfield_prefix != '': + lenfield_prefix += '->' + lenfield_prefix += prefix_str + return (prefix_str, lenfield_prefix) +# _c_serialize_helper_prefix + +def _c_field_mapping(context, complex_type, prefix): + def get_prefix(field, prefix): + prefix_str, lenfield_prefix = _c_serialize_helper_prefix(prefix) + if prefix_str == '': + if context in ('serialize', 'unserialize'): + if field.type.fixed_size() or complex_type.is_switch: + prefix_str = '_aux->' + else: + raise Exception("unknown context '%s' in c_field_mapping" % context) + return prefix_str + # get_prefix() + def get_field_name(fields, complex_type, prefix): + for f in complex_type.fields: + prefix_str = get_prefix(f, 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: + get_field_name(fields, f.type, prefix+[(f.c_field_name, f.type)]) + # get_field_name() + + # dict(field_name : (c_field_name, field)) + fields = {} + get_field_name(fields, complex_type, prefix) + + # switch: get the fields referenced by the switch expr as well + # these may not belong to any structure + if complex_type.is_switch: + pass +# FIXME: fields += get_serialize_params(context, complex_type) + + return fields +# _c_field_mapping() + +def _c_serialize_helper_insert_padding(context, code_lines, space): + code_lines.append('%s xcb_buffer_len += xcb_block_len;' % space) + code_lines.append('%s /* padding */' % space) + code_lines.append('%s xcb_pad = -xcb_block_len & 3;' % space) + code_lines.append('%s if (0 != xcb_pad) {' % space) + + if 'serialize' == context: + 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: + code_lines.append('%s xcb_tmp += xcb_pad;' % space) + + code_lines.append('%s xcb_buffer_len += xcb_pad;' % space) + code_lines.append('%s xcb_pad = 0;' % space) + code_lines.append('%s }' % space) + code_lines.append('%s xcb_block_len = 0;' % space) + + return 1 +# _c_serialize_helper_insert_padding() + +def _c_serialize_helper_switch(context, self, complex_name, + code_lines, temp_vars, + space, prefix): + count = 0 + switch_prefix = prefix + [(complex_name, self)] + prefix_str, lenfield_prefix = _c_serialize_helper_prefix(switch_prefix) + switch_expr = _c_accessor_get_expr(self.expr) + + 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)) + b_prefix = switch_prefix + if b.type.has_name: + b_prefix = switch_prefix + [(b.c_field_name, b.type)] + _c_serialize_helper_fields(context, b.type, + code_lines, temp_vars, + "%s " % space, + b_prefix, + is_bitcase = True) + code_lines.append(' }') + + if 'serialize' == context: + count = _c_serialize_helper_insert_padding(context, code_lines, space) + if 'unserialize' == context: + # 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) + + return count +# _c_serialize_helper_switch + +def _c_serialize_helper_switch_field(context, self, field, c_switch_variable, prefix): + # switch is handled by this function as a special case + param_fields, wire_fields, params = get_serialize_params(context, self) + args = get_expr_fields(field.type) + field_mapping = _c_field_mapping(context, self, prefix) + + # determine which params to pass to _unserialize() and their prefixes + switch_len_fields = resolve_fields(self, field.type) + bitcase_unresolved = resolve_fields(self, self) + if len(bitcase_unresolved) != 0: + raise Exception('unresolved fields within bitcase is not supported at this point') + c_field_names = '' + for a in switch_len_fields: + 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) + return length +# _c_serialize_helper_switch_field() + +def _c_serialize_helper_list_field(context, self, field, + code_lines, temp_vars, + space, prefix): + """ + helper function for (un)serialize to cope with lists of variable length + """ + expr = field.type.expr + prefix_str, lenfield_prefix = _c_serialize_helper_prefix(prefix) + param_fields, wire_fields, params = get_serialize_params('unserialize', self) + param_names = [p[2] for p in params] + + # look if the list's lenfield is a struct member or a function argument + # special case: if the list has a length field, its name will be returned + # unchanged by calling c_accessor_get_length(expr) + if expr.lenfield_name == _c_accessor_get_length(expr): + if expr.lenfield_name in param_names: + # the length field appears as separate argument in unserialize, + # so no need for a prefix + lenfield_prefix = '' + sep = '.' if (self.is_bitcase and self.has_name) else '->' + list_length = _c_accessor_get_expr(expr, lenfield_prefix, sep) + + # default: list with fixed size elements + length = '%s * sizeof(%s)' % (list_length, field.type.member.c_wiretype) + # list with variable-sized elements + if field.type.size is None: + length = '' + if 'unserialize' == context: + int_i = ' unsigned int i;' + xcb_tmp_len = ' unsigned int xcb_tmp_len;' + if int_i not in temp_vars: + temp_vars.append(int_i) + if xcb_tmp_len not in temp_vars: + temp_vars.append(xcb_tmp_len) + code_lines.append("%s for(i=0; i<%s; i++) {" % (space, list_length)) + code_lines.append("%s xcb_tmp_len = %s(xcb_tmp);" % + (space, field.type.c_unserialize_name)) + code_lines.append("%s xcb_block_len += xcb_tmp_len;" % space) + code_lines.append("%s xcb_tmp += xcb_tmp_len;" % space) + code_lines.append("%s }" % space) + elif 'serialize' == context: + code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len = 0;' % space) + code_lines.append('%s xcb_tmp = (char *) %s%s;' % (space, prefix_str, field.c_field_name)) + code_lines.append('%s for(i=0; i<%s; i++) { ' + % (space, _c_accessor_get_expr(expr, lenfield_prefix, sep))) + code_lines.append('%s xcb_block_len = %s(xcb_tmp);' % (space, field.type.c_unserialize_name)) + code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len += xcb_block_len;' % space) + code_lines.append('%s }' % space) + code_lines.append('%s xcb_block_len = xcb_parts[xcb_parts_idx].iov_len;' % space) + + return length +# _c_serialize_helper_list_field() + +def _c_serialize_helper_fields_fixed_size(context, self, field, + code_lines, temp_vars, + space, prefix): + code_lines.append('%s /* %s.%s */' % (space, self.c_type, field.c_field_name)) + prefix_str, lenfield_prefix = _c_serialize_helper_prefix(prefix) + + length = "sizeof(%s)" % field.c_field_type + + if 'unserialize' == context: + 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 = '' + for i in range(field.type.nmemb): + 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: + value = ' xcb_parts[xcb_parts_idx].iov_base = (char *) ' + + if field.type.is_expr: + # need to register a temporary variable for the expression + if field.type.c_type is None: + raise Exception("type for field '%s' (expression '%s') unkown" % + (field.field_name, _c_accessor_get_expr(field.type.expr))) + temp_vars.append(' %s xcb_expr_%s = %s;' % (field.type.c_type, field.field_name, + _c_accessor_get_expr(field.type.expr, prefix))) + value += "&xcb_expr_%s;" % field.field_name + + elif field.type.is_pad: + if field.type.nmemb == 1: + value += "&xcb_pad;" + else: + # FIXME - possible segmentation fault!! + value = ' memset(xcb_parts[xcb_parts_idx].iov_base, 0, %d);' % field.type.nmemb + length += "*%d" % field.type.nmemb + + else: + # non-list type with fixed size + if field.type.nmemb == 1: + value += "&%s%s;" % (prefix_str, field.c_field_name) + # list with nmemb (fixed size) elements + else: + value += '%s%s;' % (prefix_str, field.c_field_name) + length = '%d' % field.type.nmemb + + return (value, length) +# _c_serialize_helper_fields_fixed_size() + +def _c_serialize_helper_fields_variable_size(context, self, field, + code_lines, temp_vars, + space, prefix): + prefix_str, lenfield_prefix = _c_serialize_helper_prefix(prefix) + + if 'unserialize' == context: + value = '' + 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) + 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: + 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) + else: + length = "%s(xcb_tmp)" % (field.type.c_unserialize_name) + + return (value, length) +# _c_serialize_helper_fields_variable_size + +def _c_serialize_helper_fields(context, self, + code_lines, temp_vars, + space, prefix, is_bitcase): + count = 0 + need_padding = False + + for field in self.fields: + if not ((field.wire and not field.auto) or field.visible): + continue + + # switch/bitcase: fixed size fields must be considered explicitly + if field.type.fixed_size(): + if self.is_bitcase: + value, length = _c_serialize_helper_fields_fixed_size(context, self, field, + code_lines, temp_vars, + space, prefix) + else: + continue + + # fields with variable size + else: + # switch/bitcase: always calculate padding before and after variable sized fields + if need_padding or is_bitcase: + _c_serialize_helper_insert_padding(context, code_lines, space) + + value, length = _c_serialize_helper_fields_variable_size(context, self, field, + code_lines, temp_vars, + space, prefix) + + # save (un)serialization C code + if '' != value: + 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 'unserialize' == context: + code_lines.append('%s xcb_tmp += %s;' % (space, length)) + else: + # padding + if '' != length: + code_lines.append('%s xcb_block_len = %s;' % (space, length)) + if 'unserialize' == context: + code_lines.append('%s xcb_tmp += xcb_block_len;' % space) + if 'serialize' == context: + if '' != length: + code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len = xcb_block_len;' % space) + code_lines.append('%s xcb_parts_idx++;' % space) + count += 1 + need_padding = True + + return count +# _c_serialize_helper_fields() + +def _c_serialize_helper(context, complex_type, + code_lines, temp_vars, + space='', prefix=[]): + count = 0 + if hasattr(complex_type, 'type'): + self = complex_type.type + complex_name = complex_type.name + else: + self = complex_type + complex_name = '_aux' + + # special case: switch is serialized by evaluating each bitcase separately + if self.is_switch: + count += _c_serialize_helper_switch(context, self, complex_name, + code_lines, temp_vars, + space, prefix) + + # 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 'unserialize' == context: + 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) + + count += _c_serialize_helper_fields(context, self, + code_lines, temp_vars, + space, prefix, False) + # "final padding" + count += _c_serialize_helper_insert_padding(context, code_lines, space) + + return count +# _c_serialize_helper() + +def _c_serialize(self): + _h_setlevel(1) + _c_setlevel(1) + + _hc('') + # _serialize() returns the buffer size + _hc('int') + + variable_size_fields = 0 + # maximum space required for type definition of function arguments + maxtypelen = 0 + param_fields, wire_fields, params = get_serialize_params('serialize', self) + + # determine N(variable_fields) + for field in param_fields: + # if self.is_switch, treat all fields as if they are variable sized + if not field.type.fixed_size() or self.is_switch: + variable_size_fields += 1 + # determine maxtypelen + for p in params: + maxtypelen = max(maxtypelen, len(p[0]) + len(p[1])) + + # write to .c/.h + for idx, p in enumerate(params): + line = "" + typespec, pointerspec, field_name = p + indent = ' '*(len(self.c_serialize_name)+2) + # p==0: function declaration + if 0==idx: + line = "%s (" % self.c_serialize_name + indent = '' + spacing = ' '*(maxtypelen-len(typespec)-len(pointerspec)) + line += "%s%s%s %s%s /**< */" % (indent, typespec, spacing, pointerspec, field_name) + if idx < len(params)-1: + _hc("%s," % line) + else: + _h("%s);" % line) + _c("%s)" % line) + + _c('{') + if not self.is_switch: + _c(' %s *xcb_out = *_buffer;', self.c_type) + _c(' unsigned int xcb_out_pad = -sizeof(%s) & 3;', self.c_type) + _c(' unsigned int xcb_buffer_len = sizeof(%s) + xcb_out_pad;', self.c_type) + else: + _c(' char *xcb_out = *_buffer;') + _c(' unsigned int xcb_buffer_len = 0;') + if variable_size_fields > 0: + code_lines = [] + temp_vars = [] + count =_c_serialize_helper('serialize', self, + code_lines, temp_vars) + # update variable size fields + variable_size_fields = count + temp_vars.append(' unsigned int xcb_pad = 0;') + temp_vars.append(' char xcb_pad0[3] = {0, 0, 0};') + temp_vars.append(' struct iovec xcb_parts[%d];' % (count+1)) + temp_vars.append(' unsigned int xcb_parts_idx = 0;') + temp_vars.append(' unsigned int xcb_block_len = 0;') + temp_vars.append(' unsigned int i;') + temp_vars.append(' char *xcb_tmp;') + for t in temp_vars: + _c(t) + + _c('') + + if variable_size_fields > 0: + for l in code_lines: + _c(l) + _c('') + + # variable sized fields have been collected, now + # allocate memory and copy everything into a continuous memory area + _c(' if (NULL == xcb_out) {') + _c(' /* allocate memory */') + _c(' *_buffer = malloc(xcb_buffer_len);') + _c(' xcb_out = *_buffer;') + _c(' }') + _c('') + + # fill in struct members + if not self.is_switch: + if len(wire_fields)>0: + _c(' *xcb_out = *_aux;') + + # copy variable size fields into the buffer + if variable_size_fields > 0: + # xcb_out padding + if not self.is_switch: + _c(' xcb_tmp = (char*)++xcb_out;') + _c(' xcb_tmp += xcb_out_pad;') + else: + _c(' xcb_tmp = xcb_out;') + + # variable sized fields + _c(' for(i=0; i' - + prefarrow = '' if prefix == '' else prefix + sep + if expr.lenfield != None and expr.lenfield.prev_varsized_field != None: - return expr.lenfield.c_accessor_name + '(' + prefix + ')' + # special case: variable and fixed size fields are intermixed + retval = expr.lenfield.c_accessor_name + '(' + prefix + ')' + if prefix in ('', '_aux'): + prefix_str = '' if prefix=='' else '%s' % (prefarrow) + retval = '%s%s' % (prefix_str, expr.lenfield_name) + return retval elif expr.lenfield_name != None: - return prefarrow + expr.lenfield_name + if prefix.endswith(sep): + return prefix + expr.lenfield_name + else: + return prefarrow + expr.lenfield_name else: return str(expr.nmemb) -def _c_accessor_get_expr(expr, prefix=''): +def _c_accessor_get_expr(expr, prefix='', sep='->'): ''' Figures out what C code is needed to get the length of a list field. Recurses for math operations. Returns bitcount for value-mask fields. Otherwise, uses the value of the length field. ''' - lenexp = _c_accessor_get_length(expr, prefix) - - if expr.op != None: - return '(' + _c_accessor_get_expr(expr.lhs, prefix) + ' ' + expr.op + ' ' + _c_accessor_get_expr(expr.rhs, prefix) + ')' + lenexp = _c_accessor_get_length(expr, prefix, sep) + + if expr.op == '~': + return '(' + '~' + _c_accessor_get_expr(expr.rhs, prefix, sep) + ')' + elif expr.op == 'popcount': + return 'xcb_popcount(' + _c_accessor_get_expr(expr.rhs, prefix, sep) + ')' + elif expr.op == 'enumref': + enum_name = expr.lenfield_type.name + constant_name = expr.lenfield_name + c_name = _n(enum_name + (constant_name,)).upper() + return c_name + elif expr.op == 'sumof': + # 1. locate the referenced list object + list_obj = expr.lenfield_type + field = None + for f in expr.lenfield_parent.fields: + if f.field_name == expr.lenfield_name: + field = f + break + if field is None: + raise Exception("list field '%s' referenced by sumof not found" % expr.lenfield_name) + if prefix != '' and not prefix.endswith(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 + 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: + return '(' + _c_accessor_get_expr(expr.lhs, prefix, sep) + ' ' + expr.op + ' ' + _c_accessor_get_expr(expr.rhs, prefix, sep) + ')' elif expr.bitfield: return 'xcb_popcount(' + lenexp + ')' else: - return lenexp + return lenexp + '/* bla */' def _c_accessors_field(self, field): ''' Declares the accessor functions for a non-list field that follows a variable-length field. ''' - if field.field_type[0] in _cardinal_types: + if field.type.is_simple: _hc('') _hc('') _hc('/*****************************************************************************') @@ -459,8 +1222,12 @@ def _c_accessors_field(self, field): _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('{') - _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) + if field.prev_varsized_field is None: + _c(' return (%s *) (R + 1);', field.c_field_type) + 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('}') else: _hc('') @@ -478,8 +1245,12 @@ def _c_accessors_field(self, field): _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('{') - _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) + if field.prev_varsized_field is None: + _c(' return (%s *) (R + 1);', field.c_field_type) + 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('}') def _c_accessors_list(self, field): @@ -509,7 +1280,7 @@ def _c_accessors_list(self, field): _c('%s (const %s *R /**< */)', field.c_accessor_name, self.c_type) _c('{') - if field.prev_varsized_field == None: + if field.prev_varsized_field is None: _c(' return (%s *) (R + 1);', field.c_field_type) else: _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) @@ -535,7 +1306,7 @@ def _c_accessors_list(self, field): _c(' return %s;', _c_accessor_get_expr(field.type.expr, 'R')) _c('}') - if field.field_type[0] in _cardinal_types: + if field.type.member.is_simple: _hc('') _hc('') _hc('/*****************************************************************************') @@ -598,9 +1369,13 @@ 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: + elif field.prev_varsized_field != None or not field.type.fixed_size(): _c_accessors_field(self, field) def c_simple(self, name): @@ -637,10 +1412,10 @@ def _c_complex(self): varfield = None for field in self.fields: - if not field.type.fixed_size(): + if not field.type.fixed_size() and not self.is_switch: varfield = field.c_field_name continue - if varfield != None and not field.type.is_pad: + if varfield != None and not field.type.is_pad and field.wire: errmsg = '%s: warning: variable field %s followed by fixed field %s\n' % (self.c_type, varfield, field.c_field_name) sys.stderr.write(errmsg) # sys.exit(1) @@ -648,12 +1423,36 @@ def _c_complex(self): struct_fields.append(field) for field in struct_fields: - if len(field.c_field_type) > maxtypelen: - maxtypelen = len(field.c_field_type) + length = len(field.c_field_type) + # account for '*' pointer_spec + if not field.type.fixed_size(): + length += 1 + maxtypelen = max(maxtypelen, length) + + def _c_complex_field(self, field, space=''): + if (field.type.fixed_size() or + # in case of switch with switch children, don't make the field a pointer + # necessary for unserialize to work + (self.is_switch and field.type.is_switch)): + spacing = ' ' * (maxtypelen - len(field.c_field_type)) + _h('%s %s%s %s%s; /**< */', space, field.c_field_type, spacing, field.c_field_name, field.c_subscript) + else: + spacing = ' ' * (maxtypelen - (len(field.c_field_type) + 1)) + _h('%s %s%s *%s%s; /**< */', space, field.c_field_type, spacing, field.c_field_name, field.c_subscript) - for field in struct_fields: - spacing = ' ' * (maxtypelen - len(field.c_field_type)) - _h(' %s%s %s%s; /**< */', field.c_field_type, spacing, field.c_field_name, field.c_subscript) + if not self.is_switch: + for field in struct_fields: + _c_complex_field(self, field) + else: + for b in self.bitcases: + space = '' + if b.type.has_name: + _h(' struct _%s {', b.c_field_name) + space = ' ' + for field in b.type.fields: + _c_complex_field(self, field, space) + if b.type.has_name: + _h(' } %s;', b.c_field_name) _h('} %s;', self.c_type) @@ -674,7 +1473,7 @@ def c_union(self, name): _c_complex(self) _c_iterator(self, name) -def _c_request_helper(self, name, cookie_type, void, regular): +def _c_request_helper(self, name, cookie_type, void, regular, aux=False): ''' Declares a request function. ''' @@ -707,15 +1506,16 @@ def _c_request_helper(self, name, cookie_type, void, regular): func_ext_global = '&' + _ns.c_ext_global_name if _ns.is_ext else '0' # What our function name is - func_name = self.c_request_name + func_name = self.c_request_name if not aux else self.c_aux_name if checked: - func_name = self.c_checked_name + func_name = self.c_checked_name if not aux else self.c_aux_checked_name if unchecked: - func_name = self.c_unchecked_name + func_name = self.c_unchecked_name if not aux else self.c_aux_unchecked_name param_fields = [] wire_fields = [] maxtypelen = len('xcb_connection_t') + serial_fields = [] for field in self.fields: if field.visible: @@ -724,10 +1524,15 @@ def _c_request_helper(self, name, cookie_type, void, regular): if field.wire and not field.auto: # We need to set the field up in the structure wire_fields.append(field) + if field.type.need_serialize: + serial_fields.append(field) for field in param_fields: - if len(field.c_field_const_type) > maxtypelen: - maxtypelen = len(field.c_field_const_type) + c_field_const_type = field.c_field_const_type + if field.type.need_serialize and not aux: + c_field_const_type = "const void" + if len(c_field_const_type) > maxtypelen: + maxtypelen = len(c_field_const_type) _h_setlevel(1) _c_setlevel(1) @@ -759,8 +1564,11 @@ def _c_request_helper(self, name, cookie_type, void, regular): _hc(' ** @param xcb_connection_t%s *c', spacing) for field in param_fields: - spacing = ' ' * (maxtypelen - len(field.c_field_const_type)) - _hc(' ** @param %s%s %s%s', field.c_field_const_type, spacing, field.c_pointer, field.c_field_name) + c_field_const_type = field.c_field_const_type + if field.type.need_serialize and not aux: + c_field_const_type = "const void" + spacing = ' ' * (maxtypelen - len(c_field_const_type)) + _hc(' ** @param %s%s %s%s', c_field_const_type, spacing, field.c_pointer, field.c_field_name) _hc(' ** @returns %s', cookie_type) _hc(' **') @@ -778,16 +1586,28 @@ def _c_request_helper(self, name, cookie_type, void, regular): count = len(param_fields) for field in param_fields: count = count - 1 - spacing = ' ' * (maxtypelen - len(field.c_field_const_type)) + c_field_const_type = field.c_field_const_type + c_pointer = field.c_pointer + if field.type.need_serialize and not aux: + c_field_const_type = "const void" + c_pointer = '*' + spacing = ' ' * (maxtypelen - len(c_field_const_type)) comma = ',' if count else ');' - _h('%s%s%s %s%s /**< */%s', func_spacing, field.c_field_const_type, spacing, field.c_pointer, field.c_field_name, comma) + _h('%s%s%s %s%s /**< */%s', func_spacing, c_field_const_type, + spacing, c_pointer, field.c_field_name, comma) comma = ',' if count else ')' - _c('%s%s%s %s%s /**< */%s', func_spacing, field.c_field_const_type, spacing, field.c_pointer, field.c_field_name, comma) + _c('%s%s%s %s%s /**< */%s', func_spacing, c_field_const_type, + spacing, c_pointer, field.c_field_name, comma) count = 2 + if self.var_followed_by_fixed_fields: + count += len(wire_fields)*2 for field in param_fields: if not field.type.fixed_size(): count = count + 2 + if field.type.need_serialize: + # _serialize() keeps track of padding automatically + count -= 1 _c('{') _c(' static const xcb_protocol_request_t xcb_req = {') @@ -797,46 +1617,103 @@ def _c_request_helper(self, name, cookie_type, void, regular): _c(' /* isvoid */ %d', 1 if void else 0) _c(' };') _c(' ') + _c(' struct iovec xcb_parts[%d];', count + 2) _c(' %s xcb_ret;', func_cookie) - _c(' %s xcb_out;', self.c_type) - _c(' ') - - for field in wire_fields: - if field.type.fixed_size(): - if field.type.is_expr: - _c(' xcb_out.%s = %s;', field.c_field_name, _c_accessor_get_expr(field.type.expr)) + if not self.var_followed_by_fixed_fields: + _c(' %s xcb_out;', self.c_type) - elif field.type.is_pad: - if field.type.nmemb == 1: - _c(' xcb_out.%s = 0;', field.c_field_name) + for idx, f in enumerate(serial_fields): + if not aux: + _c(' %s xcb_aux%d;' % (f.type.c_type, idx)) + else: + _c(' void *xcb_aux%d = 0;' % (idx)) + _c(' ') +# _c(' printf("in function %s\\n");' % func_name) + + # fixed size fields + if not self.var_followed_by_fixed_fields: + for field in wire_fields: + if field.type.fixed_size(): + if field.type.is_expr: + _c(' xcb_out.%s = %s;', field.c_field_name, _c_accessor_get_expr(field.type.expr)) + elif field.type.is_pad: + if field.type.nmemb == 1: + _c(' xcb_out.%s = 0;', field.c_field_name) + else: + _c(' memset(xcb_out.%s, 0, %d);', field.c_field_name, field.type.nmemb) + else: + if field.type.nmemb == 1: + _c(' xcb_out.%s = %s;', field.c_field_name, field.c_field_name) + else: + _c(' memcpy(xcb_out.%s, %s, %d);', field.c_field_name, field.c_field_name, field.type.nmemb) + + _c(' ') + _c(' xcb_parts[2].iov_base = (char *) &xcb_out;') + _c(' xcb_parts[2].iov_len = sizeof(xcb_out);') + _c(' xcb_parts[3].iov_base = 0;') + _c(' xcb_parts[3].iov_len = -xcb_parts[2].iov_len & 3;') + + # calls in order to free dyn. all. memory + free_calls = [] + if not self.var_followed_by_fixed_fields: + count = 4 + else: + count = 2 + var_size_fields = param_fields + if self.var_followed_by_fixed_fields: + var_size_fields = wire_fields + param_fields + for field in var_size_fields: + if field.type.fixed_size and self.var_followed_by_fixed_fields: + _c(' xcb_parts[%d].iov_base = (char *) &%s;', count, field.c_field_name) + _c(' xcb_parts[%d].iov_len = sizeof(%s);', count, field.type.c_type) + count += 1 + if not field.type.fixed_size(): + # default: simple cast to char * + if not field.type.need_serialize: + _c(' xcb_parts[%d].iov_base = (char *) %s;', count, field.c_field_name) + if field.type.is_list: + _c(' xcb_parts[%d].iov_len = %s * sizeof(%s);', count, + _c_accessor_get_expr(field.type.expr), field.type.member.c_wiretype) else: - _c(' memset(xcb_out.%s, 0, %d);', field.c_field_name, field.type.nmemb) + # not supposed to happen + raise Exception("unhandled variable size field %s" % field.c_field_name) + #_c('/* eeeeeeeeeee */') else: - if field.type.nmemb == 1: - _c(' xcb_out.%s = %s;', field.c_field_name, field.c_field_name) + if not aux: + _c(' xcb_parts[%d].iov_base = (char *) %s;', count, field.c_field_name) + idx = serial_fields.index(field) + if not aux: + serialize_args = get_serialize_params('unserialize', field.type, + field.c_field_name, + '&xcb_aux%d' % idx)[2] else: - _c(' memcpy(xcb_out.%s, %s, %d);', field.c_field_name, field.c_field_name, field.type.nmemb) - - _c(' ') - _c(' xcb_parts[2].iov_base = (char *) &xcb_out;') - _c(' xcb_parts[2].iov_len = sizeof(xcb_out);') - _c(' xcb_parts[3].iov_base = 0;') - _c(' xcb_parts[3].iov_len = -xcb_parts[2].iov_len & 3;') + serialize_args = get_serialize_params('serialize', field.type, + '&xcb_aux%d' % idx, + field.c_field_name)[2] + serialize_args = reduce(lambda x,y: "%s, %s" % (x,y), [a[2] for a in serialize_args]) + _c(' xcb_parts[%d].iov_len = ', count) + if aux: + _c(' %s (%s);', field.type.c_serialize_name, serialize_args) + _c(' xcb_parts[%d].iov_base = xcb_aux%d;' % (count, idx)) + free_calls.append(' free(xcb_aux%d);' % idx) + else: + _c(' %s (%s);', field.type.c_unserialize_name, serialize_args) - count = 4 - for field in param_fields: - if not field.type.fixed_size(): - _c(' xcb_parts[%d].iov_base = (char *) %s;', count, field.c_field_name) - if field.type.is_list: - _c(' xcb_parts[%d].iov_len = %s * sizeof(%s);', count, _c_accessor_get_expr(field.type.expr), field.type.member.c_wiretype) - else: - _c(' xcb_parts[%d].iov_len = %s * sizeof(%s);', count, 'Uh oh', field.type.c_wiretype) - _c(' xcb_parts[%d].iov_base = 0;', count + 1) - _c(' xcb_parts[%d].iov_len = -xcb_parts[%d].iov_len & 3;', count + 1, count) - count = count + 2 + + count += 1 + if not field.type.need_serialize: + # the _serialize() function keeps track of padding automatically + _c(' xcb_parts[%d].iov_base = 0;', count) + _c(' xcb_parts[%d].iov_len = -xcb_parts[%d].iov_len & 3;', count, count-1) + count += 1 + _c(' ') _c(' xcb_ret.sequence = xcb_send_request(c, %s, xcb_parts + 2, &xcb_req);', func_flags) + + # free dyn. all. data, if any + for f in free_calls: + _c(f) _c(' return xcb_ret;') _c('}') @@ -930,6 +1807,9 @@ def c_request(self, name): # Request prototypes _c_request_helper(self, name, self.c_cookie_type, False, True) _c_request_helper(self, name, self.c_cookie_type, False, False) + if self.need_aux: + _c_request_helper(self, name, self.c_cookie_type, False, True, True) + _c_request_helper(self, name, self.c_cookie_type, False, False, True) # Reply accessors _c_accessors(self.reply, name + ('reply',), name) _c_reply(self, name) @@ -937,6 +1817,10 @@ def c_request(self, name): # Request prototypes _c_request_helper(self, name, 'xcb_void_cookie_t', True, False) _c_request_helper(self, name, 'xcb_void_cookie_t', True, True) + if self.need_aux: + _c_request_helper(self, name, 'xcb_void_cookie_t', True, False, True) + _c_request_helper(self, name, 'xcb_void_cookie_t', True, True, True) + def c_event(self, name): ''' @@ -984,7 +1868,7 @@ output = {'open' : c_open, 'union' : c_union, 'request' : c_request, 'event' : c_event, - 'error' : c_error + 'error' : c_error, } # Boilerplate below this point