Skip to content

Commit 0cf6d3a

Browse files
cruesslerByron
authored andcommitted
Sketch recursive lookup
This does not compile as there is only one buffer for potentially many times `lookup_entry` is called. This is intended to check whether the general approach is correct.
1 parent d121fe7 commit 0cf6d3a

File tree

2 files changed

+83
-14
lines changed

2 files changed

+83
-14
lines changed

gix-object/src/tree/ref_iter.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ use winnow::{error::ParserError, prelude::*};
33

44
use crate::{tree, tree::EntryRef, TreeRef, TreeRefIter};
55

6+
/// The error type returned by the [`Tree`](crate::Tree) trait.
7+
pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
8+
69
impl<'a> TreeRefIter<'a> {
710
/// Instantiate an iterator from the given tree data.
811
pub fn from_bytes(data: &'a [u8]) -> TreeRefIter<'a> {
@@ -47,11 +50,16 @@ impl<'a> TreeRef<'a> {
4750
///
4851
/// # Performance Notes
4952
///
50-
/// Searching tree entries is currently done in sequence, which allows to the search to be allocation free. It would be possible
53+
/// Searching tree entries is currently done in sequence, which allows the search to be allocation free. It would be possible
5154
/// to reuse a vector and use a binary search instead, which might be able to improve performance over all.
5255
/// However, a benchmark should be created first to have some data and see which trade-off to choose here.
5356
///
54-
pub fn lookup_entry<I, P>(&self, path: I) -> Option<EntryRef<'a>>
57+
pub fn lookup_entry<I, P>(
58+
&self,
59+
odb: impl crate::Find + crate::FindExt,
60+
buffer: &'a mut Vec<u8>,
61+
path: I,
62+
) -> Result<Option<EntryRef<'a>>, Error>
5563
where
5664
I: IntoIterator<Item = P>,
5765
P: PartialEq<BStr>,
@@ -62,15 +70,18 @@ impl<'a> TreeRef<'a> {
6270
match self.entries.iter().find(|entry| component.eq(entry.filename)) {
6371
Some(entry) => {
6472
if path.peek().is_none() {
65-
return Some(*entry);
66-
} else if !entry.mode.is_tree() {
67-
return None;
73+
return Ok(Some(*entry));
74+
} else {
75+
let next_id = entry.oid.to_owned();
76+
let obj = odb.find_tree(&next_id, buffer)?;
77+
78+
return obj.lookup_entry(odb, buffer, path);
6879
}
6980
}
70-
None => return None,
81+
None => return Ok(None),
7182
}
7283
}
73-
None
84+
Ok(None)
7485
}
7586

7687
/// Like [`Self::lookup_entry()`], but takes a `Path` directly via `relative_path`, a path relative to this tree.
@@ -79,13 +90,22 @@ impl<'a> TreeRef<'a> {
7990
///
8091
/// If any path component contains illformed UTF-8 and thus can't be converted to bytes on platforms which can't do so natively,
8192
/// the returned component will be empty which makes the lookup fail.
82-
pub fn lookup_entry_by_path(&self, relative_path: impl AsRef<std::path::Path>) -> Option<EntryRef<'a>> {
93+
pub fn lookup_entry_by_path(
94+
&self,
95+
odb: impl crate::Find,
96+
buffer: &'a mut Vec<u8>,
97+
relative_path: impl AsRef<std::path::Path>,
98+
) -> Result<Option<EntryRef<'a>>, Error> {
8399
use crate::bstr::ByteSlice;
84-
self.lookup_entry(relative_path.as_ref().components().map(|c: std::path::Component<'_>| {
85-
gix_path::os_str_into_bstr(c.as_os_str())
86-
.unwrap_or_else(|_| "".into())
87-
.as_bytes()
88-
}))
100+
self.lookup_entry(
101+
odb,
102+
buffer,
103+
relative_path.as_ref().components().map(|c: std::path::Component<'_>| {
104+
gix_path::os_str_into_bstr(c.as_os_str())
105+
.unwrap_or_else(|_| "".into())
106+
.as_bytes()
107+
}),
108+
)
89109
}
90110

91111
/// Create an instance of the empty tree.

gix-object/tests/object/tree/iter.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
use gix_object::{bstr::ByteSlice, tree, tree::EntryRef, TreeRefIter};
1+
use gix_object::{
2+
bstr::ByteSlice,
3+
tree::{self, EntryRef},
4+
FindExt, TreeRefIter,
5+
};
6+
use pretty_assertions::assert_eq;
27

38
use crate::{fixture_name, hex_to_id};
49

@@ -52,3 +57,47 @@ fn everything() -> crate::Result {
5257
);
5358
Ok(())
5459
}
60+
61+
#[test]
62+
fn lookup_entry_toplevel() -> crate::Result {
63+
let odb = utils::tree_odb()?;
64+
let root_tree_id = hex_to_id("ff7e7d2aecae1c3fb15054b289a4c58aa65b8646");
65+
66+
let mut buf = Vec::new();
67+
let root_tree = odb.find_tree(&root_tree_id, &mut buf)?;
68+
69+
let mut buf = Vec::new();
70+
let entry = root_tree.lookup_entry_by_path(&odb, &mut buf, "bin").unwrap().unwrap();
71+
72+
assert!(matches!(entry, EntryRef { .. }));
73+
assert_eq!(entry.filename, "bin");
74+
75+
Ok(())
76+
}
77+
78+
#[test]
79+
fn lookup_entry_nested_path() -> crate::Result {
80+
let odb = utils::tree_odb()?;
81+
let root_tree_id = hex_to_id("ff7e7d2aecae1c3fb15054b289a4c58aa65b8646");
82+
83+
let mut buf = Vec::new();
84+
let root_tree = odb.find_tree(&root_tree_id, &mut buf)?;
85+
86+
let mut buf = Vec::new();
87+
let entry = root_tree
88+
.lookup_entry_by_path(&odb, &mut buf, "file/a")
89+
.unwrap()
90+
.unwrap();
91+
92+
assert!(matches!(entry, EntryRef { .. }));
93+
assert_eq!(entry.filename, "a");
94+
95+
Ok(())
96+
}
97+
98+
mod utils {
99+
pub(super) fn tree_odb() -> gix_testtools::Result<gix_odb::Handle> {
100+
let root = gix_testtools::scripted_fixture_read_only("make_trees.sh")?;
101+
Ok(gix_odb::at(root.join(".git/objects"))?)
102+
}
103+
}

0 commit comments

Comments
 (0)