Skip to content
Field Encoding
step 1/5

Reading — step 1 of 5

Read

~1 min readSchema & Encoding

Field Encoding

Each field encoded as: tag (varint) + value.

Tag combines field number + wire type:

tag = (field_number << 3) | wire_type

Wire types:

  • 0: varint (int32, int64, uint32, uint64, bool, enum).
  • 1: 64-bit fixed (fixed64, sfixed64, double).
  • 2: length-delimited (string, bytes, embedded message, packed repeated).
  • 5: 32-bit fixed (fixed32, sfixed32, float).
  • 3: start group (deprecated, was for proto2).
  • 4: end group (deprecated).

Examples:

int32 age = 2; age = 30:

  • Tag: (2 << 3) | 0 = 0x10 = 10.
  • Value: 30 = 1e.
  • Bytes: 10 1e.

string name = 1; name = "Alice":

  • Tag: (1 << 3) | 2 = 0x0A = 0a.
  • Length: 5 = 05.
  • Value: Alice = 41 6c 69 63 65.
  • Bytes: 0a 05 41 6c 69 63 65.

Embedded message:

  • Same as string (wire type 2).
  • Length-prefixed bytes contain serialized inner message.

Repeated fields (proto3):

  • Default for scalars: packed = single tag + length + concatenated values.
  • For messages: separate tag per element.

Default values:

  • Skipped during encoding (proto3).
  • 0 for numbers, "" for strings, false for bools, empty for arrays.
  • On decode: missing fields stay at default.

Unknown fields:

  • Decoder encounters tag for field it doesn't know.
  • Preserved as raw bytes (forward compatibility).
  • Re-emitted on serialize (so old code can pass through new fields).

Field ordering: technically unspecified but standard is by tag number.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…