wire / wire-format / protobuf-wire

Protocol Buffers wire format (tag + value)

Protobuf serializes a message as a stream of fields, each preceded by a varint 'tag' that packs the field number and a 3-bit wire type: tag = (field_number << 3) | wire_type. The wire type (0 varint, 1 64-bit, 2 length-delimited, 5 32-bit) tells the parser how to read the value, so unknown fields can be skipped. It is a tag-then-value scheme, a lean cousin of ASN.1 TLV.

wire-format kind wire-format status de-facto verification verified tier B wire-format@1

aka: protobuf · protocol buffers · proto wire format · field tag

wire format

spec: Protocol Buffers Encoding (protobuf.dev)

Each field on the wire is a key (a varint tag) optionally followed by its value. The tag encodes both the field number and a wire type: tag = (field_number << 3) | wire_type. Wire type 0 = varint (int32/64, uint, bool, enum, sint via zigzag); 1 = 64-bit (fixed64, double); 2 = length-delimited (string, bytes, embedded messages, packed repeated) which is itself a length-then-value TLV; 5 = 32-bit (fixed32, float). Wire types 3/4 (start/end group) are deprecated.

fieldsizemeaning
Tag (key)1+ bytes (varint)(field_number << 3) | wire_type. Low 3 bits = wire type, the rest = field number. LEB128 varint, so large field numbers take more bytes.
Valuedepends on wire typewire type 0: a varint. type 1: exactly 8 bytes (little-endian). type 2: a varint length followed by that many content bytes (a nested TLV). type 5: exactly 4 bytes (little-endian).

example:

08 96 01

Field #1 = 150. Tag 0x08 = (1<<3)|0 => field 1, wire type 0 (varint). Value 96 01 is the LEB128 varint for 150 (0x96 has continuation bit set: 0x16=22 low 7 bits; 0x01 high => 1*128 + 22 = 150).

see: wire-format/leb128-varint


provenance

see also

agent: curl -H 'accept: application/json' wire.phall.io/wire-format/protobuf-wire or /wire-format/protobuf-wire.json