Protobuf 编码(2)
更多值类型
有符号整数
正如您在上一节中看到的,所有与类型0相关联的Protocol Buffer类型都被编码为varints。然而,在编码负数时,有符号int类型( sint32和sint64 )和“标准”int类型( int32和int64 )之间有一个重要的区别。如果使用int32或int64作为负数的类型,得到的varints总是10字节长:实际上,它被视为一个非常大的无符号整数。如果您使用其中一种有符号类型,结果varints将使用ZigZag编码,这种编码效率更高。
ZigZag编码将有符号整数映射到无符号整数,因此绝对值小的数字(例如,-1)也有一个小的可变编码值。它这样做的方式是通过正整数和负整数来回“zig-zags”,因此-1编码为1,1编码为2,-2编码为3,依此类推,如下表所示:
原始值 | 编码为 |
---|---|
0 | 0 |
-1 | 1 |
1 | 2 |
-2 | 3 |
2147483647 | 4294967294 |
-2147483648 | 4294967295 |
换句话说,每个值n使用:
(n << 1) ^ (n >> 31)
对于 sint32,
(n << 1) ^ (n >> 63)
对于64位版本。
请注意,第二个移位(n >> 31)部分是算术移位。换句话说,移位的结果要么是一个全为零位的数字(如果n为正),要么是全一位的数字(如果n为负)。
当sint32或sint64被解析时,其值被解码回原始的签名版本。
非varint数字
非varint数字类型很简单:double和固定64位具有类型1,这告诉解析器期望固定的64位数据块;类似地,float和固定32位具有类型5,这告诉它期望32位。在这两种情况下,这些值都以小字节顺序存储。
字符串
类型为2 (长度分隔)意味着该值是一个可变的编码长度,后跟指定的数据字节数。
message Test2 { optional string b = 2; }
将b的值设置为“testing”:
12 07 74 65 73 74 69 6e 67
红色部分是“testing”的UTF8。这里的键是0x12 →字段号= 2,类型= 2。值的长度变化是7,我们发现后面有7个字节:我们的字符串。
嵌入消息
下面是一个消息定义,其中嵌入了我们示例类型的消息,Test1 :
message Test3 { optional Test1 c = 3; }
这是编码版本,Test1的字段设置为150 :
1a 03 08 96 01
如您所见,最后三个字节与我们的第一个示例( 08 96 01 )完全相同,前面是数字3:嵌入消息与字符串的处理方式完全相同(wire type = 2)。