在外部消息中包含预编码的协议缓冲区消息

时间:2013-08-29 11:58:46

标签: c++ protocol-buffers

有没有办法在C ++中创建包含预编码内部消息的协议缓冲区消息,而无需解析然后重新序列化内部消息?

为澄清一下,请考虑以下消息定义:

message Inner {
    required int i = 1;
    // ... more fields ...
}

message Outer {
    repeated Inner inners = 1;
    // ... more fields ...
}

假设您有一个10字节数组的集合,每个数组都包含一个Inner的编码版本。你想创建一个包含10个内部的外部。您不想手动编码,因为Outer有其他字段,并且可能本身也包含在其他消息中。有没有办法让协议缓冲区直接复制预编码的内部?

1 个答案:

答案 0 :(得分:1)

没有一个干净的方式,但有一些hacky方式。一种是定义第二条消息:

message RawOuter {
    repeated bytes inners = 1;
    // ... same fields as Outer ...
}

RawOuterOuter相同,只是inners重复字段已从类型Inner更改为类型bytes。如果使用编码的inners实例填充Inner,然后序列化RawOuter,则会得到与使用已解析的版本生成Outer完全相同的结果。也就是说,嵌套消息的有线格式与包含该嵌套消息序列化的bytes字段的有线格式相同。这是protobuf编码的一些有趣的可利用怪癖。

但是,这个黑客有一些问题。特别是,如果您尝试构建嵌入在其他原型中的Outer实例,则它不能很好地工作,因为您可能不希望维护每个包含消息的两个副本,一个使用{ {1}}和使用Outer的人。

另一个甚至更黑的选择是将编码的消息注入RawOuter实例的Outer

UnknownFieldSet

Outer outer; for (auto& inner: inners) { outer.mutable_unknown_fields() ->AddLengthDelimited(1, inner); } 旨在存储解析时看到的与UnknownFieldSet文件中定义的任何已知字段编号不匹配的字段。这个想法是,这允许您编写一个代理服务器,只需接收消息并将它们转发到另一个服务器,而无需在每次向协议添加新字段时重新编译代理。在这里,我们通过在其中添加一个实际上与已知字段对应的值来滥用它,但是实现不会注意到,因此它会写出这些字段就好了。

这种方法的主要问题是,如果其他人在此期间检查您的.proto实例,那么它们就会显示为Outer列表为空,因为这些值实际上隐藏在某个地方其他。这是一个非常丑陋的黑客,可能会在以后再次困扰你。如果你测量了性能差异并发现它很大,我只会推荐它。

另请注意,序列化代码始终最后写入未知字段,而已知字段按字段编号按顺序写入。解析器应该接受任何订单,但偶尔你会发现有人使用未解析的数据作为哈希映射键或其他东西,并且如果字段被重新排序则完全中断。

顺便说一句,你可以通过将字符串交换到位而不是复制来提高这两种方法的性能,即

inners

raw_outer->add_inners()->swap(inner);