mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-06 03:22:59 +00:00
merged_tree: make TreeEntriesIterator
not self-referential
While importing the `ouroboros` crate and the `aliasable` crate it depends on, the "unsafe Rust reviewer" expressed some concern that they contain a lot of unsafe code that's hard to review. We can avoid the unsafe code altogether by making `TreeEntriesIterator` not self-refential. Instead, we can collect the matching entries in an individual tree up front. It does have some performance cost: ``` ❯ hyperfine --warmup 3 --runs 30 \ '/tmp/jj-before --ignore-working-copy files -r v6.0' \ '/tmp/jj-after --ignore-working-copy files -r v6.0' Benchmark 1: /tmp/jj-before --ignore-working-copy files -r v6.0 Time (mean ± σ): 461.4 ms ± 14.3 ms [User: 232.1 ms, System: 229.4 ms] Range (min … max): 443.4 ms … 496.3 ms 30 runs Benchmark 2: /tmp/jj-after --ignore-working-copy files -r v6.0 Time (mean ± σ): 482.0 ms ± 14.3 ms [User: 257.2 ms, System: 224.9 ms] Range (min … max): 461.8 ms … 513.3 ms 30 runs Summary '/tmp/jj-before --ignore-working-copy files -r v6.0' ran 1.04 ± 0.04 times faster than '/tmp/jj-after --ignore-working-copy files -r v6.0' ``` I think that's acceptable.
This commit is contained in:
parent
c2d5e3d343
commit
61d87fe296
1 changed files with 29 additions and 20 deletions
|
@ -575,25 +575,37 @@ pub struct TreeEntriesIterator<'matcher> {
|
||||||
matcher: &'matcher dyn Matcher,
|
matcher: &'matcher dyn Matcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ouroboros::self_referencing]
|
|
||||||
struct TreeEntriesDirItem {
|
struct TreeEntriesDirItem {
|
||||||
tree: MergedTree,
|
tree: MergedTree,
|
||||||
#[borrows(tree)]
|
entries: Vec<(RepoPath, MergedTreeValue)>,
|
||||||
#[not_covariant]
|
|
||||||
entry_iterator: TreeEntriesNonRecursiveIterator<'this>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MergedTree> for TreeEntriesDirItem {
|
impl TreeEntriesDirItem {
|
||||||
fn from(tree: MergedTree) -> Self {
|
fn new(tree: MergedTree, matcher: &dyn Matcher) -> Self {
|
||||||
TreeEntriesDirItem::new(tree, |tree| tree.entries_non_recursive())
|
let mut entries = vec![];
|
||||||
|
let dir = tree.dir();
|
||||||
|
for (name, value) in tree.entries_non_recursive() {
|
||||||
|
let path = dir.join(name);
|
||||||
|
let value = value.to_merge();
|
||||||
|
if value.is_tree() {
|
||||||
|
// TODO: Handle the other cases (specific files and trees)
|
||||||
|
if matcher.visit(&path).is_nothing() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if !matcher.matches(&path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entries.push((path, value));
|
||||||
|
}
|
||||||
|
entries.reverse();
|
||||||
|
TreeEntriesDirItem { tree, entries }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'matcher> TreeEntriesIterator<'matcher> {
|
impl<'matcher> TreeEntriesIterator<'matcher> {
|
||||||
fn new(tree: MergedTree, matcher: &'matcher dyn Matcher) -> Self {
|
fn new(tree: MergedTree, matcher: &'matcher dyn Matcher) -> Self {
|
||||||
// TODO: Restrict walk according to Matcher::visit()
|
|
||||||
Self {
|
Self {
|
||||||
stack: vec![TreeEntriesDirItem::from(tree)],
|
stack: vec![TreeEntriesDirItem::new(tree, matcher)],
|
||||||
matcher,
|
matcher,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,19 +616,16 @@ impl Iterator for TreeEntriesIterator<'_> {
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
while let Some(top) = self.stack.last_mut() {
|
while let Some(top) = self.stack.last_mut() {
|
||||||
if let (tree, Some((name, value))) = top.with_mut(|x| (x.tree, x.entry_iterator.next()))
|
if let Some((path, value)) = top.entries.pop() {
|
||||||
{
|
|
||||||
let path = tree.dir().join(name);
|
|
||||||
let value = value.to_merge();
|
|
||||||
if value.is_tree() {
|
if value.is_tree() {
|
||||||
// TODO: Handle the other cases (specific files and trees)
|
let tree_merge = value
|
||||||
if self.matcher.visit(&path).is_nothing() {
|
.to_tree_merge(top.tree.store(), &path)
|
||||||
continue;
|
.unwrap()
|
||||||
}
|
.unwrap();
|
||||||
let tree_merge = value.to_tree_merge(tree.store(), &path).unwrap().unwrap();
|
|
||||||
let merged_tree = MergedTree::Merge(tree_merge);
|
let merged_tree = MergedTree::Merge(tree_merge);
|
||||||
self.stack.push(TreeEntriesDirItem::from(merged_tree));
|
self.stack
|
||||||
} else if self.matcher.matches(&path) {
|
.push(TreeEntriesDirItem::new(merged_tree, self.matcher));
|
||||||
|
} else {
|
||||||
return Some((path, value));
|
return Some((path, value));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue