Reading — step 1 of 5
Read
ZigZag Encoding for Signed Ints
Naively varint-encoding a signed integer is a disaster. Because protobuf
sign-extends signed values to 64 bits before varint-encoding, the value -1
becomes 0xFFFFFFFFFFFFFFFF — a 10-byte varint. Every small negative number
takes the maximum possible space.
ZigZag fixes this by mapping signed integers to unsigned ones so that values with small magnitude — positive or negative — get small encodings:
| signed | zigzag |
|---|---|
| 0 | 0 |
| -1 | 1 |
| 1 | 2 |
| -2 | 3 |
| 2 | 4 |
| ... | ... |
The formula (for 32-bit):
zigzag(n) = (n << 1) ^ (n >> 31)
The arithmetic right shift turns negatives into -1 (all 1s) and positives
into 0. XOR with n << 1 then flips all bits for negatives.
Decoding:
decode(n) = (n >> 1) ^ -(n & 1)
When to use it
Use sint32 / sint64 when your field commonly holds negative values
(deltas, signed offsets, latitudes encoded as integers, etc.). For unsigned
or always-positive values, stick with int32 / int64 / uint32 / uint64.
Wire compatibility caveat
int32, uint32, and sint32 all use wire type 0 (VARINT) — but their
interpretations differ. Switching int32 -> sint32 in a schema is a
breaking change even though the wire type stays the same: old data
encoded as int32 will decode as wrong values when re-read as sint32.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…