mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-13 22:12:12 +00:00
craft-pdn: stop surface reads when the visitor fails
* pdn: let surface visitors stop decoding * pdn: preserve the legacy surface reader error type
This commit is contained in:
1 parent
7e09989ce4
commit
e2dbb04aec
2 files changed
+38
-3
No files matched your search
+30
-3
@@ -313,18 +313,45 @@ impl<'a> Document<'a> {
|
||||
/// [`Layer::surface`] names). Only one decoded surface is held at a time unless `each` keeps
|
||||
/// them. Data after the last block is an error.
|
||||
pub fn read_surfaces(&self, keep_going: &dyn Fn() -> bool, each: &mut dyn FnMut(usize, Surface)) -> Result<()> {
|
||||
self.try_read_surfaces(keep_going, &mut |index, surface| -> std::result::Result<(), std::convert::Infallible> {
|
||||
each(index, surface);
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|error| match error {
|
||||
ReadSurfacesError::Decode(error) => error,
|
||||
ReadSurfacesError::Callback(never) => match never {},
|
||||
})
|
||||
}
|
||||
|
||||
/// Decodes surfaces as [`Self::read_surfaces`], stopping when `each` returns an error.
|
||||
/// Cancellation returned by the callback is propagated before the next surface is allocated.
|
||||
pub fn try_read_surfaces<E>(
|
||||
&self,
|
||||
keep_going: &dyn Fn() -> bool,
|
||||
each: &mut dyn FnMut(usize, Surface) -> std::result::Result<(), E>,
|
||||
) -> std::result::Result<(), ReadSurfacesError<E>> {
|
||||
let mut reader = Reader { bytes: self.bytes, pos: self.pixels_at };
|
||||
for (i, block) in self.blocks.iter().enumerate() {
|
||||
let data = pixels(&mut reader, block.length, keep_going)?;
|
||||
each(i, Surface { width: self.width, stride: block.stride, data });
|
||||
check(keep_going).map_err(ReadSurfacesError::Decode)?;
|
||||
let data = pixels(&mut reader, block.length, keep_going).map_err(ReadSurfacesError::Decode)?;
|
||||
each(i, Surface { width: self.width, stride: block.stride, data }).map_err(ReadSurfacesError::Callback)?;
|
||||
}
|
||||
if reader.pos != self.bytes.len() {
|
||||
return Err(invalid("unexpected trailing data"));
|
||||
return Err(ReadSurfacesError::Decode(invalid("unexpected trailing data")));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Failure while decoding surfaces or handling one in [`Document::try_read_surfaces`].
|
||||
#[derive(Debug)]
|
||||
pub enum ReadSurfacesError<E> {
|
||||
/// The PDN pixel stream is invalid, or decoding was cancelled through `keep_going`.
|
||||
Decode(Error),
|
||||
/// The caller stopped while handling a decoded surface.
|
||||
Callback(E),
|
||||
}
|
||||
|
||||
/// Reads a memory block's numbered chunks in their on-disk order; each chunk has its own gzip
|
||||
/// stream and checksum. Inflates directly into the bounded destination, checking its exact size.
|
||||
fn pixels(reader: &mut Reader<'_>, length: usize, keep_going: &dyn Fn() -> bool) -> Result<Vec<u8>> {
|
||||
|
||||
@@ -229,6 +229,14 @@ fn reading_can_be_cancelled_during_parsing_and_pixel_decode() {
|
||||
let seen = Cell::new(0);
|
||||
let result = doc.read_surfaces(&|| seen.get() == 0, &mut |_, _| seen.set(seen.get() + 1));
|
||||
assert_eq!((result, seen.get()), (Err(Error::Cancelled), 1), "cancelled after the first surface");
|
||||
|
||||
let seen = Cell::new(0);
|
||||
let result = doc.try_read_surfaces(GO, &mut |_, _| {
|
||||
seen.set(seen.get() + 1);
|
||||
Err(Error::Cancelled)
|
||||
});
|
||||
assert!(matches!(result, Err(ReadSurfacesError::Callback(Error::Cancelled))));
|
||||
assert_eq!(seen.get(), 1, "a failed callback stops before decoding another surface");
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
|
||||
Reference in new issue
Block a user