+def _c_helper_absolute_name(prefix, field=None):
+ """
+ turn prefix, which is a list of tuples (name, separator, Type obj) into a string
+ representing a valid name in C (based on the context)
+ if field is not None, append the field name as well
+ """
+ prefix_str = ''
+ for name, sep, obj in prefix:
+ prefix_str += name
+ if '' == sep:
+ sep = '->'
+ if ((obj.is_bitcase and obj.has_name) or # named bitcase
+ (obj.is_switch and len(obj.parents)>1)):
+ sep = '.'
+ prefix_str += sep
+ if field is not None:
+ prefix_str += _cpp(field.field_name)
+ return prefix_str
+# _c_absolute_name
+
+def _c_helper_field_mapping(complex_type, prefix, flat=False):
+ """
+ generate absolute names, based on prefix, for all fields starting from complex_type
+ if flat == True, nested complex types are not taken into account
+ """
+ all_fields = {}
+ if complex_type.is_switch:
+ for b in complex_type.bitcases:
+ if b.type.has_name:
+ switch_name, switch_sep, switch_type = prefix[-1]
+ bitcase_prefix = prefix + [(b.type.name[-1], '.', b.type)]
+ else:
+ bitcase_prefix = prefix
+
+ if (True==flat and not b.type.has_name) or False==flat:
+ all_fields.update(_c_helper_field_mapping(b.type, bitcase_prefix, flat))
+ else:
+ for f in complex_type.fields:
+ fname = _c_helper_absolute_name(prefix, f)
+ if f.field_name in all_fields:
+ raise Exception("field name %s has been registered before" % f.field_name)
+
+ all_fields[f.field_name] = (fname, f)
+ if f.type.is_container and flat==False:
+ if f.type.is_bitcase and not f.type.has_name:
+ new_prefix = prefix
+ elif f.type.is_switch and len(f.type.parents)>1:
+ # nested switch gets another separator
+ new_prefix = prefix+[(f.c_field_name, '.', f.type)]
+ else:
+ new_prefix = prefix+[(f.c_field_name, '->', f.type)]
+ all_fields.update(_c_helper_field_mapping(f.type, new_prefix, flat))
+
+ return all_fields
+# _c_field_mapping()
+
+def _c_helper_resolve_field_names (prefix):
+ """
+ get field names for all objects in the prefix array
+ """
+ all_fields = {}
+ tmp_prefix = []
+ # look for fields in the remaining containers
+ for idx, p in enumerate(prefix):
+ name, sep, obj = p
+ if ''==sep:
+ # sep can be preset in prefix, if not, make a sensible guess
+ sep = '.' if (obj.is_switch or obj.is_bitcase) else '->'
+ # exception: 'toplevel' object (switch as well!) always have sep '->'
+ sep = '->' if idx<1 else sep
+ if not obj.is_bitcase or (obj.is_bitcase and obj.has_name):
+ tmp_prefix.append((name, sep, obj))
+ all_fields.update(_c_helper_field_mapping(obj, tmp_prefix, flat=True))
+
+ return all_fields
+# _c_helper_resolve_field_names
+
+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':
+ # sumof expr references another list,
+ # we need that list's length field here
+ 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)
+ # referenced list + its length field
+ return [expr.lenfield_name] + get_expr_field_names(field.type.expr)
+ 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_names = get_expr_field_names(self.expr)
+
+ # construct prefix from self
+ prefix = [('', '', p) for p in self.parents]
+ if self.is_container:
+ prefix.append(('', '', self))
+
+ all_fields = _c_helper_resolve_field_names (prefix)
+ resolved_fields_names = list(filter(lambda x: x in all_fields.keys(), unresolved_fields_names))
+ if len(unresolved_fields_names) != len(resolved_fields_names):
+ raise Exception("could not resolve all fields for %s" % self.name)
+
+ resolved_fields = [all_fields[n][1] for n in resolved_fields_names]
+ return resolved_fields
+# get_expr_fields()
+
+def resolve_expr_fields(complex_obj):
+ """
+ find expr fields appearing in complex_obj and descendents that cannot be resolved within complex_obj
+ these are normally fields that need to be given as function parameters
+ """
+ all_fields = []
+ expr_fields = []
+ unresolved = []
+
+ 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_expr_fields(field.type)
+
+ # try to resolve expr fields
+ for e in expr_fields:
+ if e not in all_fields and e not in unresolved:
+ unresolved.append(e)
+ return unresolved
+# resolve_expr_fields()
+
+def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'):
+ """
+ functions like _serialize(), _unserialize(), and _unpack() sometimes need additional parameters:
+ E.g. in order to unpack switch, extra parameters might be needed to evaluate the switch
+ expression. This function tries to resolve all fields within a structure, and returns the
+ unresolved fields as the list of external parameters.
+ """
+ def add_param(params, param):
+ if param not in params:
+ params.append(param)
+
+ # collect all fields into param_fields