新聞中心
在編寫網(wǎng)絡通訊的時候我們經(jīng)常需要把一些數(shù)據(jù)存儲到byte[]中然后再發(fā)送出去,數(shù)值則是我們經(jīng)常處理的數(shù)據(jù)成員。發(fā)越少的東西意味著使用更少的IO和帶寬 ,所以對傳輸數(shù)據(jù)進行壓縮也是件非常重要的事情。接下來提到的就是一種基于數(shù)字存儲的方式在大多數(shù)情況下可以節(jié)省數(shù)值存儲空間。

Varint 是一種緊湊的表示數(shù)字的方法。它用一個或多個字節(jié)來表示一個數(shù)字,值越小的數(shù)字使用越少的字節(jié)數(shù)。這能減少用來表示數(shù)字的字節(jié)數(shù)。比如對于 int32 類型的數(shù)字,一般需要 4 個 byte 來表示。但是采用 Varint,對于很小的 int32 類型的數(shù)字,則可以用 1 個 byte 來表示。當然凡事都有好的也有不好的一面,采用 Varint 表示法,大的數(shù)字則需要 5 個 byte 來表示。從統(tǒng)計的角度來說,一般不會所有的消息中的數(shù)字都是大數(shù),因此大多數(shù)情況下,采用 Varint 后,可以用更少的字節(jié)數(shù)來表示數(shù)字信息。下面就詳細介紹一下 Varint。
Varint 中的每個 byte 的最高位 bit 有特殊的含義,如果該位為 1,表示后續(xù)的 byte 也是該數(shù)字的一部分,如果該位為 0,則結(jié)束。其他的 7 個 bit 都用來表示數(shù)字。因此小于 128 的數(shù)字都可以用一個 byte 表示。大于 128 的數(shù)字,比如 300,會用兩個字節(jié)來表示:1010 1100 0000 0010
由于負數(shù)的高位為1,所以采用這種壓縮處理的時候必須負數(shù)轉(zhuǎn)成正數(shù),可以通過以下代碼實現(xiàn)int to uint的轉(zhuǎn)換
- 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
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
(data, 0, count); - }
data[count] = (byte)((value & 0x7F) | 0x80); 得到頭7位的數(shù)值, | 0x80是表明后面的byte也是數(shù)字的一部分。
while ((value >>= 7) != 0) 右移7位如果不為零的情況下則繼續(xù)上面的工作。
data[count - 1] &= 0x7F 把最后byte的最高位設置成0;
接下來就是一個uint的解碼過程
- private static uint ReadUInt32Variant(ArraySegment
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已經(jīng)不是數(shù)值組成部分。
(chunk & 0xF0) == 0 chunk只有4位,如果不是則表明這個byte不是數(shù)值存儲的一部分。
測試一下看下編碼效果
- ArraySegment
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
其實有人會有凝問,為什么不根據(jù)情況來用int16等來存儲,如果一旦用了int16就說明以后需要轉(zhuǎn)int32就是件非常麻煩的事情,雙方程序都需要調(diào)整。如果采用Varint進行處理就能達到最好擴展效果和帶寬利用率.
原文鏈接:http://www.cnblogs.com/smark/archive/2012/05/03/2480034.html
當前名稱:談談數(shù)值壓縮存儲方法Varint
文章源于:http://m.fisionsoft.com.cn/article/djjogej.html


咨詢
建站咨詢
