An int32 in memory is four bytes whether it holds 3 or three hundred million, but a varint uses one byte for small numbers and adds a byte for every 7 more bits a larger number needs. That's why tags for field numbers 1 through 15 take a single byte.
Only 7 bits of each byte carry the number. The top bit, the most significant one, is a continuation flag. It is set to 1 on every byte except the last, so a decoder reading bytes one at a time keeps going until it reaches a byte whose top bit is 0, which is the last byte of the varint.
The 7-bit groups come out least significant group first. So the groups are in the reverse of the order you would read the number in, 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 look reversed in a hex dump.
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
Storing differences between consecutive values so varints stay small, which shrinks 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.