【问题标题】:Maximum serialized Protobuf message size最大序列化 Protobuf 消息大小
【发布时间】:2021-06-14 13:44:45
【问题描述】:

有没有办法在某个 protobuf 消息被序列化后获取其最大大小?

我指的是不包含“重复”元素的消息。

请注意,我不是指的是具有特定内容的 protobuf 消息的大小,而是它可以达到的 最大可能大小(在最坏的情况)。

【问题讨论】:

    标签: protocol-buffers


    【解决方案1】:

    一般来说,由于可能存在未知字段,任何 Protobuf 消息都可以是任意长度。

    如果您接收一条消息,则不能对长度做出任何假设。

    如果您发送一条您自己构建的消息,那么您也许可以假设它只包含您知道的字段——但话又说回来,您也可以轻松地计算出确切的消息大小这种情况。

    因此,通常询问最大尺寸是多少是没有用的。

    话虽如此,您可以编写代码,使用Descriptor 接口来迭代FieldDescriptors 以获得消息类型(MyMessageType::descriptor())。

    见:https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor

    类似的接口存在于 Java、Python 和可能的其他语言中。

    以下是要实施的规则:

    每个字段由一个标签和一些数据组成。

    对于标签:

    • 字段编号 1-15 有一个 1 字节的标签。
    • 字段编号 16 及以上具有 2 字节标签。

    对于数据:

    • bool 始终为一个字节。
    • int32int64uint64sint64 的最大数据长度为 10 个字节(是的,很遗憾,int32 可以是 10 个字节)。
    • sint32uint32 的最大数据长度为 5 个字节。
    • fixed32sfixed32float 总是正好是 4 个字节。
    • fixed64sfixed64double 总是正好是 8 个字节。
    • 枚举类型字段的最大长度取决于最大枚举值:
      • 0-127:1 个字节
      • 128-16384:2 个字节
      • ...它是每字节 7 位,但希望你的枚举不是那么大!
      • 另请注意,负值将被编码为 10 个字节,但希望没有。
    • 消息类型字段的最大长度是消息类型的最大长度加上长度前缀字节。同样,长度前缀是每 7 位整数数据一个字节。
    • 组(您不应该使用它;它们是一个陈旧的旧功能,在 protobuf 甚至公开发布之前已被弃用)的最大大小等于内容的最大大小加上第二个字​​段标签(见上文)。

    如果您的消息包含以下任何内容,则其最大长度是无限的:

    • stringbytes 类型的任何字段。 (除非您知道它们的最大长度,在这种情况下,它是最大长度加上一个长度前缀,就像子消息一样。)
    • 任何重复的字段。 (除非你知道它的最大长度,在这种情况下,列表的每个元素都有一个最大长度,就好像它是一个独立的字段,包括标签。这里没有总长度前缀。除非你使用[packed=true],在这种情况下,您必须查看详细信息。)
    • 扩展。

    【讨论】:

    • 你确定int32 如果为负数最多可以占用 10 个字节吗? AFAIK 任何使用 varint 编码的 int32 都可以使用最多 5 个字节进行编码。
    • @tigrou 是的,我确定,因为我编写了代码。 :) 负的 int32 必须填充到 10 个字节,因为 int32 应该与 int64 向前兼容,因此您可以在将来需要时将现有的 int32 字段更改为 int64。
    【解决方案2】:

    据我所知,Google 自己的 protobuf 中没有计算最大尺寸的功能。

    Nanopb generator 尽可能计算最大大小并将其导出为生成文件中的#define

    根据protobuf encoding documentation手动计算小消息也很简单。

    【讨论】:

    • 太棒了!如果有一些东西可以在运行时执行此操作会很棒,但我想编译时会这样做......下周将尝试使用 Python 脚本,看看是否可以完成这项工作。谢谢!
    【解决方案3】:

    在实现 protobuffer 3 消息大小计算时,我发现 Kenton 所说的大部分内容都是正确的。不过,我确实遇到了一个疏忽:标签是从字段编号创建的,该字段编号左移 3 位,然后与线路类型(在 wire_format_lite.h 中找到)进行逐位或运算。然后将该结果编码为var int。所以对于刚刚超过 16 的标签,标签将是 2 个字节,但如果字段编号更大(>~1000),那么标签将大于 3 个字节。对于 protobuffer 3 用户来说,这可能不是问题,因为有这么大的字段编号是对 protobuf 的滥用。

    【讨论】:

      猜你喜欢
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-27
      • 2023-03-20
      • 1970-01-01
      • 2019-12-22
      • 2012-04-01
      相关资源
      最近更新 更多