CodeWalk

Protocol Buffers的Varint编码与ZigZag原理

作者:Yahuda · 2026-05-30 12:55

解释Protobuf中Varint编码的压缩原理,包括Base 128 Varint的编码方式、负数的处理(ZigZag)、以及Field Key的计算规则(wire type + field number)。

回答

Yahuda

1. Varint编码原理

  • 每个字节使用7位存储数据,最高位(MSB)作为延续标志位
    • MSB=1:后续还有字节
    • MSB=0:此为最后一个字节
  • 小整数占用很少字节(如127仅1字节,但16384需要3字节)
  • 示例:300的Varint编码
    • 300 = 256 + 44 = 0000_0001_0010_1100
    • 逆序分7位:010_1100 000_0010
    • 加MSB:1010_1100 0000_0010 → [0xAC, 0x02]

2. ZigZag编码(处理负数)

  • 负数在Varint中会占10字节(因为符号位),效率极低
  • ZigZag将负数映射为正数:
    n  →  (n << 1) ^ (n >> 31)   // sint32
    n  →  (n << 1) ^ (n >> 63)   // sint64
    
  • 效果:-1 → 1, 1 → 2, -2 → 3, 2 → 4...
  • 适用于有符号整数场景

3. Wire Type与Field Key

  • Key = (field_number << 3) | wire_type
    • Wire Type 0:Varint(int32/uint64/bool/enum)
    • Wire Type 1:64-bit(fixed64/sfixed64/double)
    • Wire Type 2:Length-delimited(string/bytes/embedded message/repeated)
    • Wire Type 5:32-bit(fixed32/sfixed32/float)
  • 解码时通过Key知道字段编号和数据类型

4. 编码优势

  • 消息体紧凑,无冗余字段名
  • 兼容性:新字段用新编号,旧客户端可忽略