Skip to content

Commit 4ecb5e3

Browse files
committed
do not recurse into trees during fsck
1 parent 7a88b42 commit 4ecb5e3

File tree

1 file changed

+30
-65
lines changed

1 file changed

+30
-65
lines changed

gix-fsck/src/lib.rs

Lines changed: 30 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
use gix_hash::ObjectId;
55
use gix_hashtable::HashSet;
66
use gix_object::{tree::EntryMode, Exists, FindExt, Kind};
7-
use std::cell::RefCell;
8-
use std::ops::{Deref, DerefMut};
7+
use std::collections::VecDeque;
98

109
/// Perform a connectivity check.
1110
pub struct Connectivity<T, F>
@@ -18,9 +17,9 @@ where
1817
/// Closure to invoke when a missing object is encountered
1918
missing_cb: F,
2019
/// Set of Object IDs already (or about to be) scanned during the check
21-
oid_set: HashSet,
22-
/// A free-list of buffers for recursive tree decoding.
23-
free_list: FreeList,
20+
seen: HashSet,
21+
/// A buffer to keep a single object at a time.
22+
buf: Vec<u8>,
2423
}
2524

2625
impl<T, F> Connectivity<T, F>
@@ -33,8 +32,8 @@ where
3332
Connectivity {
3433
db,
3534
missing_cb,
36-
oid_set: HashSet::default(),
37-
free_list: Default::default(),
35+
seen: HashSet::default(),
36+
buf: Default::default(),
3837
}
3938
}
4039

@@ -49,67 +48,33 @@ where
4948
/// - TODO: consider how to handle a missing commit (invoke `missing_cb`, or possibly return a Result?)
5049
pub fn check_commit(&mut self, oid: &ObjectId) -> Result<(), gix_object::find::existing_object::Error> {
5150
// Attempt to insert the commit ID in the set, and if already present, return immediately
52-
if !self.oid_set.insert(*oid) {
51+
if !self.seen.insert(*oid) {
5352
return Ok(());
5453
}
5554
// Obtain the commit's tree ID
5655
let tree_id = {
57-
let mut buf = self.free_list.buf();
58-
let commit = self.db.find_commit(oid, &mut buf)?;
56+
let commit = self.db.find_commit(oid, &mut self.buf)?;
5957
commit.tree()
6058
};
6159

62-
if self.oid_set.insert(tree_id) {
63-
check_tree(
64-
&tree_id,
65-
&self.db,
66-
&mut self.free_list,
67-
&mut self.missing_cb,
68-
&mut self.oid_set,
69-
);
60+
let mut tree_ids = VecDeque::from_iter(Some(tree_id));
61+
while let Some(tree_id) = tree_ids.pop_front() {
62+
if self.seen.insert(tree_id) {
63+
check_tree(
64+
&tree_id,
65+
&self.db,
66+
&mut self.buf,
67+
&mut self.missing_cb,
68+
&mut self.seen,
69+
&mut tree_ids,
70+
);
71+
}
7072
}
7173

7274
Ok(())
7375
}
7476
}
7577

76-
#[derive(Default)]
77-
struct FreeList(RefCell<Vec<Vec<u8>>>);
78-
79-
impl FreeList {
80-
fn buf(&self) -> ReturnToFreeListOnDrop<'_> {
81-
let buf = self.0.borrow_mut().pop().unwrap_or_default();
82-
ReturnToFreeListOnDrop { buf, list: &self.0 }
83-
}
84-
}
85-
86-
struct ReturnToFreeListOnDrop<'a> {
87-
list: &'a RefCell<Vec<Vec<u8>>>,
88-
buf: Vec<u8>,
89-
}
90-
91-
impl Drop for ReturnToFreeListOnDrop<'_> {
92-
fn drop(&mut self) {
93-
if !self.buf.is_empty() {
94-
self.list.borrow_mut().push(std::mem::take(&mut self.buf));
95-
}
96-
}
97-
}
98-
99-
impl Deref for ReturnToFreeListOnDrop<'_> {
100-
type Target = Vec<u8>;
101-
102-
fn deref(&self) -> &Self::Target {
103-
&self.buf
104-
}
105-
}
106-
107-
impl DerefMut for ReturnToFreeListOnDrop<'_> {
108-
fn deref_mut(&mut self) -> &mut Self::Target {
109-
&mut self.buf
110-
}
111-
}
112-
11378
fn check_blob<F>(db: impl Exists, oid: &ObjectId, mut missing_cb: F)
11479
where
11580
F: FnMut(&ObjectId, Kind),
@@ -121,35 +86,35 @@ where
12186
}
12287
}
12388

89+
/// Blobs are checked right away, trees are stored in `tree_ids` for the parent to iterate them, and only
90+
/// if they have not been `seen` yet.
12491
fn check_tree<F>(
12592
oid: &ObjectId,
12693
db: &(impl FindExt + Exists),
127-
list: &FreeList,
94+
buf: &mut Vec<u8>,
12895
missing_cb: &mut F,
129-
oid_set: &mut HashSet,
96+
seen: &mut HashSet,
97+
tree_ids: &mut VecDeque<ObjectId>,
13098
) where
13199
F: FnMut(&ObjectId, Kind),
132100
{
133-
let mut buf = list.buf();
134-
let Ok(tree) = db.find_tree(oid, &mut buf) else {
101+
let Ok(tree) = db.find_tree(oid, buf) else {
135102
missing_cb(oid, Kind::Tree);
136103
return;
137104
};
138105

139-
// Build up a set of trees and a set of blobs
140-
// For each entry in the tree
141106
for entry_ref in tree.entries.iter() {
142107
match entry_ref.mode {
143108
EntryMode::Tree => {
144109
let tree_id = entry_ref.oid.to_owned();
145-
if oid_set.insert(tree_id) {
146-
check_tree(&tree_id, &*db, list, &mut *missing_cb, oid_set);
110+
if seen.insert(tree_id) {
111+
tree_ids.push_back(tree_id);
147112
}
148113
}
149114
EntryMode::Blob | EntryMode::BlobExecutable | EntryMode::Link => {
150115
let blob_id = entry_ref.oid.to_owned();
151-
if oid_set.insert(blob_id) {
152-
check_blob(&*db, &blob_id, &mut *missing_cb);
116+
if seen.insert(blob_id) {
117+
check_blob(db, &blob_id, &mut *missing_cb);
153118
}
154119
}
155120
EntryMode::Commit => {

0 commit comments

Comments
 (0)