protobuf的编码和存储方式
Protobuf 支持多种数据类型,如整数、浮点数、布尔值、字符串、字节数组、嵌套消息和枚举等。不同的数据类型在编码时有不同的规则。
整型的编码方式
Protobuf 对整数采用 Varint 编码,对于有符号的整数(如 sint32 和 sint64),Protobuf 使用 ZigZag 编码将负数转换为正数,这是可变长度整数编码的一种方式:
- Varint 使用一个或多个字节来表示,较小的整数使用较少的字节。
- 每个字节的最高位是继续位,如果设置为1,则表示后续还有字节;如果为0,则表示这是最后一个字节。
Varint 编码避免了固定长度整数带来的额外空间浪费。
定长的编码方式
对于定长数据类型(如 fixed32 和 fixed64),Protobuf 使用固定的字节数进行编码:
fixed32:使用 4 个字节(32 位)。
fixed64:使用 8 个字节(64 位)。
字符串的编码方式
UTF-8 编码:
- Protobuf 强制要求字符串使用 UTF-8 编码。也就是说,字符串首先会被转换成 UTF-8 字节序列。
例如,字符串 “Hello” 用 UTF-8 编码后是字节序列 [72, 101, 108, 108, 111] (每个字符转成一个字节)。 - 长度前缀变长整数编码(Varint 编码):
在 Protobuf 中,所有长度字段都使用 Varint 编码。Varint 是一种可变长度的整数编码方式,用于有效地编码小整数。 - 字符串的长度首先被编码为 Varint。
例如,“Hello” 的长度是 5,因此先编码 5,其 Varint 编码也是 [5]。 - 拼接结果:
最终编码形式为 长度前缀 + UTF-8 字节序列。
对于 “Hello”,整体编码结果是 [5, 72, 101, 108, 108, 111]。
存储方式
Protobuf 的数据结构由 .proto 文件定义,该文件描述了消息(Message)格式和字段(Field)。示例如下:
syntax = "proto3";
message Person {
int32 id = 1;
string name = 2;
bool is_student = 3;
}
- message
消息是数据结构的基本单元,每个消息包含一个或多个字段:
message Person {
int32 id = 1;
string name = 2;
bool is_student = 3;
}
- 字段编码
每个字段由字段编号和类型编码,紧随其后的数据值。字段编号与类型使用以下规则编码:
1)字段编号与类型合并成一个 Tag,用来标识字段。
2)Tag 的低 3 位表示字段类型,高位表示字段编号。
- 序列化过程
序列化过程将 Protobuf 消息转换为二进制格式:
按字段定义的顺序编码各个字段及其数据值。
转换为紧凑的二进制格式后,可以高效地存储在文件中或通过网络传输。
- 反序列化
读取二进制数据,根据 Tag 确定字段编号和字段类型。
根据字段类型解码数据值,重建原始消息对象。