
Protobufs 101 (Part 2)
In Part 1 we explored the basics of Protobuf wire formats. Now let’s move on to some slightly more advanced concepts:
- Repeated Fields
- Packed Repeated Fields
- Maps
- (Next: Negative Numbers)
Repeated Fields
Proto Definition
message FruitBasket {
repeated string fruits = 1;
}
How It Works
In repeated fields, each value is encoded as a separate tag-value pair, even though they share the same field number.
Example
fruits: ["Apple", "Banana"]
This results in two tag-value pairs, both with field number 1.
Encoded Output (Hex)
0a 05 41 70 70 6c 65 0a 06 42 61 6e 61 6e 61
Breakdown
-
0a→ Tag (field 1, length-delimited) -
05→ Length of "Apple" -
41 70 70 6c 65→ "Apple" -
0a→ Tag again (same field) -
06→ Length of "Banana" -
42 61 6e 61 6e 61→ "Banana"
First 7 bytes → Apple
Next 8 bytes → Banana
Packed Repeated Fields
Packed repeated fields are an optimization used for numeric types.
Instead of repeating the tag for every value, all values are grouped together.
Proto Definition
message FruitCounts {
repeated int32 values = 1 [packed = true];
}
Encoding Structure
A packed repeated field is encoded as:
- Tag
- Length (total bytes of all values combined)
- Concatenated varint-encoded values
Example
values: [3, 270, 86942]
Encoded Output (Hex)
0a 06 03 8e 02 9e a7 05
Breakdown
0a→ Tag (field 1, length-delimited)06→ Total byte length03→ Varint(3)8e 02→ Varint(270)9e a7 05→ Varint(86942)
Key Insight
- Tag appears once
- Values are packed together
This reduces size significantly for large numeric arrays
Maps
Maps in Protobuf are actually syntactic sugar. Internally, they are encoded as repeated key-value message entries.
Proto Definition
message FruitInventory {
map<string, int32> stock = 1;
}
What It Becomes Internally
Protobuf converts it into something like:
message FruitInventory {
repeated Entry stock = 1;
}
message Entry {
string key = 1;
int32 value = 2;
}
Example
stock: {
"apple": 3,
"banana": 5
}
Encoded Output (Conceptual)
Each map entry is encoded as a separate embedded message:
Entry 1:
key = "apple"
value = 3
Entry 2:
key = "banana"
value = 5
So it behaves like:
stock: [
{ key: "apple", value: 3 },
{ key: "banana", value: 5 }
]
Key Insight
- Maps = repeated embedded messages
- Each entry has:
- field 1 → key
- field 2 → value
- Order is not guaranteed
Next up: Negative Numbers