协议缓冲区的官方文档https://developers.google.com/protocol-buffers/docs/proto3说 protobuf 消息中字段的最大字段数是 2^29-1。但是为什么会有这个限制?请问有人可以详细解释一下吗?我是新手。
我阅读了这个问题的答案,为什么 2^29-1 是协议缓冲区中最大的键。但我没有澄清
协议缓冲区的官方文档https://developers.google.com/protocol-buffers/docs/proto3说 protobuf 消息中字段的最大字段数是 2^29-1。但是为什么会有这个限制?请问有人可以详细解释一下吗?我是新手。
我阅读了这个问题的答案,为什么 2^29-1 是协议缓冲区中最大的键。但我没有澄清
编码协议缓冲区中的每个字段都有一个以实际编码值为前缀的标头(称为键或标签)。编码规范定义了这个键:
流式消息中的每个键都是一个 varint,其值为 (field_number << 3) | 线型——换句话说,数字的最后三位存储线型。
这里的规范说标签是一个 varint,其中前 3 位用于编码线路类型。varint 可以编码一个 64 位的值,因此只要继续这个定义,限制就会是2^61-1
.
除此之外,语言指南将其缩小到最大 32 位值。
您可以指定的最小字段数是 1,最大的是 2^29 - 1,即 536,870,911。
没有给出这样做的原因。我只能推测这背后的原因:
人为限制,因为没有人期望消息具有这么多字段。只需考虑将具有这么多字段的消息放入内存中。
由于键是 varint,它不仅仅是原始缓冲区中接下来的 4 个字节,而是可变长度的字节(读取 varint32 的 Java 代码)。每个字节有 7 位实际数据和 1 位指示是否到达末尾。出于性能原因,限制范围被认为是更好的选择。
由于 proto3 是协议缓冲区的第 3 个版本,因此 proto1 或 proto2 可能将标记定义为 varint32。为了保持向后兼容性,这个限制在今天的 proto3 中仍然有效。
因为这条线:
#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 字段对于单个消息声明来说足够大。
我怀疑这只是为了将字段标题(线路类型和标签编号)解码并作为 32 位值处理。接线类型始终是 3 个最低有效位,留下 29 位作为标签号。从技术上讲,“varint”应该支持 64 位,但将其限制为合理的数字是有意义的,尤其是因为“varint”编码意味着更大的数字需要更多的字节来编码。
编辑:我现在意识到这与链接的帖子相似,但是......它仍然是真实的!protobuf 中的每个字段都以“varint”为前缀,表示后面跟着什么字段(标签号),以及它是什么数据类型(线型)。后者尤其重要,以便可以正确存储或跳过意外字段(版本差异)。大多数框架可以轻松地处理该字段标题,并且大多数框架都可以使用 32 位整数。
这是另一个问题,而不是评论,在它说的文件中,
16 到 2047 范围内的字段编号占用两个字节。因此,您应该为非常频繁出现的消息元素保留数字 1 到 15。请记住为将来可能添加的频繁出现的元素留出一些空间。
因为对于第一个字节,前5位用于字段编号,而后3位用于字段类型,那么从31(因为没有使用零)到2047的字段编号是不是占了两个字节?(而且我还猜想第二个字节的低 3 位也用于字段类型。我正在阅读它,所以我会在知道后修复它)