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
use std::{
convert::TryInto,
fmt::{Debug, Formatter},
path::Path,
};
use crate::file::{self, commit::Commit, File, COMMIT_DATA_ENTRY_SIZE_SANS_HASH};
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 object_hash(&self) -> git_hash::Kind {
self.object_hash
}
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 * self.hash_len);
git_hash::oid::from_bytes_unchecked(&self.data[start..][..self.hash_len])
}
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..][..self.hash_len * usize::from(self.base_graph_count)];
base_graphs_list
.chunks(self.hash_len)
.map(git_hash::oid::from_bytes_unchecked)
}
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 entry_size = self.hash_len + COMMIT_DATA_ENTRY_SIZE_SANS_HASH;
let start = self.commit_data_offset + (pos * entry_size);
&self.data[start..][..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())
}
}