varint原理 - 负数的编码和解码

前一篇博客 varint原理 - 正数的编码和解码_YZF_Kevin的博客-CSDN博客我们讲了varint的实现原理,举例和分析varint类型对于正数的编码,解码过程

本篇博客,我们开始举例分析varint对负数的编码和解码,因为负数有原码,反码,补码的概念,如果大家对这一块不熟,可以先看下我的这篇博客,里面有详细的解释 大端 小端 原码 反码 补码 及内存中的表现_YZF_Kevin的博客-CSDN博客

先回顾下varint的编码,解码过程

编码原理

varint 对数字的二进制,从右往左,每7位分割成一块

如果不是最后一块,则最左边补1,组成一个字节

如果是最后一块,则左边缺的全部补0,组成1个字节

最终保存的时候,根据分割块的顺序依次从左往右

解码原理

读取字节流后,从左往右,逐个字节判断

如果该字节最左边为1,说明varint值没读取完,后面的字节还是本varint的值,继续读。本字节最左边的1去掉后,剩下的7位留作备用

如果该字节最左边为0,说明varint值读取完毕

最终,根据分割块的顺序依次从右往左填,组成的字节流按int型解析即可

举例

数字 -1,负数在内存中以补码形式存放,先计算补码,我们以32位的整型为例

-1的源码:10000000  00000000  00000000  00000001(符号位为1表负,其他跟正数一样)

-1的反码:11111111    11111111    11111111   11111110  (符号位不变,其他位全部取反)

-1的补码:11111111    11111111    11111111    11111111  (反码+1即可)

但是varint对负数统一是用64位来表示的,所以

64位的-1在内存中表示如下

11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111

每7位标一个颜色,一共分成了10块(因为64位 = 7位 * 9 + 1) 

1 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111

编码过程

1. 从右往左依次取7位,第一次取出来的1111111,因为自己不是最后一块,根据规则左边补1,得到11111111

2. 重复上面的步骤,依次处理每块数据

3. 最后一块1,因为是最后一块了,根据规则左边补0,得到00000001

把这些分割后新组成的块,从左往右

所以,最终varint表示法-1的最终格式是

11111111 11111111 11111111 1111111111111111 11111111 1111111 11111111 11111111 00000001

动手实验

proto文件和程序如下(注意:我们定义的是int32类型)

// 玩家信息
message Player {
    int32               uniNum          = 1;    // 唯一序号
}

实验结果(可以看到实际经过Marshal()序列化之后,-1确实占用了10个字节,内容和我们计算的完全一样)

结论

1. varint可以对正数编码,也可以对负数编码

2. varint编码用来表示负数时,即使指定了int32类型,varint一样会作为int64来处理,且发送时要占用10个字节,非常坑

针对varint这个问题,protobuffer也做出了优化,可以使用sint32,那就是我们下一篇要讲的zigzag编码

protobuf中zigzag编码原理_YZF_Kevin的博客-CSDN博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值