Sebastian Thielmake fmt (#427)4b320e74 years agoHistory
162 lines · 4.9 KB · Rust
RawDownload
use std::{    collections::{hash_map, HashMap},    ops::Range,}; use bstr::ByteVec; use crate::{extension, State, Version}; impl State {    pub fn write_to(&self, options: Options) -> Vec<u8> {        let mut writer = Writer::new(self, options);        writer.generate();        writer.data    }} #[derive(Default)]pub struct Options {    hash_kind: git_hash::Kind,} struct Writer<'a> {    state: &'a State,    options: Options,    data: Vec<u8>,    index_table: HashMap<&'static str, Range<usize>>,} impl<'a> Writer<'a> {    pub fn new(state: &'a State, options: Options) -> Self {        Self {            state,            options,            data: Vec::default(),            index_table: Default::default(),        }    }     pub fn generate(&mut self) {        self.header();        self.entries();         // TODO: Tree extension is always included, I think        if let Some(t) = self.state.tree() {            self.tree(t)        }         self.end_of_index();    }     fn push(&mut self, data: &[u8], key: &'static str) {        let start = self.data.len();        let end = start + data.len();         match self.index_table.entry(key) {            hash_map::Entry::Occupied(mut e) => e.get_mut().end = end,            hash_map::Entry::Vacant(e) => {                e.insert(start..end);            }        };         self.data.push_str(data);    }     fn header(&mut self) {        let signature = b"DIRC";        let version = match self.state.version() {            Version::V2 => 2_u32.to_be_bytes(),            Version::V3 => 3_u32.to_be_bytes(),            Version::V4 => 4_u32.to_be_bytes(),        };        let num_entries = self.state.entries().len() as u32;         self.push(signature, "header");        self.push(&version, "header");        self.push(&(num_entries).to_be_bytes(), "header");    }     fn entries(&mut self) {        for e in self.state.entries() {            self.push(&e.stat.ctime.secs.to_be_bytes(), "entries");            self.push(&e.stat.ctime.nsecs.to_be_bytes(), "entries");            self.push(&e.stat.mtime.secs.to_be_bytes(), "entries");            self.push(&e.stat.mtime.nsecs.to_be_bytes(), "entries");            self.push(&e.stat.dev.to_be_bytes(), "entries");            self.push(&e.stat.ino.to_be_bytes(), "entries");            self.push(&e.mode.bits().to_be_bytes(), "entries");            self.push(&e.stat.uid.to_be_bytes(), "entries");            self.push(&e.stat.gid.to_be_bytes(), "entries");            self.push(&e.stat.size.to_be_bytes(), "entries");            self.push(e.id.as_bytes(), "entries");            //FIXME: correct flag values            // probably convert 'in-memory' Flags to at_rest::Flags            // self.push(&e.flags.bits().to_be_bytes(), "entries");            self.push(b"\x00\x01\x61\x00", "entries");             println!("{:?}", e.flags.bits());        }    }     fn tree(&mut self, tree: &extension::Tree) {        let signature = b"TREE";        let mut size: u32 = 0;         self.push(signature, "tree");        self.push(&size.to_be_bytes(), "tree");         self.tree_entry(tree);         if let Some(range) = self.index_table.get("tree") {            size = (range.end - (range.start + 8)) as u32;            self.data[range.start + 4..range.start + 8].copy_from_slice(&size.to_be_bytes());        }    }     fn tree_entry(&mut self, tree: &extension::Tree) {        let path = [tree.name.as_slice(), b"\0"].concat();         let num_entries_ascii = tree.num_entries.to_string();        let num_children_ascii = tree.children.len().to_string();         self.push(path.as_slice(), "tree");        self.push(num_entries_ascii.as_bytes(), "tree");        self.push(b" ", "tree");        self.push(num_children_ascii.as_bytes(), "tree");        self.push(b"\n", "tree");        self.push(tree.id.as_bytes(), "tree");         for child in &tree.children {            self.tree_entry(child);        }    }     fn end_of_index(&mut self) {        match self.index_table.get("entries") {            Some(range) => {                let signature = b"EOIE";                let extension_size = 4 + self.options.hash_kind.len_in_bytes() as u32;                let offset: u32 = range.end as u32;                 let mut hasher = git_features::hash::hasher(self.options.hash_kind);                 match self.index_table.get("tree") {                    Some(range) => {                        hasher.update(b"TREE");                        hasher.update(&self.data[range.start + 4..range.start + 8]);                    }                    None => {}                }                 let hash = hasher.digest();                 self.data.push_str(signature);                self.data.push_str(extension_size.to_be_bytes());                self.data.push_str(offset.to_be_bytes());                self.data.push_str(hash);            }            None => {}        }    }}