便携式C二进制序列化原语
据我所知,C库没有帮助将数值序列化为非文本字节流。 如我错了请纠正我。
使用的最标准工具是来自POSIX的htonl
等人。 这些function有缺点:
- 没有64位支持。
- 没有浮点支持。
- 签名类型没有版本。 反序列化时,无符号到符号的转换依赖于有符号整数溢出,即UB。
- 它们的名称没有说明数据类型的大小。
- 它们依赖于8位字节和精确大小的uint_ N _t的存在。
- 输入类型与输出类型相同,而不是引用字节流。
- 这要求用户执行指针类型转换,这可能在对齐时不安全。
- 执行该类型转换后,用户可能会尝试在其本机内存布局中转换和输出结构,这种做法很糟糕,导致意外错误。
用于将任意大小的char
化为8位标准字节的接口将落在C标准之间,该标准不真正确认8位字节,并且无论标准(ITU?)将八位位组设置为基本传输单位。 但旧的标准没有得到修订。
现在C11有许多可选组件,可以添加二进制序列化扩展以及线程之类的东西,而不需要对现有实现提出要求。
这样的扩展是否有用,或者担心非二进制补充机器是否毫无意义?
我从未使用它们,但我认为Google的Protocol Buffers满足您的要求。
- 支持64位类型,有符号/无符号和浮点类型。
- 生成的API是类型安全的
- 可以对流进行序列化
本教程似乎是一个非常好的介绍 ,您可以在这里阅读有关实际的二进制存储格式。
从他们的网页 :
什么是协议缓冲?
协议缓冲区是Google的语言中立,平台中立,可扩展的机制,用于序列化结构化数据 – 思考XML,但更小,更快,更简单。 您可以定义数据的结构化时间,然后使用特殊生成的源代码,可以使用各种语言(Java,C ++或Python)轻松地在各种数据流中编写和读取结构化数据。
纯C(仅限C ++)中没有正式实现,但有两个C端口可能满足您的需求:
-
Nanopb, http: //koti.kapsi.fi/jpa/nanopb/
-
Protobuf-c, url为http://code.google.com/p/protobuf-c/
我不知道它们是否存在非8位字节,但它应该相对容易找到。
在我看来,像htonl()
这样的函数的主要缺点是它们只执行序列化工作的一半。 如果你的机器是小端,它们只会翻转多字节整数中的字节。 序列化时必须完成的另一件重要事情是处理对齐,而这些函数不会这样做。
许多CPU不能(有效地)访问未存储在存储器位置的多字节整数,该存储器位置的地址不是整数字节大小的倍数。 这是永远不会使用struct overlays(de)序列化网络数据包的原因。 我不确定这是否是’就地转换’的意思。
我在嵌入式系统上工作很多,而且我在自己的库中运行,在生成或解析网络数据包(或任何其他I / O:磁盘,RS232等)时我总是使用它:
/* Serialize an integer into a little or big endian byte buffer, resp. */ void SerializeLeInt(uint64_t value, uint8_t *buffer, size_t nrBytes); void SerializeBeInt(uint64_t value, uint8_t *buffer, size_t nrBytes); /* Deserialize an integer from a little or big endian byte buffer, resp. */ uint64_t DeserializeLeInt(const uint8_t *buffer, size_t nrBytes); uint64_t DeserializeBeInt(const uint8_t *buffer, size_t nrBytes);
除了这些函数之外,还有一些宏定义如下:
#define SerializeBeInt16(value, buffer) SerializeBeInt(value, buffer, sizeof(int16_t)) #define SerializeBeUint16(value, buffer) SerializeBeInt(value, buffer, sizeof(uint16_t)) #define DeserializeBeInt16(buffer) DeserializeBeType(buffer, int16_t) #define DeserializeBeUint16(buffer) DeserializeBeType(buffer, uint16_t)
(de)序列化函数逐字节读取或写入值,因此不会出现对齐问题。 您也不必担心签名。 首先,所有系统现在使用2s补码(除了几个ADC,但是你不会使用这些function)。 然而,它甚至应该在使用1s补码的系统上工作,因为(据我所知)有符号整数在转换为无符号时被转换为2s补码(并且函数接受/返回无符号整数)。
你的另一个论点是它们依赖于8位字节和精确大小的uint_N_t
的存在。 这也是我的function,但在我看来这不是一个问题(这些类型总是为我使用的系统及其编译器定义)。 您可以调整函数原型以使用unsigned char
而不是uint8_t
,如果您愿意,可以使用long long
或uint_least64_t
而不是uint64_t
。
请参阅xdr库和XDR标准RFC-1014 RFC-4506