protobuf序列化学习
2019年2月15日 星期五, 发表于 深圳
如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)
protobuf 整数编码 中使用了varint和zigzag对整数进行编码,达到节省空间的目的;
如文章有任何冒犯之处,例如侵权或者未标明引用,请联系删除。
本人水平有限,如有错误之处,请不吝赐教。
1. varint编码
看到varint编码了,首先想到了 huffman编码 ,huffman编码通过统计字符出现频率获取变长编码表,使用变长编码表对源符号进行编码,这样使 得编码后的字符长度降低,达到数据压缩的效果;
varint和huffman都是变长编码,varint可以压缩较小的数(protobuf认为在实际应用中较小的数具有较高的出现频次),varint用较少的byte表示较小的数,例如int32类型的数值,一般需要4个byte来表示,在varint编码中可以使用1byte表示小数,但是遇到大数可能需要5byte;
看看varint具体的编码方式;https://www.cnblogs.com/smark/archive/2012/05/03/2480034.html
varint编码每一个Byte的最高位bit有特殊含义;如果为1,则表示下一个Byte也是该数值的一个部分,如果该位置为0,则结束,使用剩下的7bit来表示数值,因此,小于128的数值都可以用一个byte表示;
//进行varint32编码
int encodeVarint32( unsigned char* inBuff, uint32_t num)
{
const int B = 128;
int iEncodeLen = 0;
while (num >= B)
{
*(inBuff + (iEncodeLen++)) = (unsigned char)((num & (B -1)) | B);
num >>= 7;
}
*(inBuff + (iEncodeLen++)) = num;
return iEncodeLen;
}
//进行varint32解码
uint32_t decodeVarint32( unsigned char* inBuff)
{
uint32_t b = 0,len = 0;
while (true)
{
b |= ((inBuff[len] & (0x7f) ) << (7*len));
if ( (inBuff[len] & 0x80) != 0x80) break;
len++;
}
return b;
}
c++ 的位操作优先级比较低,编码的时候需要特别注意,最好多使用括号,例如在上面的语句
if ( (inBuff[len] & 0x80) != 0x80) break; //正确
if ( inBuff[len] & 0x80 != 0x80) break; //错误
2. ZigZag编码
ZigZag编码是将有符号数统一映射到无符号数的一种编码方式;
这篇文章写的很好:https://blog.csdn.net/zgwangbo/article/details/51590186
1. 补码的第一位是符号位,他阻碍了我们对于前导0的压缩,那么,我们就把这个符号位放到补码的最后,其他位整体前移一位;
2. 因为数字绝对值越小,他所含的前导1越多。于是,这个算法就把负数的所有数据位按位求反,符号位保持不变,得到了这样的整数;
uint32_t encodeZigzag(int32_t num)
{
return (( num << 1) ^ ( num >> 31));
}
int decodeZigzg(int num)
{
return (((uint32_t)num) >>1) ^ -(num & 1);
}
使用ZigZag编码结合varint32编码就可以对绝对值较小的数进行编码,节省部分空间;