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

推荐订阅源

Forbes - Security
Forbes - Security
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
SegmentFault 最新的问题
Y
Y Combinator Blog
Recorded Future
Recorded Future
博客园 - Franky
I
InfoQ
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园_首页
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
Cyberwarzone
Cyberwarzone
The Register - Security
The Register - Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
雷峰网
雷峰网
P
Palo Alto Networks Blog
G
GRAHAM CLULEY
Cloudbric
Cloudbric
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MongoDB | Blog
MongoDB | Blog
F
Full Disclosure
Google DeepMind News
Google DeepMind News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
Check Point Blog
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
T
Threat Research - Cisco Blogs
U
Unit 42
N
Netflix TechBlog - Medium
The Cloudflare Blog
Spread Privacy
Spread Privacy
Microsoft Azure Blog
Microsoft Azure Blog
美团技术团队
T
Troy Hunt's Blog
Engineering at Meta
Engineering at Meta
H
Heimdal Security Blog
TaoSecurity Blog
TaoSecurity Blog
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tenable Blog
B
Blog
S
Securelist
H
Hacker News: Front Page
Google Online Security Blog
Google Online Security Blog
G
Google Developers 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 标志位 实现了高效的空间压缩,尤其适合小整数和稀疏数据。理解其规范对优化消息大小和解析性能至关重要