Google Protocol Buffer 的编码方式
Google Protocol Buffer 使用到了两种编码方式:Varints 和 zigzag。
一 Varints 编码
每个 byte 只用 7bit 表示数字,最高位 bit作为标志位,如果为:
1,表示后续的 byte 也是该数字的一部分;
0,表示结束。
因此值越小的数字使用越少的字节数。例如小于 128 的数只需要用一个 byte 表示。
1: 0000 0001
128: 0111 1111
129: 1000 0001 0111 1111
二 Zigzag 编码
负数最高位(符号位)是1,就相当于一个很大的整数,如果用varints,很浪费空间。
Zigzag 编码用无符号数来表示有符号数字,正数和负数交错,对照表如下:
原始数值 | 编码后 |
---|---|
0 | 0 |
-1 | 1 |
1 | 2 |
-2 | 3 |
2 | 4 |
-3 | 5 |
3 | 6 |
... | ... |
使用 Zigzag 编码后,绝对值小的数字,无论正负都可以采用较少的 byte 来表示,充分利用了 Varints 这种技术。
如果数值有可能为负数,使用 sint 类型,sint 类型数先使用Zigzag编码,再使用 Varints编码。
三 pb的二进制编码设计
先看官方文档中给的 .proto 文件:
package tutorial;
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
repeated Person person = 1;
}
1 Message Buffer
[Field1 | Field2 | ... | Fieldn]
每个Message都是由多个Field的组成。
2 Field
对于简单类型:
Field : [Key | Value]
而集合类型(string, bytes, embedded messages, packed repeated fields)
Field : [Key | Length | content]
Length采用Varints编码,表示content的Bytes数。
3 Key
Key : [tag | type], 其中 tag 占 5bit,type 占 3bit
tag是Message中定义的标记,type是变量的类型。
type最终会被转换成一个枚举类型,即unsigned int类型,对于关系从下面的代码中可以看到:
const FieldDescriptor::CppType
FieldDescriptor::kTypeToCppTypeMap[MAX_TYPE + 1] = {
static_cast<CppType>(0), // 0 is reserved for errors
CPPTYPE_DOUBLE, // TYPE_DOUBLE
CPPTYPE_FLOAT, // TYPE_FLOAT
CPPTYPE_INT64, // TYPE_INT64
CPPTYPE_UINT64, // TYPE_UINT64
CPPTYPE_INT32, // TYPE_INT32
CPPTYPE_UINT64, // TYPE_FIXED64
CPPTYPE_UINT32, // TYPE_FIXED32
CPPTYPE_BOOL, // TYPE_BOOL
CPPTYPE_STRING, // TYPE_STRING
CPPTYPE_MESSAGE, // TYPE_GROUP
CPPTYPE_MESSAGE, // TYPE_MESSAGE
CPPTYPE_STRING, // TYPE_BYTES
CPPTYPE_UINT32, // TYPE_UINT32
CPPTYPE_ENUM, // TYPE_ENUM
CPPTYPE_INT32, // TYPE_SFIXED32
CPPTYPE_INT64, // TYPE_SFIXED64
CPPTYPE_INT32, // TYPE_SINT32
CPPTYPE_INT64, // TYPE_SINT64
};
const char * const FieldDescriptor::kTypeToName[MAX_TYPE + 1] = {
"ERROR", // 0 is reserved for errors
"double", // TYPE_DOUBLE
"float", // TYPE_FLOAT
"int64", // TYPE_INT64
"uint64", // TYPE_UINT64
"int32", // TYPE_INT32
"fixed64", // TYPE_FIXED64
"fixed32", // TYPE_FIXED32
"bool", // TYPE_BOOL
"string", // TYPE_STRING
"group", // TYPE_GROUP
"message", // TYPE_MESSAGE
"bytes", // TYPE_BYTES
"uint32", // TYPE_UINT32
"enum", // TYPE_ENUM
"sfixed32", // TYPE_SFIXED32
"sfixed64", // TYPE_SFIXED64
"sint32", // TYPE_SINT32
"sint64", // TYPE_SINT64
};
参考:
https://developers.google.com/protocol-buffers/docs/overview