Skip to main content

lerc_reader/
lib.rs

1//! Pure-Rust LERC decoder.
2//!
3//! The public API distinguishes strict single-blob entry points from
4//! concatenated-band helpers:
5//!
6//! - inspect a single blob with [`get_blob_info`]
7//! - inspect only the first blob with [`inspect_first`]
8//! - pass an explicit shared or external mask with the `*_with_mask` single-blob variants
9//! - count concatenated blobs with [`get_band_count`]
10//! - decode a single blob with [`decode`]
11//! - decode only the first blob with [`decode_first`]
12//! - decode concatenated band sets with [`decode_band_set`]
13//! - seed a first-band external mask with the band-set `*_with_mask` variants
14//! - decode promoted `f64` buffers with [`decode_to_f64`]
15//! - decode only the first promoted `f64` blob with [`decode_first_to_f64`]
16//! - decode directly into `ndarray::ArrayD` with [`decode_ndarray`]
17
18mod allocation;
19mod bitstuff;
20mod huffman;
21mod io;
22mod lerc1;
23mod lerc2;
24mod pixel;
25mod types;
26
27#[cfg(test)]
28mod tests;
29
30use lerc_band_materialize::{BandLayout as MaterializeLayout, BandMaterializer, BandSink};
31use lerc_core::{BandLayout, BandSetInfo, BlobInfo, Error, Result};
32use ndarray::ArrayD;
33
34use crate::pixel::Sample;
35pub use crate::types::{
36    into_band_mask_ndarray, BandElement, BandElementKind, Decoded, DecodedBandSet, DecodedF64,
37    NdArrayElement,
38};
39
40macro_rules! dispatch_band_element {
41    ($target:ty, |$concrete:ident| $body:block) => {
42        match <$target as BandElement>::KIND {
43            BandElementKind::I8 => {
44                type $concrete = i8;
45                $body
46            }
47            BandElementKind::U8 => {
48                type $concrete = u8;
49                $body
50            }
51            BandElementKind::I16 => {
52                type $concrete = i16;
53                $body
54            }
55            BandElementKind::U16 => {
56                type $concrete = u16;
57                $body
58            }
59            BandElementKind::I32 => {
60                type $concrete = i32;
61                $body
62            }
63            BandElementKind::U32 => {
64                type $concrete = u32;
65                $body
66            }
67            BandElementKind::F32 => {
68                type $concrete = f32;
69                $body
70            }
71            BandElementKind::F64 => {
72                type $concrete = f64;
73                $body
74            }
75        }
76    };
77}
78
79pub fn inspect_first(blob: &[u8]) -> Result<BlobInfo> {
80    if lerc1::is_lerc1(blob) {
81        return lerc1::inspect(blob, None);
82    }
83    if lerc2::is_lerc2(blob) {
84        return lerc2::inspect(blob, None);
85    }
86    Err(Error::InvalidMagic)
87}
88
89pub fn inspect_first_with_mask(blob: &[u8], mask: &[u8]) -> Result<BlobInfo> {
90    let (info, _) = inspect_first_mask_with_info(blob, Some(mask), Some(mask))?;
91    Ok(info)
92}
93
94pub fn get_blob_info(blob: &[u8]) -> Result<BlobInfo> {
95    let info = inspect_first(blob)?;
96    ensure_single_blob_consumed(blob.len(), info.blob_size, "get_blob_info", "inspect_first")?;
97    Ok(info)
98}
99
100pub fn get_blob_info_with_mask(blob: &[u8], mask: &[u8]) -> Result<BlobInfo> {
101    let info = inspect_first_with_mask(blob, mask)?;
102    ensure_single_blob_consumed(
103        blob.len(),
104        info.blob_size,
105        "get_blob_info_with_mask",
106        "inspect_first_with_mask",
107    )?;
108    Ok(info)
109}
110
111pub fn get_band_count(blob: &[u8]) -> Result<usize> {
112    let mut offset = 0usize;
113    let mut count = 0usize;
114    let mut lerc1_mask: Option<Vec<u8>> = None;
115
116    while offset < blob.len() {
117        let slice = &blob[offset..];
118        let next_len = if lerc1::is_lerc1(slice) {
119            let parsed = lerc1::parse(slice, lerc1_mask.as_deref())?;
120            let next_len = parsed.info.blob_size;
121            lerc1_mask = parsed.mask;
122            next_len
123        } else if lerc2::is_lerc2(slice) {
124            let (parsed, _) = lerc2::parse(slice)?;
125            parsed.info.blob_size
126        } else {
127            return Err(Error::InvalidMagic);
128        };
129
130        offset = checked_next_offset(offset, next_len, blob.len())?;
131        count += 1;
132    }
133
134    Ok(count)
135}
136
137pub fn decode_first(blob: &[u8]) -> Result<Decoded> {
138    decode_first_with_masks(blob, None, None)
139}
140
141pub fn decode_first_with_mask(blob: &[u8], mask: &[u8]) -> Result<Decoded> {
142    decode_first_with_masks(blob, Some(mask), Some(mask))
143}
144
145pub fn decode(blob: &[u8]) -> Result<Decoded> {
146    let decoded = decode_first(blob)?;
147    ensure_single_blob_consumed(blob.len(), decoded.info.blob_size, "decode", "decode_first")?;
148    Ok(decoded)
149}
150
151pub fn decode_with_mask(blob: &[u8], mask: &[u8]) -> Result<Decoded> {
152    let decoded = decode_first_with_mask(blob, mask)?;
153    ensure_single_blob_consumed(
154        blob.len(),
155        decoded.info.blob_size,
156        "decode_with_mask",
157        "decode_first_with_mask",
158    )?;
159    Ok(decoded)
160}
161
162pub fn decode_band_set(blob: &[u8]) -> Result<DecodedBandSet> {
163    decode_band_set_with_lerc2_mask(blob, None)
164}
165
166pub fn decode_band_set_with_mask(blob: &[u8], mask: &[u8]) -> Result<DecodedBandSet> {
167    decode_band_set_with_lerc2_mask(blob, Some(mask))
168}
169
170pub fn decode_band_set_vec<T: BandElement>(
171    blob: &[u8],
172    layout: BandLayout,
173) -> Result<(BandSetInfo, Vec<T>)> {
174    decode_band_set_owned(blob, layout, None)
175}
176
177pub fn decode_band_set_vec_with_mask<T: BandElement>(
178    blob: &[u8],
179    mask: &[u8],
180    layout: BandLayout,
181) -> Result<(BandSetInfo, Vec<T>)> {
182    decode_band_set_owned(blob, layout, Some(mask))
183}
184
185pub fn decode_band_set_into<T: BandElement>(
186    blob: &[u8],
187    layout: BandLayout,
188    out: &mut [T],
189) -> Result<BandSetInfo> {
190    decode_band_set_into_direct(blob, layout, None, out)
191}
192
193pub fn decode_band_set_into_with_mask<T: BandElement>(
194    blob: &[u8],
195    mask: &[u8],
196    layout: BandLayout,
197    out: &mut [T],
198) -> Result<BandSetInfo> {
199    decode_band_set_into_direct(blob, layout, Some(mask), out)
200}
201
202pub fn decode_band_set_ndarray<T: BandElement>(blob: &[u8]) -> Result<ArrayD<T>> {
203    decode_band_set_ndarray_with_layout(blob, BandLayout::Interleaved)
204}
205
206pub fn decode_band_set_ndarray_with_mask<T: BandElement>(
207    blob: &[u8],
208    mask: &[u8],
209) -> Result<ArrayD<T>> {
210    decode_band_set_ndarray_with_layout_and_mask(blob, BandLayout::Interleaved, mask)
211}
212
213pub fn decode_band_set_ndarray_with_layout<T: BandElement>(
214    blob: &[u8],
215    layout: BandLayout,
216) -> Result<ArrayD<T>> {
217    decode_band_set_ndarray_with_layout_impl(blob, layout, None)
218}
219
220pub fn decode_band_set_ndarray_with_layout_and_mask<T: BandElement>(
221    blob: &[u8],
222    layout: BandLayout,
223    mask: &[u8],
224) -> Result<ArrayD<T>> {
225    decode_band_set_ndarray_with_layout_impl(blob, layout, Some(mask))
226}
227
228pub fn decode_band_set_ndarray_f64(blob: &[u8]) -> Result<ArrayD<f64>> {
229    decode_band_set_ndarray_f64_with_optional_mask(blob, None)
230}
231
232pub fn decode_band_set_ndarray_f64_with_mask(blob: &[u8], mask: &[u8]) -> Result<ArrayD<f64>> {
233    decode_band_set_ndarray_f64_with_optional_mask(blob, Some(mask))
234}
235
236pub fn decode_band_mask_ndarray(blob: &[u8]) -> Result<Option<ArrayD<u8>>> {
237    let (info, band_masks) = inspect_band_masks(blob, None)?;
238    into_band_mask_ndarray(info, band_masks)
239}
240
241pub fn decode_band_mask_ndarray_with_mask(blob: &[u8], mask: &[u8]) -> Result<Option<ArrayD<u8>>> {
242    let (info, band_masks) = inspect_band_masks(blob, Some(mask))?;
243    into_band_mask_ndarray(info, band_masks)
244}
245
246fn decode_band_set_with_lerc2_mask(
247    blob: &[u8],
248    initial_lerc2_mask: Option<&[u8]>,
249) -> Result<DecodedBandSet> {
250    let mut offset = 0usize;
251    let mut bands = Vec::new();
252    let mut infos = Vec::new();
253    let mut band_masks = Vec::new();
254    let mut lerc1_mask: Option<Vec<u8>> = None;
255    let mut lerc2_mask = initial_lerc2_mask.map(|mask| mask.to_vec());
256
257    while offset < blob.len() {
258        let decoded = decode_first_with_masks(
259            &blob[offset..],
260            lerc1_mask.as_deref(),
261            lerc2_mask.as_deref(),
262        )?;
263
264        if lerc1::is_lerc1(&blob[offset..]) {
265            lerc1_mask = decoded.mask.clone();
266            lerc2_mask = None;
267        } else {
268            lerc2_mask = decoded.mask.clone();
269            lerc1_mask = None;
270        }
271
272        offset = checked_next_offset(offset, decoded.info.blob_size, blob.len())?;
273        infos.push(decoded.info);
274        bands.push(decoded.pixels);
275        band_masks.push(decoded.mask);
276    }
277
278    Ok(DecodedBandSet {
279        info: BandSetInfo::new(infos)?,
280        bands,
281        band_masks,
282    })
283}
284
285fn decode_band_set_ndarray_with_layout_impl<T: BandElement>(
286    blob: &[u8],
287    layout: BandLayout,
288    initial_lerc2_mask: Option<&[u8]>,
289) -> Result<ArrayD<T>> {
290    let (info, values) = decode_band_set_owned(blob, layout, initial_lerc2_mask)?;
291    let shape = info.ndarray_shape_for_layout(layout);
292    ArrayD::from_shape_vec(ndarray::IxDyn(&shape), values).map_err(|e| {
293        Error::InvalidBlob(format!(
294            "failed to build ndarray from decoded band set: {e}"
295        ))
296    })
297}
298
299fn decode_band_set_ndarray_f64_with_optional_mask(
300    blob: &[u8],
301    initial_lerc2_mask: Option<&[u8]>,
302) -> Result<ArrayD<f64>> {
303    let band_info = decode_band_set_to_f64_info(blob, BandLayout::Interleaved, initial_lerc2_mask)?;
304    let shape = band_info
305        .0
306        .ndarray_shape_for_layout(BandLayout::Interleaved);
307    ArrayD::from_shape_vec(ndarray::IxDyn(&shape), band_info.1).map_err(|e| {
308        Error::InvalidBlob(format!(
309            "failed to build ndarray from decoded band set: {e}"
310        ))
311    })
312}
313
314pub fn decode_to_f64(blob: &[u8]) -> Result<DecodedF64> {
315    let decoded = decode_first_to_f64(blob)?;
316    ensure_single_blob_consumed(
317        blob.len(),
318        decoded.info.blob_size,
319        "decode_to_f64",
320        "decode_first_to_f64",
321    )?;
322    Ok(decoded)
323}
324
325pub fn decode_to_f64_with_mask(blob: &[u8], mask: &[u8]) -> Result<DecodedF64> {
326    let decoded = decode_first_to_f64_with_mask(blob, mask)?;
327    ensure_single_blob_consumed(
328        blob.len(),
329        decoded.info.blob_size,
330        "decode_to_f64_with_mask",
331        "decode_first_to_f64_with_mask",
332    )?;
333    Ok(decoded)
334}
335
336pub fn decode_first_to_f64(blob: &[u8]) -> Result<DecodedF64> {
337    decode_first_f64(blob)
338}
339
340pub fn decode_first_to_f64_with_mask(blob: &[u8], mask: &[u8]) -> Result<DecodedF64> {
341    decode_first_f64_with_masks(blob, Some(mask), Some(mask))
342}
343
344pub fn decode_ndarray<T: NdArrayElement>(blob: &[u8]) -> Result<ArrayD<T>> {
345    decode(blob)?.into_ndarray()
346}
347
348pub fn decode_ndarray_with_mask<T: NdArrayElement>(blob: &[u8], mask: &[u8]) -> Result<ArrayD<T>> {
349    decode_with_mask(blob, mask)?.into_ndarray()
350}
351
352pub fn decode_ndarray_f64(blob: &[u8]) -> Result<ArrayD<f64>> {
353    decode_to_f64(blob)?.into_ndarray()
354}
355
356pub fn decode_ndarray_f64_with_mask(blob: &[u8], mask: &[u8]) -> Result<ArrayD<f64>> {
357    decode_to_f64_with_mask(blob, mask)?.into_ndarray()
358}
359
360pub fn decode_mask_ndarray(blob: &[u8]) -> Result<Option<ArrayD<u8>>> {
361    let (info, mask) = inspect_first_mask_with_info(blob, None, None)?;
362    ensure_single_blob_consumed(
363        blob.len(),
364        info.blob_size,
365        "decode_mask_ndarray",
366        "inspect_first",
367    )?;
368    mask_to_ndarray(&info, mask)
369}
370
371pub fn decode_mask_ndarray_with_mask(blob: &[u8], mask: &[u8]) -> Result<Option<ArrayD<u8>>> {
372    let (info, decoded_mask) = inspect_first_mask_with_info(blob, Some(mask), Some(mask))?;
373    ensure_single_blob_consumed(
374        blob.len(),
375        info.blob_size,
376        "decode_mask_ndarray_with_mask",
377        "inspect_first_with_mask",
378    )?;
379    mask_to_ndarray(&info, decoded_mask)
380}
381
382fn decode_first_with_masks(
383    blob: &[u8],
384    lerc1_shared_mask: Option<&[u8]>,
385    lerc2_shared_mask: Option<&[u8]>,
386) -> Result<Decoded> {
387    if lerc1::is_lerc1(blob) {
388        return lerc1::decode(blob, lerc1_shared_mask);
389    }
390    if lerc2::is_lerc2(blob) {
391        return lerc2::decode(blob, lerc2_shared_mask);
392    }
393    Err(Error::InvalidMagic)
394}
395
396fn inspect_first_mask_with_info(
397    blob: &[u8],
398    lerc1_shared_mask: Option<&[u8]>,
399    lerc2_shared_mask: Option<&[u8]>,
400) -> Result<(BlobInfo, Option<Vec<u8>>)> {
401    if lerc1::is_lerc1(blob) {
402        return lerc1::inspect_mask(blob, lerc1_shared_mask);
403    }
404    if lerc2::is_lerc2(blob) {
405        return lerc2::inspect_with_mask(blob, lerc2_shared_mask);
406    }
407    Err(Error::InvalidMagic)
408}
409
410fn decode_first_f64(blob: &[u8]) -> Result<DecodedF64> {
411    decode_first_f64_with_masks(blob, None, None)
412}
413
414fn decode_first_f64_with_masks(
415    blob: &[u8],
416    lerc1_shared_mask: Option<&[u8]>,
417    lerc2_shared_mask: Option<&[u8]>,
418) -> Result<DecodedF64> {
419    if lerc1::is_lerc1(blob) {
420        return lerc1::decode_f64(blob, lerc1_shared_mask);
421    }
422    if lerc2::is_lerc2(blob) {
423        return lerc2::decode_f64(blob, lerc2_shared_mask);
424    }
425    Err(Error::InvalidMagic)
426}
427
428fn decode_band_set_owned<T: BandElement>(
429    blob: &[u8],
430    layout: BandLayout,
431    initial_lerc2_mask: Option<&[u8]>,
432) -> Result<(BandSetInfo, Vec<T>)> {
433    let band_info = scan_band_infos(blob)?;
434    decode_band_set_owned_direct(blob, layout, band_info, initial_lerc2_mask)
435}
436
437fn decode_band_set_into_direct<T: BandElement>(
438    blob: &[u8],
439    layout: BandLayout,
440    initial_lerc2_mask: Option<&[u8]>,
441    out: &mut [T],
442) -> Result<BandSetInfo> {
443    dispatch_band_element!(T, |Concrete| {
444        decode_band_set_into_impl::<Concrete>(
445            blob,
446            layout,
447            initial_lerc2_mask,
448            cast_slice_mut::<T, Concrete>(out),
449        )
450    })
451}
452
453fn decode_band_set_owned_direct<T: BandElement>(
454    blob: &[u8],
455    layout: BandLayout,
456    band_info: BandSetInfo,
457    initial_lerc2_mask: Option<&[u8]>,
458) -> Result<(BandSetInfo, Vec<T>)> {
459    dispatch_band_element!(T, |Concrete| {
460        decode_band_set_owned_direct_impl::<Concrete>(blob, layout, band_info, initial_lerc2_mask)
461            .map(|(info, values)| (info, cast_vec::<T, Concrete>(values)))
462    })
463}
464
465fn decode_band_set_into_impl<T: Sample + NdArrayElement>(
466    blob: &[u8],
467    layout: BandLayout,
468    initial_lerc2_mask: Option<&[u8]>,
469    out: &mut [T],
470) -> Result<BandSetInfo> {
471    let band_info = scan_band_infos(blob)?;
472    decode_band_set_into_impl_with_info(blob, layout, initial_lerc2_mask, &band_info, out)?;
473    Ok(band_info)
474}
475
476fn decode_band_set_into_impl_with_info<T: Sample + NdArrayElement>(
477    blob: &[u8],
478    layout: BandLayout,
479    initial_lerc2_mask: Option<&[u8]>,
480    band_info: &BandSetInfo,
481    out: &mut [T],
482) -> Result<()> {
483    let band_count = band_info.band_count();
484    let expected_len = band_info.value_count()?;
485    if out.len() != expected_len {
486        return Err(Error::InvalidBlob(format!(
487            "output slice length {} does not match decoded band set length {}",
488            out.len(),
489            expected_len
490        )));
491    }
492
493    let pixel_count = band_info.bands[0].pixel_count()?;
494    let depth = band_info.depth() as usize;
495    let mut offset = 0usize;
496    let mut band_index = 0usize;
497    let mut lerc1_mask: Option<Vec<u8>> = None;
498    let mut lerc2_mask = initial_lerc2_mask.map(|mask| mask.to_vec());
499
500    while offset < blob.len() {
501        let slice = &blob[offset..];
502        let is_lerc1 = lerc1::is_lerc1(slice);
503        let mut sink = BandSink::new(
504            out,
505            pixel_count,
506            depth,
507            band_index,
508            band_count,
509            materialize_layout(layout),
510        );
511        let (info, mask) = if is_lerc1 {
512            lerc1::decode_into(slice, lerc1_mask.as_deref(), &mut sink)?
513        } else if lerc2::is_lerc2(slice) {
514            lerc2::decode_into(slice, lerc2_mask.as_deref(), &mut sink)?
515        } else {
516            return Err(Error::InvalidMagic);
517        };
518
519        if is_lerc1 {
520            lerc1_mask = mask;
521            lerc2_mask = None;
522        } else {
523            lerc2_mask = mask;
524            lerc1_mask = None;
525        }
526
527        offset = checked_next_offset(offset, info.blob_size, blob.len())?;
528        band_index += 1;
529    }
530
531    Ok(())
532}
533
534fn decode_band_set_to_f64_info(
535    blob: &[u8],
536    layout: BandLayout,
537    initial_lerc2_mask: Option<&[u8]>,
538) -> Result<(BandSetInfo, Vec<f64>)> {
539    let band_info = scan_band_infos(blob)?;
540    decode_band_set_owned_direct_impl::<f64>(blob, layout, band_info, initial_lerc2_mask)
541}
542
543fn inspect_band_masks(
544    blob: &[u8],
545    initial_lerc2_mask: Option<&[u8]>,
546) -> Result<(BandSetInfo, Vec<Option<Vec<u8>>>)> {
547    let mut offset = 0usize;
548    let mut infos = Vec::new();
549    let mut band_masks = Vec::new();
550    let mut lerc1_mask: Option<Vec<u8>> = None;
551    let mut lerc2_mask = initial_lerc2_mask.map(|mask| mask.to_vec());
552
553    while offset < blob.len() {
554        let slice = &blob[offset..];
555        let is_lerc1 = lerc1::is_lerc1(slice);
556        let (info, mask) =
557            inspect_first_mask_with_info(slice, lerc1_mask.as_deref(), lerc2_mask.as_deref())?;
558
559        if is_lerc1 {
560            lerc1_mask = mask.clone();
561            lerc2_mask = None;
562        } else {
563            lerc2_mask = mask.clone();
564            lerc1_mask = None;
565        }
566
567        offset = checked_next_offset(offset, info.blob_size, blob.len())?;
568        infos.push(info);
569        band_masks.push(mask);
570    }
571
572    Ok((BandSetInfo::new(infos)?, band_masks))
573}
574
575fn scan_band_infos(blob: &[u8]) -> Result<BandSetInfo> {
576    let mut offset = 0usize;
577    let mut infos = Vec::new();
578    let mut lerc1_mask: Option<Vec<u8>> = None;
579
580    while offset < blob.len() {
581        let slice = &blob[offset..];
582        let (info, next_lerc1_mask) = if lerc1::is_lerc1(slice) {
583            let parsed = lerc1::parse(slice, lerc1_mask.as_deref())?;
584            let info = parsed.info;
585            let next_mask = parsed.mask;
586            (info, next_mask)
587        } else if lerc2::is_lerc2(slice) {
588            let (parsed, _) = lerc2::parse(slice)?;
589            (parsed.info, None)
590        } else {
591            return Err(Error::InvalidMagic);
592        };
593
594        offset = checked_next_offset(offset, info.blob_size, blob.len())?;
595        lerc1_mask = next_lerc1_mask;
596        infos.push(info);
597    }
598
599    BandSetInfo::new(infos)
600}
601
602fn ensure_single_blob_consumed(
603    blob_len: usize,
604    decoded_len: usize,
605    strict_api: &str,
606    permissive_api: &str,
607) -> Result<()> {
608    if blob_len == decoded_len {
609        return Ok(());
610    }
611    Err(Error::InvalidBlob(format!(
612        "{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",
613        blob_len - decoded_len
614    )))
615}
616
617fn checked_next_offset(offset: usize, next_len: usize, total_len: usize) -> Result<usize> {
618    let next = offset
619        .checked_add(next_len)
620        .ok_or_else(|| Error::InvalidBlob("band offset overflow".into()))?;
621    if next <= offset || next > total_len {
622        return Err(Error::InvalidBlob(
623            "invalid concatenated band blob size".into(),
624        ));
625    }
626    Ok(next)
627}
628
629fn materialize_layout(layout: BandLayout) -> MaterializeLayout {
630    match layout {
631        BandLayout::Interleaved => MaterializeLayout::Interleaved,
632        BandLayout::Bsq => MaterializeLayout::Bsq,
633    }
634}
635
636fn materialize_error(err: lerc_band_materialize::MaterializeError) -> Error {
637    Error::InvalidBlob(err.to_string())
638}
639
640fn mask_to_ndarray(info: &BlobInfo, mask: Option<Vec<u8>>) -> Result<Option<ArrayD<u8>>> {
641    let shape = info.mask_ndarray_shape();
642    mask.map(|mask| {
643        ArrayD::from_shape_vec(ndarray::IxDyn(&shape), mask).map_err(|e| {
644            Error::InvalidBlob(format!("failed to build ndarray from decoded mask: {e}"))
645        })
646    })
647    .transpose()
648}
649
650fn decode_band_set_owned_direct_impl<T: Sample + NdArrayElement + Copy + Default>(
651    blob: &[u8],
652    layout: BandLayout,
653    band_info: BandSetInfo,
654    initial_lerc2_mask: Option<&[u8]>,
655) -> Result<(BandSetInfo, Vec<T>)> {
656    let expected_len = band_info.value_count()?;
657    if expected_len == 0 {
658        return Ok((band_info, Vec::new()));
659    }
660
661    let pixel_count = band_info.bands[0].pixel_count()?;
662    let depth = band_info.depth() as usize;
663    let band_count = band_info.band_count();
664    let mut materializer =
665        BandMaterializer::new(pixel_count, depth, band_count, materialize_layout(layout))
666            .map_err(materialize_error)?;
667    let mut offset = 0usize;
668    let mut band_index = 0usize;
669    let mut lerc1_mask: Option<Vec<u8>> = None;
670    let mut lerc2_mask = initial_lerc2_mask.map(|mask| mask.to_vec());
671
672    while offset < blob.len() {
673        let slice = &blob[offset..];
674        let is_lerc1 = lerc1::is_lerc1(slice);
675        let (info, mask) = {
676            let mut writer = materializer
677                .band_writer(band_index)
678                .map_err(materialize_error)?;
679            let decoded = if is_lerc1 {
680                lerc1::decode_into(slice, lerc1_mask.as_deref(), &mut writer)?
681            } else if lerc2::is_lerc2(slice) {
682                lerc2::decode_into(slice, lerc2_mask.as_deref(), &mut writer)?
683            } else {
684                return Err(Error::InvalidMagic);
685            };
686            writer.finish().map_err(materialize_error)?;
687            decoded
688        };
689
690        if is_lerc1 {
691            lerc1_mask = mask;
692            lerc2_mask = None;
693        } else {
694            lerc2_mask = mask;
695            lerc1_mask = None;
696        }
697
698        offset = checked_next_offset(offset, info.blob_size, blob.len())?;
699        band_index += 1;
700    }
701
702    Ok((band_info, materializer.finish().map_err(materialize_error)?))
703}
704
705fn cast_slice_mut<T, U>(slice: &mut [T]) -> &mut [U] {
706    unsafe { &mut *(slice as *mut [T] as *mut [U]) }
707}
708
709fn cast_vec<T, U>(values: Vec<U>) -> Vec<T> {
710    let len = values.len();
711    let cap = values.capacity();
712    let ptr = values.as_ptr() as *mut T;
713    std::mem::forget(values);
714    unsafe { Vec::from_raw_parts(ptr, len, cap) }
715}