def simple_serializer_generator(msg_context, spec, start, end, serialize): #primitives that can be handled with struct """ Generator (de)serialization code for multiple fields from spec :param spec: :class:`genmsg.MsgSpec` :param start: first field to serialize, ``int`` :param end: last field to serialize, ``int`` """ # optimize member var access if end - start > 1 and _serial_context.endswith('.'): yield '_x = '+_serial_context[:-1] vars_ = '_x.' + (', _x.').join(spec.names[start:end]) else: vars_ = _serial_context + (', '+_serial_context).join(spec.names[start:end]) pattern = compute_struct_pattern(spec.types[start:end]) if serialize: yield pack(pattern, vars_) else: yield "start = end" yield "end += %s"%struct.calcsize('<%s'%reduce_pattern(pattern)) yield unpack('(%s,)'%vars_, pattern, 'str[start:end]') # convert uint8 to bool. this doesn't add much value as Python # equality test on a field will return that True == 1, but I # want to be consistent with bool bool_vars = [(f, t) for f, t in zip(spec.names[start:end], spec.types[start:end]) if t == 'bool'] for f, t in bool_vars: #TODO: could optimize this as well var = _serial_context+f yield "%s = bool(%s)"%(var, var)
def array_serializer_generator(msg_context, package, type_, name, serialize, is_numpy): """ Generator for array types :raises: :exc:`MsgGenerationException` If array spec is invalid """ base_type, is_array, array_len = genmsg.msgs.parse_type(type_) if not is_array: raise MsgGenerationException("Invalid array spec: %s"%type_) var_length = array_len is None # handle fixed-size byte arrays could be slightly more efficient # as we recalculated the length in the generated code. if base_type in ['char', 'uint8']: #treat unsigned int8 arrays as string type for y in string_serializer_generator(package, type_, name, serialize): yield y return var = _serial_context+name # yield length serialization, if necessary if var_length: for y in len_serializer_generator(var, False, serialize): yield y #serialize array length length = None else: length = array_len #optimization for simple arrays if is_simple(base_type): if var_length: pattern = compute_struct_pattern([base_type]) yield "pattern = '<%%s%s'%%length"%pattern if serialize: if is_numpy: yield pack_numpy(var) else: yield pack2('pattern', "*"+var) else: yield "start = end" yield "end += struct.calcsize(pattern)" if is_numpy: dtype = NUMPY_DTYPE[base_type] yield unpack_numpy(var, 'length', dtype, 'str[start:end]') else: yield unpack2(var, 'pattern', 'str[start:end]') else: pattern = "%s%s"%(length, compute_struct_pattern([base_type])) if serialize: if is_numpy: yield pack_numpy(var) else: yield pack(pattern, "*"+var) else: yield "start = end" yield "end += %s"%struct.calcsize('<%s'%pattern) if is_numpy: dtype = NUMPY_DTYPE[base_type] yield unpack_numpy(var, length, dtype, 'str[start:end]') else: yield unpack(var, pattern, 'str[start:end]') if not serialize and base_type == 'bool': # convert uint8 to bool if base_type == 'bool': yield "%s = map(bool, %s)"%(var, var) else: #generic recursive serializer #NOTE: this is functionally equivalent to the is_registered branch of complex_serializer_generator # choose a unique temporary variable for iterating loop_var = 'val%s'%len(_context_stack) # compute the variable context and factory to use if base_type == 'string': push_context('') factory = string_serializer_generator(package, base_type, loop_var, serialize) else: push_context('%s.'%loop_var) factory = serializer_generator(msg_context, get_registered_ex(msg_context, base_type), serialize, is_numpy) if serialize: yield 'for %s in %s:'%(loop_var, var) else: yield '%s = []'%var if var_length: yield 'for i in range(0, length):' else: yield 'for i in range(0, %s):'%length if base_type != 'string': yield INDENT + '%s = %s'%(loop_var, compute_constructor(msg_context, package, base_type)) for y in factory: yield INDENT + y if not serialize: yield INDENT + '%s.append(%s)'%(var, loop_var) pop_context()
def string_serializer_generator(package, type_, name, serialize): """ Generator for string types. similar to arrays, but with more efficient call to struct.pack. :param name: spec field name, ``str`` :param serialize: if ``True``, generate code for serialization. Other, generate code for deserialization, ``bool`` """ # don't optimize in deserialization case as assignment doesn't # work if _serial_context and serialize: # optimize as string serialization accesses field twice yield "_x = %s%s"%(_serial_context, name) var = "_x" else: var = _serial_context+name # the length generator is a noop if serialize is True as we # optimize the serialization call. base_type, is_array, array_len = genmsg.msgs.parse_type(type_) # - don't serialize length for fixed-length arrays of bytes if base_type not in ['uint8', 'char'] or array_len is None: for y in len_serializer_generator(var, True, serialize): yield y #serialize string length if serialize: #serialize length and string together #check to see if its a uint8/byte type, in which case we need to convert to string before serializing base_type, is_array, array_len = genmsg.msgs.parse_type(type_) if base_type in ['uint8', 'char']: yield "# - if encoded as a list instead, serialize as bytes instead of string" if array_len is None: yield "if type(%s) in [list, tuple]:"%var yield INDENT+pack2("'<I%sB'%length", "length, *%s"%var) yield "else:" yield INDENT+pack2("'<I%ss'%length", "length, %s"%var) else: yield "if type(%s) in [list, tuple]:"%var yield INDENT+pack('%sB'%array_len, "*%s"%var) yield "else:" yield INDENT+pack('%ss'%array_len, var) else: # FIXME: for py3k, this needs to be w/ encode(), but this interferes with actual byte data yield "if python3 or type(%s) == unicode:"%(var) yield INDENT+"%s = %s.encode('utf-8')"%(var,var) #For unicode-strings in Python2, encode using utf-8 yield INDENT+"length = len(%s)"%(var) # Update the length after utf-8 conversion yield "if python3:" yield INDENT+pack2("'<I%sB'%length", "length, *%s"%var) yield "else:" yield INDENT+pack2("'<I%ss'%length", "length, %s"%var) else: yield "start = end" if array_len is not None: yield "end += %s" % array_len yield "%s = str[start:end]" % var else: yield "end += length" if base_type in ['uint8', 'char']: yield "%s = str[start:end]" % (var) else: yield "if python3:" yield INDENT+"%s = str[start:end].decode('utf-8')" % (var) #If messages are python3-decode back to unicode yield "else:" yield INDENT+"%s = str[start:end]" % (var)