ProtoBuf

 

ProtoBuf 笔记

分类: 序列化   308人阅读  评论(0)  收藏  举报

编码

Base 128 Varints

uint32中1,10 ,2^32 都使用32位表示,比较浪费。

MSB为0代表一个数字结束,1表示还未结束;这样对于较小的数字比较节省空间,当然大的数字可能要多一个字节。

采用小端表示,以下为300的Base 128 Varints表示的解码过程。

varints 编码:10101100   00000010

去掉最高位:   0101100     0000010

小端->大端:   0000010     0101100

去掉前面的0:            10     0101100  

二进制->10: 300

消息格式

protobuf格式的消息只是K,V对,K为字段的id而不是名字用于节省空间。

到了服务端后通过消息定义文件(如.proto或类定义文件)可以重新从id获得名字。

序列化中,类型信息和字段ID合在了一起 (field_number << 3) | wire_type。wire types 只包含类型的大小信息,不指出具体哪种类型,最多3位。

More Value Types

Signed Integers

protobuf的varints编码策略就是:假定程序使用过程中,较小的数使用较多;于是将小的数编码为更少的位数。

这个策略很直白,是从整体上应用,而不是像哈夫曼编码那样具体分析每次数据中每个字符的频率。

对于有符号整数sint32和sint64,也应用了这个策略,使用了Zigzag编码:假定程序使用过程中,绝对值越小的使用频率越高;于是将绝对值小的数编码为更少的位数。

即:-1编码为1,1编为2,-2编为3,2编为4,-3编为5......。

换句话说,每个值n都按下式编码:

(n << 1) ^ (n >> 31)

如果是64bit,那么用下面的式子:

(n << 1) ^ (n >> 63)

实际上个人使用过程中应该是在某个范围内的正数,使用频率比负数要高。但是这里是编码需要一种简单的策略,而不是压缩。

Non-varint Numbers

varint指的是变长的,包括int32,、sint32、int64、sint64。

non-varint就是指采用不变长的方式编码。包括32位的float、fixed32、sfix32以及64位的double、fixed64、sfixed64。其中fixed64,fixed32、sfix32、sfixed64其实和int32,、sint32、int64、sint64表达能力一样,只是采用了平常使用的定长方式编码。

String

wire type 2(length-delimited)意味着后面跟的value是一个用varint编码的length,然后跟着一个length指定长度的bytes。

message Test2 {
  required string b = 2;
}

如果b被set为“testing”, 那么你会得到下面这个序列:

12 07 74 65 73 74 69 6e 67

标红色的bytes是UTF8编码下的“testing”。

这个stream中的key是0×12 –> id=2, type=2。(id << 3) | wire_type。也就是10 010,10进制为18,16进制为12。

表示长度的varint是7。

随后跟着7个bytes,就是我们的string。

Embedded Messages

下面定义了一个message,里面又包含了一个Test1类型的message:

message Test3 {
  required Test1 c = 3;
}

下面是编码后的版本

 1a 03 08 96 01

后三个byte正是第一个例子中的结果(08 96 01), 150的varint编码。

而且他们前面的byte是3(也就是length)。

id=3, wiretype = 2。 (id << 3) | wire_type。11  010,16进制为1a。

可以看出embedded message的处理方法和string是完全一样的(wiretype = 2).

Optional And Repeated Elements

如果message中包含repeated类型,应该是类似数组的意思。

没有加[packed=true]选项,每个元素都还要加 key,但是不需要排列一起可以和其他字段穿插,这个特点没什么用。

新版本可以加[packed=true]选项,只需要写一次key就行了。但是不能和其他字段穿插。

例如,这样的一个message type:

message Test4 {
  repeated int32 d = 4[packed=true];
}

假设你创建了一个Test4对象,repeated field d中存入了3,270,86942. 那么,编码后的形式将会是:

22        // tag (field number 4, wire type 2)
06        // payload size (6 bytes)
03        // first element (varint 3)
8E 02     // second element (varint 270)
9E A7 05  // third element (varint 86942)

只有类型为原始数字类型的repeated field才能被声明为“packed”。

Field Order

在.proto文件中,你可以以任意顺序安排field number,但是,序列化一个message时,它的fields应该按照field number的顺序写入。这样可以让负责parse的code可以使用一些依赖于field number顺序的优化技术。当然,protocol buffer的parser必须能够以任意顺序解析field,因为并不是所有的message都是仅仅经过简单的序列化生成的。

你可能感兴趣的:(Stream,String,序列化,byte,64bit,testing,Types)