最大序列化 Protobuf 消息大小
Maximum serialized Protobuf message size
有没有办法在序列化后获取某个 protobuf 消息的最大大小?
我指的是不包含 "repeated" 元素的消息。
请注意,我不是指的是具有特定内容的 protobuf 消息的大小,而是可能的最大大小它可以达到(在最坏的情况下)。
据我所知,Google 自己的 protobuf 中没有计算最大大小的功能。
Nanopb generator 尽可能计算最大大小并将其导出为生成文件中的 #define
。
手动计算小消息也很简单
一般来说,由于可能存在未知字段,任何 Protobuf 消息都可以是任意长度。
如果您收到一条消息,您不能对长度做出任何假设。
如果您发送您自己构建的消息,那么您或许可以假设它只包含您知道的字段——但话又说回来,您也可以轻松计算本例中的确切消息大小。
因此,通常询问最大尺寸是多少是没有用的。
话虽如此,您可以编写使用 Descriptor
接口的代码来迭代消息类型 (MyMessageType::descriptor()
) 的 FieldDescriptor
。
参见:https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor
Java、Python 和其他可能存在类似的界面。
执行规则如下:
每个字段都由一个标记和一些数据组成。
对于标签:
- 字段编号 1-15 有一个 1 字节的标记。
- 字段编号 16 及以上具有 2 字节标记。
对于数据:
bool
总是一个字节。
int32
、int64
、uint64
和 sint64
的最大数据长度为 10 个字节(是的,int32
可以是 10 个字节,如果不幸的是,它是负面的。
sint32
和 uint32
的最大数据长度为 5 个字节。
fixed32
、sfixed32
和 float
始终正好是 4 个字节。
fixed64
、sfixed64
和 double
始终正好是 8 个字节。
- 枚举类型字段的最大长度取决于最大枚举值:
- 0-127: 1 字节
- 128-16384:2 个字节
- ...每个字节 7 位,但希望您的枚举不会那么大!
- 另请注意,负值将被编码为 10 个字节,但希望没有负值。
- 消息类型字段的最大长度是消息类型的最大长度加上长度前缀的字节数。同样,长度前缀是每 7 位整数数据一个字节。
- 组(你不应该使用它;它们是一个破旧的旧功能,甚至在 protobuf 公开发布之前就被弃用了)的最大大小等于内容的最大大小加上第二个字段标签(见上文) ).
如果您的消息包含以下任何内容,则其最大长度不受限制:
string
或 bytes
类型的任何字段。 (除非你知道它们的最大长度,在这种情况下,就是最大长度加上一个长度前缀,就像子消息一样。)
- 任何重复字段。 (除非你知道它的最大长度,在这种情况下,列表的每个元素都有一个最大长度,就好像它是一个独立的字段,包括标签。这里没有总长度前缀。除非你使用
[packed=true]
, 在这种情况下,您必须查看详细信息。)
- 扩展。
在实现 protobuffer 3 消息大小计算时,我发现 Kenton 所说的大部分内容都是正确的。不过,我做了 运行 一个疏忽:标签是根据字段编号创建的,该字段编号左移 3 位,然后与电线类型(在 wire_format_lite.h 中找到)按位或运算。然后将该结果编码为 var int
。所以对于刚刚超过 16 个的标签,标签将是 2 个字节,但如果字段编号更大 (>~1000),则标签将大于 3 个字节。对于 protobuffer 3 用户来说,这可能不是问题,因为拥有这么大的字段编号是对 protobuf 的滥用。
有没有办法在序列化后获取某个 protobuf 消息的最大大小?
我指的是不包含 "repeated" 元素的消息。
请注意,我不是指的是具有特定内容的 protobuf 消息的大小,而是可能的最大大小它可以达到(在最坏的情况下)。
据我所知,Google 自己的 protobuf 中没有计算最大大小的功能。
Nanopb generator 尽可能计算最大大小并将其导出为生成文件中的 #define
。
一般来说,由于可能存在未知字段,任何 Protobuf 消息都可以是任意长度。
如果您收到一条消息,您不能对长度做出任何假设。
如果您发送您自己构建的消息,那么您或许可以假设它只包含您知道的字段——但话又说回来,您也可以轻松计算本例中的确切消息大小。
因此,通常询问最大尺寸是多少是没有用的。
话虽如此,您可以编写使用 Descriptor
接口的代码来迭代消息类型 (MyMessageType::descriptor()
) 的 FieldDescriptor
。
参见:https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor
Java、Python 和其他可能存在类似的界面。
执行规则如下:
每个字段都由一个标记和一些数据组成。
对于标签:
- 字段编号 1-15 有一个 1 字节的标记。
- 字段编号 16 及以上具有 2 字节标记。
对于数据:
bool
总是一个字节。int32
、int64
、uint64
和sint64
的最大数据长度为 10 个字节(是的,int32
可以是 10 个字节,如果不幸的是,它是负面的。sint32
和uint32
的最大数据长度为 5 个字节。fixed32
、sfixed32
和float
始终正好是 4 个字节。fixed64
、sfixed64
和double
始终正好是 8 个字节。- 枚举类型字段的最大长度取决于最大枚举值:
- 0-127: 1 字节
- 128-16384:2 个字节
- ...每个字节 7 位,但希望您的枚举不会那么大!
- 另请注意,负值将被编码为 10 个字节,但希望没有负值。
- 消息类型字段的最大长度是消息类型的最大长度加上长度前缀的字节数。同样,长度前缀是每 7 位整数数据一个字节。
- 组(你不应该使用它;它们是一个破旧的旧功能,甚至在 protobuf 公开发布之前就被弃用了)的最大大小等于内容的最大大小加上第二个字段标签(见上文) ).
如果您的消息包含以下任何内容,则其最大长度不受限制:
string
或bytes
类型的任何字段。 (除非你知道它们的最大长度,在这种情况下,就是最大长度加上一个长度前缀,就像子消息一样。)- 任何重复字段。 (除非你知道它的最大长度,在这种情况下,列表的每个元素都有一个最大长度,就好像它是一个独立的字段,包括标签。这里没有总长度前缀。除非你使用
[packed=true]
, 在这种情况下,您必须查看详细信息。) - 扩展。
在实现 protobuffer 3 消息大小计算时,我发现 Kenton 所说的大部分内容都是正确的。不过,我做了 运行 一个疏忽:标签是根据字段编号创建的,该字段编号左移 3 位,然后与电线类型(在 wire_format_lite.h 中找到)按位或运算。然后将该结果编码为 var int
。所以对于刚刚超过 16 个的标签,标签将是 2 个字节,但如果字段编号更大 (>~1000),则标签将大于 3 个字节。对于 protobuffer 3 用户来说,这可能不是问题,因为拥有这么大的字段编号是对 protobuf 的滥用。