Sebastian Thielthanks clippye1964e45 years agoHistory
138 lines · 5.1 KB · Rust
RawDownload
use crate::file::{self, commit::Commit, File, COMMIT_DATA_ENTRY_SIZE};use git_hash::SIZE_OF_SHA1_DIGEST as SHA1_SIZE;use std::{    convert::TryInto,    fmt::{Debug, Formatter},    path::Path,}; /// Accessimpl File {    /// The number of base graphs that this file depends on.    pub fn base_graph_count(&self) -> u8 {        self.base_graph_count    }     /// Returns the commit data for the commit located at the given lexigraphical position.    ///    /// `pos` must range from 0 to self.num_commits().    ///    /// # Panics    ///    /// Panics if `pos` is out of bounds.    pub fn commit_at(&self, pos: file::Position) -> Commit<'_> {        Commit::new(self, pos)    }     /// The kind of hash used in this File.    ///    /// Note that it is always conforming to the hash used in the owning repository.    pub fn hash_kind(&self) -> git_hash::Kind {        git_hash::Kind::Sha1    }     /// Returns 20 bytes sha1 at the given index in our list of (sorted) sha1 hashes.    /// The position ranges from 0 to self.num_commits()    // copied from git-odb/src/pack/index/access.rs    pub fn id_at(&self, pos: file::Position) -> &git_hash::oid {        assert!(            pos.0 < self.num_commits(),            "expected lexigraphical position less than {}, got {}",            self.num_commits(),            pos.0        );        let pos: usize = pos            .0            .try_into()            .expect("an architecture able to hold 32 bits of integer");        let start = self.oid_lookup_offset + (pos * SHA1_SIZE);        git_hash::oid::try_from(&self.data[start..start + SHA1_SIZE]).expect("20 bytes SHA1 to be alright")    }     /// Return an iterator over all object hashes stored in the base graph.    pub fn iter_base_graph_ids(&self) -> impl Iterator<Item = &git_hash::oid> {        let start = self.base_graphs_list_offset.unwrap_or(0);        let base_graphs_list = &self.data[start..start + (SHA1_SIZE * usize::from(self.base_graph_count))];        base_graphs_list            .chunks(SHA1_SIZE)            .map(|bytes| git_hash::oid::try_from(bytes).expect("20 bytes SHA1 to be alright"))    }     /// return an iterator over all commits in this file.    pub fn iter_commits(&self) -> impl Iterator<Item = Commit<'_>> {        (0..self.num_commits()).map(move |i| self.commit_at(file::Position(i)))    }     /// Return an iterator over all object hashes stored in this file.    pub fn iter_ids(&self) -> impl Iterator<Item = &git_hash::oid> {        (0..self.num_commits()).map(move |i| self.id_at(file::Position(i)))    }     /// Translate the given object hash to its position within this file, if present.    // copied from git-odb/src/pack/index/access.rs    pub fn lookup(&self, id: impl AsRef<git_hash::oid>) -> Option<file::Position> {        let id = id.as_ref();        let first_byte = usize::from(id.first_byte());        let mut upper_bound = self.fan[first_byte];        let mut lower_bound = if first_byte != 0 { self.fan[first_byte - 1] } else { 0 };         // Bisect using indices        // TODO: Performance of V2 could possibly be better if we would be able to do a binary search        // on 20 byte chunks directly, but doing so requires transmuting and that is not safe, even though        // it should not be if the bytes match up and the type has no destructor.        while lower_bound < upper_bound {            let mid = (lower_bound + upper_bound) / 2;            let mid_sha = self.id_at(file::Position(mid));             use std::cmp::Ordering::*;            match id.cmp(mid_sha) {                Less => upper_bound = mid,                Equal => return Some(file::Position(mid)),                Greater => lower_bound = mid + 1,            }        }        None    }     /// Returns the number of commits in this graph file.    ///    /// The maximum valid `file::Position` that can be used with this file is one less than    /// `num_commits()`.    pub fn num_commits(&self) -> u32 {        self.fan[255]    }     /// Returns the path to this file.    pub fn path(&self) -> &Path {        &self.path    }} impl File {    /// Returns the byte slice for the given commit in this file's Commit Data (CDAT) chunk.    pub(crate) fn commit_data_bytes(&self, pos: file::Position) -> &[u8] {        assert!(            pos.0 < self.num_commits(),            "expected lexigraphical position less than {}, got {}",            self.num_commits(),            pos.0        );        let pos: usize = pos            .0            .try_into()            .expect("an architecture able to hold 32 bits of integer");        let start = self.commit_data_offset + (pos * COMMIT_DATA_ENTRY_SIZE);        &self.data[start..start + COMMIT_DATA_ENTRY_SIZE]    }     /// Returns the byte slice for this file's entire Extra Edge List (EDGE) chunk.    pub(crate) fn extra_edges_data(&self) -> Option<&[u8]> {        Some(&self.data[self.extra_edges_list_range.clone()?])    }} impl Debug for File {    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {        write!(f, r#"File("{:?}")"#, self.path.display())    }}