1#![deny(unsafe_op_in_unsafe_fn)]
2#![warn(missing_docs)]
3
4mod allocation;
22mod bitstuff;
23mod huffman;
24mod io;
25mod lerc1;
26mod lerc2;
27mod materialize;
28mod pixel;
29mod stream;
30mod types;
31
32#[cfg(test)]
33mod test_support;
34#[cfg(test)]
35mod tests;
36
37use lerc_core::{BandLayout, BandSetInfo, BlobInfo, DataType, Error, Result};
38use materialize::BandSink;
39#[cfg(feature = "rayon")]
40use materialize::{copy_band_values_into_slice, PixelDataWriter};
41#[cfg(feature = "ndarray")]
42use ndarray::ArrayD;
43#[cfg(feature = "rayon")]
44use rayon::prelude::*;
45
46use crate::allocation::default_vec;
47use crate::pixel::Sample;
48#[cfg(feature = "ndarray")]
49pub use crate::types::NdArrayElement;
50pub use crate::types::{BandElement, BandElementKind, Decoded, DecodedBandSet, DecodedF64};
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54#[non_exhaustive]
55pub struct InspectOptions {
56 pub compute_value_range: bool,
58}
59
60impl InspectOptions {
61 pub const fn new() -> Self {
63 Self {
64 compute_value_range: true,
65 }
66 }
67
68 pub const fn with_compute_value_range(mut self, compute_value_range: bool) -> Self {
70 self.compute_value_range = compute_value_range;
71 self
72 }
73}
74
75impl Default for InspectOptions {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81macro_rules! dispatch_band_element {
82 ($target:ty, |$concrete:ident| $body:block) => {
83 match <$target as BandElement>::KIND {
84 BandElementKind::I8 => {
85 type $concrete = i8;
86 $body
87 }
88 BandElementKind::U8 => {
89 type $concrete = u8;
90 $body
91 }
92 BandElementKind::I16 => {
93 type $concrete = i16;
94 $body
95 }
96 BandElementKind::U16 => {
97 type $concrete = u16;
98 $body
99 }
100 BandElementKind::I32 => {
101 type $concrete = i32;
102 $body
103 }
104 BandElementKind::U32 => {
105 type $concrete = u32;
106 $body
107 }
108 BandElementKind::F32 => {
109 type $concrete = f32;
110 $body
111 }
112 BandElementKind::F64 => {
113 type $concrete = f64;
114 $body
115 }
116 }
117 };
118}
119
120pub fn inspect_first(blob: &[u8]) -> Result<BlobInfo> {
125 inspect_first_with_options(blob, InspectOptions::default())
126}
127
128pub fn inspect_first_with_options(blob: &[u8], options: InspectOptions) -> Result<BlobInfo> {
133 if lerc1::is_lerc1(blob) {
134 return lerc1::inspect_with_mask_options(blob, None, options.compute_value_range)
135 .map(|(info, _)| info);
136 }
137 if lerc2::is_lerc2(blob) {
138 return lerc2::inspect(blob, None);
139 }
140 Err(Error::InvalidMagic)
141}
142
143pub fn inspect_first_with_mask(blob: &[u8], mask: &[u8]) -> Result<BlobInfo> {
148 let (info, _) = inspect_first_mask_with_info(blob, Some(mask), Some(mask))?;
149 Ok(info)
150}
151
152pub fn get_blob_info(blob: &[u8]) -> Result<BlobInfo> {
157 let info = inspect_first(blob)?;
158 ensure_single_blob_consumed(blob.len(), info.blob_size, "get_blob_info", "inspect_first")?;
159 Ok(info)
160}
161
162pub fn get_blob_info_with_mask(blob: &[u8], mask: &[u8]) -> Result<BlobInfo> {
167 let info = inspect_first_with_mask(blob, mask)?;
168 ensure_single_blob_consumed(
169 blob.len(),
170 info.blob_size,
171 "get_blob_info_with_mask",
172 "inspect_first_with_mask",
173 )?;
174 Ok(info)
175}
176
177pub fn get_band_count(blob: &[u8]) -> Result<usize> {
182 let mut offset = 0usize;
183 let mut count = 0usize;
184 let mut lerc1_mask: Option<Vec<u8>> = None;
185 let mut lerc2_mask: Option<Vec<u8>> = None;
186 let mut declared_band_count: Option<usize> = None;
187
188 while offset < blob.len() {
189 if declared_band_count == Some(count) {
190 return Err(Error::invalid_blob(
191 "Lerc2 v6 remaining-band count is smaller than the payload",
192 ));
193 }
194 let slice = &blob[offset..];
195 let info = if lerc1::is_lerc1(slice) {
196 let parsed = lerc1::parse(slice, lerc1_mask.as_deref())?;
197 lerc1_mask = parsed.mask;
198 lerc2_mask = None;
199 parsed.info
200 } else if lerc2::is_lerc2(slice) {
201 let (info, mask) = lerc2::inspect_with_mask(slice, lerc2_mask.as_deref())?;
202 lerc2_mask = mask;
203 lerc1_mask = None;
204 info
205 } else {
206 return Err(Error::InvalidMagic);
207 };
208
209 if matches!(info.version, lerc_core::Version::Lerc2(version) if version >= 6) {
210 let declared = count
211 .checked_add(1)
212 .and_then(|count| count.checked_add(info.remaining_band_count as usize))
213 .ok_or(Error::SizeOverflow("declared Lerc2 band count"))?;
214 if declared_band_count.is_some_and(|previous| previous != declared) {
215 return Err(Error::invalid_blob(
216 "Lerc2 v6 headers disagree about the remaining-band count",
217 ));
218 }
219 declared_band_count = Some(declared);
220 }
221
222 offset = checked_next_offset(offset, info.blob_size, blob.len())?;
223 count += 1;
224 }
225
226 if declared_band_count.is_some_and(|declared| declared != count) {
227 return Err(Error::invalid_blob(
228 "Lerc2 v6 remaining-band count exceeds the payload",
229 ));
230 }
231 Ok(count)
232}
233
234pub fn decode_first(blob: &[u8]) -> Result<Decoded> {
239 decode_first_with_masks(blob, None, None)
240}
241
242pub fn decode_first_with_mask(blob: &[u8], mask: &[u8]) -> Result<Decoded> {
247 decode_first_with_masks(blob, Some(mask), Some(mask))
248}
249
250pub fn decode(blob: &[u8]) -> Result<Decoded> {
255 let decoded = decode_first(blob)?;
256 ensure_single_blob_consumed(blob.len(), decoded.info.blob_size, "decode", "decode_first")?;
257 Ok(decoded)
258}
259
260pub fn decode_with_mask(blob: &[u8], mask: &[u8]) -> Result<Decoded> {
265 let decoded = decode_first_with_mask(blob, mask)?;
266 ensure_single_blob_consumed(
267 blob.len(),
268 decoded.info.blob_size,
269 "decode_with_mask",
270 "decode_first_with_mask",
271 )?;
272 Ok(decoded)
273}
274
275pub fn decode_from_reader<R: std::io::Read + ?Sized>(reader: &mut R) -> Result<Decoded> {
280 let blob = read_required_stream_blob(reader, None)?;
281 decode(&blob)
282}
283
284pub fn decode_from_reader_with_mask<R: std::io::Read + ?Sized>(
289 reader: &mut R,
290 mask: &[u8],
291) -> Result<Decoded> {
292 let blob = read_required_stream_blob(reader, Some(mask))?;
293 decode_with_mask(&blob, mask)
294}
295
296pub fn decode_band_set(blob: &[u8]) -> Result<DecodedBandSet> {
301 decode_band_set_with_lerc2_mask(blob, None)
302}
303
304pub fn decode_band_set_with_mask(blob: &[u8], mask: &[u8]) -> Result<DecodedBandSet> {
309 decode_band_set_with_lerc2_mask(blob, Some(mask))
310}
311
312pub fn decode_band_set_from_reader<R: std::io::Read + ?Sized>(
317 reader: &mut R,
318) -> Result<DecodedBandSet> {
319 decode_band_set_from_reader_impl(reader, None)
320}
321
322pub fn decode_band_set_from_reader_with_mask<R: std::io::Read + ?Sized>(
327 reader: &mut R,
328 mask: &[u8],
329) -> Result<DecodedBandSet> {
330 decode_band_set_from_reader_impl(reader, Some(mask))
331}
332
333pub fn decode_band_set_vec<T: BandElement>(
338 blob: &[u8],
339 layout: BandLayout,
340) -> Result<(BandSetInfo, Vec<T>)> {
341 decode_band_set_owned(blob, layout, None)
342}
343
344pub fn decode_band_set_vec_with_mask<T: BandElement>(
349 blob: &[u8],
350 mask: &[u8],
351 layout: BandLayout,
352) -> Result<(BandSetInfo, Vec<T>)> {
353 decode_band_set_owned(blob, layout, Some(mask))
354}
355
356pub fn decode_band_set_into<T: BandElement>(
361 blob: &[u8],
362 layout: BandLayout,
363 out: &mut [T],
364) -> Result<BandSetInfo> {
365 decode_band_set_into_direct(blob, layout, None, out)
366}
367
368pub fn decode_band_set_into_with_mask<T: BandElement>(
373 blob: &[u8],
374 mask: &[u8],
375 layout: BandLayout,
376 out: &mut [T],
377) -> Result<BandSetInfo> {
378 decode_band_set_into_direct(blob, layout, Some(mask), out)
379}
380
381#[cfg(feature = "ndarray")]
382pub fn decode_band_set_ndarray<T: BandElement>(blob: &[u8]) -> Result<ArrayD<T>> {
387 decode_band_set_ndarray_with_layout(blob, BandLayout::Interleaved)
388}
389
390#[cfg(feature = "ndarray")]
391pub fn decode_band_set_ndarray_with_mask<T: BandElement>(
396 blob: &[u8],
397 mask: &[u8],
398) -> Result<ArrayD<T>> {
399 decode_band_set_ndarray_with_layout_and_mask(blob, BandLayout::Interleaved, mask)
400}
401
402#[cfg(feature = "ndarray")]
403pub fn decode_band_set_ndarray_with_layout<T: BandElement>(
408 blob: &[u8],
409 layout: BandLayout,
410) -> Result<ArrayD<T>> {
411 decode_band_set_ndarray_with_layout_impl(blob, layout, None)
412}
413
414#[cfg(feature = "ndarray")]
415pub fn decode_band_set_ndarray_with_layout_and_mask<T: BandElement>(
420 blob: &[u8],
421 layout: BandLayout,
422 mask: &[u8],
423) -> Result<ArrayD<T>> {
424 decode_band_set_ndarray_with_layout_impl(blob, layout, Some(mask))
425}
426
427#[cfg(feature = "ndarray")]
428pub fn decode_band_set_ndarray_f64(blob: &[u8]) -> Result<ArrayD<f64>> {
433 decode_band_set_ndarray_f64_with_layout(blob, BandLayout::Interleaved)
434}
435
436#[cfg(feature = "ndarray")]
437pub fn decode_band_set_ndarray_f64_with_mask(blob: &[u8], mask: &[u8]) -> Result<ArrayD<f64>> {
442 decode_band_set_ndarray_f64_with_layout_and_mask(blob, BandLayout::Interleaved, mask)
443}
444
445#[cfg(feature = "ndarray")]
446pub fn decode_band_set_ndarray_f64_with_layout(
451 blob: &[u8],
452 layout: BandLayout,
453) -> Result<ArrayD<f64>> {
454 decode_band_set_ndarray_f64_with_optional_mask(blob, layout, None)
455}
456
457#[cfg(feature = "ndarray")]
458pub fn decode_band_set_ndarray_f64_with_layout_and_mask(
463 blob: &[u8],
464 layout: BandLayout,
465 mask: &[u8],
466) -> Result<ArrayD<f64>> {
467 decode_band_set_ndarray_f64_with_optional_mask(blob, layout, Some(mask))
468}
469
470#[cfg(feature = "ndarray")]
471pub fn decode_band_mask_ndarray(blob: &[u8]) -> Result<Option<ArrayD<u8>>> {
476 let (info, band_masks) = inspect_band_masks(blob, None)?;
477 crate::types::band_masks_into_ndarray(info, band_masks)
478}
479
480#[cfg(feature = "ndarray")]
481pub fn decode_band_mask_ndarray_with_mask(blob: &[u8], mask: &[u8]) -> Result<Option<ArrayD<u8>>> {
486 let (info, band_masks) = inspect_band_masks(blob, Some(mask))?;
487 crate::types::band_masks_into_ndarray(info, band_masks)
488}
489
490fn decode_band_set_with_lerc2_mask(
491 blob: &[u8],
492 initial_mask: Option<&[u8]>,
493) -> Result<DecodedBandSet> {
494 let mut offset = 0usize;
495 let mut bands = Vec::new();
496 let mut infos = Vec::new();
497 let mut band_masks = Vec::new();
498 let mut lerc1_mask = initial_mask.map(<[u8]>::to_vec);
499 let mut lerc2_mask = initial_mask.map(<[u8]>::to_vec);
500
501 while offset < blob.len() {
502 let decoded = decode_first_with_masks(
503 &blob[offset..],
504 lerc1_mask.as_deref(),
505 lerc2_mask.as_deref(),
506 )?;
507
508 if lerc1::is_lerc1(&blob[offset..]) {
509 lerc1_mask = decoded.mask.clone();
510 lerc2_mask = None;
511 } else {
512 lerc2_mask = decoded.mask.clone();
513 lerc1_mask = None;
514 }
515
516 offset = checked_next_offset(offset, decoded.info.blob_size, blob.len())?;
517 infos.push(decoded.info);
518 bands.push(decoded.pixels);
519 band_masks.push(decoded.mask);
520 }
521
522 DecodedBandSet::new(BandSetInfo::new(infos)?, bands, band_masks)
523}
524
525#[cfg(feature = "ndarray")]
526fn decode_band_set_ndarray_with_layout_impl<T: BandElement>(
527 blob: &[u8],
528 layout: BandLayout,
529 initial_mask: Option<&[u8]>,
530) -> Result<ArrayD<T>> {
531 let (info, values) = decode_band_set_owned(blob, layout, initial_mask)?;
532 let shape = info.ndarray_shape_for_layout(layout);
533 ArrayD::from_shape_vec(ndarray::IxDyn(&shape), values).map_err(|e| {
534 Error::invalid_blob(format!(
535 "failed to build ndarray from decoded band set: {e}"
536 ))
537 })
538}
539
540#[cfg(feature = "ndarray")]
541fn decode_band_set_ndarray_f64_with_optional_mask(
542 blob: &[u8],
543 layout: BandLayout,
544 initial_mask: Option<&[u8]>,
545) -> Result<ArrayD<f64>> {
546 let band_info = decode_band_set_to_f64_info(blob, layout, initial_mask)?;
547 let shape = band_info.0.ndarray_shape_for_layout(layout);
548 ArrayD::from_shape_vec(ndarray::IxDyn(&shape), band_info.1).map_err(|e| {
549 Error::invalid_blob(format!(
550 "failed to build ndarray from decoded band set: {e}"
551 ))
552 })
553}
554
555pub fn decode_to_f64(blob: &[u8]) -> Result<DecodedF64> {
560 let decoded = decode_first_to_f64(blob)?;
561 ensure_single_blob_consumed(
562 blob.len(),
563 decoded.info.blob_size,
564 "decode_to_f64",
565 "decode_first_to_f64",
566 )?;
567 Ok(decoded)
568}
569
570pub fn decode_to_f64_with_mask(blob: &[u8], mask: &[u8]) -> Result<DecodedF64> {
575 let decoded = decode_first_to_f64_with_mask(blob, mask)?;
576 ensure_single_blob_consumed(
577 blob.len(),
578 decoded.info.blob_size,
579 "decode_to_f64_with_mask",
580 "decode_first_to_f64_with_mask",
581 )?;
582 Ok(decoded)
583}
584
585pub fn decode_to_f64_from_reader<R: std::io::Read + ?Sized>(reader: &mut R) -> Result<DecodedF64> {
590 let blob = read_required_stream_blob(reader, None)?;
591 decode_to_f64(&blob)
592}
593
594pub fn decode_to_f64_from_reader_with_mask<R: std::io::Read + ?Sized>(
599 reader: &mut R,
600 mask: &[u8],
601) -> Result<DecodedF64> {
602 let blob = read_required_stream_blob(reader, Some(mask))?;
603 decode_to_f64_with_mask(&blob, mask)
604}
605
606pub fn decode_first_to_f64(blob: &[u8]) -> Result<DecodedF64> {
611 decode_first_f64(blob)
612}
613
614pub fn decode_first_to_f64_with_mask(blob: &[u8], mask: &[u8]) -> Result<DecodedF64> {
619 decode_first_f64_with_masks(blob, Some(mask), Some(mask))
620}
621
622#[cfg(feature = "ndarray")]
623pub fn decode_ndarray<T: NdArrayElement>(blob: &[u8]) -> Result<ArrayD<T>> {
628 decode(blob)?.into_ndarray()
629}
630
631#[cfg(feature = "ndarray")]
632pub fn decode_ndarray_with_mask<T: NdArrayElement>(blob: &[u8], mask: &[u8]) -> Result<ArrayD<T>> {
637 decode_with_mask(blob, mask)?.into_ndarray()
638}
639
640#[cfg(feature = "ndarray")]
641pub fn decode_ndarray_f64(blob: &[u8]) -> Result<ArrayD<f64>> {
646 decode_to_f64(blob)?.into_ndarray()
647}
648
649#[cfg(feature = "ndarray")]
650pub fn decode_ndarray_f64_with_mask(blob: &[u8], mask: &[u8]) -> Result<ArrayD<f64>> {
655 decode_to_f64_with_mask(blob, mask)?.into_ndarray()
656}
657
658#[cfg(feature = "ndarray")]
659pub fn decode_mask_ndarray(blob: &[u8]) -> Result<Option<ArrayD<u8>>> {
664 let (info, mask) = inspect_first_mask_with_info(blob, None, None)?;
665 ensure_single_blob_consumed(
666 blob.len(),
667 info.blob_size,
668 "decode_mask_ndarray",
669 "inspect_first",
670 )?;
671 mask_to_ndarray(&info, mask)
672}
673
674#[cfg(feature = "ndarray")]
675pub fn decode_mask_ndarray_with_mask(blob: &[u8], mask: &[u8]) -> Result<Option<ArrayD<u8>>> {
680 let (info, decoded_mask) = inspect_first_mask_with_info(blob, Some(mask), Some(mask))?;
681 ensure_single_blob_consumed(
682 blob.len(),
683 info.blob_size,
684 "decode_mask_ndarray_with_mask",
685 "inspect_first_with_mask",
686 )?;
687 mask_to_ndarray(&info, decoded_mask)
688}
689
690fn decode_first_with_masks(
691 blob: &[u8],
692 lerc1_shared_mask: Option<&[u8]>,
693 lerc2_shared_mask: Option<&[u8]>,
694) -> Result<Decoded> {
695 if lerc1::is_lerc1(blob) {
696 return lerc1::decode(blob, lerc1_shared_mask);
697 }
698 if lerc2::is_lerc2(blob) {
699 return lerc2::decode(blob, lerc2_shared_mask);
700 }
701 Err(Error::InvalidMagic)
702}
703
704fn inspect_first_mask_with_info(
705 blob: &[u8],
706 lerc1_shared_mask: Option<&[u8]>,
707 lerc2_shared_mask: Option<&[u8]>,
708) -> Result<(BlobInfo, Option<Vec<u8>>)> {
709 if lerc1::is_lerc1(blob) {
710 return lerc1::inspect_mask(blob, lerc1_shared_mask);
711 }
712 if lerc2::is_lerc2(blob) {
713 return lerc2::inspect_with_mask(blob, lerc2_shared_mask);
714 }
715 Err(Error::InvalidMagic)
716}
717
718fn decode_first_f64(blob: &[u8]) -> Result<DecodedF64> {
719 decode_first_f64_with_masks(blob, None, None)
720}
721
722fn decode_first_f64_with_masks(
723 blob: &[u8],
724 lerc1_shared_mask: Option<&[u8]>,
725 lerc2_shared_mask: Option<&[u8]>,
726) -> Result<DecodedF64> {
727 if lerc1::is_lerc1(blob) {
728 return lerc1::decode_f64(blob, lerc1_shared_mask);
729 }
730 if lerc2::is_lerc2(blob) {
731 return lerc2::decode_f64(blob, lerc2_shared_mask);
732 }
733 Err(Error::InvalidMagic)
734}
735
736fn decode_band_set_owned<T: BandElement>(
737 blob: &[u8],
738 layout: BandLayout,
739 initial_mask: Option<&[u8]>,
740) -> Result<(BandSetInfo, Vec<T>)> {
741 let band_info = scan_band_infos(blob, initial_mask)?;
742 decode_band_set_owned_direct(blob, layout, band_info, initial_mask)
743}
744
745fn decode_band_set_into_direct<T: BandElement>(
746 blob: &[u8],
747 layout: BandLayout,
748 initial_mask: Option<&[u8]>,
749 out: &mut [T],
750) -> Result<BandSetInfo> {
751 dispatch_band_element!(T, |Concrete| {
752 decode_band_set_into_impl::<Concrete>(
753 blob,
754 layout,
755 initial_mask,
756 cast_slice_mut::<T, Concrete>(out),
757 )
758 })
759}
760
761fn decode_band_set_owned_direct<T: BandElement>(
762 blob: &[u8],
763 layout: BandLayout,
764 band_info: BandSetInfo,
765 initial_mask: Option<&[u8]>,
766) -> Result<(BandSetInfo, Vec<T>)> {
767 dispatch_band_element!(T, |Concrete| {
768 decode_band_set_owned_direct_impl::<Concrete>(blob, layout, band_info, initial_mask)
769 .map(|(info, values)| (info, cast_vec::<T, Concrete>(values)))
770 })
771}
772
773fn decode_band_set_into_impl<T: Sample + BandElement>(
774 blob: &[u8],
775 layout: BandLayout,
776 initial_mask: Option<&[u8]>,
777 out: &mut [T],
778) -> Result<BandSetInfo> {
779 let band_info = scan_band_infos(blob, initial_mask)?;
780 decode_band_set_into_impl_with_info(blob, layout, initial_mask, &band_info, out)
781}
782
783fn decode_band_set_into_impl_with_info<T: Sample + BandElement>(
784 blob: &[u8],
785 layout: BandLayout,
786 initial_mask: Option<&[u8]>,
787 band_info: &BandSetInfo,
788 out: &mut [T],
789) -> Result<BandSetInfo> {
790 validate_band_output_type::<T>(band_info)?;
791 #[cfg(feature = "rayon")]
792 {
793 if band_info.band_count() > 1 {
794 return decode_band_set_into_parallel(blob, layout, initial_mask, band_info, out);
795 }
796 }
797 decode_band_set_into_sequential(blob, layout, initial_mask, band_info, out)
798}
799
800fn validate_band_output_type<T: BandElement>(band_info: &BandSetInfo) -> Result<()> {
801 if T::KIND == BandElementKind::F64 {
802 return Ok(());
803 }
804 let expected = match T::KIND {
805 BandElementKind::I8 => DataType::I8,
806 BandElementKind::U8 => DataType::U8,
807 BandElementKind::I16 => DataType::I16,
808 BandElementKind::U16 => DataType::U16,
809 BandElementKind::I32 => DataType::I32,
810 BandElementKind::U32 => DataType::U32,
811 BandElementKind::F32 => DataType::F32,
812 BandElementKind::F64 => unreachable!("f64 promotion returned above"),
813 };
814 if band_info
815 .bands()
816 .iter()
817 .any(|band| band.data_type != expected)
818 {
819 return Err(Error::InvalidArgument(
820 "output element type must match every native band type unless decoding to f64",
821 ));
822 }
823 Ok(())
824}
825
826fn decode_band_set_into_sequential<T: Sample + BandElement>(
827 blob: &[u8],
828 layout: BandLayout,
829 initial_mask: Option<&[u8]>,
830 band_info: &BandSetInfo,
831 out: &mut [T],
832) -> Result<BandSetInfo> {
833 let band_count = band_info.band_count();
834 let expected_len = band_info.value_count()?;
835 if out.len() != expected_len {
836 return Err(Error::InvalidArgument(
837 "output slice length does not match decoded band set length",
838 ));
839 }
840
841 let pixel_count = band_info.first().pixel_count()?;
842 let depth = band_info.depth() as usize;
843 let mut offset = 0usize;
844 let mut band_index = 0usize;
845 let mut lerc1_mask = initial_mask.map(<[u8]>::to_vec);
846 let mut lerc2_mask = initial_mask.map(<[u8]>::to_vec);
847 let mut decoded_infos = Vec::with_capacity(band_count);
848
849 while offset < blob.len() {
850 let slice = &blob[offset..];
851 let is_lerc1 = lerc1::is_lerc1(slice);
852 let mut sink = BandSink::new(out, pixel_count, depth, band_index, band_count, layout);
853 let (info, mask) = if is_lerc1 {
854 lerc1::decode_into(slice, lerc1_mask.as_deref(), &mut sink)?
855 } else if lerc2::is_lerc2(slice) {
856 lerc2::decode_into(slice, lerc2_mask.as_deref(), &mut sink)?
857 } else {
858 return Err(Error::InvalidMagic);
859 };
860
861 if is_lerc1 {
862 lerc1_mask = mask;
863 lerc2_mask = None;
864 } else {
865 lerc2_mask = mask;
866 lerc1_mask = None;
867 }
868
869 let blob_size = info.blob_size;
870 decoded_infos.push(info);
871 offset = checked_next_offset(offset, blob_size, blob.len())?;
872 band_index += 1;
873 }
874
875 BandSetInfo::new(decoded_infos)
876}
877
878#[cfg(feature = "rayon")]
879#[derive(Debug, Clone, Copy)]
880enum BandFormat {
881 Lerc1,
882 Lerc2,
883}
884
885#[cfg(feature = "rayon")]
886#[derive(Debug)]
887struct ParallelBand<'a> {
888 blob: &'a [u8],
889 format: BandFormat,
890 inherited_mask: Option<std::sync::Arc<[u8]>>,
891}
892
893#[cfg(feature = "rayon")]
894fn scan_parallel_bands<'a>(
895 blob: &'a [u8],
896 initial_mask: Option<&[u8]>,
897) -> Result<Vec<ParallelBand<'a>>> {
898 use std::sync::Arc;
899
900 let mut offset = 0usize;
901 let mut bands = Vec::new();
902 let mut lerc1_mask = initial_mask.map(Arc::<[u8]>::from);
903 let mut lerc2_mask = initial_mask.map(Arc::<[u8]>::from);
904
905 while offset < blob.len() {
906 let slice = &blob[offset..];
907 let format = if lerc1::is_lerc1(slice) {
908 BandFormat::Lerc1
909 } else if lerc2::is_lerc2(slice) {
910 BandFormat::Lerc2
911 } else {
912 return Err(Error::InvalidMagic);
913 };
914 let inherited_mask = match format {
915 BandFormat::Lerc1 => lerc1_mask.clone(),
916 BandFormat::Lerc2 => lerc2_mask.clone(),
917 };
918 let (info, decoded_mask) =
919 inspect_first_mask_with_info(slice, lerc1_mask.as_deref(), lerc2_mask.as_deref())?;
920 let next = checked_next_offset(offset, info.blob_size, blob.len())?;
921
922 bands.push(ParallelBand {
923 blob: &blob[offset..next],
924 format,
925 inherited_mask,
926 });
927
928 match format {
929 BandFormat::Lerc1 => {
930 lerc1_mask = decoded_mask.map(Arc::from);
931 lerc2_mask = None;
932 }
933 BandFormat::Lerc2 => {
934 lerc2_mask = decoded_mask.map(Arc::from);
935 lerc1_mask = None;
936 }
937 }
938 offset = next;
939 }
940
941 Ok(bands)
942}
943
944#[cfg(feature = "rayon")]
945fn decode_parallel_band<T: Sample>(
946 band: &ParallelBand<'_>,
947 out: &mut [T],
948 depth: usize,
949) -> Result<BlobInfo> {
950 let mut writer = PixelDataWriter::new(out, depth);
951 let (info, _) = match band.format {
952 BandFormat::Lerc1 => {
953 lerc1::decode_into(band.blob, band.inherited_mask.as_deref(), &mut writer)?
954 }
955 BandFormat::Lerc2 => {
956 lerc2::decode_into(band.blob, band.inherited_mask.as_deref(), &mut writer)?
957 }
958 };
959 Ok(info)
960}
961
962#[cfg(feature = "rayon")]
963fn decode_band_set_into_parallel<T: Sample + BandElement>(
964 blob: &[u8],
965 layout: BandLayout,
966 initial_mask: Option<&[u8]>,
967 band_info: &BandSetInfo,
968 out: &mut [T],
969) -> Result<BandSetInfo> {
970 let expected_len = band_info.value_count()?;
971 if out.len() != expected_len {
972 return Err(Error::InvalidArgument(
973 "output slice length does not match decoded band set length",
974 ));
975 }
976
977 let bands = scan_parallel_bands(blob, initial_mask)?;
978 let band_count = band_info.band_count();
979 if bands.len() != band_count {
980 return Err(Error::Internal(
981 "parallel band scan disagrees with decoded band metadata",
982 ));
983 }
984 let pixel_count = band_info.first().pixel_count()?;
985 let depth = band_info.depth() as usize;
986 let band_len = band_info.first().sample_count()?;
987
988 let infos = match layout {
989 BandLayout::Bsq => out
990 .par_chunks_mut(band_len)
991 .zip(bands.par_iter())
992 .map(|(band_out, band)| decode_parallel_band(band, band_out, depth))
993 .collect::<Result<Vec<_>>>()?,
994 BandLayout::Interleaved => {
995 let decoded = bands
996 .par_iter()
997 .map(|band| {
998 let mut values = default_vec(band_len, "parallel decoded band")?;
999 let info = decode_parallel_band(band, &mut values, depth)?;
1000 Ok((info, values))
1001 })
1002 .collect::<Result<Vec<_>>>()?;
1003 let mut infos = Vec::with_capacity(band_count);
1004 for (band_index, (info, values)) in decoded.into_iter().enumerate() {
1005 copy_band_values_into_slice(
1006 out,
1007 &values,
1008 pixel_count,
1009 depth,
1010 band_index,
1011 band_count,
1012 layout,
1013 )?;
1014 infos.push(info);
1015 }
1016 infos
1017 }
1018 };
1019
1020 BandSetInfo::new(infos)
1021}
1022
1023#[cfg(feature = "ndarray")]
1024fn decode_band_set_to_f64_info(
1025 blob: &[u8],
1026 layout: BandLayout,
1027 initial_mask: Option<&[u8]>,
1028) -> Result<(BandSetInfo, Vec<f64>)> {
1029 let band_info = scan_band_infos(blob, initial_mask)?;
1030 decode_band_set_owned_direct_impl::<f64>(blob, layout, band_info, initial_mask)
1031}
1032
1033#[cfg(feature = "ndarray")]
1034fn inspect_band_masks(
1035 blob: &[u8],
1036 initial_mask: Option<&[u8]>,
1037) -> Result<(BandSetInfo, Vec<Option<Vec<u8>>>)> {
1038 let mut offset = 0usize;
1039 let mut infos = Vec::new();
1040 let mut band_masks = Vec::new();
1041 let mut lerc1_mask = initial_mask.map(<[u8]>::to_vec);
1042 let mut lerc2_mask = initial_mask.map(<[u8]>::to_vec);
1043
1044 while offset < blob.len() {
1045 let slice = &blob[offset..];
1046 let is_lerc1 = lerc1::is_lerc1(slice);
1047 let (info, mask) =
1048 inspect_first_mask_with_info(slice, lerc1_mask.as_deref(), lerc2_mask.as_deref())?;
1049
1050 if is_lerc1 {
1051 lerc1_mask = mask.clone();
1052 lerc2_mask = None;
1053 } else {
1054 lerc2_mask = mask.clone();
1055 lerc1_mask = None;
1056 }
1057
1058 offset = checked_next_offset(offset, info.blob_size, blob.len())?;
1059 infos.push(info);
1060 band_masks.push(mask);
1061 }
1062
1063 Ok((BandSetInfo::new(infos)?, band_masks))
1064}
1065
1066fn scan_band_infos(blob: &[u8], initial_mask: Option<&[u8]>) -> Result<BandSetInfo> {
1067 let mut offset = 0usize;
1068 let mut infos = Vec::new();
1069 let mut lerc1_mask = initial_mask.map(<[u8]>::to_vec);
1070 let mut lerc2_mask = initial_mask.map(<[u8]>::to_vec);
1071
1072 while offset < blob.len() {
1073 let slice = &blob[offset..];
1074 let info = if lerc1::is_lerc1(slice) {
1075 let parsed = lerc1::parse(slice, lerc1_mask.as_deref())?;
1076 let info = parsed.info;
1077 lerc1_mask = parsed.mask;
1078 lerc2_mask = None;
1079 info
1080 } else if lerc2::is_lerc2(slice) {
1081 let (info, mask) = lerc2::inspect_with_mask(slice, lerc2_mask.as_deref())?;
1082 lerc2_mask = mask;
1083 lerc1_mask = None;
1084 info
1085 } else {
1086 return Err(Error::InvalidMagic);
1087 };
1088
1089 offset = checked_next_offset(offset, info.blob_size, blob.len())?;
1090 infos.push(info);
1091 }
1092
1093 BandSetInfo::new(infos)
1094}
1095
1096fn read_required_stream_blob<R: std::io::Read + ?Sized>(
1097 reader: &mut R,
1098 lerc1_shared_mask: Option<&[u8]>,
1099) -> Result<Vec<u8>> {
1100 stream::read_next_blob(reader, lerc1_shared_mask)?.ok_or(Error::Truncated {
1101 offset: 0,
1102 needed: 10,
1103 available: 0,
1104 })
1105}
1106
1107fn decode_band_set_from_reader_impl<R: std::io::Read + ?Sized>(
1108 reader: &mut R,
1109 initial_mask: Option<&[u8]>,
1110) -> Result<DecodedBandSet> {
1111 let mut bands = Vec::new();
1112 let mut infos = Vec::new();
1113 let mut band_masks = Vec::new();
1114 let mut lerc1_mask = initial_mask.map(<[u8]>::to_vec);
1115 let mut lerc2_mask = initial_mask.map(<[u8]>::to_vec);
1116
1117 while let Some(blob) = stream::read_next_blob(reader, lerc1_mask.as_deref())? {
1118 let is_lerc1 = lerc1::is_lerc1(&blob);
1119 let decoded = decode_first_with_masks(&blob, lerc1_mask.as_deref(), lerc2_mask.as_deref())?;
1120
1121 if is_lerc1 {
1122 lerc1_mask = decoded.mask.clone();
1123 lerc2_mask = None;
1124 } else {
1125 lerc2_mask = decoded.mask.clone();
1126 lerc1_mask = None;
1127 }
1128 infos.push(decoded.info);
1129 bands.push(decoded.pixels);
1130 band_masks.push(decoded.mask);
1131 }
1132
1133 DecodedBandSet::new(BandSetInfo::new(infos)?, bands, band_masks)
1134}
1135
1136fn ensure_single_blob_consumed(
1137 blob_len: usize,
1138 decoded_len: usize,
1139 strict_api: &str,
1140 permissive_api: &str,
1141) -> Result<()> {
1142 if blob_len == decoded_len {
1143 return Ok(());
1144 }
1145 Err(Error::invalid_blob(format!(
1146 "{strict_api} only accepts a single LERC blob; found {} trailing bytes, use {permissive_api} for first-blob decoding or decode_band_set for concatenated rasters",
1147 blob_len - decoded_len
1148 )))
1149}
1150
1151fn checked_next_offset(offset: usize, next_len: usize, total_len: usize) -> Result<usize> {
1152 let next = offset
1153 .checked_add(next_len)
1154 .ok_or(Error::SizeOverflow("concatenated band offset"))?;
1155 if next <= offset || next > total_len {
1156 return Err(Error::invalid_blob("invalid concatenated band blob size"));
1157 }
1158 Ok(next)
1159}
1160
1161#[cfg(feature = "ndarray")]
1162fn mask_to_ndarray(info: &BlobInfo, mask: Option<Vec<u8>>) -> Result<Option<ArrayD<u8>>> {
1163 let shape = info.mask_ndarray_shape();
1164 mask.map(|mask| {
1165 ArrayD::from_shape_vec(ndarray::IxDyn(&shape), mask).map_err(|e| {
1166 Error::invalid_blob(format!("failed to build ndarray from decoded mask: {e}"))
1167 })
1168 })
1169 .transpose()
1170}
1171
1172fn decode_band_set_owned_direct_impl<T: Sample + BandElement + Copy + Default>(
1173 blob: &[u8],
1174 layout: BandLayout,
1175 band_info: BandSetInfo,
1176 initial_mask: Option<&[u8]>,
1177) -> Result<(BandSetInfo, Vec<T>)> {
1178 let mut values = default_vec(band_info.value_count()?, "decoded band set")?;
1179 let decoded_info =
1180 decode_band_set_into_impl_with_info(blob, layout, initial_mask, &band_info, &mut values)?;
1181 Ok((decoded_info, values))
1182}
1183
1184fn cast_slice_mut<T, U>(slice: &mut [T]) -> &mut [U] {
1185 debug_assert_eq!(std::mem::size_of::<T>(), std::mem::size_of::<U>());
1186 debug_assert_eq!(std::mem::align_of::<T>(), std::mem::align_of::<U>());
1187 unsafe { &mut *(slice as *mut [T] as *mut [U]) }
1190}
1191
1192fn cast_vec<T, U>(values: Vec<U>) -> Vec<T> {
1193 debug_assert_eq!(std::mem::size_of::<T>(), std::mem::size_of::<U>());
1194 debug_assert_eq!(std::mem::align_of::<T>(), std::mem::align_of::<U>());
1195 let len = values.len();
1196 let cap = values.capacity();
1197 let ptr = values.as_ptr() as *mut T;
1198 std::mem::forget(values);
1199 unsafe { Vec::from_raw_parts(ptr, len, cap) }
1203}