Skip to content

Commit a133073

Browse files
committed
Rework stitch to an explicit state machine
1 parent 5c4eb6d commit a133073

File tree

3 files changed

+95
-64
lines changed

3 files changed

+95
-64
lines changed

src/backup.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,12 @@ impl BackupWriter {
154154
if gc_lock::GarbageCollectionLock::is_locked(archive)? {
155155
return Err(Error::GarbageCollectionLockHeld);
156156
}
157-
let basis_index = IterStitchedIndexHunks::new(archive, archive.last_band_id()?)
158-
.iter_entries(Apath::root(), Exclude::nothing());
157+
let basis_index = if let Some(basis_band_id) = archive.last_band_id()? {
158+
IterStitchedIndexHunks::new(archive, basis_band_id)
159+
} else {
160+
IterStitchedIndexHunks::empty(archive)
161+
}
162+
.iter_entries(Apath::root(), Exclude::nothing());
159163

160164
// Create the new band only after finding the basis band!
161165
let band = Band::create(archive)?;

src/stitch.rs

Lines changed: 87 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,36 @@
2828
//! seen.
2929
//! * Bands might be deleted, so their numbers are not contiguous.
3030
31-
use tracing::warn;
31+
use tracing::{error, trace};
3232

33-
use crate::index::IndexEntryIter;
33+
use crate::index::{IndexEntryIter, IndexHunkIter};
3434
use crate::*;
3535

3636
pub struct IterStitchedIndexHunks {
37-
/// Current band_id: initially the requested band_id.
38-
/// This moves back to earlier bands when we reach the end of an incomplete band.
39-
/// Might be none, if no more bands are available.
40-
band_id: Option<BandId>,
41-
4237
/// The latest (and highest-ordered) apath we have already yielded.
4338
last_apath: Option<Apath>,
4439

45-
/// Currently pending index hunks.
46-
index_hunks: Option<crate::index::IndexHunkIter>,
47-
4840
archive: Archive,
41+
42+
state: State,
43+
}
44+
45+
/// What state is a stitch iter in, and what should happen next?
46+
enum State {
47+
/// We've read to the end of a finished band, or to the earliest existing band, and there is no more content.
48+
Done,
49+
50+
/// We have know the band to read and have not yet read it at all.
51+
BeforeBand(BandId),
52+
53+
/// We have some index hunks from a band and can return them gradually.
54+
InBand {
55+
band_id: BandId,
56+
index_hunks: IndexHunkIter,
57+
},
58+
59+
/// We finished reading a band
60+
AfterBand(BandId),
4961
}
5062

5163
impl IterStitchedIndexHunks {
@@ -58,12 +70,19 @@ impl IterStitchedIndexHunks {
5870
/// the same point in the previous band, continuing backwards recursively
5971
/// until either there are no more previous indexes, or a complete index
6072
/// is found.
61-
pub(crate) fn new(archive: &Archive, band_id: Option<BandId>) -> IterStitchedIndexHunks {
73+
pub(crate) fn new(archive: &Archive, band_id: BandId) -> IterStitchedIndexHunks {
6274
IterStitchedIndexHunks {
6375
archive: archive.clone(),
64-
band_id,
6576
last_apath: None,
66-
index_hunks: None,
77+
state: State::BeforeBand(band_id),
78+
}
79+
}
80+
81+
pub(crate) fn empty(archive: &Archive) -> IterStitchedIndexHunks {
82+
IterStitchedIndexHunks {
83+
archive: archive.clone(),
84+
last_apath: None,
85+
state: State::Done,
6786
}
6887
}
6988

@@ -81,51 +100,60 @@ impl Iterator for IterStitchedIndexHunks {
81100

82101
fn next(&mut self) -> Option<Self::Item> {
83102
loop {
84-
// Until we find the next hunk or run out of bands.
85-
// If we're already reading an index, and it has more content, return that.
86-
if let Some(index_hunks) = &mut self.index_hunks {
87-
// An index iterator must be assigned to a band.
88-
debug_assert!(self.band_id.is_some());
89-
90-
for hunk in index_hunks {
91-
if let Some(last_entry) = hunk.last() {
92-
self.last_apath = Some(last_entry.apath().clone());
103+
self.state = match &mut self.state {
104+
State::Done => return None,
105+
State::InBand {
106+
band_id,
107+
index_hunks,
108+
} => {
109+
if let Some(hunk) = index_hunks.next() {
110+
if let Some(last_apath) = hunk.last().map(|entry| entry.apath.clone()) {
111+
trace!(%last_apath, "return hunk");
112+
self.last_apath = Some(last_apath);
113+
} else {
114+
trace!("return empty hunk");
115+
}
93116
return Some(hunk);
94-
} // otherwise, empty, try the next
117+
} else {
118+
State::AfterBand(*band_id)
119+
}
95120
}
96-
// There are no more index hunks in the current band.
97-
self.index_hunks = None;
98-
99-
let band_id = self.band_id.take().expect("last band id should be present");
100-
if self.archive.band_is_closed(band_id).unwrap_or(false) {
101-
// We reached the end of a complete index in this band,
102-
// so there's no need to look at any earlier bands, and we're done iterating.
103-
return None;
121+
State::BeforeBand(band_id) => {
122+
// Start reading this new index and skip forward until after last_apath
123+
match Band::open(&self.archive, *band_id) {
124+
Ok(band) => {
125+
let mut index_hunks = band.index().iter_hunks();
126+
if let Some(last) = &self.last_apath {
127+
index_hunks = index_hunks.advance_to_after(last)
128+
}
129+
State::InBand {
130+
band_id: *band_id,
131+
index_hunks,
132+
}
133+
}
134+
Err(err) => {
135+
error!(?band_id, ?err, "Failed to open next band");
136+
State::AfterBand(*band_id)
137+
}
138+
}
104139
}
105-
106-
// self.band_id might be None afterwards, if there is no previous band.
107-
// If so, we're done.
108-
self.band_id = previous_existing_band(&self.archive, band_id);
109-
}
110-
111-
if let Some(band_id) = self.band_id {
112-
let band = match Band::open(&self.archive, band_id) {
113-
Ok(band) => band,
114-
Err(err) => {
115-
warn!(?err, ?band_id, "Failed to open band, skipping it");
116-
self.band_id = previous_existing_band(&self.archive, band_id);
117-
continue;
140+
State::AfterBand(band_id) => {
141+
if self.archive.band_is_closed(*band_id).unwrap_or(false) {
142+
trace!(?band_id, "band is closed; stitched iteration complete");
143+
State::Done
144+
} else if let Some(prev_band_id) =
145+
previous_existing_band(&self.archive, *band_id)
146+
{
147+
trace!(?band_id, ?prev_band_id, "moving back to previous band");
148+
State::BeforeBand(prev_band_id)
149+
} else {
150+
trace!(
151+
?band_id,
152+
"no previous band to stitch; stitched iteration is complete"
153+
);
154+
State::Done
118155
}
119-
};
120-
// Start reading this new index and skip forward until after last_apath
121-
let mut iter_hunks = band.index().iter_hunks();
122-
if let Some(last) = &self.last_apath {
123-
iter_hunks = iter_hunks.advance_to_after(last)
124156
}
125-
self.index_hunks = Some(iter_hunks);
126-
} else {
127-
// We got no more bands with possible new index information.
128-
return None;
129157
}
130158
}
131159
}
@@ -136,9 +164,10 @@ fn previous_existing_band(archive: &Archive, mut band_id: BandId) -> Option<Band
136164
// TODO: It might be faster to list the present bands and calculate
137165
// from that, rather than walking backwards one at a time...
138166
if let Some(prev_band_id) = band_id.previous() {
139-
band_id = prev_band_id;
140-
if archive.band_exists(band_id).unwrap_or(false) {
141-
return Some(band_id);
167+
if archive.band_exists(prev_band_id).unwrap_or(false) {
168+
return Some(prev_band_id);
169+
} else {
170+
band_id = prev_band_id;
142171
}
143172
} else {
144173
return None;
@@ -167,7 +196,7 @@ mod test {
167196
}
168197

169198
fn simple_ls(archive: &Archive, band_id: BandId) -> String {
170-
let strs: Vec<String> = IterStitchedIndexHunks::new(archive, Some(band_id))
199+
let strs: Vec<String> = IterStitchedIndexHunks::new(archive, band_id)
171200
.flatten()
172201
.map(|entry| format!("{}:{}", &entry.apath, entry.target.unwrap()))
173202
.collect();
@@ -301,7 +330,7 @@ mod test {
301330

302331
let band_id = band_ids.first().expect("expected at least one band");
303332

304-
let mut iter = IterStitchedIndexHunks::new(&af, Some(*band_id));
333+
let mut iter = IterStitchedIndexHunks::new(&af, *band_id);
305334
// Get the first and only index entry.
306335
// `index_hunks` and `band_id` should be `Some`.
307336
assert!(iter.next().is_some());

src/stored_tree.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,8 @@ impl ReadTree for StoredTree {
6161
/// Return an iter of index entries in this stored tree.
6262
// TODO: Should return an iter of Result<Entry> so that we can inspect them...
6363
fn iter_entries(&self, subtree: Apath, exclude: Exclude) -> Result<Self::IT> {
64-
Ok(
65-
IterStitchedIndexHunks::new(&self.archive, Some(self.band.id()))
66-
.iter_entries(subtree, exclude),
67-
)
64+
Ok(IterStitchedIndexHunks::new(&self.archive, self.band.id())
65+
.iter_entries(subtree, exclude))
6866
}
6967
}
7068

0 commit comments

Comments
 (0)