tree: rewrite recursive entries iterator to not use machine stack

In the same vein as c02c4168fe2f.
This commit is contained in:
Martin von Zweigbergk 2023-05-27 06:00:24 -07:00 committed by Martin von Zweigbergk
parent a897b27770
commit dbcecf7244

View File

@ -231,24 +231,34 @@ impl Tree {
} }
pub struct TreeEntriesIterator<'matcher> { pub struct TreeEntriesIterator<'matcher> {
entry_iterator: TreeEntriesNonRecursiveIterator<'static>, stack: Vec<TreeEntriesDirItem>,
// On drop, tree must outlive entry_iterator
tree: Pin<Box<Tree>>,
subdir_iterator: Option<Box<TreeEntriesIterator<'matcher>>>,
matcher: &'matcher dyn Matcher, matcher: &'matcher dyn Matcher,
} }
impl<'matcher> TreeEntriesIterator<'matcher> { struct TreeEntriesDirItem {
fn new(tree: Tree, matcher: &'matcher dyn Matcher) -> Self { entry_iterator: TreeEntriesNonRecursiveIterator<'static>,
// On drop, tree must outlive entry_iterator
tree: Pin<Box<Tree>>,
}
impl TreeEntriesDirItem {
fn new(tree: Tree) -> Self {
let tree = Box::pin(tree); let tree = Box::pin(tree);
// TODO: Restrict walk according to Matcher::visit()
let entry_iterator = tree.entries_non_recursive(); let entry_iterator = tree.entries_non_recursive();
let entry_iterator: TreeEntriesNonRecursiveIterator<'static> = let entry_iterator: TreeEntriesNonRecursiveIterator<'static> =
unsafe { std::mem::transmute(entry_iterator) }; unsafe { std::mem::transmute(entry_iterator) };
Self { Self {
entry_iterator, entry_iterator,
tree, tree,
subdir_iterator: None, }
}
}
impl<'matcher> TreeEntriesIterator<'matcher> {
fn new(tree: Tree, matcher: &'matcher dyn Matcher) -> Self {
// TODO: Restrict walk according to Matcher::visit()
Self {
stack: vec![TreeEntriesDirItem::new(tree)],
matcher, matcher,
} }
} }
@ -258,31 +268,26 @@ impl Iterator for TreeEntriesIterator<'_> {
type Item = (RepoPath, TreeValue); type Item = (RepoPath, TreeValue);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
loop { while let Some(top) = self.stack.last_mut() {
// First return results from any subdirectory we're currently visiting. if let Some(entry) = top.entry_iterator.next() {
if let Some(subdir_iter) = &mut self.subdir_iterator { let path = top.tree.dir().join(entry.name());
if let Some(item) = subdir_iter.next() {
return Some(item);
}
self.subdir_iterator = None;
}
let entry = self.entry_iterator.next()?;
match entry.value() { match entry.value() {
TreeValue::Tree(id) => { TreeValue::Tree(id) => {
let subtree = self.tree.known_sub_tree(entry.name(), id); let subtree = top.tree.known_sub_tree(entry.name(), id);
self.subdir_iterator = self.stack.push(TreeEntriesDirItem::new(subtree));
Some(Box::new(TreeEntriesIterator::new(subtree, self.matcher)));
} }
other => { value => {
let path = self.tree.dir().join(entry.name()); if self.matcher.matches(&path) {
if !self.matcher.matches(&path) { return Some((path, value.clone()));
continue;
} }
return Some((path, other.clone()));
} }
}; };
} else {
self.stack.pop();
} }
} }
None
}
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]