An int32 in memory is four bytes whether it holds 3 or three hundred million. A varint is not. It uses one byte for small numbers and grows only when the number needs the room, which is why field numbers 1 through 15 cost so little.
The trick is that only 7 bits of each byte carry the number. The top bit, the most significant one, is spent on bookkeeping. It is set to 1 on every byte except the last, so a decoder reading bytes one at a time knows to keep going until it hits a byte whose top bit is 0. That byte is the end.
The 7-bit groups come out least significant group first. So the number is written backwards from how you would say it, and a decoder has to read the whole varint before it knows what value it has. In practice this never matters to you, but it does explain why varints show up reversed if you ever dump the bytes by hand.
Chunk Data
The number gets split into 7-bit groups. A byte has 8 bits, so that leaves the top one free to use as a continuation bit.
Reverse & Add MSB Flag
Those groups go out least significant first, which is the little-endian order. Every byte then gets its top bit set to 1, apart from the last one.
Further Reading
- Delta Encoding in Protobuf
A technique that leans on varints staying small to shrink repeated numeric data further.
Next
ZigZag
Why negative numbers are expensive as plain varints and how ZigZag encoding maps signed integers to small unsigned ones for sint32 and sint64.