在上一篇 深入 ProtoBuf - 编码 中,我们详细解析了 ProtoBuf 的编码原理。
有了这个知识储备,我们就可以深入 ProtoBuf 序列化、反序列化的源码,从代码的层面理解 ProtoBuf 具体是如何实现对数据的编码(序列化)和解码(反序列化)的。
我们重新复习一下, ProtoBuf 的序列化使用过程:
定义 .proto 文件
protoc 编译器编译 .proto 文件生成一系列接口代码
调用生成的接口实现对 .proto 定义的字段的读取以及 message 对象的序列化、反序列化方法
具体调用代码如下:
Example1 example1;
example1.set_int32val(val);
example1.set_stringval("hello,world");
example1.SerializeToString(&output);
调用 SerializeToString 函数将 example1 对象序列化(编码)成字符串。我们的目的就是了解 SerializeToString 函数里到底发生了什么,是怎么一步一步得到最终的序列化结果的。
注意:并非编码成字符串数据,string 只是作为编码结果的容器
我们在 .proto 文件中定义的 message 在最终生成的对应语言的代码中,例如在 C++ (xxxx.pb.h、xxxx.pb.cpp) 中每一个在 .proto 文件中定义的 message 字段都会在代码中构造成一个类,且这些 message 消息类继承于 ::google::protobuf::Message,而 ::google::protobuf::Message 继承于一个更为轻量的 MessageLite 类。其相关的类图如下所示:
protobuf-class-analysis.png
而我们经常调用的序列化函数 SerializeToString 并定义在基类 MessageLite 中。
编码
当某个 Message 调用 SerializeToString 时,经过一层层调用最终会调用底层的关键编码函数 WriteVarint32ToArray 或 WriteVarint64ToArray,整个过程如下图所示:
ProtoBuf 序列化_反序列化时序图.png
WriteVarint32ToArray 函数可在源码目录下的 google.protobuf.io 包下的 coded_stream.h 中找到。在上一篇 深入 ProtoBuf - 编码 中我们解析了 Va