gix_commitgraph/file/
access.rs

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