Protobuf消息中的最大字段数量

5
官方文件《协议缓冲区》(Protocol Buffers)https://developers.google.com/protocol-buffers/docs/proto3规定,Protobuf消息中字段的最大字段号为2^29-1。但是,为什么会有这个限制呢?有没有人能够详细解释一下呢?我是新手,请讲得通俗易懂一些。
我在Stack Overflow上看到了对这个问题的回答,但是还是不太明白。
4个回答

3
在编码协议缓冲区中,每个字段都有一个标题(称为键或标记),前缀到实际编码值。 编码规范 定义了这个键:
每个流消息中的键都是具有值(field_number << 3)| wire_type 的变长整数,换句话说,数字的最后三位存储线路类型。
在这里,规范指出标记是一个变量,其中使用前3位来编码线路类型。Varint可以编码64位值,因此仅按照此定义,限制将是2 ^ 61-1。
除此之外,《语言指南》将其缩小到最大值为32位。
您无法指定的最小字段号码为1,而最大值为2 ^ 29-1,即536,870,911。
这些限制的原因没有给出。只能猜测背后的原因:
1. 作为人工限制,因为没有人期望一条消息具有那么多字段。只需考虑将具有如此多字段的消息放入内存中。
2. 由于键是varint,因此它不仅仅是原始缓冲区中的下4个字节,而是可变长度的字节,每个字节具有7位实际数据和1位指示是否已到达结尾。出于性能原因,可能认为限制范围更好。
3. 由于proto3是协议缓冲区的第三个版本,因此proto1或proto2可能将标记定义为varint32。为了保持向后兼容性,这个限制在proto3中仍然存在。

1
由于这行代码:
#define GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(FIELD_NUMBER, TYPE) \
  static_cast<uint32>((static_cast<uint32>(FIELD_NUMBER) << 3) | (TYPE))

这行代码创建了一个“标记”,只留下29(32-3)位来保存字段索引。
不知道为什么谷歌使用uint32而不是uint64,因为字段号是varint类型,也许他们认为2^29-1个字段已经足够单个消息声明使用了。

0

我怀疑这只是为了解码和处理字段头(线路类型和标记号)作为32位值。线路类型始终是最不重要的3位,留下29位用于标记号。从技术上讲,“varint”应该支持64位,但限制在合理的数字范围内是有意义的,因为“varint”编码意味着较大的数字需要更多的字节来编码。

编辑:我现在意识到这与链接的帖子类似,但...它仍然是真实的!Protobuf中的每个字段都以“varint”为前缀,表示后面跟随的字段(标记号)以及数据类型(线路类型)。后者尤其重要,以便可以正确存储或跳过意外字段(版本差异)。对于大多数框架来说,使该字段头轻松处理非常方便,而且大多数框架都可以使用32位整数。


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

0

这是一个问题而不是评论,在文档中写道:

范围在16到2047的字段号占用两个字节。因此,您应该为非常频繁出现的消息元素保留1到15号码。记得留出一些空间,以便将来可能添加的频繁出现的元素。

因为对于第一个字节,前5位用于字段号,后3位用于字段类型,所以字段号从31(因为0未使用)到2047需要占用两个字节吗?(我还猜测第二个字节的低3位也用于字段类型...我正在阅读中,所以等我弄清楚了再修正它)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接