惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
J
Java Code Geeks
L
LangChain Blog
V
V2EX
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Cloudflare Blog
博客园 - 司徒正美
B
Blog
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
MyScale Blog
MyScale Blog
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog

博客园 - 三驾马车

Claude Code 官宣:可以在 IDEA 用了! idea gitee 更新已取消 解决方案 ByteBuffer和ByteBuf区别 Marshalling.getProvidedMarshallerFactory("serial") 参数有那些 ProtobufVarint32FrameDecoder和ProtobufDecoder区别 netty initChannel ch.pipeline().addLast 先后顺序很重要 ChannelInboundHandlerAdapter 的channelRead和channelReadComplete的区别 Unpooled.buffer()和Unpooled.copiedBuffer区别 ServerBootstrap 和Bootstrap 区别 childhandler 和 handler 区别 ChannelInitializer<SocketChannel> 的作用详解 ChannelHandlerAdapter 和 ChannelInboundHandlerAdapter 的区别 SimpleChannelInboundHandler 中的 messageReceived 和 channelRead0 ChannelHandlerAdapter 与 ChannelInboundHandler 的区别 Application run failed .ParserException: while parsing a block mapping in 'reader' openssl genrsa 自签名ssl证书 上传本地项目到新建git项目 save download pdf
protobuf 的 Varint 编码规范
三驾马车 · 2025-05-21 · via 博客园 - 三驾马车

Protocol Buffers(protobuf)中的 Varint(Variable-length integer) 是一种可变长度的整数编码格式,用于高效压缩小整数的存储空间。其核心规范如下:


1. 基本规则

  • 目标:用尽可能少的字节表示整数(尤其适合小值)。

  • 原理

    • 每个字节的最高位(MSB, Most Significant Bit)是 标志位

      • 1:表示后续还有字节。

      • 0:表示当前是最后一个字节。

    • 每个字节的低 7 位(bits 0-6)存储实际的数值片段,按 小端序(低位在前)组合。


2. 编码示例

(1) 单字节(值 ≤ 127)

  • 数值 5 的二进制:00000101
    → 直接编码为 0x05(MSB=0,无需后续字节)。

(2) 多字节(值 > 127)

  • 数值 300 的编码过程:

    1. 二进制表示:300 = 1 00101100(9位,需 2 字节)。

    2. 分割为 7 位一组(小端序):

      • 低 7 位:01011000x2C)→ 加上 MSB=1(还有后续字节):101011000xAC)。

      • 高 2 位:00000100x02)→ 加上 MSB=0(结束):000000100x02)。

    3. 最终编码:[0xAC, 0x02](字节序列)。


3. 解码步骤

以字节序列 [0xAC, 0x02] 解码为 300 为例:

  1. 读取 0xAC10101100):

    • MSB=1 → 还有后续字节。

    • 有效数据:01011000x2C)。

  2. 读取 0x0200000010):

    • MSB=0 → 结束。

    • 有效数据:00000100x02)。

  3. 组合结果:

    • 0x02 << 7 | 0x2C = 0x12C(即 300)。


4. 特殊规则

(1) 有符号整数(sint32/sint64)

  • 先通过 ZigZag 编码 将负数映射到无符号域:

    • 0 → 0-1 → 11 → 2-2 → 3, ...

    • 公式:(n << 1) ^ (n >> 31)(32位)。

  • 再按 Varint 编码。

(2) 固定长度类型(fixed32/fixed64)

  • 直接使用固定 4/8 字节存储(不适用 Varint)。


5. 最大字节数

类型最大字节数数值范围
Varint32 5 字节 -2^31 到 2^31-1
Varint64 10 字节 -2^63 到 2^63-1

6. 伪代码实现

编码(Encode Varint32)

def encode_varint(value):
    bytes = []
    while value > 0x7F:
        bytes.append((value & 0x7F) | 0x80)
        value >>= 7
    bytes.append(value)
    return bytes

解码(Decode Varint32)

def decode_varint(buffer):
    result = 0
    shift = 0
    while True:
        byte = buffer.read_byte()
        result |= (byte & 0x7F) << shift
        if not (byte & 0x80):
            break
        shift += 7
    return result

7. 实际应用场景

  1. Protobuf 消息长度前缀
    消息头部的长度字段通常用 Varint32 编码。

  2. 整数字段压缩
    如 int32uint32 等字段的存储优化。

  3. 网络传输
    减少小整数的带宽占用。


8. 为什么用 Varint?

  • 空间效率:小整数(如 142)仅需 1 字节。

  • 兼容性:字段编号(Field Number)和枚举值常用 Varint 编码。

  • 流式解析:无需提前知道整数长度。


9. 注意事项

  • 性能权衡
    Varint 的解码比固定长度整数稍慢(需位操作和循环)。

  • 负数效率
    直接编码负数(如 int32)可能占用 5 字节,建议用 sint32 通过 ZigZag 优化。


总结

Protobuf 的 Varint 编码通过 动态字节长度 和 MSB 标志位 实现了高效的空间压缩,尤其适合小整数和稀疏数据。理解其规范对优化消息大小和解析性能至关重要