mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-14 06:22:24 +00:00
craft-xcf: allow cancellation between layer records
* xcf: stop parsing layer metadata on cancellation
This commit is contained in:
1 parent
9fd3db9484
commit
7e09989ce4
2 files changed
+24
No files matched your search
@@ -566,6 +566,12 @@ impl<'a> Xcf<'a> {
|
||||
/// Reads the header, the image properties and every layer structure of an XCF file
|
||||
/// (already inflated when it was gzipped).
|
||||
pub fn parse(bytes: &'a [u8]) -> Result<Xcf<'a>> {
|
||||
Self::parse_with(bytes, &|| true)
|
||||
}
|
||||
|
||||
/// Parses an XCF file like [`Self::parse`], checking `keep_going` between layer records.
|
||||
/// This lets callers stop large metadata parses when their operation is cancelled.
|
||||
pub fn parse_with(bytes: &'a [u8], keep_going: &dyn Fn() -> bool) -> Result<Xcf<'a>> {
|
||||
if !is_xcf(bytes) {
|
||||
return Err(invalid("not an XCF file"));
|
||||
}
|
||||
@@ -639,6 +645,9 @@ impl<'a> Xcf<'a> {
|
||||
let c = c.at(0);
|
||||
let mut meta_budget = MAX_METADATA_BYTES;
|
||||
for ptr in layer_ptrs {
|
||||
if !keep_going() {
|
||||
return Err(Error::Cancelled);
|
||||
}
|
||||
let layer = layer(c.at(ptr), &image, &mut meta_budget)?;
|
||||
image.layers.push(layer);
|
||||
}
|
||||
|
||||
@@ -1003,6 +1003,21 @@ fn the_pixel_budget_is_charged_before_decoding_and_decoding_can_be_cancelled() {
|
||||
assert_eq!(xcf.layer_pixels(0, &mut MAX_PIXEL_BYTES.clone(), &|| false), Err(Error::Cancelled));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parsing_layer_metadata_can_be_cancelled_between_records() {
|
||||
let mut img = TestImage::rgb8(3, 3);
|
||||
img.layers.push(TestLayer::rgba("first", 3, 3, rgba(3, 3, |_, _| [1, 2, 3, 255])));
|
||||
img.layers.push(TestLayer::rgba("second", 3, 3, rgba(3, 3, |_, _| [4, 5, 6, 255])));
|
||||
let bytes = img.write();
|
||||
let checks = Cell::new(0);
|
||||
let keep_going = || {
|
||||
checks.set(checks.get() + 1);
|
||||
checks.get() <= 1
|
||||
};
|
||||
assert_eq!(Xcf::parse_with(&bytes, &keep_going).err(), Some(Error::Cancelled));
|
||||
assert_eq!(checks.get(), 2, "the second layer is not parsed after cancellation");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gzipped_files_are_recognised_by_name_and_inflated() {
|
||||
use std::io::Write;
|
||||
|
||||
Reference in new issue
Block a user