Every field of a message starts with a tag, which gives the field’s number and its wire type. The wire type says how the value after the tag is stored, so a reader can step over a field it does not know without the schema.
The tag
The tag is a varint
holding (field_number << 3) | wire_type: the low three bits are the
wire type and the rest is the field number.
| Bits of the tag | Name | Meaning |
|---|---|---|
| 0 to 2 | wire type | How the value is stored: 0 to 5, from the table below. |
| 3 and up | field number | The number the schema gives the field: 1 to 536,870,911. |
Field numbers 1 to 15 fit in a one-byte tag, which is why schemas give them to the fields used most; 16 to 2047 take two bytes; the largest, 536,870,911 (229 − 1), takes five.
| Tag bytes | Tag value | Field | Wire type |
|---|---|---|---|
08 | 8 | 1 | 0, VARINT |
10 | 16 | 2 | 0, VARINT |
12 | 18 | 2 | 2, LEN |
3A | 58 | 7 | 2, LEN |
4A | 74 | 9 | 2, LEN |
3D | 61 | 7 | 5, I32 |
41 | 65 | 8 | 1, I64 |
A2 06 | 802 | 100 | 2, LEN |
The explorer turns down a tag whose value is larger than 32 bits, and a tag with field number 0.
Field numbers
A field number is from 1 to 536,870,911. Number 0 is never valid. Numbers 19,000 to
19,999 are reserved for the Protocol Buffers implementation: protoc
refuses them in a schema, but on the wire they are numbers like any other. A schema
may also mark numbers as reserved so that old fields are not reused; this
is not visible in the encoded bytes.
Wire types
| ID | Name | Value | Used for |
|---|---|---|---|
| 0 | VARINT | A varint. | int32, int64, uint32, uint64, sint32, sint64, bool, enum |
| 1 | I64 | 8 bytes, little-endian. | fixed64, sfixed64, double |
| 2 | LEN | A varint length, then that many bytes. | string, bytes, embedded messages, packed repeated fields |
| 3 | SGROUP | None: fields follow until the matching EGROUP. | The start of a group (deprecated) |
| 4 | EGROUP | None. | The end of a group (deprecated) |
| 5 | I32 | 4 bytes, little-endian. | fixed32, sfixed32, float |
Wire types 6 and 7 do not exist. A reader that meets one cannot go on, because it cannot know how long the value is; the explorer reports the offset of the tag.
I64: eight-byte values
An I64 value is eight bytes, little-endian. fixed64 is unsigned,
sfixed64 is two’s complement, and double is an IEEE 754
double. Field 8 holding the double 2.5:
41 00 00 00 00 00 00 04 40
41 (field 8, I64), then 2.5 as a little-endian double (built for this page)Without the schema the explorer shows an I64 value as a double, and its note gives it as unsigned and signed 64-bit integers too.
I32: four-byte values
An I32 value is four bytes, little-endian. fixed32 is unsigned,
sfixed32 is two’s complement, and float is an IEEE 754
single. Field 7 holding the float 1.5:
3D 00 00 C0 3F
3D (field 7, I32), then 1.5 as a little-endian float; read as fixed32 the same bytes are 1069547520 (built for this page)Fixed-width types are larger than varints for small numbers but smaller for large
ones: a fixed32 always takes four bytes, while a varint takes five for
values of 228 and more.