支持的数据类型:
复合类型
复合类型 | 默认值 | 说明 |
---|---|---|
message | 语言决定 | |
enum | 第一个枚举值 | |
oneof | ||
map | {} | |
repeat | [] |
下面是相关的形式化语法定义:[1]
message = "message" messageName messageBody
messageBody = "{" { field | enum | message | option | oneof | mapField | reserved | emptyStatement } "}"
field = [ "repeated" | "optional" ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
fieldOptions = fieldOption { "," fieldOption }
fieldOption = optionName "=" constant
enum = "enum" enumName enumBody
enumBody = "{" { option | enumField | emptyStatement | reserved } "}"
enumField = ident "=" [ "-" ] intLit [ "[" enumValueOption { "," enumValueOption } "]" ]";"
enumValueOption = optionName "=" constant
注意这里 enum 的类型为 int32。 |
oneof = "oneof" oneofName "{" { option | oneofField } "}"
oneofField = type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
mapField = "map" "<" keyType "," type ">" mapName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" |
"fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string"
序列化
若一个标量字段为默认值,则此字段在序列化时会被跳过。注意对于 float/double 而言,+0 会被跳过,但是 -0 并不会被跳过。 |
序列化的形式化语法定义如下:[2]
message := (tag value)*
tag := (field << 3) bit-or wire_type;
encoded as uint32 varint
value := varint for wire_type == VARINT,
i32 for wire_type == I32,
i64 for wire_type == I64,
len-prefix for wire_type == LEN,
<empty> for wire_type == SGROUP or EGROUP
varint := int32 | int64 | uint32 | uint64 | bool | enum | sint32 | sint64;
encoded as varints (sintN are ZigZag-encoded first)
i32 := sfixed32 | fixed32 | float;
encoded as 4-byte little-endian;
memcpy of the equivalent C types (u?int32_t, float)
i64 := sfixed64 | fixed64 | double;
encoded as 8-byte little-endian;
memcpy of the equivalent C types (u?int64_t, double)
len-prefix := size (message | string | bytes | packed);
size encoded as int32 varint
string := valid UTF-8 string (e.g. ASCII);
max 2GB of bytes
bytes := any sequence of 8-bit bytes;
max 2GB of bytes
packed := varint* | i32* | i64*,
consecutive values of the type specified in `.proto`
从上面可以看到,对于一个 message 而言,proto3 会将所有字段依次进行序列化,在序列化过程中,首先写入 field id,然后写入 field value。string 和 bytes 类型,value 是由 int32 构成的长度描述和紧跟其后的数据构成。定长类型的 value 等价于小端的 C 类型表示。
tag 被编码为 uint32 variant。
对于变长类型编码方式为 ZigZag-encoded。
ZigZag 编码是为了解决对负数编码效率低的问题。ZigZag 将有符号整数映射为无符号整数。简而言之,ZigZag 编码方式为:
|
map 类型:
对于一个 map 类型:
message Test6 {
map<string, int32> g = 7;
}
等价于:
message Test6 {
message g_Entry {
optional string key = 1;
optional int32 value = 2;
}
repeated g_Entry g = 7;
}
repeat 类型:
对于 repeat 类型而言,只是简单地重复编码字段。例如:
message Test4 {
optional string d = 4;
repeated int32 e = 5;
}
被编码为:
4: {"hello"}
5: 1
5: 2
5: 3
除了 string 和 bytes 之外的 [标量类型] repeat 可以设置属性 为了兼容性的考虑,两种行使的编码方式解码器应当都能接受。 |
variant 编码
变长数字编码可以使用 0-10B 储存 64 bit unsigned 整数。数字越小占据的字节空间越小。在 variant 编码中每个字节都有一个 MSB(most significant bit) 用来指示当前字节是否为最后一个字节。对于一个字节而言,低 7 位储存了 paylaod,最后一位储存了 MSB。
例如对于数字 1 被编码为:
0000 00001
^ msb
有符号 variant 首先通过 ZigZag 编码转为正整数,然后通过 variant 编码进行储存。