git_commitgraph/file/
access.rs

1use std::{
2    convert::TryInto,
3    fmt::{Debug, Formatter},
4    path::Path,
5};
6
7use crate::file::{self, commit::Commit, File, COMMIT_DATA_ENTRY_SIZE_SANS_HASH};
8
9/// Access
10impl File {
11    /// The number of base graphs that this file depends on.
12    pub fn base_graph_count(&self) -> u8 {
13        self.base_graph_count
14    }
15
16    /// Returns the commit data for the commit located at the given lexigraphical position.
17    ///
18    /// `pos` must range from 0 to self.num_commits().
19    ///
20    /// # Panics
21    ///
22    /// Panics if `pos` is out of bounds.
23    pub fn commit_at(&self, pos: file::Position) -> Commit<'_> {
24        Commit::new(self, pos)
25    }
26
27    /// The kind of hash used in this File.
28    ///
29    /// Note that it is always conforming to the hash used in the owning repository.
30    pub fn object_hash(&self) -> git_hash::Kind {
31        self.object_hash
32    }
33
34    /// Returns an object id at the given index in our list of (sorted) hashes.
35    /// The position ranges from 0 to self.num_commits()
36    // copied from git-odb/src/pack/index/ext
37    pub fn id_at(&self, pos: file::Position) -> &git_hash::oid {
38        assert!(
39            pos.0 < self.num_commits(),
40            "expected lexigraphical position less than {}, got {}",
41            self.num_commits(),
42            pos.0
43        );
44        let pos: usize = pos
45            .0
46            .try_into()
47            .expect("an architecture able to hold 32 bits of integer");
48        let start = self.oid_lookup_offset + (pos * self.hash_len);
49        git_hash::oid::from_bytes_unchecked(&self.data[start..][..self.hash_len])
50    }
51
52    /// Return an iterator over all object hashes stored in the base graph.
53    pub fn iter_base_graph_ids(&self) -> impl Iterator<Item = &git_hash::oid> {
54        let start = self.base_graphs_list_offset.unwrap_or(0);
55        let base_graphs_list = &self.data[start..][..self.hash_len * usize::from(self.base_graph_count)];
56        base_graphs_list
57            .chunks(self.hash_len)
58            .map(git_hash::oid::from_bytes_unchecked)
59    }
60
61    /// return an iterator over all commits in this file.
62    pub fn iter_commits(&self) -> impl Iterator<Item = Commit<'_>> {
63        (0..self.num_commits()).map(move |i| self.commit_at(file::Position(i)))
64    }
65
66    /// Return an iterator over all object hashes stored in this file.
67    pub fn iter_ids(&self) -> impl Iterator<Item = &git_hash::oid> {
68        (0..self.num_commits()).map(move |i| self.id_at(file::Position(i)))
69    }
70
71    /// Translate the given object hash to its position within this file, if present.
72    // copied from git-odb/src/pack/index/ext
73    pub fn lookup(&self, id: impl AsRef<git_hash::oid>) -> Option<file::Position> {
74        let id = id.as_ref();
75        let first_byte = usize::from(id.first_byte());
76        let mut upper_bound = self.fan[first_byte];
77        let mut lower_bound = if first_byte != 0 { self.fan[first_byte - 1] } else { 0 };
78
79        while lower_bound < upper_bound {
80            let mid = (lower_bound + upper_bound) / 2;
81            let mid_sha = self.id_at(file::Position(mid));
82
83            use std::cmp::Ordering::*;
84            match id.cmp(mid_sha) {
85                Less => upper_bound = mid,
86                Equal => return Some(file::Position(mid)),
87                Greater => lower_bound = mid + 1,
88            }
89        }
90        None
91    }
92
93    /// Returns the number of commits in this graph file.
94    ///
95    /// The maximum valid `file::Position` that can be used with this file is one less than
96    /// `num_commits()`.
97    pub fn num_commits(&self) -> u32 {
98        self.fan[255]
99    }
100
101    /// Returns the path to this file.
102    pub fn path(&self) -> &Path {
103        &self.path
104    }
105}
106
107impl File {
108    /// Returns the byte slice for the given commit in this file's Commit Data (CDAT) chunk.
109    pub(crate) fn commit_data_bytes(&self, pos: file::Position) -> &[u8] {
110        assert!(
111            pos.0 < self.num_commits(),
112            "expected lexigraphical position less than {}, got {}",
113            self.num_commits(),
114            pos.0
115        );
116        let pos: usize = pos
117            .0
118            .try_into()
119            .expect("an architecture able to hold 32 bits of integer");
120        let entry_size = self.hash_len + COMMIT_DATA_ENTRY_SIZE_SANS_HASH;
121        let start = self.commit_data_offset + (pos * entry_size);
122        &self.data[start..][..entry_size]
123    }
124
125    /// Returns the byte slice for this file's entire Extra Edge List (EDGE) chunk.
126    pub(crate) fn extra_edges_data(&self) -> Option<&[u8]> {
127        Some(&self.data[self.extra_edges_list_range.clone()?])
128    }
129}
130
131impl Debug for File {
132    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
133        write!(f, r#"File("{:?}")"#, self.path.display())
134    }
135}