From daf5fa32724832dfb19570e9b47de731ffc054a0 Mon Sep 17 00:00:00 2001 From: Patrick Marie Date: Sat, 8 Feb 2025 19:07:58 +0100 Subject: [PATCH] feat: parse idx files --- src/main.rs | 9 +++++++ src/pack.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6a1b0f7..b34d644 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,6 +67,11 @@ enum Command { WriteIndex, /// Dump Pack Files DumpPackFiles, + /// Dump Pack Index file + DumpPackIndexFile { + /// The pack index file to dump + pack_id: String, + }, } fn main() -> Result<(), Error> { @@ -119,6 +124,10 @@ fn main() -> Result<(), Error> { Ok(_) => (), Err(e) => eprintln!("Failed to dump pack files: {}", e), }, + Command::DumpPackIndexFile { pack_id } => match repo.dump_pack_index_file(&pack_id) { + Ok(_) => (), + Err(e) => eprintln!("Failed to dump pack index file: {}", e), + }, } Ok(()) diff --git a/src/pack.rs b/src/pack.rs index 160ea6b..509f890 100644 --- a/src/pack.rs +++ b/src/pack.rs @@ -344,8 +344,79 @@ impl Repository { ); } - // At the end of the file, there should be a 20-byte SHA-1 checksum - // TBD + let mut checksum_pack = [0; 20]; + file.read_exact(&mut checksum_pack)?; + + Ok(()) + } + + pub fn dump_pack_index_file(&self, pack_id: &str) -> Result<(), Error> { + let file_path = self + .path + .join(format!(".git/objects/pack/pack-{}.idx", pack_id)); + + let mut file = File::open(file_path)?; + + let mut buf = [0; 4]; + file.read_exact(&mut buf)?; + + if buf[0] != 0xff || "t0c".as_bytes() == &buf[1..4] { + return Err(Error::msg("Invalid pack index magic")); + } + + file.read_exact(&mut buf)?; + let version = u32::from_be_bytes(buf); + println!("{}", version); + if version != 2 { + return Err(Error::msg("Invalid pack index version")); + } + + let mut num_objects: u32 = 0; + let mut fanout_table = [0u32; 256]; + + // fanout table: 256 x 8 bytes + let mut buf = [0u8; 256 * 4]; + file.read_exact(&mut buf)?; + + for idx in 0..256 { + let offset = idx * 4; + num_objects = u32::from_be_bytes(buf[offset..offset + 4].try_into().unwrap()); + fanout_table[idx] = num_objects; + } + + let mut names = vec![0u8; 20 * num_objects as usize]; + file.read_exact(&mut names)?; + + let mut crc32_buf = vec![0u8; 4 * num_objects as usize]; + file.read_exact(&mut crc32_buf)?; + let crc32: Vec = crc32_buf + .chunks_exact(4) + .map(|chunk| u32::from_be_bytes(chunk.try_into().unwrap())) + .collect(); + + let mut offsets_buf = vec![0u8; 4 * num_objects as usize]; + file.read_exact(&mut offsets_buf)?; + let offsets: Vec = offsets_buf + .chunks_exact(4) + .map(|chunk| u32::from_be_bytes(chunk.try_into().unwrap())) + .collect(); + + for i in 0..num_objects { + let offset = offsets[i as usize]; + let crc32 = crc32[i as usize]; + let name = &names[(i * 20) as usize..(i * 20 + 20) as usize]; + println!( + "{} offset: 0x{:x} crc32: {}", + hex::encode(name), + offset, + crc32 + ); + } + + let mut checksum_pack = [0; 20]; + file.read_exact(&mut checksum_pack)?; + let mut checksum_idx = [0; 20]; + file.read_exact(&mut checksum_idx)?; Ok(()) }