参数和数据结构的序列化
序列化是基于接口规范定义的参数列表。为了允许服务接口的**迁移,反序列化,**代码应忽略附加在先前已知的参数列表末尾的参数;也就是说,忽略在用于生成或参数化反序列化代码的接口规范中未定义的参数。
接口规范定义了PDU中所有参数的确切位置,并考虑了内存对齐。序列化过程不应尝试自动对齐参数,而应按照接口规范中指定的对齐方式进行对齐。 SOME/IP负载应该在内存中放置,以使SOME/IP负载能够适当地对齐。对于车载娱乐设备的ECU,应该实现8字节(即64位)的对齐,对于所有ECU,至少应实现4字节的对齐。
接下来将指定不同参数的反序列化过程。
基本数据类型
应支持以下基本数据类型:
|---------|-----------------------|--------------|--------------------------------------|
| Type | Description | Size [bit] | Remark |
| boolean | TRUE/FALSE value | 8 | FALSE (0), TRUE (1) |
| uint8 | unsigned Integer | 8 | |
| uint16 | unsigned Integer | 16 | |
| uint32 | unsigned Integer | 32 | |
| sint8 | signed Integer | 8 | |
| sint16 | signed Integer | 16 | |
| sint32 | signed Integer | 32 | |
| float32 | floating point number | 32 | IEEE 754 binary32 (Single Precision) |
| float64 | floating point number | 64 | IEEE 754 binary64 (Double Precision) |
每个参数的字节顺序由接口定义进行规定。
结构化数据类型 (结构体)
结构体的序列化应尽可能接近内存布局。这意味着参数应按顺序序列化到缓冲区中。对于结构体来说,特别要考虑正确的内存对齐。如果需要对齐,请在接口定义中插入保留/填充元素,因为SOME/IP实现不会自动添加此类填充。
如果SOME/IP实现遇到导致PDU未正确对齐的接口规范(例如,由于未对齐的结构体),SOME/IP实现应在发现不对齐的结构体时发出警告,但不应在生成代码时失败。
结构体的序列化应按照规范精确地进行。
SOME/IP实现不应自动插入虚拟/填充元素。
接口规范可以在结构体前面添加一个8位、16位或32位的长度字段。如果未指定长度字段的长度,则必须假定长度为0,并且消息中没有长度字段。
结构体的长度字段描述了结构体的字节数。如果长度大于接口定义中指定的结构体长度,则只有接口规范中指定的字节将被解释,其他字节将根据长度字段被跳过。这样可以实现可扩展的结构体,从而更好地实现接口的迁移。
字符串(定长)
字符串使用Unicode进行编码,并以"\0"字符结尾。在接口定义中必须指定字符串的字节长度(包括"\0")。使用"\0"填充未使用的空间。
应支持不同的Unicode编码,包括UTF-8、UTF-16BE和UTF-16LE。由于这些编码每个字符的字节长度是动态的,字节的最大长度是UTF-8中字符长度的三倍加上1个字节的"\0"终止符,或者UTF-16中字符长度的两倍加上2个字节的"\0"终止符。
字符串的编码方式应在接口定义中指定。
字符串(动态长度)
动态长度的字符串以长度字段开头。长度以字节为单位,并跟随以"\0"结尾的字符串数据。接口定义还应定义字符串(包括以"\0"结尾)可以占用的最大字节数。
长度字段的长度可以是8位、16位或32位。固定长度的字符串可以视为长度字段为0位。
如果接口规范没有另外指定,长度字段的长度为32位(长度字段的默认长度)。
字符串长度字段的值不考虑在长度字段本身的值中;也就是说,长度字段不计入自身的长度。
支持的编码方式如[SIP_RPC_232第19页]中所定义。
如果接口定义提示下一个数据元素的对齐方式,字符串应通过添加"\0"字符来扩展以满足对齐要求。
数组(固定长度)
固定长度数组的长度由接口定义确定。它们可以被视为重复的元素。在[SIP_RPC_253 页错误!未定义书签。]中展示了动态长度数组,也可以使用。然而,固定长度数组可以更容易地集成到早期版本的AUTOSAR和非常小的设备中;因此,两种选项都得到支持。
一维数组
固定长度为n的一维数组包含恰好n个相同类型的元素。布局如图2所示。
多维数组
多维数组的序列化遵循C++编程语言中多维数组的内存布局(按行主序),如图3所示
可选字段
可选字段应被编码为包含0到1个元素的数组。对于具有动态长度的数组的序列化,请参阅[SIP_RPC_253]。
动态长度数组
具有动态长度的数组的布局基本上基于固定长度数组的布局。为了确定数组的大小,序列化在数据前面添加一个长度字段(默认长度为32位),该字段计算数组的字节数。长度不包括长度字段的大小。因此,在传输零元素的数组时,长度设置为零。
接口定义可以定义长度字段的长度。允许长度为0、8、16和32位。如果将长度设置为0位,则数组中的元素数量必须是固定的;因此,它是具有固定长度的数组。
动态数组的布局如图4和图5所示。
在一维数组中,使用一个长度字段,该字段表示数组使用的字节数。可以通过将字节数除以元素的大小来轻松计算元素的数量。
在多维数组中,需要多个长度字段。接口定义应定义每个维度的最大长度,以便允许进行静态缓冲区大小的分配。在以字节为单位测量长度时,可以在反序列化过程中跳过复杂的多维数组。
枚举
接口定义可以基于无符号整数数据类型(uint8、uint16、uint32、uint64)指定枚举类型。
位域
位域应作为基本数据类型uint8/uint16/uint32进行传输。接口定义应能够定义每个位的名称。接口定义应能够定义位可以设置为的值的名称。
每个SOME/IP实现可以选择对位域进行解/序列化,或者将uint8/uint16/uint32传递给应用程序。SOME/IP实现可以允许开启或关闭位域的解/序列化。
联合(变体)
联合(也称为变体)是一种可以包含不同类型元素的参数。例如,如果定义了一个uint8类型和uint16类型的联合,该联合将携带一个uint8或uint16类型的元素。显然,当使用不同类型的元素时,后续参数的对齐可能会被扭曲。为了解决这个问题,可能需要填充。
默认情况下,SOME/IP中联合的序列化布局如下:
长度字段和类型字段的顺序可以通过接口规范进行调整。如果未指定,则使用[TR_SOMEIP_00119]中的默认布局。
长度字段的长度应由接口规范定义,并且可以是32位、16位、8位或0位。长度字段为0位表示不会将长度字段写入PDU。如果长度字段为0位,则联合中的所有类型的长度必须相同。
如果接口规范定义了一个长度字段为0位的联合,并且具有不同长度的类型,则SOME/IP实现应该发出警告,并使用最长元素的长度,并用零(0x00)填充其他元素。
如果接口规范未指定联合的长度字段的长度,则应使用32位长度的长度字段。
长度字段以字节为单位定义元素和填充的大小,不包括长度字段和类型字段的大小。
类型字段的长度应由接口规范定义,并且可以是32位、16位或8位。
如果接口规范未指定联合的类型字段的长度,则应使用32位长度的类型字段。
类型字段描述元素的类型。类型字段的可能值由接口规范为每个联合单独定义。类型按照接口规范中的编码以升序排列,从1开始。0保留为NULL类型,即空联合。接口定义应允许使用NULL。
根据类型字段中的类型,元素将进行序列化。结合长度字段,可以在元素后面添加填充。反序列化器应根据长度字段跳过字节。每种类型的长度字段的值应由接口规范定义。通过使用结构体,可以实现不同的填充布局。
示例:uint8/uint16联合,均填充为32位
在此示例中,长度字段的长度指定为32位。该联合应支持uint8和uint16作为元素。两者都填充到32位边界(长度为4)。uint8的序列化如下所示:
uint16的序列化如下所示:
将映射或字典描述为键值对数组是最基本的方式。实现映射或字典的最基本方法是使用一个具有两个字段(键和值)的结构体数组。由于结构体没有长度字段,这种方式与特殊的映射或字典类型一样高效。如果选择uint16作为键和值,一个包含3个条目的序列化映射如下所示: