FlatBuffers/Protobuf 中是否有支持任意 24 位有符号整数定义的可移植二进制序列化架构?

Is there a portable Binary-serialisation schema in FlatBuffers/Protobuf that supports arbitrary 24bit signed integer definitions?

本文关键字:二进制 可移植 定义 序列化 整数 Protobuf 是否 支持 FlatBuffers 任意 符号      更新时间:2023-10-16

我们通过UART串行以高数据速率发送数据,因此数据大小很重要。对于我们的数据,最优化的格式是 Int24,它可以在 C/C++ 下简化为 C 位字段结构(GCC 编译器(以达到完美最佳:

#pragma pack(push, 1)
struct Int24
{
int32_t value : 24;
};
#pragma pack(pop)
typedef std::array<Int24,32> ArrayOfInt24;

此数据与其他数据打包,并在设备和云基础架构之间共享。基本上,我们需要有一个二进制序列化,该序列化在不同体系结构和编程语言的设备之间发送。我们希望使用基于模式的二进制序列化,例如ProtoBuffers或FlatBuffers,以避免客户端代码需要处理各自的位移和恢复二进制补码符号位处理本身。即,在非 C 语言中读取 24 位值需要以下内容:

bool isSigned = (_b2 & (byte)0x80) != 0; // Sign extend negative quantities 
int32_t value = _b0 | (_b1 << 8) | (_b2 << 16) | (isSigned ? 0xFF : 0x00) << 24;

如果不存在,可以轻松修改现有的二进制序列化库(如果有的话(以扩展对此的支持,因为我们愿意在这方面添加到任何开源项目中。

根据各种情况,您可能希望查看ASN.1和未对齐的打包编码规则(uPER(。这是一种二进制序列化,广泛用于电话服务,可轻松最大程度地减少传输位的数量。工具可用于C,C++,C#,Java,Python(我认为它们涵盖了uPER(。一个好的起点是有用的旧技术。

您可能选择使用它的原因之一是uPER最终可能比其他任何东西都做得更好。其他好处是约束(值和数组大小(。您可以在架构中表达这些内容,生成的代码将根据它们检查数据。这是可以对项目产生真正影响的事情 - 自动清理传入数据是抵御攻击的好方法 - 并且是GPB不做的事情。

不使用它的原因是最好的工具是商业的,而且相当昂贵。虽然有一些开源工具非常好,但不一定实现整个ASN.1标准(这是巨大的(。这也是一个学习曲线,尽管(在基本层面上(与Google协议缓冲区没有太大区别。事实上,在谷歌宣布GPB的会议上,有人问"为什么不使用ASN.1?"。谷歌的博德没有听说过它;有点讽刺的是,一家搜索公司没有在网上搜索二进制序列化技术,而是直接发明了自己的......

协议缓冲区使用称为varint的动态大小的整数编码,因此您可以只使用uint32sint32,并且编码值对于所有值将为四个字节或更少,对于任何值<2^21,编码值将为三个字节或更少(编码整数的实际大小为⌈HB/7⌉其中HB是值中设置的最高位(。

确保不要使用int32因为它对负值使用非常低效的固定大小编码(10 字节!对于重复值,只需将它们标记为重复,这样多个值将被有效地打包发送。

syntax = "proto3";
message Test {
repeated sint32 data = 1;
}

FlatBuffers 不支持 24 位整数。表示它的唯一方法是:

struct Int24 { a:ubyte; b:ubyte; c:ubyte; }

这显然不会为您进行位移,但仍然允许您有效地将多个Int24打包到父向量或结构中。当存储在表中时,它也会节省一个字节,尽管在那里你可能最好只使用 32 位 int,因为开销更高。

protobuf 变体格式的一个特别有效的使用是通过写入值之间的增量将其用作一种压缩方案。

在您的情况下,如果连续值之间存在任何相关性,则可以有一个repeated sint32 values字段。然后作为数组中的第一个条目,写入第一个值。对于所有进一步的条目,请写下与先前值的差异。

这种方式例如[100001, 100050, 100023, 95000]将被编码为[100001, 49, -27, -5023].作为一个打包的变体数组,增量将占用 3、1、1 和 2 个字节,总共 7 个字节。与固定的 24 位编码占用 12 个字节或非增量变体占用 12 个字节相比。

当然,这也需要在接收端处理一些代码。但是,将先前的值相加很容易在任何语言中实现。