# HG changeset patch # User Nathan Goldbaum # Date 1560178295 14400 # Mon Jun 10 10:51:35 2019 -0400 # Node ID 0585ddebf295fc6ce623086dc4904671b579aaec # Parent ae0220d9fb018d4890f3b7a8ab7585d49f74899c refactor revlog-handling code into a revlogs module diff --git a/src/main.rs b/src/main.rs --- a/src/main.rs +++ b/src/main.rs @@ -1,207 +1,7 @@ -use byteorder::{BigEndian, ReadBytesExt}; -use flate2::read::ZlibDecoder; use std::env; -use std::fmt; use std::fs::File; -use std::io; -use std::io::prelude::*; -use std::io::Cursor; -use std::io::SeekFrom; - -#[derive(Debug)] -struct RevlogHeader { - offset: u64, // really 6 bytes but easier to represent as a u64 - bitflags: [u8; 2], - compressedlength: u32, - uncompressedlength: u32, - baserev: i32, - linkrev: i32, - p1rev: i32, - p2rev: i32, - hash: [u8; 32], -} - -impl RevlogHeader { - fn new(buffer: &[u8; 64]) -> Result { - let mut cursor = Cursor::new(buffer as &[u8]); - Ok(RevlogHeader { - offset: cursor.read_u48::()? as u64, - bitflags: cursor.read_u16::()?.to_be_bytes(), - compressedlength: cursor.read_u32::()?, - uncompressedlength: cursor.read_u32::()?, - baserev: cursor.read_i32::()?, - linkrev: cursor.read_i32::()?, - p1rev: cursor.read_i32::()?, - p2rev: cursor.read_i32::()?, - hash: { - let mut res = [0u8; 32]; - cursor.read_exact(&mut res)?; - res - }, - }) - } -} - -#[derive(Debug)] -struct RevlogEntry { - header: RevlogHeader, - content: RevlogContent, -} - -#[derive(Debug)] -enum RevlogContent { - Generic(Vec), -} - -impl RevlogEntry { - fn new(f: &mut File, header_buffer: &[u8; 64]) -> Result { - let header = RevlogHeader::new(&header_buffer)?; - let mut content_buffer = vec![0u8; header.compressedlength as usize]; - f.read_exact(&mut content_buffer)?; - let decompressed_data = match content_buffer[0] { - 0 => vec![0u8; 0], - 0x75 => content_buffer[1..].to_vec(), - 0x78 => { - let mut gz = ZlibDecoder::new(&content_buffer[..]); - let mut decompressed_data = String::new(); - gz.read_to_string(&mut decompressed_data)?; - decompressed_data.into_bytes() - } - _ => panic!(), - }; - Ok(RevlogEntry { - header: header, - content: RevlogContent::Generic(decompressed_data), - }) - } -} -impl fmt::Display for RevlogHeader { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let hash: Vec = self.hash.iter().map(|b| format!("{:02x}", b)).collect(); - write!( - f, - "offset: {}\n\ - bitflags: {:?}\n\ - compressed length: {}\n\ - uncompressed length: {}\n\ - base revision: {}\n\ - linked revision: {}\n\ - p1: {}\n\ - p2: {}\n\ - hash: {}\n", - self.offset, - self.bitflags, - self.compressedlength, - self.uncompressedlength, - self.baserev, - self.linkrev, - self.p1rev, - self.p2rev, - hash.join("") - ) - } -} - -impl fmt::Display for RevlogEntry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "header:\n{}\ncontent: {}\n", self.header, self.content) - } -} - -impl fmt::Display for RevlogContent { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let printable = match self { - RevlogContent::Generic(s) => match std::str::from_utf8(s) { - Ok(parsed) => parsed.to_string(), - Err(_) => s - .iter() - .map(|c| { - std::ascii::escape_default(*c) - .map(|b| b as char) - .collect::() - }) - .collect::(), - }, - }; - write!(f, "{}", printable) - } -} - -#[derive(Debug)] -struct Revlog { - inline: bool, - generaldelta: bool, - entries: Vec, -} - -impl Revlog { - fn new(mut f: &mut File) -> Result { - let mut revlog_header = [0u8; 4]; - f.read_exact(&mut revlog_header)?; - let inline; - let generaldelta; - match revlog_header { - [0, 3, 0, 1] => { - inline = true; - generaldelta = true; - } - [0, 2, 0, 1] => { - inline = false; - generaldelta = true; - } - [0, 1, 0, 1] => { - inline = true; - generaldelta = false; - } - [0, 0, 0, 1] => { - inline = false; - generaldelta = false; - } - _ => panic!(), - } - if !inline { - // haven't implemented this yet - panic!() - } - let mut header_buffer = [0u8; 64]; - (*f).seek(SeekFrom::Start(0))?; - f.read_exact(&mut header_buffer)?; - // zero out header in first offset entry - for i in 0..4 { - header_buffer[i] = 0; - } - let mut entries: Vec = Vec::new(); - loop { - entries.push(RevlogEntry::new(&mut f, &header_buffer)?); - header_buffer = [0u8; 64]; - match f.read_exact(&mut header_buffer) { - Ok(_) => {} - Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => break, - Err(e) => return Err(e), - }; - } - Ok(Revlog { - inline: inline, - generaldelta: generaldelta, - entries: entries, - }) - } -} - -impl fmt::Display for Revlog { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "inline:\n{}\ngeneraldelta: {}\n", - self.inline, self.generaldelta - )?; - for entry in &self.entries { - write!(f, "{}\n\n", entry)?; - } - write!(f, "") - } -} +mod revlogs; fn main() -> std::io::Result<()> { let args: Vec = env::args().collect(); @@ -210,7 +10,7 @@ // open file let mut f = File::open(fname)?; - let revlog = Revlog::new(&mut f)?; + let revlog = revlogs::Revlog::new(&mut f)?; println!("{}", revlog); diff --git a/src/revlogs/mod.rs b/src/revlogs/mod.rs new file mode 100644 --- /dev/null +++ b/src/revlogs/mod.rs @@ -0,0 +1,203 @@ +use byteorder::{BigEndian, ReadBytesExt}; +use flate2::read::ZlibDecoder; +use std::fmt; +use std::fs::File; +use std::io; +use std::io::prelude::*; +use std::io::Cursor; +use std::io::SeekFrom; + +#[derive(Debug)] +struct RevlogHeader { + offset: u64, // really 6 bytes but easier to represent as a u64 + bitflags: [u8; 2], + compressedlength: u32, + uncompressedlength: u32, + baserev: i32, + linkrev: i32, + p1rev: i32, + p2rev: i32, + hash: [u8; 32], +} + +impl RevlogHeader { + fn new(buffer: &[u8; 64]) -> Result { + let mut cursor = Cursor::new(buffer as &[u8]); + Ok(RevlogHeader { + offset: cursor.read_u48::()? as u64, + bitflags: cursor.read_u16::()?.to_be_bytes(), + compressedlength: cursor.read_u32::()?, + uncompressedlength: cursor.read_u32::()?, + baserev: cursor.read_i32::()?, + linkrev: cursor.read_i32::()?, + p1rev: cursor.read_i32::()?, + p2rev: cursor.read_i32::()?, + hash: { + let mut res = [0u8; 32]; + cursor.read_exact(&mut res)?; + res + }, + }) + } +} + +#[derive(Debug)] +struct RevlogEntry { + header: RevlogHeader, + content: RevlogContent, +} + +#[derive(Debug)] +enum RevlogContent { + Generic(Vec), +} + +impl RevlogEntry { + fn new(f: &mut File, header_buffer: &[u8; 64]) -> Result { + let header = RevlogHeader::new(&header_buffer)?; + let mut content_buffer = vec![0u8; header.compressedlength as usize]; + f.read_exact(&mut content_buffer)?; + let decompressed_data = match content_buffer[0] { + 0 => vec![0u8; 0], + 0x75 => content_buffer[1..].to_vec(), + 0x78 => { + let mut gz = ZlibDecoder::new(&content_buffer[..]); + let mut decompressed_data = String::new(); + gz.read_to_string(&mut decompressed_data)?; + decompressed_data.into_bytes() + } + _ => panic!(), + }; + Ok(RevlogEntry { + header: header, + content: RevlogContent::Generic(decompressed_data), + }) + } +} + +impl fmt::Display for RevlogHeader { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let hash: Vec = self.hash.iter().map(|b| format!("{:02x}", b)).collect(); + write!( + f, + "offset: {}\n\ + bitflags: {:?}\n\ + compressed length: {}\n\ + uncompressed length: {}\n\ + base revision: {}\n\ + linked revision: {}\n\ + p1: {}\n\ + p2: {}\n\ + hash: {}\n", + self.offset, + self.bitflags, + self.compressedlength, + self.uncompressedlength, + self.baserev, + self.linkrev, + self.p1rev, + self.p2rev, + hash.join("") + ) + } +} + +impl fmt::Display for RevlogEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "header:\n{}\ncontent: {}\n", self.header, self.content) + } +} + +impl fmt::Display for RevlogContent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let printable = match self { + RevlogContent::Generic(s) => match std::str::from_utf8(s) { + Ok(parsed) => parsed.to_string(), + Err(_) => s + .iter() + .map(|c| { + std::ascii::escape_default(*c) + .map(|b| b as char) + .collect::() + }) + .collect::(), + }, + }; + write!(f, "{}", printable) + } +} + +#[derive(Debug)] +pub struct Revlog { + inline: bool, + generaldelta: bool, + entries: Vec, +} + +impl Revlog { + pub fn new(mut f: &mut File) -> Result { + let mut revlog_header = [0u8; 4]; + f.read_exact(&mut revlog_header)?; + let inline; + let generaldelta; + match revlog_header { + [0, 3, 0, 1] => { + inline = true; + generaldelta = true; + } + [0, 2, 0, 1] => { + inline = false; + generaldelta = true; + } + [0, 1, 0, 1] => { + inline = true; + generaldelta = false; + } + [0, 0, 0, 1] => { + inline = false; + generaldelta = false; + } + _ => panic!(), + } + if !inline { + // haven't implemented this yet + panic!() + } + let mut header_buffer = [0u8; 64]; + (*f).seek(SeekFrom::Start(0))?; + f.read_exact(&mut header_buffer)?; + // zero out header in first offset entry + for i in 0..4 { + header_buffer[i] = 0; + } + let mut entries: Vec = Vec::new(); + loop { + entries.push(RevlogEntry::new(&mut f, &header_buffer)?); + header_buffer = [0u8; 64]; + match f.read_exact(&mut header_buffer) { + Ok(_) => {} + Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => break, + Err(e) => return Err(e), + }; + } + Ok(Revlog { + inline: inline, + generaldelta: generaldelta, + entries: entries, + }) + } +} + +impl fmt::Display for Revlog { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "inline:\n{}\ngeneraldelta: {}\n", + self.inline, self.generaldelta + )?; + for entry in &self.entries { + write!(f, "{}\n\n", entry)?; + } + write!(f, "") + } +}