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

上一篇:python实现的电影票房数据可视化


下一篇:iOS 根据字符串来定位地址