def _PythonToCtype(data, c_type): """Populate a ctypes data type with a Python structure.""" if c_type is actuator_util.Vec3: # Handle Vec3. assert len(data) == 3 c_data = c_type() c_data.x = data[0] c_data.y = data[1] c_data.z = data[2] return c_data elif hasattr(c_type, '_length_'): # Handle arrays. length = getattr(c_type, '_length_') assert len(data) == length c_data = c_type() for i in range(length): c_data[i] = _PythonToCtype(data[i], getattr(c_type, '_type_')) elif hasattr(c_type, '_fields_'): # Handle structures. fields = autogen_util.GetCFields(c_type) assert set(data.keys()) == {field for field, _ in fields} c_data = c_type() for field, field_type in fields: setattr(c_data, field, _PythonToCtype(data[field], field_type)) else: c_data = c_type(data) return c_data
def FindChildTypes(parent): """Return name of all the substructures on which a parent struct depends.""" struct_lst = [] ctype_lst = [] for _, field_type in autogen_util.GetCFields(parent): if isinstance(field_type, type(ctypes.Array)): t = field_type()._type_ else: t = field_type if isinstance(t, type(ctypes.Structure)): struct_lst.append(t.__name__) slst, clst = FindChildTypes(t) struct_lst += slst ctype_lst += clst # Matches any of the fundamental c types elif isinstance(t, type(ctypes.c_double)): ctype_lst.append(t.__name__) return struct_lst, ctype_lst
def WritePackFunc(f, s, is_static, name=None): """Writes a pack function.""" if not name: name = ConvTypeToStr(s)[0] f.write('\n' '\n%ssize_t %s(const %s *in, size_t num, uint8_t *out) {' '\n size_t byte_ind = 0U, elmt_ind;' '\n for (elmt_ind = 0U; elmt_ind < num; ++elmt_ind) {' % (GetStaticStr(is_static), GenPackFuncName(name), name)) for field_name, field_type in autogen_util.GetCFields(s): type_str, num, dim = ConvTypeToStr(field_type) if field_name in s._enums_: type_str = s._enums_[field_name] f.write('\n byte_ind += %s(&in[elmt_ind].%s%s, %d, &out[byte_ind]);' % (GenPackFuncName(type_str), field_name, '[0]'*dim, num)) f.write('\n }' '\n return byte_ind;' '\n}')
def GetTypePackedSize(t): if isinstance(t, type(ctypes.c_double)): if t == ctypes.c_double: return 8 elif t == ctypes.c_float: return 4 elif t == ctypes.c_int8: return 1 elif t == ctypes.c_int16: return 2 elif t == ctypes.c_int32: return 4 elif t == ctypes.c_int64: return 8 elif t == ctypes.c_int: return 4 elif t == ctypes.c_uint8: return 1 elif t == ctypes.c_uint16: return 2 elif t == ctypes.c_uint32: return 4 elif t == ctypes.c_uint64: return 8 elif t == ctypes.c_uint: return 4 elif t == ctypes.c_char: return 1 elif t == ctypes.c_bool: return 1 else: raise ValueError('Unrecognized type: %s' % t) elif isinstance(t, type(ctypes.Structure)): num_bytes = 0 for (_, field_type) in autogen_util.GetCFields(t): num_bytes += GetTypePackedSize(field_type) return num_bytes elif isinstance(t, type(ctypes.Array)): return GetTypePackedSize(t()._type_) * len(t()) else: raise ValueError('Unrecognized type: %s' % t)
def GetRequiredTypes(t): """Get types whose loading functions are required to load a given type. Args: t: A type. Returns: A set of types whose loading functions are required to implement the loading function for t. """ types = set() if _IsArrayType(t): scalar_type = _ArrayCTypeScalarType(t) types = {scalar_type} types = types.union(GetRequiredTypes(scalar_type)) elif _IsStructType(t): fields = autogen_util.GetCFields(t) types = {f_type for (_, f_type) in fields} types = types.union( *[GetRequiredTypes(f_type) for (_, f_type) in fields]) return types
def _MessageStructToDtype(ctype): """Construct a numpy dtype from a ctypes type. This function exists because numpy's dtype constructor squeezes multidimensional arrays and converts ctypes.c_char to length one strings (see see b/26933384). This function does not respect the underlying ctype's alignment. In the context of _MessageStructToH5T this is fine, as we later pack the structure. Args: ctype: Type to convert. Returns: A numpy.dtype. This type may not have the correct offsets. Raises: InvalidCtypeException: if a type is encountered that isn't a Structure, Array, c_char, or a member of the _ALLOWED_BASIC_CTYPES. """ if isinstance(ctype, type(ctypes.Structure)): names, fields = zip(*autogen_util.GetCFields(ctype)) field_dtypes = [_MessageStructToDtype(f) for f in fields] return numpy.dtype(zip(names, field_dtypes)) elif type(ctype) is type(ctypes.Array): return numpy.dtype((_MessageStructToDtype(getattr(ctype, '_type_')), (getattr(ctype, '_length_'),))) elif ctype == ctypes.c_char: # Numpy converts ctypes.c_char to 'S1', i.e. a string of length # one. This is generally not desired, and in particular, causes # MATLAB to expect scalar characters to be null-terminated strings # (see b/26933384). This branch is added to force character # arrays to be regarded as byte arrays. return 'u1' elif ctype in _ALLOWED_BASIC_CTYPES: return numpy.dtype(ctype) else: raise InvalidCtypeException('Invalid ctypes type.', ctype)
def _NativeStructToH5T(ctype): """Creates an HDF5 Type object from a ctypes type. This function exists as h5t.py_create does not respect field alignment, and the conversion of ctypes.c_char to length one strings is undesired. Args: ctype: Type to convert. Returns: HDF5 type object with correct size and offsets. Raises: InvalidCtypeException: if a type is encountered that isn't a Structure, Array, c_char, or a member of the _ALLOWED_BASIC_CTYPES. """ if isinstance(ctype, type(ctypes.Structure)): t = h5py.h5t.create(h5py.h5t.COMPOUND, ctypes.sizeof(ctype)) for (field_name, field_type) in autogen_util.GetCFields(ctype): t.insert(field_name, getattr(ctype, field_name).offset, _NativeStructToH5T(field_type)) return t elif type(ctype) is type(ctypes.Array): return h5py.h5t.array_create( _NativeStructToH5T(getattr(ctype, '_type_')), (getattr(ctype, '_length_'),)) elif ctype == ctypes.c_char: # The py_create function, like numpy's dtype constructor, converts # characters into strings. This branch is added to force # character arrays to be regarded as byte arrays. return h5py.h5t.py_create(ctypes.c_uint8) elif ctype in _ALLOWED_BASIC_CTYPES: return h5py.h5t.py_create(ctype) else: raise InvalidCtypeException('Invalid ctypes type.', ctype)
def MatchesCStruct(pytype, pyclass, parent=''): """Checks if the Python type matches an equivalent C type. This will display in bold any fields that are in one of pytype and pyclass but not the other. Args: pytype: Python dictionary to be compared to a C struct. pyclass: ctypes Python class equivalent to a C struct. parent: A string used in recursive calls to the function to keep track of the parent fields so they may be displayed. Returns: True if the dictionary and ctypes Python class match, otherwise False. """ def Bold(s): return '\033[1m' + s + '\033[0m' if hasattr(pyclass, '_fields_'): fields = autogen_util.GetCFields(pyclass) # If we are at a leaf struct (like Vec3 or Quat), the pytype can # use an array rather than a dictionary. if (isinstance(pytype, list) and len(pytype) == len(fields) and all([c == ctypes.c_double for (_, c) in fields])): all_floats = True for i in range(len(pytype)): if not isinstance(pytype[i], float): print Bold('%s[%d] must be a float.' % (parent, i)) all_floats = False return all_floats diff = set(pytype) ^ {k for (k, _) in fields} if diff: print Bold(parent + '.{' + ', '.join(diff) + '}') + ' are different.' return False for k, c in fields: if not MatchesCStruct(pytype[k], c, parent + '.' + k): return False return True elif hasattr(pyclass, '_length_'): if isinstance(pytype, str): if getattr(pyclass, '_type_') is not ctypes.c_char: print Bold(parent) + ' should not be a string.\n' return False elif len(pytype) + 1 > getattr(pyclass, '_length_'): # Strings can be shorter than the allotted array (but require # room for null termination). print Bold(parent) + ' has the wrong length.\n' return False else: return True else: expected_length = getattr(pyclass, '_length_') try: actual_length = len(pytype) except TypeError: print( Bold(parent) + ' is expected to be an array of length %d.' % expected_length) return False if actual_length != expected_length: print( Bold(parent) + ' has the wrong length (expected %d, actual %d).\n' % (expected_length, actual_length)) return False else: expected_type = getattr(pyclass, '_type_') return all([ MatchesCStruct(pytype[i], expected_type, '%s[%d]' % (parent, i)) for i in xrange(len(pytype)) ]) else: # Check that scalar types are properly specified. if pyclass == ctypes.c_double and not isinstance(pytype, float): print Bold(parent + ' must be a float.') return False elif pyclass == ctypes.c_int and not isinstance(pytype, int): print Bold(parent + ' must be an int.') return False elif pyclass == ctypes.c_bool and not isinstance(pytype, bool): print Bold(parent + ' must be a bool.') return False return True
def _GetStruct(type_id, path): """Recursively parse a ctypes structure to build a (path, type) list.""" out = [] for field_name, field_type in autogen_util.GetCFields(type_id): out.extend(_GetType(field_type, path + '.' + field_name)) return out
def GetLoadFunc(t): """Generate the JSON loading implementation for a given type. Args: t: A type. Returns: A pair containing C code. The first element is a string with the prototype of the function and the second element is a list of strings giving the lines of C code. Raises: TypeError: The given type is a string type, a fundamental type, or unknown. """ prototype = _GetLoadFunctionPrototype(t) body = [] # This will be an array strings corresponding to lines of code. if _IsArrayType(t): (_, _, args) = _GetTypeInfo(t) body = [ 'if (JSONLoadArrayCheck(obj, len0) < 0) return -1;', 'int32_t status;' ] sub_dim = ['len%d' % d for d in range(1, len(args))] if sub_dim: # TODO: Check for overflow? body += ['uint32_t stride = %s;' % ' * '.join(sub_dim)] dst = '&s[i*stride]' else: dst = 's[i]' scalar_type = _ArrayCTypeScalarType(t) body += [ 'for (uint32_t i = 0U; i < len0; ++i) {', ' ' + _GetLoadFuncCall( scalar_type, 'json_array_get(obj, i)', dst, 'status', sub_dim), ' if (status == -1) {', ' JSONLoadPrintIndexError(i);', ' return -1;', ' }', '}', 'return 0;' ] elif _IsStructType(t): fields = autogen_util.GetCFields(t) body = [ 'if (s == NULL) return -1;', 'if (JSONLoadStructCheck(obj, %uU) < 0) return -1;' % len(fields), 'json_t *value;', 'int32_t status;', '' ] for (f_name, f_type) in fields: body += [ 'value = JSONLoadStructGet(obj, "%s");' % f_name, 'if (value == NULL) return -1;', _GetLoadFuncCall(f_type, 'value', 's->%s' % f_name, 'status'), 'if (status == -1) {', ' JSONLoadPrintKeyError("%s");' % f_name, ' return -1;', '}', '' ] body += ['return 0;'] # These functions are hand written in the files json_load_basic.{c,h}. elif _IsFundamentalType(t) or _IsStringType(t): raise TypeError('Cannot generate load function for type %s.' % str(t)) else: raise TypeError('Unknown type: %s.' % str(t)) return (prototype, ['' if not line else ' ' + line for line in body])