https://zhuanlan.zhihu.com/p/557457644https://zhuanlan.zhihu.com/p/557457644
[新文导读] 从Base64到Protobuf,详解Protobuf的数据编码原理本篇将从Base64再到Base128编码,带你一起从底层来理解Protobuf的数据编码原理。本文结构总体与 Protobuf 官方文档相似,不少内容也来自官方文档,并在官方文档的基础上添加作者理解的内容,如有出入请以官方文档为准。https://mp.weixin.qq.com/s/OgPnO2TEGSc2Eb8wxQTs6g?spm_id_from=444.41.rich-text.link.click
目录
安装
Base128 Varints 编码
uint
sint
字符串
安装
1. 下载:
wget https://github.com/protocolbuffers/protobuf/releases/download/v21.5/protobuf-cpp-3.21.5.tar.gz
tar zxvf protobuf-cpp-3.21.5.tar.gz protobuf-3.21.5/
2. 编译
cd protobuf-3.21.5/
./configure
make
sudo make install
sudo ldconfig
3. 生成 xxx.pb.h 和 xxx.pb.cc
protoc -I=input_dir --cpp_out=output_dir [*.proto |/input_dir/specific.proto]
// -I 原文件.proto的所在的文件夹
// --cpp_out 生存pb.c pb.h 存放的文件夹
// 后面跟着所需的.proto文件
Base128 Varints 编码
Varint 是一种紧凑的表示数字的方法。它使用小端标识(意味着计算时需要调换顺序)
,用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。其中每个字节的最高位用来表示后面一个字节是否属于当前这个数的组成部分,1 代表是,0表示不是。
uint
对于无符号整数,其编码比较简单,以 1 和 300 为例。
step1: 1 的二进制为 0000 0001
step2: 取 7 位,即 000 0001
step3: 多个byte的话,需要进行翻转(因为varints是用小端表示,低位字节放在前面)
step3: 最高位添加一个表示下一个byte是否属于当前数字的标志位,因为 1 只有1个字节表示,所以表示位为0,即最终varints编码表示位为 0000 0001
step1: 300 的二进制表示为 100101100
step2: 每7位隔开,不足的补0 -> 0000010 0101100
step3: 翻转一下byte顺序 0101100 0000010
step4: 填写标志位 1010 1100 0000 0010 (ac 02)
sint
对于有符号数,因为计算机定义负数的符号位为数字的最高位。如果采用 Varint 表示一个负数,那么一定需要 10 个 byte 长度。
因此定义了 sint32 这种类型,采用 zigzag 编码,先进行zigzag编码,将所有整数映射成无符号整数,然后再采用 varint 编码方式编码
,这样,绝对值小的整数,编码后也会有一个较小的 varint 编码值。
Zigzag(n) = (n << 1) ^ (n >> 31), n 为 sint32 时
Zigzag(n) = (n << 1) ^ (n >> 63), n 为 sint64 时
字符串
wire_type 类型为 2 的数据,是一种指定长度的编码方式:key + length + content,key 的编码方式是统一的((field_number << 3) | wire_type),length 采用 varints 编码方式,content 就是由 length 指定长度的 Bytes。
message Test2 {
optional string b = 2;
}
# 设置该值为"testing"
# testing 的utf8编码为:74 65 73 74 69 6e 67
# 则 field_num = 2 wire_type = 2 key = 0001 0010 (12)
# length = 07
则 设置为testing后的编码为:12 07 74 65 73 74 69 6e 67
message 的二进制版本只是使用字段号(field's number 和 wire_type)作为 key。所以protocol buffer 比 JSON,XML 安全一点的原因,如果没有数据结构描述 .proto 文件,拿到数据以后是无法解释成正常的数据的