1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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,
};
impl File {
pub fn base_graph_count(&self) -> u8 {
self.base_graph_count
}
pub fn commit_at(&self, pos: file::Position) -> Commit<'_> {
Commit::new(self, pos)
}
pub fn hash_kind(&self) -> git_hash::Kind {
git_hash::Kind::Sha1
}
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")
}
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"))
}
pub fn iter_commits(&self) -> impl Iterator<Item = Commit<'_>> {
(0..self.num_commits()).map(move |i| self.commit_at(file::Position(i)))
}
pub fn iter_ids(&self) -> impl Iterator<Item = &git_hash::oid> {
(0..self.num_commits()).map(move |i| self.id_at(file::Position(i)))
}
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 };
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
}
pub fn num_commits(&self) -> u32 {
self.fan[255]
}
pub fn path(&self) -> &Path {
&self.path
}
}
impl File {
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]
}
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())
}
}