序列化protobuf
来源:互联网 发布:淘宝网卫裤 编辑:程序博客网 时间:2024/06/02 03:02
参考:
https://developers.google.com/protocol-buffers/
https://developers.google.com/protocol-buffers/docs/encoding属性标记
protobuf的属性的三个标识required,序列化的内容里必须有这个属性键对key-value pair
optional,序列化的内容里有0个或1个属性键对
repeated,序列化的内容里有0个或多个属性键对
当保存Varint类型时,每个字节的最高位(1:有;0:无)表示后面是否还有字节传过来,低7位存储数值的补码,
一般来说在计算机保存int都是4个byte,当protobuf序列化int的话,数值越小,则占用的字节越小,那么序列化后的二进制也就是越小,
除非是非常大的数在Varint中会占5个字节,
一般来说,除非是非常大的数,否则protobuf序列化int后占用的字节比一般int占用的字节少。
序列化
getSerializedSize()计算对象的数据大小,
如果接收端无法识别field key,则会跳过该key;
每个key会包含两部分
1.proto文件定义field key的序列field_number
2.key的wire_type
根据这两部分,可以知道field key对应的value值长度
每个key由以下计算得来(结果为varint类型):
(field_number << 3) | wire_type
则最后3位保存着wire_type的值
比如key序列化为0X08则
0000 1000
去掉最高标识位,剩下
_000 1000
最后3位为000,即是0,看下面的wire_type列表,知道是Varint,然后右移3位000 0001得到field_number为1,知道key在proto文件定义的序号是1
wire type 列表
------------------------------------------------------------------------------------
Type Meaning Used For
------------------------------------------------------------------------------------
0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit fixed64, sfixed64, double
2 Length-delimited string, bytes, embedded messages, packed repeated fields
3 Start group groups (deprecated)
4 End group groups (deprecated)
5 32-bit fixed32, sfixed32, float
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
wire_type:1
------------------------------------------------------------------------------------比如300,protobuf序列化:
1010 1100 0000 0010
每个byte去年最高位标识位,则每组byte各剩下低7位
_010 1100 _000 0010
protobuf采用小端字节保存值,则反转两组7位
000 0010 和 010 1100 拼接起来:100101100 = 300
int:
max_value:01111111111111111111111111111111(正数)
min_value:10000000000000000000000000000000(负数)
负数的负号一般计算机是采用最高位来表示的,所以负数被表示为很大的数,如果使用Varint的int32或者int64来表示负数,则需要5个byte来个表示。protobuf采用了ZigZag来编码有符号数
ZigZag
==============================Signed Original | Encoded As
------------------------------
0 0
------------------------------
-1 1
------------------------------
1 2
------------------------------
-2 3
------------------------------
2147483647 4294967294
------------------------------
-2147483648 4294967295
==============================
In other words, each value n is encoded using
(n << 1) ^ (n >> 31)
for sint32s, or
(n << 1) ^ (n >> 63)
for the 64-bit version.
所以有符号的整数应该用Varint中的sint32或者sint64
如:
message Hello{
required sint32 kk = 1;
required sint64 bb = 2;
}
kk,bb使用sint32,sint64,protobuf序列化时会使用ZigZag编码。
假设Hello的属性kk设值-1;属性bb设值-2;protobug序列化后的二进制只有4个字节0X08 01 10 03
序号:字节值(进制)
------------------------
0:8(16)========1000(2)
1:1(16)========1(2)
2:10(16)========10000(2)
3:3(16)========11(2)
message Hello{
required int32 kk = 1;
required int64 bb = 2;
}
如果使用int32,int64,则序列化后的字节数为22,是原来的5倍多,汗!
=============================length:22
0:8(16)========1000(2)
1:-1(16)========11111111111111111111111111111111(2)
2:-1(16)========11111111111111111111111111111111(2)
3:-1(16)========11111111111111111111111111111111(2)
4:-1(16)========11111111111111111111111111111111(2)
5:-1(16)========11111111111111111111111111111111(2)
6:-1(16)========11111111111111111111111111111111(2)
7:-1(16)========11111111111111111111111111111111(2)
8:-1(16)========11111111111111111111111111111111(2)
9:-1(16)========11111111111111111111111111111111(2)
10:1(16)========1(2)
11:10(16)========10000(2)
12:-2(16)========11111111111111111111111111111110(2)
13:-1(16)========11111111111111111111111111111111(2)
14:-1(16)========11111111111111111111111111111111(2)
15:-1(16)========11111111111111111111111111111111(2)
16:-1(16)========11111111111111111111111111111111(2)
17:-1(16)========11111111111111111111111111111111(2)
18:-1(16)========11111111111111111111111111111111(2)
19:-1(16)========11111111111111111111111111111111(2)
20:-1(16)========11111111111111111111111111111111(2)
21:1(16)========1(2)
------------------------------------------------------------------------------------
wire_type:2
------------------------------------------------------------------------------------对于Type:2的wire type,包括string,和embedded messages(即我们自定义的类对应的类型),和packed repeated fields类型,序列化后的二进制跟Varint不同的是,
不仅仅是key-value形式,Type为2的wire type还会指明value的字节长度,如
message Test2 {
required string b = 2;
}
序列化是
12 07 74 65 73 74 69 6e 67
其中
0X12,二进制0001 0010,wire type是2,field number是2
0X07,表明value占用7个字节
0X74 65 73 74 69 6e 67,value值testing
message Test1 {
required int32 a = 1;
}
a传值150
key a序列化为
08 96 01
message Test3 {
required Test1 c = 3;
}
将Test1 c的a设为150,
则序列化为
1a 03 08 96 01
其中
0X1a,0001 1010,field_number:3,wire_type:2,自定义的类,wire type也是2,跟string一样
0X03,value值长度3
0X08 96 01,值为150
------------------------------------------------------------------------------------
Optional And Repeated Elements
------------------------------------------------------------------------------------The order of the elements with respect to each other is preserved when parsing, though the ordering with respect to other fields is lost.
在protobuf verson2.0,repeated的[packed=true]是可选的;到了versio3.0,repeated的[packed=true]是默认的。
不过,只有当repeated标识的属性wire type是Varint(wire type:0), 32-bit(wire type:5), 或者 64-bit(wire type:1)才可以声明[packed=true]
message Test4 {
repeated int32 d = 4 [packed=true];
}
假设Test4对象的属性field d有值3,270和86942,则采用packed序列化成二进制:
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)
------------------------------------------------------------------------------------
Maps
------------------------------------------------------------------------------------map<key_type, value_type> map_field = N;
map的key只能是整型 或者 string type,value除了不能是map类型,其他类型都可以。
其实,protobuf中的map是等同于下面这种message的:
message MapFieldEntry {
optional key_type key = 1;
optional value_type value = 2;
}
repeated MapFieldEntry map_field = N;
protoc -I=. --java_out=D:\project\src\main\java Foo.proto
Foo.proto文件是在.目录下的,即是在当前目录下的。
--java_out就生成java文件后放在的目录
注意:
compile编译器的版本,和protobuf-java的版本要保证兼容。
java_outer_classname
比如proto文件:
syntax = "proto3";
package lam.serialization.protobuf;
option java_package = "lam.serialization.protobuf";
option java_outer_classname = "FooProto";
message Foo{
map<int32, string> map_field = 1;
}
注意,java_outer_classname的值不能和Foo同名,否则编译器编译时出异常
其中,将Foo的map属性map_field设置为{"1":"ONE","2":"SECOND","0":"ZERO"}
序列化二进制为:
length of byte[]:31
index:16进制数
0:A===== 00001010:低3位010:2,表示wire type是2;右移3位得到1,表示field number是1.
1:7===== 00000111
2:8===== 00001000:map中的key,wire type是0,field number是1
3:1===== key的值是1
4:12===== 00010010:map中的value,wire type是2(Length-delimited,则紧接长度,然后再是值),field number是2,其中protobuf中的map等同于自定义一个message MapFieldEntry
5:3===== value值的长度3
6:4F=====O
7:4E=====N
8:45=====E
9:A=====
10:A=====
11:8=====
12:2=====
13:12=====
14:6=====
15:53=====S
16:45=====E
17:43=====C
18:4F=====O
19:4E=====N
20:44=====D
21:A=====
22:8=====
23:8=====
24:0=====
25:12=====
26:4=====
27:5A=====Z
28:45=====E
29:52=====R
30:4F=====O
可见map中的key-value对,其中也是可以当作多个message来序列化成二进制的。
阅读全文
0 0
- Protobuf序列化协议
- protobuf序列化存储
- protobuf (序列化协议)
- 序列化protobuf
- protobuf序列化原理
- Jackson--protobuf序列化积累
- C# Protobuf-Net 序列化
- google protobuf序列化原理
- Python 序列化之 ProtoBuf
- Protobuf 序列化协议详解
- google protobuf序列化原理
- protobuf 数据序列化和反解析
- protobuf搭建环境,序列化示例
- 网络-数据序列化之protobuf
- C++序列化方法 参考google protobuf
- protobuf对象二进制序列化存储
- Android下使用Protobuf进行序列化
- Android下使用Protobuf进行序列化
- 【转】 ThreadPoolExecutor使用介绍
- HoloLens开发笔记-使用UGUI-创建按钮
- HBase笔记-2.安装
- Web基础之Cookie对象与Session对象(一)
- 【大数据部落】r语言多均线量化策略回测
- 序列化protobuf
- CSDN博客不能正常发布的问题
- sort-排序
- Codeforces Combination Lock
- 51nod 1110 距离之和最小 V3
- SQL Server添加Delete操作回滚日志
- 来听大师讲设计(下)
- Redis设计
- Python中的repr()函数