支持的数据类型:

Table 1. 标量类型
类型默认值注释

double

0

float

0

int32

0

变长编码。对负数编码效率较低,负数请改用 sint32。

int64

0

变长编码。对负数编码效率较低,负数请改用 sint32。

uint32

0

变长编码。

uint64

0

变长编码。

sint32

0

变长编码。对负数编码比 int 效率更高。

sint64

0

变长编码。对负数编码比 int 效率更高。

fixed32

0

定长类型。如果值大于 \(2^{28}\) 性能比 uint32 更高

fixed64

0

定长类型。如果值大于 \(2^{56}\) 性能比 uint32 更高

sfixed32

0

定长类型。

sfixed64

0

定长类型。

bool

false

string

""

包含了 utf8 编码或者 ascii 的字符串。长度不能超过 \(2^{32}\)。

bytes

[]

复合类型

复合类型默认值说明

message

语言决定

enum

第一个枚举值

oneof

map

{}

repeat

[]

下面是相关的形式化语法定义:[1]

message
message = "message" messageName messageBody
messageBody = "{" { field | enum | message | option | oneof | mapField | reserved | emptyStatement } "}"
field
field = [ "repeated" | "optional" ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
fieldOptions = fieldOption { ","  fieldOption }
fieldOption = optionName "=" constant
enum
enum = "enum" enumName enumBody
enumBody = "{" { option | enumField | emptyStatement | reserved } "}"
enumField = ident "=" [ "-" ] intLit [ "[" enumValueOption { ","  enumValueOption } "]" ]";"
enumValueOption = optionName "=" constant
注意这里 enum 的类型为 int32。
oneof
oneof = "oneof" oneofName "{" { option | oneofField } "}"
oneofField = type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
map
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 编码方式为:

(n << 1) ^ (n >> 31) // 32bit
(n << 1) ^ (n >> 63) // 64bit

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 可以设置属性 packed=true,这会更改 repeat 的编码方式。repeat 从 record by record 的形式被编码为 len + data 的形式。

为了兼容性的考虑,两种行使的编码方式解码器应当都能接受。

variant 编码

变长数字编码可以使用 0-10B 储存 64 bit unsigned 整数。数字越小占据的字节空间越小。在 variant 编码中每个字节都有一个 MSB(most significant bit) 用来指示当前字节是否为最后一个字节。对于一个字节而言,低 7 位储存了 paylaod,最后一位储存了 MSB。

例如对于数字 1 被编码为:

0000 00001
^ msb

有符号 variant 首先通过 ZigZag 编码转为正整数,然后通过 variant 编码进行储存。

Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx