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}