Skip to main content

lerc_band_materialize/
lib.rs

1use std::fmt;
2use std::mem::MaybeUninit;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum BandLayout {
6    Interleaved,
7    Bsq,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum BandWriteOrder {
12    PixelMajor,
13    DimMajor,
14    Arbitrary,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct MaterializeError {
19    message: String,
20}
21
22impl MaterializeError {
23    fn new(message: impl Into<String>) -> Self {
24        Self {
25            message: message.into(),
26        }
27    }
28}
29
30impl fmt::Display for MaterializeError {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        f.write_str(&self.message)
33    }
34}
35
36impl std::error::Error for MaterializeError {}
37
38pub type Result<T> = std::result::Result<T, MaterializeError>;
39
40pub trait BandWriter<T: Copy + Default> {
41    fn fill_default(&mut self);
42    fn write(&mut self, pixel: usize, dim: usize, value: T);
43    fn read(&self, pixel: usize, dim: usize) -> T;
44    fn depth(&self) -> usize;
45    fn set_write_order(&mut self, _order: BandWriteOrder) {}
46}
47
48pub fn copy_band_values_into_slice<T: Clone>(
49    out: &mut [T],
50    values: &[T],
51    pixel_count: usize,
52    depth: usize,
53    band_index: usize,
54    band_count: usize,
55    layout: BandLayout,
56) -> Result<()> {
57    let band_len = band_len(pixel_count, depth)?;
58    if values.len() != band_len {
59        return Err(MaterializeError::new(
60            "decoded band length does not match its metadata",
61        ));
62    }
63    if band_index >= band_count {
64        return Err(MaterializeError::new(format!(
65            "band index {band_index} exceeds band count {band_count}"
66        )));
67    }
68
69    match layout {
70        BandLayout::Interleaved => {
71            if depth <= 1 {
72                for pixel in 0..pixel_count {
73                    out[pixel * band_count + band_index] = values[pixel].clone();
74                }
75            } else {
76                for pixel in 0..pixel_count {
77                    let src_base = pixel * depth;
78                    let dst_base = (pixel * band_count + band_index) * depth;
79                    out[dst_base..dst_base + depth]
80                        .clone_from_slice(&values[src_base..src_base + depth]);
81                }
82            }
83        }
84        BandLayout::Bsq => {
85            let dst_base = band_index * band_len;
86            out[dst_base..dst_base + band_len].clone_from_slice(values);
87        }
88    }
89
90    Ok(())
91}
92
93#[derive(Debug)]
94pub struct BandSink<'a, T> {
95    out: &'a mut [T],
96    shape: BandShape,
97    band_index: usize,
98    layout: BandLayout,
99}
100
101impl<'a, T: Copy + Default> BandSink<'a, T> {
102    pub fn new(
103        out: &'a mut [T],
104        pixel_count: usize,
105        depth: usize,
106        band_index: usize,
107        band_count: usize,
108        layout: BandLayout,
109    ) -> Self {
110        Self {
111            out,
112            shape: BandShape {
113                pixel_count,
114                depth: depth.max(1),
115                band_count,
116            },
117            band_index,
118            layout,
119        }
120    }
121
122    pub fn fill_default(&mut self) {
123        match self.layout {
124            BandLayout::Interleaved => {
125                for pixel in 0..self.shape.pixel_count {
126                    let base = (pixel * self.shape.band_count + self.band_index) * self.shape.depth;
127                    self.out[base..base + self.shape.depth].fill(T::default());
128                }
129            }
130            BandLayout::Bsq => {
131                let band_len = self.shape.pixel_count * self.shape.depth;
132                let base = self.band_index * band_len;
133                self.out[base..base + band_len].fill(T::default());
134            }
135        }
136    }
137
138    pub fn write(&mut self, pixel: usize, dim: usize, value: T) {
139        let index =
140            band_value_index_for_pixel(self.shape, self.band_index, self.layout, pixel, dim);
141        self.out[index] = value;
142    }
143
144    pub fn read(&self, pixel: usize, dim: usize) -> T {
145        self.out[band_value_index_for_pixel(self.shape, self.band_index, self.layout, pixel, dim)]
146    }
147
148    pub fn depth(&self) -> usize {
149        self.shape.depth
150    }
151}
152
153impl<T: Copy + Default> BandWriter<T> for BandSink<'_, T> {
154    fn fill_default(&mut self) {
155        Self::fill_default(self);
156    }
157
158    fn write(&mut self, pixel: usize, dim: usize, value: T) {
159        Self::write(self, pixel, dim, value);
160    }
161
162    fn read(&self, pixel: usize, dim: usize) -> T {
163        Self::read(self, pixel, dim)
164    }
165
166    fn depth(&self) -> usize {
167        Self::depth(self)
168    }
169}
170
171pub struct BandMaterializer<T> {
172    shape: BandShape,
173    layout: BandLayout,
174    out: Vec<MaybeUninit<T>>,
175    written_bands: Vec<bool>,
176    active_band: Option<ActiveBand>,
177}
178
179#[derive(Debug, Clone, Copy)]
180struct BandShape {
181    pixel_count: usize,
182    depth: usize,
183    band_count: usize,
184}
185
186#[derive(Debug, Clone)]
187struct ActiveBand {
188    band_index: usize,
189    order: BandWriteOrder,
190    progress: ActiveBandProgress,
191}
192
193#[derive(Debug, Clone)]
194enum ActiveBandProgress {
195    Prefix(usize),
196    Sparse(Vec<bool>),
197}
198
199impl<T> BandMaterializer<T> {
200    pub fn new(
201        pixel_count: usize,
202        depth: usize,
203        band_count: usize,
204        layout: BandLayout,
205    ) -> Result<Self> {
206        let sample_count = total_len(pixel_count, depth, band_count)?;
207        let mut out = Vec::with_capacity(sample_count);
208        if sample_count != 0 {
209            unsafe {
210                out.set_len(sample_count);
211            }
212        }
213        Ok(Self {
214            shape: BandShape {
215                pixel_count,
216                depth,
217                band_count,
218            },
219            layout,
220            out,
221            written_bands: vec![false; band_count],
222            active_band: None,
223        })
224    }
225
226    pub fn copy_band_with<F>(&mut self, band_index: usize, mut value_at: F) -> Result<()>
227    where
228        F: FnMut(usize) -> T,
229    {
230        self.ensure_no_active_band()?;
231        if band_index >= self.shape.band_count {
232            return Err(MaterializeError::new(format!(
233                "band index {band_index} exceeds band count {}",
234                self.shape.band_count
235            )));
236        }
237        if self.written_bands[band_index] {
238            return Err(MaterializeError::new(format!(
239                "band index {band_index} was materialized more than once"
240            )));
241        }
242
243        let band_len = band_len(self.shape.pixel_count, self.shape.depth)?;
244        self.active_band = Some(ActiveBand {
245            band_index,
246            order: BandWriteOrder::PixelMajor,
247            progress: ActiveBandProgress::Prefix(0),
248        });
249        for value_index in 0..band_len {
250            let out_index = band_value_index(self.shape, band_index, self.layout, value_index);
251            self.out[out_index].write(value_at(value_index));
252            let ActiveBandProgress::Prefix(written_values) =
253                &mut self.active_band.as_mut().unwrap().progress
254            else {
255                unreachable!();
256            };
257            *written_values += 1;
258        }
259        self.active_band = None;
260        self.written_bands[band_index] = true;
261        Ok(())
262    }
263
264    pub fn finish(self) -> Result<Vec<T>> {
265        let mut this = self;
266        if let Some(active_band) = this.active_band.as_ref() {
267            validate_active_band_complete(this.shape, active_band)?;
268            return Err(MaterializeError::new(format!(
269                "band {} was not finalized before finishing the output buffer",
270                active_band.band_index
271            )));
272        }
273        if this.written_bands.iter().any(|written| !written) {
274            return Err(MaterializeError::new(
275                "not all decoded bands were materialized into the output buffer",
276            ));
277        }
278
279        let out = std::mem::take(&mut this.out);
280        this.active_band = None;
281        this.written_bands.fill(false);
282        Ok(unsafe { assume_init_vec(out) })
283    }
284
285    pub fn band_writer(&mut self, band_index: usize) -> Result<MaterializerBandWriter<'_, T>>
286    where
287        T: Copy + Default,
288    {
289        self.ensure_no_active_band()?;
290        if band_index >= self.shape.band_count {
291            return Err(MaterializeError::new(format!(
292                "band index {band_index} exceeds band count {}",
293                self.shape.band_count
294            )));
295        }
296        if self.written_bands[band_index] {
297            return Err(MaterializeError::new(format!(
298                "band index {band_index} was materialized more than once"
299            )));
300        }
301        self.active_band = Some(ActiveBand {
302            band_index,
303            order: BandWriteOrder::PixelMajor,
304            progress: ActiveBandProgress::Prefix(0),
305        });
306        Ok(MaterializerBandWriter {
307            materializer: self,
308            band_index,
309            finished: false,
310        })
311    }
312
313    fn ensure_no_active_band(&self) -> Result<()> {
314        if let Some(active_band) = self.active_band.as_ref() {
315            return Err(MaterializeError::new(format!(
316                "band {} is still active; finalize it before starting another band",
317                active_band.band_index
318            )));
319        }
320        Ok(())
321    }
322}
323
324impl<T: Clone> BandMaterializer<T> {
325    pub fn copy_band(&mut self, band_index: usize, values: &[T]) -> Result<()> {
326        self.ensure_no_active_band()?;
327        if band_index >= self.shape.band_count {
328            return Err(MaterializeError::new(format!(
329                "band index {band_index} exceeds band count {}",
330                self.shape.band_count
331            )));
332        }
333        if self.written_bands[band_index] {
334            return Err(MaterializeError::new(format!(
335                "band index {band_index} was materialized more than once"
336            )));
337        }
338
339        let band_len = band_len(self.shape.pixel_count, self.shape.depth)?;
340        if values.len() != band_len {
341            return Err(MaterializeError::new(
342                "decoded band length does not match its metadata",
343            ));
344        }
345
346        self.active_band = Some(ActiveBand {
347            band_index,
348            order: BandWriteOrder::PixelMajor,
349            progress: ActiveBandProgress::Prefix(0),
350        });
351        write_band_values_into_uninit_slice(
352            &mut self.out,
353            values,
354            self.shape,
355            self.layout,
356            self.active_band.as_mut().unwrap(),
357        );
358        self.active_band = None;
359        self.written_bands[band_index] = true;
360        Ok(())
361    }
362}
363
364pub struct MaterializerBandWriter<'a, T> {
365    materializer: &'a mut BandMaterializer<T>,
366    band_index: usize,
367    finished: bool,
368}
369
370impl<T: Copy + Default> MaterializerBandWriter<'_, T> {
371    pub fn finish(mut self) -> Result<()> {
372        let active_band = self.materializer.active_band.as_ref().ok_or_else(|| {
373            MaterializeError::new("cannot finalize a band that is no longer active")
374        })?;
375        validate_active_band_complete(self.materializer.shape, active_band)?;
376        self.materializer.written_bands[self.band_index] = true;
377        self.materializer.active_band = None;
378        self.finished = true;
379        Ok(())
380    }
381}
382
383impl<T: Copy + Default> BandWriter<T> for MaterializerBandWriter<'_, T> {
384    fn fill_default(&mut self) {
385        let band_len = band_len(
386            self.materializer.shape.pixel_count,
387            self.materializer.shape.depth,
388        )
389        .expect("band length was validated when the materializer was created");
390        match &mut self.materializer.active_band.as_mut().unwrap().progress {
391            ActiveBandProgress::Prefix(written_values) => {
392                for value_index in 0..band_len {
393                    let out_index = band_value_index(
394                        self.materializer.shape,
395                        self.band_index,
396                        self.materializer.layout,
397                        value_index,
398                    );
399                    if value_index < *written_values {
400                        unsafe {
401                            *self.materializer.out[out_index].assume_init_mut() = T::default();
402                        }
403                    } else {
404                        self.materializer.out[out_index].write(T::default());
405                    }
406                }
407                *written_values = band_len;
408            }
409            ActiveBandProgress::Sparse(initialized) => {
410                for (value_index, is_initialized) in
411                    initialized.iter_mut().enumerate().take(band_len)
412                {
413                    let out_index = band_value_index(
414                        self.materializer.shape,
415                        self.band_index,
416                        self.materializer.layout,
417                        value_index,
418                    );
419                    if *is_initialized {
420                        unsafe {
421                            *self.materializer.out[out_index].assume_init_mut() = T::default();
422                        }
423                    } else {
424                        self.materializer.out[out_index].write(T::default());
425                        *is_initialized = true;
426                    }
427                }
428            }
429        }
430    }
431
432    fn write(&mut self, pixel: usize, dim: usize, value: T) {
433        let logical_index = pixel * self.materializer.shape.depth + dim;
434        loop {
435            let out_index = band_value_index_for_pixel(
436                self.materializer.shape,
437                self.band_index,
438                self.materializer.layout,
439                pixel,
440                dim,
441            );
442            let active_band = self.materializer.active_band.as_mut().unwrap();
443            match &mut active_band.progress {
444                ActiveBandProgress::Prefix(written_values) => {
445                    let ordered_index = match active_band.order {
446                        BandWriteOrder::PixelMajor => logical_index,
447                        BandWriteOrder::DimMajor => {
448                            dim * self.materializer.shape.pixel_count + pixel
449                        }
450                        BandWriteOrder::Arbitrary => usize::MAX,
451                    };
452                    if ordered_index < *written_values {
453                        unsafe {
454                            *self.materializer.out[out_index].assume_init_mut() = value;
455                        }
456                        return;
457                    }
458                    if ordered_index == *written_values {
459                        self.materializer.out[out_index].write(value);
460                        *written_values += 1;
461                        return;
462                    }
463
464                    let mut initialized =
465                        vec![
466                            false;
467                            band_len(
468                                self.materializer.shape.pixel_count,
469                                self.materializer.shape.depth,
470                            )
471                            .expect("band length was validated when the materializer was created")
472                        ];
473                    for step_index in 0..*written_values {
474                        initialized[logical_index_for_step(
475                            self.materializer.shape,
476                            active_band.order,
477                            step_index,
478                        )] = true;
479                    }
480                    active_band.progress = ActiveBandProgress::Sparse(initialized);
481                }
482                ActiveBandProgress::Sparse(initialized) => {
483                    if initialized[logical_index] {
484                        unsafe {
485                            *self.materializer.out[out_index].assume_init_mut() = value;
486                        }
487                    } else {
488                        self.materializer.out[out_index].write(value);
489                        initialized[logical_index] = true;
490                    }
491                    return;
492                }
493            }
494        }
495    }
496
497    fn read(&self, pixel: usize, dim: usize) -> T {
498        let out_index = band_value_index_for_pixel(
499            self.materializer.shape,
500            self.band_index,
501            self.materializer.layout,
502            pixel,
503            dim,
504        );
505        let logical_index = pixel * self.materializer.shape.depth + dim;
506        let active_band = self.materializer.active_band.as_ref().unwrap();
507        match &active_band.progress {
508            ActiveBandProgress::Prefix(written_values) => {
509                let ordered_index = match active_band.order {
510                    BandWriteOrder::PixelMajor => logical_index,
511                    BandWriteOrder::DimMajor => dim * self.materializer.shape.pixel_count + pixel,
512                    BandWriteOrder::Arbitrary => usize::MAX,
513                };
514                assert!(
515                    ordered_index < *written_values,
516                    "attempted to read an uninitialized materialized sample"
517                );
518            }
519            ActiveBandProgress::Sparse(initialized) => {
520                assert!(
521                    initialized[logical_index],
522                    "attempted to read an uninitialized materialized sample"
523                );
524            }
525        }
526        unsafe { *self.materializer.out[out_index].assume_init_ref() }
527    }
528
529    fn depth(&self) -> usize {
530        self.materializer.shape.depth
531    }
532
533    fn set_write_order(&mut self, order: BandWriteOrder) {
534        let active_band = self.materializer.active_band.as_mut().unwrap();
535        match &active_band.progress {
536            ActiveBandProgress::Prefix(0) => active_band.order = order,
537            _ => debug_assert_eq!(active_band.order, order),
538        }
539    }
540}
541
542impl<T> Drop for BandMaterializer<T> {
543    fn drop(&mut self) {
544        if self.out.is_empty() {
545            return;
546        }
547
548        for (band_index, written) in self.written_bands.iter().copied().enumerate() {
549            if !written {
550                continue;
551            }
552            drop_band_prefix(
553                &mut self.out,
554                self.shape,
555                band_index,
556                self.layout,
557                BandWriteOrder::PixelMajor,
558                band_len(self.shape.pixel_count, self.shape.depth).unwrap_or(0),
559            );
560        }
561
562        if let Some(active_band) = self.active_band.take() {
563            match active_band.progress {
564                ActiveBandProgress::Prefix(written_values) => drop_band_prefix(
565                    &mut self.out,
566                    self.shape,
567                    active_band.band_index,
568                    self.layout,
569                    active_band.order,
570                    written_values,
571                ),
572                ActiveBandProgress::Sparse(initialized) => drop_sparse_band(
573                    &mut self.out,
574                    self.shape,
575                    active_band.band_index,
576                    self.layout,
577                    &initialized,
578                ),
579            }
580        }
581    }
582}
583
584fn write_band_values_into_uninit_slice<T: Clone>(
585    out: &mut [MaybeUninit<T>],
586    values: &[T],
587    shape: BandShape,
588    layout: BandLayout,
589    active_band: &mut ActiveBand,
590) {
591    match layout {
592        BandLayout::Interleaved => {
593            if shape.depth <= 1 {
594                for pixel in 0..shape.pixel_count {
595                    out[pixel * shape.band_count + active_band.band_index]
596                        .write(values[pixel].clone());
597                    let ActiveBandProgress::Prefix(written_values) = &mut active_band.progress
598                    else {
599                        unreachable!();
600                    };
601                    *written_values += 1;
602                }
603            } else {
604                for pixel in 0..shape.pixel_count {
605                    let src_base = pixel * shape.depth;
606                    let dst_base =
607                        (pixel * shape.band_count + active_band.band_index) * shape.depth;
608                    for offset in 0..shape.depth {
609                        out[dst_base + offset].write(values[src_base + offset].clone());
610                        let ActiveBandProgress::Prefix(written_values) = &mut active_band.progress
611                        else {
612                            unreachable!();
613                        };
614                        *written_values += 1;
615                    }
616                }
617            }
618        }
619        BandLayout::Bsq => {
620            let band_len = values.len();
621            let dst_base = active_band.band_index * band_len;
622            for (index, value) in values.iter().enumerate() {
623                out[dst_base + index].write(value.clone());
624                let ActiveBandProgress::Prefix(written_values) = &mut active_band.progress else {
625                    unreachable!();
626                };
627                *written_values += 1;
628            }
629        }
630    }
631}
632
633fn drop_band_prefix<T>(
634    out: &mut [MaybeUninit<T>],
635    shape: BandShape,
636    band_index: usize,
637    layout: BandLayout,
638    order: BandWriteOrder,
639    written_values: usize,
640) {
641    for step_index in 0..written_values {
642        let out_index = band_value_index_for_step(shape, band_index, layout, order, step_index);
643        unsafe {
644            out[out_index].assume_init_drop();
645        }
646    }
647}
648
649fn drop_sparse_band<T>(
650    out: &mut [MaybeUninit<T>],
651    shape: BandShape,
652    band_index: usize,
653    layout: BandLayout,
654    initialized: &[bool],
655) {
656    for (value_index, is_initialized) in initialized.iter().copied().enumerate() {
657        if !is_initialized {
658            continue;
659        }
660        let out_index = band_value_index(shape, band_index, layout, value_index);
661        unsafe {
662            out[out_index].assume_init_drop();
663        }
664    }
665}
666
667fn band_value_index(
668    shape: BandShape,
669    band_index: usize,
670    layout: BandLayout,
671    value_index: usize,
672) -> usize {
673    match layout {
674        BandLayout::Interleaved => {
675            if shape.depth <= 1 {
676                value_index * shape.band_count + band_index
677            } else {
678                let pixel = value_index / shape.depth;
679                let sample = value_index % shape.depth;
680                (pixel * shape.band_count + band_index) * shape.depth + sample
681            }
682        }
683        BandLayout::Bsq => band_index * shape.pixel_count * shape.depth.max(1) + value_index,
684    }
685}
686
687fn logical_index_for_step(shape: BandShape, order: BandWriteOrder, step_index: usize) -> usize {
688    match order {
689        BandWriteOrder::PixelMajor => step_index,
690        BandWriteOrder::DimMajor => {
691            let pixel = step_index % shape.pixel_count;
692            let dim = step_index / shape.pixel_count;
693            pixel * shape.depth + dim
694        }
695        BandWriteOrder::Arbitrary => {
696            unreachable!("arbitrary-order writes cannot be represented as a prefix")
697        }
698    }
699}
700
701fn band_value_index_for_step(
702    shape: BandShape,
703    band_index: usize,
704    layout: BandLayout,
705    order: BandWriteOrder,
706    step_index: usize,
707) -> usize {
708    let logical_index = logical_index_for_step(shape, order, step_index);
709    band_value_index(shape, band_index, layout, logical_index)
710}
711
712fn band_value_index_for_pixel(
713    shape: BandShape,
714    band_index: usize,
715    layout: BandLayout,
716    pixel: usize,
717    dim: usize,
718) -> usize {
719    match layout {
720        BandLayout::Interleaved => ((pixel * shape.band_count + band_index) * shape.depth) + dim,
721        BandLayout::Bsq => (band_index * shape.pixel_count + pixel) * shape.depth + dim,
722    }
723}
724
725fn band_len(pixel_count: usize, depth: usize) -> Result<usize> {
726    pixel_count
727        .checked_mul(depth.max(1))
728        .ok_or_else(|| MaterializeError::new("decoded band length overflows usize"))
729}
730
731fn total_len(pixel_count: usize, depth: usize, band_count: usize) -> Result<usize> {
732    band_len(pixel_count, depth)?
733        .checked_mul(band_count)
734        .ok_or_else(|| MaterializeError::new("decoded band set length overflows usize"))
735}
736
737fn validate_active_band_complete(shape: BandShape, active_band: &ActiveBand) -> Result<()> {
738    let band_len = band_len(shape.pixel_count, shape.depth)?;
739    let is_complete = match &active_band.progress {
740        ActiveBandProgress::Prefix(written_values) => *written_values == band_len,
741        ActiveBandProgress::Sparse(initialized) => {
742            initialized.len() == band_len && initialized.iter().all(|initialized| *initialized)
743        }
744    };
745    if is_complete {
746        return Ok(());
747    }
748    Err(MaterializeError::new(format!(
749        "band {} was finalized before all decoded values were initialized",
750        active_band.band_index
751    )))
752}
753
754unsafe fn assume_init_vec<T>(values: Vec<MaybeUninit<T>>) -> Vec<T> {
755    let len = values.len();
756    let cap = values.capacity();
757    let ptr = values.as_ptr() as *mut T;
758    std::mem::forget(values);
759    Vec::from_raw_parts(ptr, len, cap)
760}
761
762#[cfg(test)]
763mod tests {
764    use std::panic::{catch_unwind, AssertUnwindSafe};
765    use std::sync::atomic::{AtomicUsize, Ordering};
766    use std::sync::Arc;
767
768    use super::{
769        ActiveBand, ActiveBandProgress, BandLayout, BandMaterializer, BandWriteOrder, BandWriter,
770    };
771
772    #[derive(Debug)]
773    struct CloneBomb {
774        state: Arc<State>,
775    }
776
777    #[derive(Debug)]
778    struct State {
779        live: AtomicUsize,
780        clones: AtomicUsize,
781        panic_at: usize,
782    }
783
784    impl CloneBomb {
785        fn new(state: &Arc<State>) -> Self {
786            state.live.fetch_add(1, Ordering::SeqCst);
787            Self {
788                state: Arc::clone(state),
789            }
790        }
791    }
792
793    impl Clone for CloneBomb {
794        fn clone(&self) -> Self {
795            let clone_number = self.state.clones.fetch_add(1, Ordering::SeqCst) + 1;
796            if clone_number == self.state.panic_at {
797                panic!("clone panic for test");
798            }
799            self.state.live.fetch_add(1, Ordering::SeqCst);
800            Self {
801                state: Arc::clone(&self.state),
802            }
803        }
804    }
805
806    impl Drop for CloneBomb {
807        fn drop(&mut self) {
808            self.state.live.fetch_sub(1, Ordering::SeqCst);
809        }
810    }
811
812    #[test]
813    fn drops_fully_and_partially_written_interleaved_bands_on_panic() {
814        let state = Arc::new(State {
815            live: AtomicUsize::new(0),
816            clones: AtomicUsize::new(0),
817            panic_at: 10,
818        });
819        let first_band: Vec<_> = (0..6).map(|_| CloneBomb::new(&state)).collect();
820        let second_band: Vec<_> = (0..6).map(|_| CloneBomb::new(&state)).collect();
821
822        let result = catch_unwind(AssertUnwindSafe(|| {
823            let mut materializer = BandMaterializer::new(3, 2, 2, BandLayout::Interleaved).unwrap();
824            materializer.copy_band(0, &first_band).unwrap();
825            materializer.copy_band(1, &second_band).unwrap();
826        }));
827
828        assert!(result.is_err());
829        assert_eq!(state.live.load(Ordering::SeqCst), 12);
830
831        drop(first_band);
832        drop(second_band);
833        assert_eq!(state.live.load(Ordering::SeqCst), 0);
834    }
835
836    #[test]
837    fn drops_partially_written_bsq_band_on_panic() {
838        let state = Arc::new(State {
839            live: AtomicUsize::new(0),
840            clones: AtomicUsize::new(0),
841            panic_at: 3,
842        });
843        let values: Vec<_> = (0..4).map(|_| CloneBomb::new(&state)).collect();
844
845        let result = catch_unwind(AssertUnwindSafe(|| {
846            let mut materializer = BandMaterializer::new(4, 1, 1, BandLayout::Bsq).unwrap();
847            materializer.copy_band(0, &values).unwrap();
848        }));
849
850        assert!(result.is_err());
851        assert_eq!(state.live.load(Ordering::SeqCst), 4);
852
853        drop(values);
854        assert_eq!(state.live.load(Ordering::SeqCst), 0);
855    }
856
857    #[test]
858    fn rejects_finalizing_incomplete_band_writer() {
859        let mut materializer =
860            BandMaterializer::<u16>::new(2, 2, 1, BandLayout::Interleaved).unwrap();
861        let mut writer = materializer.band_writer(0).unwrap();
862        writer.set_write_order(BandWriteOrder::DimMajor);
863        writer.write(0, 0, 1);
864        writer.write(1, 0, 2);
865        let err = writer.finish().unwrap_err();
866        assert_eq!(
867            err.to_string(),
868            "band 0 was finalized before all decoded values were initialized"
869        );
870    }
871
872    #[test]
873    fn rejects_starting_new_band_while_previous_band_is_active() {
874        let mut materializer =
875            BandMaterializer::<u16>::new(2, 1, 2, BandLayout::Interleaved).unwrap();
876        materializer.active_band = Some(ActiveBand {
877            band_index: 0,
878            order: BandWriteOrder::PixelMajor,
879            progress: ActiveBandProgress::Prefix(1),
880        });
881        let err = match materializer.band_writer(1) {
882            Ok(_) => panic!("starting a second band while one is active should fail"),
883            Err(err) => err,
884        };
885        assert_eq!(
886            err.to_string(),
887            "band 0 is still active; finalize it before starting another band"
888        );
889    }
890}