从protobuffer,窥探整数编码

Base 128 varint 变长编码

该算法主要目的是降低整数的存储空间,可以作为序列化编码的一种方案。目前googleprotoBuff在处理整数时就采用了改方案。算法内容比较简单,先附上算法说明,之后会提供java代码实现。

 

算法内容:

对于正数M编码:

① 将M转成二进制B,擦除多余的零,保留最短的二进制格式,例如B=000101,则应处理为B=101

② 对B从低位开始,以7位分组,不够高位填零,得B=b0 b1 b2 ....,再按组倒置为C=bn...b2 b1 b0

③依次对C按组添加标识位S,格式为D=Snbn ... S2b2 S1b1 S0b0S位长为1bit,取值01,取值规则,若Si后面有分组,则Si=1,否则Si=0

D即为编码后的数据

 

对于负数M编码:

① 将M转成二进制B,计算机中是采用补码表示

② 对B从低位开始,以7位分组,不够高位填零,得B=b0 b1 b2 ....,再按组倒置为C=bn...b2 b1 b0

③依次对C按组添加标识位S,格式为D=Snbn ... S2b2 S1b1 S0b0S位长为1bit,取值01,取值规则,若Si后面有分组,则Si=1,否则Si=0

D即为编码后的数据

 

解码为其反过程

 

Int型数字Java代码如下:(其他类型类似)

public byte[] encode(int a) {

int length = 5;

if (a > -1) {

length = 0;

do {

length++; // 计算正数编码空间

} while ((1 << length * 7 - 1) < a);

}

byte[] data = new byte[length];

byte temp;

for (int i = 0; i < length; i++) {

temp = (byte) (a >> (i * 7) & 0x7f);

if (i > 0) {

data[i - 1] |= 0x80;// i-1后有数据,标识位置1

}

data[i] = temp;

}

return data;

}

 

public int decode(byte[] data) {

int result = 0;

for (int i = 0; i < data.length; i++) {

result |= (data[i] & 0x7f) << (i * 7);

}

return result;

}

 

为什么会选择7位一组,而不是其他?

首先计算机存储空间最小单位为B,即8bit,这就决定每组的大小一定不能超过8,否则分组时,填充的0会很多。其次如果选择以小于7位分组,则编码后需要填充更多的标识位,所以7是一个不错的选择。

 

该编码的缺点也很明显,对于负数起不到压缩的效果反而导致空间占用增大了,那有什么好的解决方法吗?

拍脑袋想一个,可以添加数字类型位呀,0表示正数,1表示负数,这样负数就可以使用正数表示,空间压缩效果就可以提高了。但这个也有缺陷,就是在实际应用中会很别扭,用户在申明整数类型时还要知道,这个整数一定是正数或者一定是负数,当然我们也可以在程序内部解决,但为了这个强行搞出一个字节来标示类型有点不值当,那有其他好办法吗?

 

GoogleprotoBuff中提供了一种解决方案,Zigzag函数映射,先对整数进行处理,通过公式将其全部映射到正数空间,这样再使用Base 128 varint算法编码时,可以获的一个不错的压缩比。映射函数如下:

Zigzag(n) = (n << 1) ^ (n >> 31), n有符号32

Zigzag(n) = (n << 1) ^ (n >> 63), n有符号64

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值