【技术类】PB二进制序列化

自从使用protobuf作序列化工具之后,每次面试都问我,为什么用?
很迅速的回答了体积小,解析快。
为什么小,采用了varint的压缩方式,那你讲一下这个压缩方式,然后emmm…
那为什么解析快,有没有跟其他做过对比,又是emmm…

test.proto
syntax = "proto2";

message Test
{
	required int32 first = 1;
	optional int32 second = 2;
	optional string third = 3;
}
这里使用C++语言,编译到当前目录命令: protoc test.proto --cpp_out=./
产生两个文件:test.pb.h	test.pb.cc(动态链接库)
#include 
#include "test.pb.h"
using namespace std;

int main()
{
	Test st;
	st.set_first(10);
	st.set_second(20);
	st.set_third("hello");
	
	char buffer[64] = {0};
	if(!st.SerializeToArray(buffer,st.ByteSize()))
		cout<<"Serlialize Error"<
下面的文献内容,摘自大佬的博客,自己只是用来学习,附上地址,如有侵犯,联系本人,会尽快处理。
https://blog.csdn.net/chengzi_comm/article/details/53199278

protobuf的message中每个字段的格式为: 修饰符 字段类型 字段名 = 域号;
在序列化时,protobuf按照TLV的格式序列化每一个字段,T即Tag,也叫Key;L是Value的长度,如果一个字段是整形,这个L部分会省略;V是该字段对应的值value。

序列化后的Value是按原样保存到字符串或者文件中,Key按照一定的转换条件保存起来,序列化后的结果就是 KeyValueKeyValue。

Key的序列化格式是按照message中字段后面的域号与字段类型来转换。转换公式如下:
(field_number << 3) | wire_type,wire_type与字段的类型有关(不同类型采用的压缩方式不一)

比如:int32,int64等采用varint(wire_type=0),string就采用Length-delimi(wire_type=2)
这里只列举这两个,其他的type类型可以去官网查看。

protobuf规定:
1.如果域号在[1,15]范围内,会使用一个字节表示Key;
2.如果域号大于等于16,会使用两个字节表示Key;
3.Key编码过后,该字节的第一个比特位表示之后的一个字节是否与当前这个字节有关:
	a.如果第一个比特位为1,表示有关,即连续两个字节都是Key的编码;
	b.如果第一个比特位为0,表示Key的编码只有当前一个字节,后面的字节是Length或者Value;
计算first
key = (1 << 3) | 0 = 8, 8对应的ASCLL就是‘\b’,因为是int型所以不需要指定长度
那么‘\n’就是value,10喽。

second
key = (2 << 3) | 0 = 16, 16的八进制码是20
24就是value(20)的八进制码了。

third
key = (3 << 3) | 2 = 26,26的八进制码是32,后面的5就是长度,以及后面的内容了

到这里我们只是把数据对应上了,体积小表现在哪?

我们可以清晰的看到10和20两个4字节的数被编码成了一个字节的八进制数。怎么做的。
例: 10的二进制码 = 00000000 00000000 00000000 00001010
	1.从最后字节取7位,如果当前字节后面没字节了,那么就在最高位补1,否则补0。
	2.讲得到的字节拼接起来得到00000000 00000000 00000000 10001010
	3.如果字节表示的数为0,那么舍弃该字节,此时得到的就是10001010
做反序列化工作时:第一位是1,那么剩下7位都是数据,则为0001010 = 10。

如果数据比较大时,那么编码可能会占用两个字节,比如300:10101100 00000010 
解析工作:高位为0,代表这是最后一个字节,1代表还有数据。去掉最高位拼接得到 0000010 0101100

你可能感兴趣的:(修炼)