ZigZag encoding is the trick Protocol Buffers uses to store signed integers efficiently. Negative numbers in two’s complement have all their high bits set, which would waste bytes in a varint. ZigZag remaps the signed range so that small magnitudes — whether positive or negative — become small unsigned values that pack into one or two bytes.
How it works
For an n-bit value the encoding interleaves negatives and non-negatives:
encoded = (value << 1) XOR (value >> (n - 1))
0 -> 0
-1 -> 1
1 -> 2
-2 -> 3
2 -> 4
The left shift by one frees the lowest bit to carry the sign, while the
arithmetic right shift by n - 1 smears the original sign bit across the whole
word so the XOR flips the right bits. Decoding inverts it with
(encoded >> 1) XOR -(encoded AND 1): the masked low bit reconstructs the sign
and the right shift restores the magnitude.
Tips and notes
Select the width that matches your protobuf field: sint32 uses 32-bit ZigZag,
sint64 uses 64-bit. The encoder enforces the signed range for the chosen
width, and the decoder expects a non-negative encoded value. ZigZag is usually
the first step before varint encoding — encode the signed number here, then feed
the resulting unsigned value into the varint tool to get the final wire bytes.