Git Pack 文件中对象 Header 结构分析
在 Git Pack 文件中,每个对象都有一个 Header,Header 的结构如上图所示,由 2 部分组成,分别是对象类型和对象大小。
在第一个 byte 的第一个 bit 是 MSB(Most Significant Bit),只是否继续读取下一个 byte,如果为 1,则继续读取下一个 byte,如果为 0,则表示当前 byte 是最后一个 byte。这个规则就是之前 Git Pack 文件中对象长度的变长编码的编解码解析 中提到的 标志位 的编码规则。
接下来的 3 个 bit 是对象类型,Git 中的对象类型有 6 种,分别是:
OBJ_COMMIT = 1
OBJ_TREE = 2
OBJ_BLOB = 3
OBJ_TAG = 4
OBJ_OFS_DELTA = 6
OBJ_REF_DELTA = 7
前 4 种对象类型是 Git 的基本的对象类型,后 2 种对象类型是 Git Pack 文件中的特殊对象类型,分别是 偏移量差异对象 和 引用差异对象。
后续会继续分析这些内部对象,希望最终能组成一个系列的文章
fn parse_object_header<R: Read>(mut reader: R) -> std::io::Result<(ObjectType, usize)> {
let mut buffer = [0; 1];
reader.read_exact(&mut buffer)?;
let byte = buffer[0];
let object_type = byte >> 4 & 0x70;
let mut initial_size = (byte & 0xf) as usize;
// Recursively read the following bytes if the MSB is set
if byte & 0x80 != 0 {
initial_size = parse_object_size(&mut reader, initial_size, 0)?;
}
Ok((object_type, initial_size))
}
byte >> 4 & 0x70的逻辑是先将byte右移 4 位,然后与0x70进行&操作,这样就可以得到byte的 1 - 3 位,也就是对象类型。byte & 0xf的逻辑是直接与0xf进行&操作,这样就可以得到byte的后 4 位,也就是对象大小在第一个byte的值。byte & 0x80是判断byte的 MSB 是否为1,
如果 MSB 为 0,则表示当前 byte 是最后一个 byte;如果是 1 的话就是后面一个 byte 同样是记录对象大小,就需要继续读取下一个 byte。
如果读取第二个 byte,则需要将第二个 byte 中得到的 size 左移 4 位,加入第一个 byte 的 size 进行解码计算; 后面再读取的 byte 时,需要右移 4 + 7 位,加入之前的 size 进行解码计算,直到读取到的 byte 的 MSB 为 0,则表示当前 byte 是最后一个 byte,这样就可以得到对象的大小。
这里需要注意的就是左移位数是 n * 7 + 4 ,第一次是 n = 0 移动 4 位 ,然后 n = 1 移动 11 位,n = 2 移动 18 位,以此类推进行计算。
