Varint和Zigzag

460 阅读2分钟

Varint就是一种对数字进行编码的方法,编码后二进制数据是不定长的,数值越小的数字使用的字节数越少。例如对于int32_t,采用Varint编码后需要1~5个字节,小的数据使用1个字节,大的数字使用5个字节。基于实际场景中小数字的使用远远多于大数字,因此通过Varint编码对于大部分场景可以起到一个压缩的效果。

无符号

Varint中的每个字节的最高位bit有特殊的含义,如果该位为1,表示后续的字节也是该数字的一部分,如果该位为0,则结束。其他的7个bit都用来表示数字。因此小于128的数字都可以用一个字节表示。大于128的数字,会用两个字节。

例如整数1的表示,仅需一个字节:

0000 0001

例如300的表示,需要四个字节:

1010 1100 0000 0010

def encode(num):
    buf = ""
    while True:
        n = num & 0x7f
        num >>= 7
        if num != 0:
        	n |= 0x80
            buf += chr(n)
        else:
            buf += chr(n)
            break
    return buf

def decode(buf):
    num = 0
    shift = 0
    i = 0
    while True:
        n = ord(buf[i])
        i += 1
        num |= (n & 0x7f) << shift
        shift += 7
        if not (n & 0x80):
            break
    return num

有符号

在有符号的情况下,需要使用有符号数到无符号数的映射,Zigzag编码。

详细的映射表如下:

Signed OriginalEncoded As
00
-11
12
-23
24
21474836474294967294
-21474836474294967295
inline uint32 WireFormatLite::ZigZagEncode32(int32 n) {
  // Note:  the right-shift must be arithmetic
  return (n << 1) ^ (n >> 31);
}

inline int32 WireFormatLite::ZigZagDecode32(uint32 n) {
  return (n >> 1) ^ -static_cast<int32>(n & 1);
}

inline uint64 WireFormatLite::ZigZagEncode64(int64 n) {
  // Note:  the right-shift must be arithmetic
  return (n << 1) ^ (n >> 63);
}

inline int64 WireFormatLite::ZigZagDecode64(uint64 n) {
  return (n >> 1) ^ -static_cast<int64>(n & 1);
}

通过Zigzag编码后,就可以对负数进行varint编码了。

但是,如何发现这种映射方法,目前还没有明白,希望大佬们可以在评论区留言解惑。

www.cnblogs.com/jacksu-tenc…

izualzhy.cn/protobuf-en…