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,113 +48,60 @@ 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
+ self . check_tree ( & tree_id, & mut tree_ids) ;
64
+ }
70
65
}
71
66
72
67
Ok ( ( ) )
73
68
}
74
- }
75
69
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
- }
70
+ /// Blobs are checked right away, trees are stored in `tree_ids` for the parent to iterate them, and only
71
+ /// if they have not been `seen` yet.
72
+ fn check_tree ( & mut self , oid : & ObjectId , tree_ids : & mut VecDeque < ObjectId > ) {
73
+ let Ok ( tree) = self . db . find_tree ( oid, & mut self . buf ) else {
74
+ ( self . missing_cb ) ( oid, Kind :: Tree ) ;
75
+ return ;
76
+ } ;
90
77
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 ) ) ;
78
+ for entry_ref in tree. entries . iter ( ) {
79
+ match entry_ref. mode {
80
+ EntryMode :: Tree => {
81
+ let tree_id = entry_ref. oid . to_owned ( ) ;
82
+ if self . seen . insert ( tree_id) {
83
+ tree_ids. push_back ( tree_id) ;
84
+ }
85
+ }
86
+ EntryMode :: Blob | EntryMode :: BlobExecutable | EntryMode :: Link => {
87
+ let blob_id = entry_ref. oid . to_owned ( ) ;
88
+ if self . seen . insert ( blob_id) {
89
+ check_blob ( & self . db , & blob_id, & mut self . missing_cb ) ;
90
+ }
91
+ }
92
+ EntryMode :: Commit => {
93
+ // Skip submodules as it's not in this repository!
94
+ }
95
+ }
95
96
}
96
97
}
97
98
}
98
99
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
100
fn check_blob < F > ( db : impl Exists , oid : & ObjectId , mut missing_cb : F )
114
101
where
115
102
F : FnMut ( & ObjectId , Kind ) ,
116
103
{
117
- // Check if the blob is missing from the ODB
118
104
if !db. exists ( oid) {
119
- // Blob is missing, so invoke `missing_cb`
120
105
missing_cb ( oid, Kind :: Blob ) ;
121
106
}
122
107
}
123
-
124
- fn check_tree < F > (
125
- oid : & ObjectId ,
126
- db : & ( impl FindExt + Exists ) ,
127
- list : & FreeList ,
128
- missing_cb : & mut F ,
129
- oid_set : & mut HashSet ,
130
- ) where
131
- F : FnMut ( & ObjectId , Kind ) ,
132
- {
133
- let mut buf = list. buf ( ) ;
134
- let Ok ( tree) = db. find_tree ( oid, & mut buf) else {
135
- missing_cb ( oid, Kind :: Tree ) ;
136
- return ;
137
- } ;
138
-
139
- // Build up a set of trees and a set of blobs
140
- // For each entry in the tree
141
- for entry_ref in tree. entries . iter ( ) {
142
- match entry_ref. mode {
143
- EntryMode :: Tree => {
144
- 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) ;
147
- }
148
- }
149
- EntryMode :: Blob | EntryMode :: BlobExecutable | EntryMode :: Link => {
150
- let blob_id = entry_ref. oid . to_owned ( ) ;
151
- if oid_set. insert ( blob_id) {
152
- check_blob ( & * db, & blob_id, & mut * missing_cb) ;
153
- }
154
- }
155
- EntryMode :: Commit => {
156
- // This implies a submodule (OID is the commit hash of the submodule)
157
- // Skip it as it's not in this repository!
158
- }
159
- }
160
- }
161
- }
0 commit comments