`

数值压缩存储方法Varint

    博客分类:
  • c++
 
阅读更多

转自:http://www.cnblogs.com/smark/archive/2012/05/03/2480034.html

在编写网络通讯的时候我们经常需要把一些数据存储到byte[]中然后再发送出去,数值则是我们经常处理的数据成员。发越少的东西意味着使用更少的IO和带宽 ,所以对传输数据进行压缩也是件非常重要的事情。接下来提到的就是一种基于数字存储的方式在大多数情况下可以节省数值存储空间。

 

    Varint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。比如对于 int32 类型的数字,一般需要 4 个 byte 来表示。但是采用 Varint,对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示。当然凡事都有好的也有不好的一面,采用 Varint 表示法,大的数字则需要 5 个 byte 来表示。从统计的角度来说,一般不会所有的消息中的数字都是大数,因此大多数情况下,采用 Varint 后,可以用更少的字节数来表示数字信息。下面就详细介绍一下 Varint。

 

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

 

 

 

    由于负数的高位为1,所以采用这种压缩处理的时候必须负数转成正数,可以通过以下代码实现int to uint的转换

 

private static int Zag(uint ziggedValue)

{

    int value = (int)ziggedValue;

    return (-(value & 0x01)) ^ ((value >> 1) & ~( 1<< 31));

}

private static uint Zig(int value)

{

    return (uint)((value << 1) ^ (value >> 31));

 

}

    以下操作是对一个uint进行编码处理

 

private static ArraySegment<byte> WriteUInt32Variant(uint value)

      {

          byte[] data = new byte[5];

          int count = 0;

          do

          {

              data[count] = (byte)((value & 0x7F) | 0x80);

              count++;

          } while ((value >>= 7) != 0);

          data[count - 1] &= 0x7F;

          return new ArraySegment<byte>(data, 0, count);

      }

    data[count] = (byte)((value & 0x7F) | 0x80);   得到头7位的数值, | 0x80是表明后面的byte也是数字的一部分。

 

    while ((value >>= 7) != 0)    右移7位如果不为零的情况下则继续上面的工作。

 

    data[count - 1] &= 0x7F 把最后byte的最高位设置成0;

 

    接下来就是一个uint的解码过程

 

private static uint ReadUInt32Variant(ArraySegment<byte> data)

        {

            uint value = data.Array[0];

            if ((value & 0x80) == 0) return value;

            value &= 0x7F;

            uint chunk = data.Array[1];

            value |= (chunk & 0x7F) << 7;

            if ((chunk & 0x80) == 0) return value;

            chunk = data.Array[2];

            value |= (chunk & 0x7F) << 14;

            if ((chunk & 0x80) == 0) return value;

            chunk = data.Array[3];

            value |= (chunk & 0x7F) << 21;

            if ((chunk & 0x80) == 0) return value;

            chunk = data.Array[4]; ;

            value |= chunk << 28;

            if ((chunk & 0xF0) == 0) return value;

            throw new OverflowException("ReadUInt32Variant Error!");

        }

    (value & 0x80) == 0 表示最高位为0,说明后面的byte已经不是数值组成部分。

 

    (chunk & 0xF0) == 0 chunk只有4位,如果不是则表明这个byte不是数值存储的一部分。

 

    测试一下看下编码效果

 

 

ArraySegment<byte> data = WriteUInt32Variant(Zig(0));

            Console.WriteLine(data.Count);

            data = WriteUInt32Variant(Zig(567));

            Console.WriteLine(data.Count);

            data = WriteUInt32Variant(Zig(10000));

            Console.WriteLine(data.Count);

            data = WriteUInt32Variant(Zig(-100000));

            Console.WriteLine(data.Count);

分别是1byte,2byte,3byte,3byte

 

    其实有人会有凝问,为什么不根据情况来用int16等来存储,如果一旦用了int16就说明以后需要转int32就是件非常麻烦的事情,双方程序都需要调整。如果采用Varint进行处理就能达到最好扩展效果和带宽利用率.

分享到:
评论

相关推荐

    前端开源库-signed-varint

    而signed-varint库则解决了这个问题,它能够以可变长度的方式存储有符号整数,较小的数值占用较少的字节,较大的数值占用较多的字节,但总体上比固定宽度的整数更节省空间。 signed-varint库的工作原理是基于Google...

    简单易懂的时序数据压缩算法分析.doc

    1. **Varint**:这是一种无符号整型的压缩方法,它利用了小数值出现概率较高的特性,通过自适应编码来减少存储空间。Varint编码时,每个字节的最高位用作继续标志,其余位用来存储数值的一部分。然而,Varint并不...

    Var_Fib_fib2020.com_压缩编码_

    在IT领域,压缩编码是一种非常重要的技术,它用于减少数据的存储空间和传输带宽,提高效率。在“Var_Fib_fib2020.com_压缩编码_”这个主题中,我们主要关注两种编码方式:Varint编码和Fibonacci编码。 **Varint编码...

    QuantBox行情数据存储方案1

    - Varint编码优化了int类型存储,小数值可以用更少的字节表示,减少了空间占用。 - ZigZag编码解决了负数占用更多字节的问题,使得正负数交替表示时能节省空间。 二、差分算法 1. 差分算法的基本思想是只保存数据...

    终稿-李淼-高性能消息数据存储引擎的设计解析.pdf

    - 数值数据采用VarInt编码。 - 业务数据采用quicklz压缩算法。 - 数据写入采用双循环Buffer机制。 - 重复数据引用写入,减少实际存储的数据量。 #### 九、性能测试结果 根据测试结果,在配备Intel i7 8550U...

    google protobuf 源代码

    这种编码方式对于小数值占用更少的字节,对于大数值,字节长度随着数值大小线性增长。 3. **压缩算法**:虽然描述中提到"对协议内容进行了编码和压缩",但Protobuf本身并不包含内置的压缩功能。然而,开发者可以在...

    leveldb_high_level_介绍1

    LevelDB的核心特性包括支持任意字节数组作为键和值、自定义的键值排序规则、简单的API接口、数据快照功能、Snappy压缩以减少存储占用和提高I/O效率,以及对大规模数据集的支持。 在LevelDB中,数据首先存储在内存中...

    38源码 6:破旧立新 —— 探索「紧凑列表」内部(1).md

    varint是一种可变长度的整数表示方法,它能够根据实际数值的大小决定使用多少字节来存储。这与UTF-8编码有异曲同工之妙,通过检查字节的最高位来判断编码长度。listpack支持使用1到5个字节来编码元素长度,这使得它...

    Protocol Buffers协议编码规则

    它的核心编码规则基于Base 128 Varints编码,这是一种压缩整数序列化的方法,能有效节省存储空间,尤其适用于在网络传输或磁盘存储中。 Varints编码的基本原理如下: 1. 每个字节的最高位(msb)作为连续标志,如果...

    经纬度优化

    例如,使用二进制编码(如varint)或基于几何属性的编码,如六角网格编码。这种编码方法可以显著减小数据的大小,同时保持定位精度。 三、空间索引 在大量经纬度数据的场景下,构建空间索引来加速查询至关重要。...

    protobuf c++使用手册

    Protobuf 使用了一种称为“变长整数编码”(Varint)的编码方式来压缩整数字段,这种方式将整数编码成一系列字节,使得小数值占用较少的空间。此外,对于字符串和其他消息类型,Protobuf 使用长度前缀编码,以确保...

    Protocol_Buffer官网文档中文版

    - **Protocol Buffer**是一种用于数据序列化的高效工具,支持多种编程语言(如Java、C++、Python等),能够实现数据的有效存储和传输。 #### 二、概念与工作原理 1. **概念介绍** - **Protocol Buffer**:一种...

    leveldb实现解析

    4. **Varint**: 变长整数编码技术,用于节省空间,尤其是在小数值频繁出现的情况下效果显著。 5. **ValueType**: 定义了数据类型的枚举,例如指明是新键值还是覆盖或删除等操作。 6. **SequenceNumber**: 顺序号,...

Global site tag (gtag.js) - Google Analytics