【问题标题】:maximum field number in protobuf messageprotobuf 消息中的最大字段数
【发布时间】:2019-12-22 13:19:39
【问题描述】:

protobuf 的官方文档https://developers.google.com/protocol-buffers/docs/proto3 说 protobuf 消息中字段的最大字段数是 2^29-1。但是为什么会有这个限制? 请问有人可以详细解释一下吗?我是新手。

我在why 2^29-1 is the biggest key in protocol buffers 阅读了这个问题的答案。 但我没有澄清

【问题讨论】:

    标签: protocol-buffers grpc-python


    【解决方案1】:

    怀疑这只是为了可以将字段标题(线路类型和标签编号)解码并作为 32 位值处理。接线类型始终是 3 个最低有效位,留下 29 位作为标签号。从技术上讲,“varint”应该支持 64 位,但将其限制为合理的数字是有意义的,尤其是因为“varint”编码意味着更大的数字需要更多的字节来编码。

    编辑:我现在意识到这与链接的帖子相似,但是......它仍然是真实的! protobuf 中的每个字段都以“varint”为前缀,表示后面跟着什么字段(标签号),以及它是什么数据类型(线型)。后者尤其重要,以便可以正确存储或跳过意外字段(版本差异)。大多数框架都可以轻松处理该字段标题,并且大多数框架都可以处理 32 位整数。

    【讨论】:

    • 标签号和字段号一样吗?
    • 字段头使用 32 位值有什么特别的原因吗?
    • @nehadeshpande 是的,我已经说过:a) 它在所有平台上都运行良好,b) 计算效率很高,c) 它可以防止字段标题变得不必要的大
    • @nehadeshpande 对比 64 位:仅通过 IEEE 处理数字的平台(例如 Lua):无法正确表示所有 64 位整数
    • 如果可能的话,你能帮我做一件事吗?我已经定义了 int32 格式的消息,最大字段号为 2^29-1。对消息进行编码后得到的输出是 b'\xf8\xff\xff\xff\x0f\x01'。我无法理解这里的每个字节代表什么
    【解决方案2】:

    编码协议缓冲区中的每个字段都有一个以实际编码值为前缀的标头(称为键或标记)。 encoding spec 定义了这个键:

    流式消息中的每个键都是一个 varint,其值为 (field_number

    这里的规范说标签是一个 varint,其中前 3 位用于编码线路类型。 varint 可以编码一个 64 位的值,因此只要继续这个定义,限制就是 2^61-1

    除此之外,Language Guide 将其缩小到最大 32 位值。

    您可以指定的最小字段编号为 1,最大为 2^29 - 1,即 536,870,911。

    没有给出原因。我只能推测这背后的原因:

    1. 人为限制,因为没有人期望消息包含这么多字段。想想把包含这么多字段的消息放入内存中。

    2. 由于键是 varint,它不仅仅是原始缓冲区中接下来的 4 个字节,而是可变长度的字节 (Java code reading a varint32)。每个字节有 7 位实际数据和 1 位指示是否到达末尾。令人困惑的是,出于性能原因,限制范围被认为更好。

    3. 由于 proto3 是协议缓冲区的第 3 个版本,因此 proto1 或 proto2 可能将标记定义为 varint32。为了保持向后兼容性,这个限制在今天的 proto3 中仍然有效。

    【讨论】:

      【解决方案3】:

      因为这条线:

      #define GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(FIELD_NUMBER, TYPE) \
        static_cast<uint32>((static_cast<uint32>(FIELD_NUMBER) << 3) | (TYPE))
      

      这一行创建一个“标签”,只剩下 29 (32 - 3) 位来保存字段索引。

      不知道为什么 google 使用 uint32 而不是 uint64,因为字段编号是一个 varint,可能他们认为 2^29-1 字段对于单个消息声明来说足够大。

      【讨论】:

        【解决方案4】:

        这是另一个问题,而不是评论,在它说的文档中,

        16 到 2047 范围内的字段编号占用两个字节。那么你 应该保留数字 1 到 15 用于非常频繁发生 消息元素。记得为经常发生的事情留一些空间 未来可能会添加的元素。

        因为对于第一个字节,前5位用于字段编号,后3位用于字段类型,那么从31(因为没有使用零)到2047的字段编号是不是占了两个字节? (而且我还猜想第二个字节的低 3 位也用于字段类型。我正在阅读它,所以我会在知道后修复它)

        【讨论】:

          猜你喜欢
          • 2018-09-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-01-09
          • 1970-01-01
          • 1970-01-01
          • 2020-04-19
          相关资源
          最近更新 更多