1use crate::{Error, ObjectFormat, ObjectId, ObjectKind, Result, names::cmp_tree_names};
3
4#[derive(Clone, Copy, Debug)]
7pub struct Commit<'a> {
8 tree: ObjectId,
9 message: &'a [u8],
10 message_offset: usize,
11}
12
13impl<'a> Commit<'a> {
14 pub fn parse(data: &'a [u8]) -> Result<Self> {
15 Self::parse_with_format(data, ObjectFormat::Sha1)
16 }
17 pub fn parse_with_format(data: &'a [u8], format: ObjectFormat) -> Result<Self> {
18 let tree = Self::root_tree_with_format(data, format)?;
19 let separator = data
20 .windows(2)
21 .position(|bytes| bytes == b"\n\n")
22 .ok_or(Error::InvalidObject)?;
23 Ok(Self {
24 tree,
25 message: &data[separator + 2..],
26 message_offset: separator + 2,
27 })
28 }
29
30 pub fn root_tree(data: &[u8]) -> Result<ObjectId> {
32 Self::root_tree_with_format(data, ObjectFormat::Sha1)
33 }
34 pub fn root_tree_with_format(data: &[u8], format: ObjectFormat) -> Result<ObjectId> {
35 let end = 5 + format.hex_length();
36 let line = data.get(..end + 1).ok_or(Error::InvalidObject)?;
37 if !line.starts_with(b"tree ") || line[end] != b'\n' {
38 return Err(Error::InvalidObject);
39 }
40 ObjectId::from_hex(&line[5..end])
41 }
42
43 pub fn tree(&self) -> ObjectId {
44 self.tree
45 }
46 pub fn message(&self) -> &'a [u8] {
47 self.message
48 }
49 pub fn message_offset(&self) -> usize {
50 self.message_offset
51 }
52}
53
54#[derive(Clone, Copy, Debug, Eq, PartialEq)]
57pub struct TreeEntry<'a> {
58 pub name: &'a [u8],
59 pub mode: u32,
60 pub id: ObjectId,
61}
62
63impl<'a> TreeEntry<'a> {
64 pub fn read(data: &'a [u8], offset: &mut usize) -> Result<Option<Self>> {
67 Self::read_with_format(data, offset, ObjectFormat::Sha1)
68 }
69 pub fn read_with_format(
70 data: &'a [u8],
71 offset: &mut usize,
72 format: ObjectFormat,
73 ) -> Result<Option<Self>> {
74 let tail = data.get(*offset..).ok_or(Error::InvalidObject)?;
75 if tail.is_empty() {
76 return Ok(None);
77 }
78 let space = tail
79 .iter()
80 .position(|&b| b == b' ')
81 .ok_or(Error::InvalidObject)?;
82 let mode = match &tail[..space] {
83 b"40000" => 0o040000,
84 b"100644" => 0o100644,
85 b"100755" => 0o100755,
86 b"120000" => 0o120000,
87 b"160000" => 0o160000,
88 _ => return Err(Error::InvalidObject),
89 };
90 let rest = &tail[space + 1..];
91 let nul = rest
92 .iter()
93 .position(|&b| b == 0)
94 .ok_or(Error::InvalidObject)?;
95 let name = &rest[..nul];
96 if name.is_empty() || name == b"." || name == b".." || name.contains(&b'/') {
97 return Err(Error::InvalidFileName);
98 }
99 let id_length = format.id_length();
100 let id = ObjectId::from_bytes(
101 format,
102 rest.get(nul + 1..nul + 1 + id_length)
103 .ok_or(Error::InvalidObject)?,
104 );
105 let id = id?;
106 *offset += space + 1 + nul + 1 + id_length;
107 Ok(Some(Self { name, mode, id }))
108 }
109}
110
111#[derive(Clone, Copy, Debug)]
116pub struct Tree<'a> {
117 data: &'a [u8],
118 format: ObjectFormat,
119}
120
121impl<'a> Tree<'a> {
122 pub fn parse(data: &'a [u8]) -> Result<Self> {
125 Self::parse_with_format(data, ObjectFormat::Sha1)
126 }
127 pub fn parse_with_format(data: &'a [u8], format: ObjectFormat) -> Result<Self> {
128 Self::parse_with_budget_and_format(data, &mut (64 * 1024 * 1024), format)
129 }
130
131 pub fn parse_with_budget(data: &'a [u8], budget: &mut u64) -> Result<Self> {
135 Self::parse_with_budget_and_format(data, budget, ObjectFormat::Sha1)
136 }
137 pub fn parse_with_budget_and_format(
138 data: &'a [u8],
139 budget: &mut u64,
140 format: ObjectFormat,
141 ) -> Result<Self> {
142 let mut offset = 0;
143 let mut previous: Option<TreeEntry<'_>> = None;
144 let mut last_file_end = 0;
145 while let Some(entry) = read_budgeted(data, &mut offset, budget, format)? {
146 if let Some(previous) = previous {
147 if cmp_tree_names(
148 previous.name,
149 previous.mode == 0o040000,
150 entry.name,
151 entry.mode == 0o040000,
152 ) != core::cmp::Ordering::Less
153 {
154 return Err(Error::UnsortedFiles);
155 }
156 }
157 if entry.mode == 0o040000 {
158 let mut earlier = 0;
159 while earlier < last_file_end {
162 if read_budgeted(data, &mut earlier, budget, format)?
163 .is_some_and(|other| other.name == entry.name)
164 {
165 return Err(Error::UnsortedFiles);
166 }
167 }
168 } else {
169 last_file_end = offset;
170 }
171 previous = Some(entry);
172 }
173 Ok(Self { data, format })
174 }
175
176 pub fn entries(&self) -> TreeEntries<'a> {
177 TreeEntries {
178 data: self.data,
179 offset: 0,
180 format: self.format,
181 }
182 }
183}
184
185fn read_budgeted<'a>(
186 data: &'a [u8],
187 offset: &mut usize,
188 budget: &mut u64,
189 format: ObjectFormat,
190) -> Result<Option<TreeEntry<'a>>> {
191 if *offset == data.len() {
192 return Ok(None);
193 }
194 if *budget == 0 {
195 return Err(Error::LimitExceeded);
196 }
197 let start = *offset;
198 let available = usize::try_from(*budget).unwrap_or(usize::MAX);
199 let end = start.saturating_add(available).min(data.len());
200 let result = TreeEntry::read_with_format(&data[..end], offset, format);
201 if result.is_err() && end < data.len() {
202 return Err(Error::LimitExceeded);
203 }
204 *budget -= (*offset - start) as u64;
205 result
206}
207
208pub struct TreeEntries<'a> {
209 data: &'a [u8],
210 offset: usize,
211 format: ObjectFormat,
212}
213impl<'a> Iterator for TreeEntries<'a> {
214 type Item = TreeEntry<'a>;
215 fn next(&mut self) -> Option<Self::Item> {
216 TreeEntry::read_with_format(self.data, &mut self.offset, self.format)
218 .expect("validated tree")
219 }
220}
221
222#[derive(Clone, Copy, Debug, Eq, PartialEq)]
225pub struct Tag {
226 pub target: ObjectId,
227 pub target_kind: ObjectKind,
228}
229
230impl Tag {
231 pub fn parse(data: &[u8]) -> Result<Self> {
232 let mut lines = data.split(|&byte| byte == b'\n');
233 let object = lines
234 .next()
235 .and_then(|line| line.strip_prefix(b"object "))
236 .ok_or(Error::InvalidObject)?;
237 let target = ObjectId::from_hex(object)?;
238 let target_kind = ObjectKind::from_tag_header(lines.next().ok_or(Error::InvalidObject)?)?;
239 let name = lines
240 .next()
241 .and_then(|line| line.strip_prefix(b"tag "))
242 .ok_or(Error::InvalidObject)?;
243 if name.is_empty() || !lines.any(|line| line.is_empty()) {
244 return Err(Error::InvalidObject);
245 }
246 Ok(Self {
247 target,
248 target_kind,
249 })
250 }
251}