Message Structure
在上一篇文章中我们提到,对于序列化后字节流,需要回答的一个重要问题是“从哪里到哪里是哪个数据成员”。
message中每一个field的格式为:
required/optional/repeated FieldType FieldName = FieldNumber(a unique number in current message)
在序列化时,一个field对应一个key-value对,整个二进制文件就是一连串紧密排列的key-value对,key也称为tag,先上图直观感受一下,图片来自Encoding and Evolution:
key由wire type和FieldNumber两部分编码而成, 具体地key = (field_number << 3) | wire_type
,field_number 部分指示了当前是哪个数据成员,通过它将cc和h文件中的数据成员与当前的key-value对应起来。
key的最低3个bit为wire type,**什么是wire type?**如下表所示:
wire type被如此设计,主要是为了解决一个问题,如何知道接下来value部分的长度(字节数),如果
- wire type = 0、1、5,编码为 key + 数据,只有一个数据,可能占数个字节,数据在编码时自带终止标记
- wire type = 2,编码为 key + length + 数据,length指示了数据长度,可能有多个数据,顺序排在length后
解码代码一窥
接下来,我们直接看一下example.pb.cc及相关的源码,看下key-value对是如何解析的。解码过程相对简单,理解了解码过程,编码也就比较显然了。
// example.proto
package example;
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
}
// in example.pb.cc
bool Person::MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input) {
#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure
::google::protobuf::uint32 tag;
// @@protoc_insertion_point(parse_start:example.Person)
for (;;) {
::std::pair<::google::protobuf::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u);
tag = p.first;
if (!p.second) goto handle_unusual;
switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
// required string name = 1;
case 1: {
if (static_cast< ::google::protobuf::uint8>(tag) == (10 & 0xFF)) {
// 10 = (1 << 3) + 2
DO_(