4
4
use gix_hash:: ObjectId ;
5
5
use gix_hashtable:: HashSet ;
6
6
use gix_object:: { tree:: EntryMode , Exists , FindExt , Kind } ;
7
- use std:: cell:: RefCell ;
8
- use std:: ops:: { Deref , DerefMut } ;
7
+ use std:: collections:: VecDeque ;
9
8
10
9
/// Perform a connectivity check.
11
10
pub struct Connectivity < T , F >
18
17
/// Closure to invoke when a missing object is encountered
19
18
missing_cb : F ,
20
19
/// 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 > ,
24
23
}
25
24
26
25
impl < T , F > Connectivity < T , F >
33
32
Connectivity {
34
33
db,
35
34
missing_cb,
36
- oid_set : HashSet :: default ( ) ,
37
- free_list : Default :: default ( ) ,
35
+ seen : HashSet :: default ( ) ,
36
+ buf : Default :: default ( ) ,
38
37
}
39
38
}
40
39
@@ -49,67 +48,33 @@ where
49
48
/// - TODO: consider how to handle a missing commit (invoke `missing_cb`, or possibly return a Result?)
50
49
pub fn check_commit ( & mut self , oid : & ObjectId ) -> Result < ( ) , gix_object:: find:: existing_object:: Error > {
51
50
// 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) {
53
52
return Ok ( ( ) ) ;
54
53
}
55
54
// Obtain the commit's tree ID
56
55
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 ) ?;
59
57
commit. tree ( )
60
58
} ;
61
59
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
+ }
70
72
}
71
73
72
74
Ok ( ( ) )
73
75
}
74
76
}
75
77
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
-
113
78
fn check_blob < F > ( db : impl Exists , oid : & ObjectId , mut missing_cb : F )
114
79
where
115
80
F : FnMut ( & ObjectId , Kind ) ,
@@ -121,35 +86,35 @@ where
121
86
}
122
87
}
123
88
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.
124
91
fn check_tree < F > (
125
92
oid : & ObjectId ,
126
93
db : & ( impl FindExt + Exists ) ,
127
- list : & FreeList ,
94
+ buf : & mut Vec < u8 > ,
128
95
missing_cb : & mut F ,
129
- oid_set : & mut HashSet ,
96
+ seen : & mut HashSet ,
97
+ tree_ids : & mut VecDeque < ObjectId > ,
130
98
) where
131
99
F : FnMut ( & ObjectId , Kind ) ,
132
100
{
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 {
135
102
missing_cb ( oid, Kind :: Tree ) ;
136
103
return ;
137
104
} ;
138
105
139
- // Build up a set of trees and a set of blobs
140
- // For each entry in the tree
141
106
for entry_ref in tree. entries . iter ( ) {
142
107
match entry_ref. mode {
143
108
EntryMode :: Tree => {
144
109
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) ;
147
112
}
148
113
}
149
114
EntryMode :: Blob | EntryMode :: BlobExecutable | EntryMode :: Link => {
150
115
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) ;
153
118
}
154
119
}
155
120
EntryMode :: Commit => {
0 commit comments