无符号整形的压缩与解压缩算法。
今天浏览公司的私有协议的实现,在将数据序列化的代码中发现对无符号整形做了一些特殊的处理,了解下才发现是无符号整形压缩跟解压缩算法的一种。
于是上网了解了下相关的内容,记录下。
对于无符号整形数字,在其二进制形式上可以将其看成两部分,实际能够表示其大小以及填充的零部分.
举几个例子:
真正能够表示其大小的是最后一位,其余的31位0都是做为填充位。
真正能够表示其大小的是后面12位 1101 1101 0111,其余的20位0都是做为填充位。
不存在填充的0,也就是填充的位数是0个,需要所有的位数才能表示出其实际的大小。
现在提供一种无符号整形的压缩算法:
将无符号整形的数据每七位数字保存在压缩后的一位字节中,压缩字节的剩余一位做为标记位,用来表示该字节是不是数据的最后一个字节,为1表示该字节不是压缩后的数据的最后一位,后面还有数据,为0表示该字节已经是压缩后的数据的最后一位,后面已经没有数据。
则对于前面的几个数字的压缩可以表示为:
uint32_t num = 1;
压缩前: | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 |
压缩后: | 0000 0001 |
uint32_t num = 3543;
压缩前: | 0000 0000 | 0000 0000 | 0000 1101 | 1101 0111 |
压缩后: | 1101 0111 | 0001 1011 |
uint32_t num = -1;
压缩前 : | 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 |
压缩后 : | 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 | 0000 1111 |
今天浏览公司的私有协议的实现,在将数据序列化的代码中发现对无符号整形做了一些特殊的处理,了解下才发现是无符号整形压缩跟解压缩算法的一种。
于是上网了解了下相关的内容,记录下。
对于无符号整形数字,在其二进制形式上可以将其看成两部分,实际能够表示其大小以及填充的零部分.
举几个例子:
uint32_t num = 1;
二进制形式 : | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 |真正能够表示其大小的是最后一位,其余的31位0都是做为填充位。
uint32_t num = 3543;
二进制形式 : | 0000 0000 | 0000 0000 | 0000 1101 | 1101 0111 |真正能够表示其大小的是后面12位 1101 1101 0111,其余的20位0都是做为填充位。
uint32_t num = -1;
二进制形式 : | 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 |不存在填充的0,也就是填充的位数是0个,需要所有的位数才能表示出其实际的大小。
需要对数据进行压缩,只要把能表示出数字实际大小的部分的信息表示出来即可,填充的0部分的信息可以全部或者部分舍弃。
现在提供一种无符号整形的压缩算法:
将无符号整形的数据每七位数字保存在压缩后的一位字节中,压缩字节的剩余一位做为标记位,用来表示该字节是不是数据的最后一个字节,为1表示该字节不是压缩后的数据的最后一位,后面还有数据,为0表示该字节已经是压缩后的数据的最后一位,后面已经没有数据。
则对于前面的几个数字的压缩可以表示为:
uint32_t num = 1;
压缩前: | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 |
压缩后: | 0000 0001 |
uint32_t num = 3543;
压缩前: | 0000 0000 | 0000 0000 | 0000 1101 | 1101 0111 |
压缩后: | 1101 0111 | 0001 1011 |
uint32_t num = -1;
压缩前 : | 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 |
压缩后 : | 1111 1111 | 1111 1111 | 1111 1111 | 1111 1111 | 0000 1111 |
从上面可以看出,对于比较小的无符号整形数字,压缩之后优势越大,占用的空间越小,对于比较大的数据压缩之后占用的空间可能反而会更大。
代码实现:
//压缩无符号16位整形到buf中, 正确返回占用buffer的长度,失败返回-1.
int32_t unint16_compress(uint16_t u16, uint8_t *buf, int32_t size)
{
uint8_t *p = buf;
int32_t left = size;
while (left > 0)
{
p[0] = (uint8_t)(u16 & 0x7f);
u16 >>= 7;
p[0] |= (u16 == 0 ? 0x00 : 0x80);
p++;
left--;
if (u16 == 0)
{
return size - left;
}
}
return -1;
}
//压缩无符号32位整形到buf中, 正确返回占用buffer的长度,失败返回-1.
int32_t unint32_compress(uint32_t u32, uint8_t *buf, int32_t size)
{
uint8_t *p = buf;
int32_t left = size;
while (left > 0)
{
p[0] = (uint8_t)(u32 & 0x7f);
u32 >>= 7;
p[0] |= (u32 == 0 ? 0x00 : 0x80);
p++;
left--;
if (u32 == 0)
{
return size - left;
}
}
return -1;
}
//压缩无符号64位整形到buf中, 正确返回占用buffer的长度,失败返回-1.
int32_t unint64_compress(uint64_t u64, uint8_t *buf, int32_t size)
{
uint8_t *p = buf;
int32_t left = size;
while (left > 0)
{
p[0] = (uint8_t)(u64 & 0x7f);
u64 >>= 7;
p[0] |= (u64 == 0 ? 0x00 : 0x80);
p++;
left--;
if (u64 == 0)
{
return size - left;
}
}
return -1;
}
//解压缩
int32_t unint16_uncompress(uint16_t *pu16, uint8_t *buf, int32_t size)
{
uint16_t u16 = 0;
int32_t shift = 0;
uint8_t *p = buf;
int32_t left = size;
while (left > 0)
{
uint8_t n = (uint8_t)(p[0] & 0x7f);
uint8_t n0 = (uint8_t)(p[0] & 0x80);
u16 += (uint16_t)((uint16_t)n << (7 * shift));
p++;
left--;
shift++;
if (0 == n0)
{
if (pu16)
{
*pu16 = u16;
return size - left;
}
break;
}
}
return -1;
}
//解压缩
int32_t unint32_uncompress(uint32_t *pu32, uint8_t *buf, int32_t size)
{
uint32_t u32 = 0;
int32_t shift = 0;
uint8_t *p = buf;
int32_t left = size;
while (left > 0)
{
uint8_t n = (uint8_t)(p[0] & 0x7f);
uint8_t n0 = (uint8_t)(p[0] & 0x80);
u32 += (uint32_t)((uint32_t)n << (7 * shift));
p++;
left--;
shift++;
if (0 == n0)
{
if (pu32)
{
*pu32 = u32;
return size - left;
}
break;
}
}
return -1;
}
//解压缩
int32_t unint64_uncompress(uint64_t *pu64, uint8_t *buf, int32_t size)
{
uint64_t u64 = 0;
int32_t shift = 0;
uint8_t *p = buf;
int32_t left = size;
while (left > 0)
{
uint8_t n = (uint8_t)(p[0] & 0x7f);
uint8_t n0 = (uint8_t)(p[0] & 0x80);
u64 += (uint64_t)(((uint64_t)n) << (7 * shift));
p++;
left--;
shift++;
if (0 == n0)
{
if (pu64)
{
*pu64 = u64;
return size - left;
}
break;
}
}
return -1;
}