Skip to content

Commit 99a3b25

Browse files
committed
fix: assure all kinds of trees can be parsed. (#1096)
Fix the slow and the fast-path tree-parsers to be able to cope with a greater variety of trees.
1 parent 8dfbb4b commit 99a3b25

File tree

7 files changed

+40
-49
lines changed

7 files changed

+40
-49
lines changed

gix-object/benches/decode_objects.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ fn parse_tag(c: &mut Criterion) {
1919
}
2020

2121
fn parse_tree(c: &mut Criterion) {
22-
c.bench_function("TreeRef(sig)", |b| {
22+
c.bench_function("TreeRef()", |b| {
2323
b.iter(|| black_box(gix_object::TreeRef::from_bytes(TREE)).unwrap())
2424
});
25-
c.bench_function("TreeRefIter(sig)", |b| {
25+
c.bench_function("TreeRefIter()", |b| {
2626
b.iter(|| black_box(gix_object::TreeRefIter::from_bytes(TREE).count()))
2727
});
2828
}

gix-object/src/tree/ref_iter.rs

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -116,17 +116,9 @@ mod decode {
116116
use std::convert::TryFrom;
117117

118118
use bstr::ByteSlice;
119-
use winnow::{
120-
combinator::{eof, repeat, terminated},
121-
error::ParserError,
122-
prelude::*,
123-
stream::AsChar,
124-
token::{take, take_while},
125-
};
119+
use winnow::{error::ParserError, prelude::*};
126120

127-
use crate::{parse::SPACE, tree, tree::EntryRef, TreeRef};
128-
129-
const NULL: &[u8] = b"\0";
121+
use crate::{tree, tree::EntryRef, TreeRef};
130122

131123
pub fn fast_entry(i: &[u8]) -> Option<(&[u8], EntryRef<'_>)> {
132124
let mut mode = 0u32;
@@ -157,24 +149,20 @@ mod decode {
157149
))
158150
}
159151

160-
pub fn entry<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<EntryRef<'a>, E> {
161-
(
162-
terminated(take_while(5..=6, AsChar::is_dec_digit), SPACE)
163-
.verify_map(|mode| tree::EntryMode::try_from(mode).ok()),
164-
terminated(take_while(1.., |b| b != NULL[0]), NULL),
165-
take(20u8), // TODO(SHA256): make this compatible with other hash lengths
166-
)
167-
.map(|(mode, filename, oid): (_, &[u8], _)| EntryRef {
168-
mode,
169-
filename: filename.as_bstr(),
170-
oid: gix_hash::oid::try_from_bytes(oid).expect("we counted exactly 20 bytes"),
171-
})
172-
.parse_next(i)
173-
}
174-
175152
pub fn tree<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<TreeRef<'a>, E> {
176-
terminated(repeat(0.., entry), eof)
177-
.map(|entries| TreeRef { entries })
178-
.parse_next(i)
153+
let mut out = Vec::new();
154+
let mut i = &**i;
155+
while !i.is_empty() {
156+
let Some((rest, entry)) = fast_entry(i) else {
157+
#[allow(clippy::unit_arg)]
158+
return Err(winnow::error::ErrMode::from_error_kind(
159+
&i,
160+
winnow::error::ErrorKind::Verify,
161+
));
162+
};
163+
i = rest;
164+
out.push(entry);
165+
}
166+
Ok(TreeRef { entries: out })
179167
}
180168
}
172 Bytes
Binary file not shown.
659 Bytes
Binary file not shown.
172 Bytes
Binary file not shown.
659 Bytes
Binary file not shown.

gix-object/tests/tree/mod.rs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ mod iter {
5656
}
5757

5858
mod from_bytes {
59-
use gix_object::{bstr::ByteSlice, tree, tree::EntryRef, TreeRef};
59+
use gix_object::{bstr::ByteSlice, tree, tree::EntryRef, TreeRef, TreeRefIter};
6060

6161
use crate::{fixture_name, hex_to_id};
6262

@@ -108,24 +108,27 @@ mod from_bytes {
108108
}
109109

110110
#[test]
111-
fn maybe_special() -> crate::Result {
112-
assert_eq!(
113-
TreeRef::from_bytes(&fixture_name("tree", "maybe-special.tree"))?
114-
.entries
115-
.len(),
116-
160
117-
);
118-
Ok(())
119-
}
120-
121-
#[test]
122-
fn definitely_special() -> crate::Result {
123-
assert_eq!(
124-
TreeRef::from_bytes(&fixture_name("tree", "definitely-special.tree"))?
125-
.entries
126-
.len(),
127-
19
128-
);
111+
fn special_trees() -> crate::Result {
112+
for (name, expected_entry_count) in [
113+
("maybe-special", 160),
114+
("definitely-special", 19),
115+
("special-1", 5),
116+
("special-2", 18),
117+
("special-3", 5),
118+
("special-4", 18),
119+
] {
120+
let fixture = fixture_name("tree", &format!("{}.tree", name));
121+
assert_eq!(
122+
TreeRef::from_bytes(&fixture)?.entries.len(),
123+
expected_entry_count,
124+
"{name}"
125+
);
126+
assert_eq!(
127+
TreeRefIter::from_bytes(&fixture).map(Result::unwrap).count(),
128+
expected_entry_count,
129+
"{name}"
130+
);
131+
}
129132
Ok(())
130133
}
131134
}

0 commit comments

Comments
 (0)