【发布时间】:2021-06-14 13:44:45
【问题描述】:
有没有办法在某个 protobuf 消息被序列化后获取其最大大小?
我指的是不包含“重复”元素的消息。
请注意,我不是指的是具有特定内容的 protobuf 消息的大小,而是它可以达到的 最大可能大小(在最坏的情况)。
【问题讨论】:
标签: protocol-buffers
有没有办法在某个 protobuf 消息被序列化后获取其最大大小?
我指的是不包含“重复”元素的消息。
请注意,我不是指的是具有特定内容的 protobuf 消息的大小,而是它可以达到的 最大可能大小(在最坏的情况)。
【问题讨论】:
标签: protocol-buffers
一般来说,由于可能存在未知字段,任何 Protobuf 消息都可以是任意长度。
如果您接收一条消息,则不能对长度做出任何假设。
如果您发送一条您自己构建的消息,那么您也许可以假设它只包含您知道的字段——但话又说回来,您也可以轻松地计算出确切的消息大小这种情况。
因此,通常询问最大尺寸是多少是没有用的。
话虽如此,您可以编写代码,使用Descriptor 接口来迭代FieldDescriptors 以获得消息类型(MyMessageType::descriptor())。
见:https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor
类似的接口存在于 Java、Python 和可能的其他语言中。
以下是要实施的规则:
每个字段由一个标签和一些数据组成。
对于标签:
对于数据:
bool 始终为一个字节。int32、int64、uint64 和 sint64 的最大数据长度为 10 个字节(是的,很遗憾,int32 可以是 10 个字节)。sint32 和 uint32 的最大数据长度为 5 个字节。fixed32、sfixed32 和 float 总是正好是 4 个字节。fixed64、sfixed64 和 double 总是正好是 8 个字节。如果您的消息包含以下任何内容,则其最大长度是无限的:
string 或 bytes 类型的任何字段。 (除非您知道它们的最大长度,在这种情况下,它是最大长度加上一个长度前缀,就像子消息一样。)[packed=true],在这种情况下,您必须查看详细信息。)【讨论】:
int32 如果为负数最多可以占用 10 个字节吗? AFAIK 任何使用 varint 编码的 int32 都可以使用最多 5 个字节进行编码。
据我所知,Google 自己的 protobuf 中没有计算最大尺寸的功能。
Nanopb generator 尽可能计算最大大小并将其导出为生成文件中的#define。
根据protobuf encoding documentation手动计算小消息也很简单。
【讨论】:
在实现 protobuffer 3 消息大小计算时,我发现 Kenton 所说的大部分内容都是正确的。不过,我确实遇到了一个疏忽:标签是从字段编号创建的,该字段编号左移 3 位,然后与线路类型(在 wire_format_lite.h 中找到)进行逐位或运算。然后将该结果编码为var int。所以对于刚刚超过 16 的标签,标签将是 2 个字节,但如果字段编号更大(>~1000),那么标签将大于 3 个字节。对于 protobuffer 3 用户来说,这可能不是问题,因为有这么大的字段编号是对 protobuf 的滥用。
【讨论】: