【问题标题】:Is there a portable Binary-serialisation schema in FlatBuffers/Protobuf that supports arbitrary 24bit signed integer definitions?FlatBuffers/Protobuf 中是否有支持任意 24 位有符号整数定义的可移植二进制序列化模式?
【发布时间】:2020-01-16 11:49:10
【问题描述】:

我们通过 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;

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

【问题讨论】:

  • 感谢您的所有回复。我正在评估选项。

标签: c++ c int protocol-buffers flatbuffers


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    根据不同的情况,您可能希望查看 ASN.1 和未对齐的打包编码规则 (uPER)。这是一种在电话中广泛使用的二进制序列化,可以轻松地最小化传输的位数。工具可用于 C、C++、C#、Java、Python(我认为它们涵盖了 uPER)。一个好的起点是Useful Old Technologies

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

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

    【讨论】:

    • 我喜欢从这个答案中学到的东西,并将这个 ASN.1 作为一个肯定的答案进行调查。
    • 所以基本上这看起来像是一个很好的标准,但它可能出现得有点早,所以在预开源系统之外没有被广泛使用。尽管使用 C 和 Java,ESA 支持的编译器听起来很棒。主要障碍(如答案中所述)是此编译器支持。我们将在我们的组合(Node.js)中使用 Javascript,但我还无法在这里找到 uPER 支持。
    • 好的,所以缺少 JavaScript 让我有点担心,但事实上我得到了你列出的这么多核心语言 + ADA 它是可行的并且在标准范围内,而不是其他选项“不太”存在。它是 ASN.1 的真正用途,来自我收集的内容!我可以手动滚动协议的 JavaScript 版本,这与其他任何答案都有关,但这个 ASN.1 需要大量维护:D
    • @Crog,您是否考虑过在 WASM 中运行 C/C++ ASN.1 工具集并与之交互? ASN.1 标准有很多内容,而自己实现整个事情是一项艰巨的任务。诚然,在 WASM 中与复杂的 C 类型接口可能会更难……另外请先彻底评估工具;商业工具非常好,但要花很多钱(但节省的时间对我来说通常是净收益)。也有一些相当不错的 OSS 实现。
    • @Crog:这里有一些针对初学者的链接。本书oss.com/asn1/resources/books-whitepapers-pubs/…,一个商业工具集oss.com,另一个商业工具集,obj-sys.com/products/asn1c/pricing.php,一个便宜但很好的商业工具集bellard.org/ffasn1,一个相当不错的OSS项目github.com/vlm/asn1c。所有这些我都用过,而且它们都可以合理使用。如果您需要源代码,该法语工具集以源代码的形式提供,而其他商业工具集则不需要(除非您财力雄厚)。
    【解决方案3】:

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

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

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

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

    【讨论】:

    • 我喜欢考虑使用 var-int 进行增量压缩。最初我想让事情保持简单,但考虑到所需的额外样板,我会平衡这一点。
    【解决方案4】:

    Protocol Buffers 使用称为varint 的动态大小的整数编码,因此您可以只使用uint32sint32,所有值的编码值将是四个字节或更少,任何值的编码值将是三个字节或更少

    确保不要使用int32,因为它使用效率非常低的fixed size encoding(10 个字节!)作为负值。对于重复值,只需将它们标记为重复,这样多个值将被高效发送packed

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-14
      • 2012-07-15
      • 2019-05-23
      • 2023-04-07
      • 1970-01-01
      • 2019-08-20
      • 1970-01-01
      相关资源
      最近更新 更多