M src/main.rs +2 -202
@@ 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<RevlogHeader, io::Error> {
- let mut cursor = Cursor::new(buffer as &[u8]);
- Ok(RevlogHeader {
- offset: cursor.read_u48::<BigEndian>()? as u64,
- bitflags: cursor.read_u16::<BigEndian>()?.to_be_bytes(),
- compressedlength: cursor.read_u32::<BigEndian>()?,
- uncompressedlength: cursor.read_u32::<BigEndian>()?,
- baserev: cursor.read_i32::<BigEndian>()?,
- linkrev: cursor.read_i32::<BigEndian>()?,
- p1rev: cursor.read_i32::<BigEndian>()?,
- p2rev: cursor.read_i32::<BigEndian>()?,
- 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<u8>),
-}
-
-impl RevlogEntry {
- fn new(f: &mut File, header_buffer: &[u8; 64]) -> Result<RevlogEntry, io::Error> {
- 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<String> = 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::<String>()
- })
- .collect::<String>(),
- },
- };
- write!(f, "{}", printable)
- }
-}
-
-#[derive(Debug)]
-struct Revlog {
- inline: bool,
- generaldelta: bool,
- entries: Vec<RevlogEntry>,
-}
-
-impl Revlog {
- fn new(mut f: &mut File) -> Result<Revlog, io::Error> {
- 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<RevlogEntry> = 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<String> = env::args().collect();
@@ 210,7 10,7 @@ fn main() -> std::io::Result<()> {
// open file
let mut f = File::open(fname)?;
- let revlog = Revlog::new(&mut f)?;
+ let revlog = revlogs::Revlog::new(&mut f)?;
println!("{}", revlog);
A => src/revlogs/mod.rs +203 -0
@@ 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<RevlogHeader, io::Error> {
+ let mut cursor = Cursor::new(buffer as &[u8]);
+ Ok(RevlogHeader {
+ offset: cursor.read_u48::<BigEndian>()? as u64,
+ bitflags: cursor.read_u16::<BigEndian>()?.to_be_bytes(),
+ compressedlength: cursor.read_u32::<BigEndian>()?,
+ uncompressedlength: cursor.read_u32::<BigEndian>()?,
+ baserev: cursor.read_i32::<BigEndian>()?,
+ linkrev: cursor.read_i32::<BigEndian>()?,
+ p1rev: cursor.read_i32::<BigEndian>()?,
+ p2rev: cursor.read_i32::<BigEndian>()?,
+ 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<u8>),
+}
+
+impl RevlogEntry {
+ fn new(f: &mut File, header_buffer: &[u8; 64]) -> Result<RevlogEntry, io::Error> {
+ 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<String> = 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::<String>()
+ })
+ .collect::<String>(),
+ },
+ };
+ write!(f, "{}", printable)
+ }
+}
+
+#[derive(Debug)]
+pub struct Revlog {
+ inline: bool,
+ generaldelta: bool,
+ entries: Vec<RevlogEntry>,
+}
+
+impl Revlog {
+ pub fn new(mut f: &mut File) -> Result<Revlog, io::Error> {
+ 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<RevlogEntry> = 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, "")
+ }
+}