[TOC]
HDiffPatch\libHDiffPatch\HPatch\patch.c
packUInt & hpatch_packUIntWithTag 可变长度整数编码
这组函数实现了一种高效的、类似于 LEB128 (Little-Endian Base 128) 或 VLQ (Variable-length quantity) 的整数编码方案。其核心目标是:用更少的字节来表示小数值,用更多的字节来表示大数值,从而在数据流中实现对数值本身的压缩。
原理与设计思路解析
您提供的注释已经非常精彩地概括了其编码方案,我将在此基础上做更详细的解析。
编码规则 (Encoding Scheme):
算法将一个整数uValue
拆分成多个字节进行存储。每个字节都由两部分组成:- 数据位 (Data Bits): 每个字节的低7位用于存储
uValue
的一部分数据。 - 连续标志位 (Continuation Bit): 每个字节的最高位 (MSB) 作为标志位。
- 如果 MSB 是
1
,表示后面还有字节属于这个整数。 - 如果 MSB 是
0
,表示这是这个整数的最后一个字节。
- 如果 MSB 是
- 数据位 (Data Bits): 每个字节的低7位用于存储
带标签的编码 (
hpatch_packUIntWithTag
)
HDiffPatch 的实现比标准的 LEB128 更进一步,它允许在第一个字节中嵌入额外的标志位 (tag)。第一个字节的结构:
1
2Bit: | 7 | 6 ... (8-kTagBit) | (7-kTagBit) | (6-kTagBit) ... 0 |
Desc: | C | highTag | C' | uValue_part1 |C'
(Bit7-kTagBit
): 这是主连续标志位。如果为1
,表示后面还有字节。highTag
(Bits(8-kTagBit)
to6
): 这是用户可以自定义的kTagBit
个标志位,用于在上层协议中传递额外信息(例如isNullSubDiff
标志)。uValue_part1
: 第一个字节中用于存储数值本身的数据位。
后续字节的结构:
1
2Bit: | 7 | 6 ... 0 |
Desc: | C | uValue_partN |C
(Bit 7): 连续标志位。uValue_partN
: 后续字节用于存储数值的数据位。
hpatch_packUIntWithTag
的实现逻辑 (大端序输出)
这个函数的实现非常巧妙,它生成的是一个大端序 (Big-Endian) 的可变长度整数,这意味着数值的最高有效位被编码在第一个字节中。分解 (Decomposition):
while (uValue > kMaxValueWithTag)
: 循环不断地从uValue
中取出低7位 (uValue & ((1<<7)-1)
),并将其逆序存入一个临时的codeBuf
缓冲区中。uValue >>= 7
: 将uValue
右移7位,准备处理下一部分。- 这个循环结束后,
uValue
中只剩下最高的一部分数据,而codeBuf
中则逆序存储了所有低位部分。
编码第一个字节 (Head Byte):
*pcode = (TByte)( (TByte)uValue | (highTag<<(8-kTagBit)) | ... );
- 将
uValue
的剩余高位部分,与highTag
和主连续标志位C'
通过位运算组合起来,形成第一个字节。
编码后续字节 (Tail Bytes):
while (codeBuf != codeEnd)
: 循环从后向前遍历codeBuf
(即按正确的顺序)。*pcode = (*codeEnd) | (TByte)(((codeBuf!=codeEnd)?1:0)<<7);
- 将
codeBuf
中的7位数据与连续标志位C
组合起来,形成后续的字节。
C++ 包装器 (
packUIntWithTag
,packUInt
)pack_uint.h
中的packUInt...
函数是对底层C函数hpatch_packUIntWithTag
的C++风格包装器。- 它们负责处理
std::vector
的内存管理,使得调用者可以方便地将编码后的字节流追加到vector
的末尾,而无需手动管理缓冲区指针和大小。packUInt
是packUIntWithTag
的一个特例,它默认tag
和kTagBit
都为0。
代码解析
1 | // Variable-length positive integer encoding scheme ... |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 wdfk-prog的个人博客!
评论