1mod bitstuff;
17mod huffman;
18mod io;
19mod lerc1;
20mod lerc2;
21mod pixel;
22mod types;
23
24#[cfg(test)]
25mod tests;
26
27use lerc_band_materialize::{BandLayout as MaterializeLayout, BandMaterializer, BandSink};
28use lerc_core::{BandLayout, BandSetInfo, BlobInfo, Error, Result};
29use ndarray::ArrayD;
30
31use crate::pixel::Sample;
32pub use crate::types::{
33 into_band_mask_ndarray, BandElement, BandElementKind, Decoded, DecodedBandSet, DecodedF64,
34 NdArrayElement,
35};
36
37macro_rules! dispatch_band_element {
38 ($target:ty, |$concrete:ident| $body:block) => {
39 match <$target as BandElement>::KIND {
40 BandElementKind::I8 => {
41 type $concrete = i8;
42 $body
43 }
44 BandElementKind::U8 => {
45 type $concrete = u8;
46 $body
47 }
48 BandElementKind::I16 => {
49 type $concrete = i16;
50 $body
51 }
52 BandElementKind::U16 => {
53 type $concrete = u16;
54 $body
55 }
56 BandElementKind::I32 => {
57 type $concrete = i32;
58 $body
59 }
60 BandElementKind::U32 => {
61 type $concrete = u32;
62 $body
63 }
64 BandElementKind::F32 => {
65 type $concrete = f32;
66 $body
67 }
68 BandElementKind::F64 => {
69 type $concrete = f64;
70 $body
71 }
72 }
73 };
74}
75
76pub fn inspect_first(blob: &[u8]) -> Result<BlobInfo> {
77 if lerc1::is_lerc1(blob) {
78 return lerc1::inspect(blob, None);
79 }
80 if lerc2::is_lerc2(blob) {
81 return lerc2::inspect(blob, None);
82 }
83 Err(Error::InvalidMagic)
84}
85
86pub fn get_blob_info(blob: &[u8]) -> Result<BlobInfo> {
87 let info = inspect_first(blob)?;
88 ensure_single_blob_consumed(blob.len(), info.blob_size, "get_blob_info", "inspect_first")?;
89 Ok(info)
90}
91
92pub fn get_band_count(blob: &[u8]) -> Result<usize> {
93 let mut offset = 0usize;
94 let mut count = 0usize;
95 let mut lerc1_mask: Option<Vec<u8>> = None;
96
97 while offset < blob.len() {
98 let slice = &blob[offset..];
99 let next_len = if lerc1::is_lerc1(slice) {
100 let parsed = lerc1::parse(slice, lerc1_mask.as_deref())?;
101 let next_len = parsed.info.blob_size;
102 lerc1_mask = parsed.mask;
103 next_len
104 } else if lerc2::is_lerc2(slice) {
105 let (info, _) = lerc2::parse(slice)?;
106 info.blob_size
107 } else {
108 return Err(Error::InvalidMagic);
109 };
110
111 offset = checked_next_offset(offset, next_len, blob.len())?;
112 count += 1;
113 }
114
115 Ok(count)
116}
117
118pub fn decode_first(blob: &[u8]) -> Result<Decoded> {
119 decode_first_with_masks(blob, None, None)
120}
121
122pub fn decode(blob: &[u8]) -> Result<Decoded> {
123 let decoded = decode_first(blob)?;
124 ensure_single_blob_consumed(blob.len(), decoded.info.blob_size, "decode", "decode_first")?;
125 Ok(decoded)
126}
127
128pub fn decode_band_set(blob: &[u8]) -> Result<DecodedBandSet> {
129 let mut offset = 0usize;
130 let mut bands = Vec::new();
131 let mut infos = Vec::new();
132 let mut band_masks = Vec::new();
133 let mut lerc1_mask: Option<Vec<u8>> = None;
134 let mut lerc2_mask: Option<Vec<u8>> = None;
135
136 while offset < blob.len() {
137 let decoded = decode_first_with_masks(
138 &blob[offset..],
139 lerc1_mask.as_deref(),
140 lerc2_mask.as_deref(),
141 )?;
142
143 if lerc1::is_lerc1(&blob[offset..]) {
144 lerc1_mask = decoded.mask.clone();
145 lerc2_mask = None;
146 } else {
147 lerc2_mask = decoded.mask.clone();
148 lerc1_mask = None;
149 }
150
151 offset = checked_next_offset(offset, decoded.info.blob_size, blob.len())?;
152 infos.push(decoded.info);
153 bands.push(decoded.pixels);
154 band_masks.push(decoded.mask);
155 }
156
157 Ok(DecodedBandSet {
158 info: BandSetInfo::new(infos)?,
159 bands,
160 band_masks,
161 })
162}
163
164pub fn decode_band_set_vec<T: BandElement>(
165 blob: &[u8],
166 layout: BandLayout,
167) -> Result<(BandSetInfo, Vec<T>)> {
168 decode_band_set_owned(blob, layout)
169}
170
171pub fn decode_band_set_into<T: BandElement>(
172 blob: &[u8],
173 layout: BandLayout,
174 out: &mut [T],
175) -> Result<BandSetInfo> {
176 decode_band_set_into_direct(blob, layout, out)
177}
178
179pub fn decode_band_set_ndarray<T: BandElement>(blob: &[u8]) -> Result<ArrayD<T>> {
180 decode_band_set_ndarray_with_layout(blob, BandLayout::Interleaved)
181}
182
183pub fn decode_band_set_ndarray_with_layout<T: BandElement>(
184 blob: &[u8],
185 layout: BandLayout,
186) -> Result<ArrayD<T>> {
187 let (info, values) = decode_band_set_owned(blob, layout)?;
188 let shape = info.ndarray_shape_for_layout(layout);
189 ArrayD::from_shape_vec(ndarray::IxDyn(&shape), values).map_err(|e| {
190 Error::InvalidBlob(format!(
191 "failed to build ndarray from decoded band set: {e}"
192 ))
193 })
194}
195
196pub fn decode_band_set_ndarray_f64(blob: &[u8]) -> Result<ArrayD<f64>> {
197 let band_info = decode_band_set_to_f64_info(blob, BandLayout::Interleaved)?;
198 let shape = band_info
199 .0
200 .ndarray_shape_for_layout(BandLayout::Interleaved);
201 ArrayD::from_shape_vec(ndarray::IxDyn(&shape), band_info.1).map_err(|e| {
202 Error::InvalidBlob(format!(
203 "failed to build ndarray from decoded band set: {e}"
204 ))
205 })
206}
207
208pub fn decode_band_mask_ndarray(blob: &[u8]) -> Result<Option<ArrayD<u8>>> {
209 let (info, band_masks) = inspect_band_masks(blob)?;
210 into_band_mask_ndarray(info, band_masks)
211}
212
213pub fn decode_to_f64(blob: &[u8]) -> Result<DecodedF64> {
214 let decoded = decode_first_to_f64(blob)?;
215 ensure_single_blob_consumed(
216 blob.len(),
217 decoded.info.blob_size,
218 "decode_to_f64",
219 "decode_first_to_f64",
220 )?;
221 Ok(decoded)
222}
223
224pub fn decode_first_to_f64(blob: &[u8]) -> Result<DecodedF64> {
225 decode_first_f64(blob)
226}
227
228pub fn decode_ndarray<T: NdArrayElement>(blob: &[u8]) -> Result<ArrayD<T>> {
229 decode(blob)?.into_ndarray()
230}
231
232pub fn decode_ndarray_f64(blob: &[u8]) -> Result<ArrayD<f64>> {
233 decode_to_f64(blob)?.into_ndarray()
234}
235
236pub fn decode_mask_ndarray(blob: &[u8]) -> Result<Option<ArrayD<u8>>> {
237 let (info, mask) = inspect_first_mask_with_info(blob, None, None)?;
238 ensure_single_blob_consumed(
239 blob.len(),
240 info.blob_size,
241 "decode_mask_ndarray",
242 "inspect_first",
243 )?;
244 mask_to_ndarray(&info, mask)
245}
246
247fn decode_first_with_masks(
248 blob: &[u8],
249 lerc1_shared_mask: Option<&[u8]>,
250 lerc2_shared_mask: Option<&[u8]>,
251) -> Result<Decoded> {
252 if lerc1::is_lerc1(blob) {
253 return lerc1::decode(blob, lerc1_shared_mask);
254 }
255 if lerc2::is_lerc2(blob) {
256 return lerc2::decode(blob, lerc2_shared_mask);
257 }
258 Err(Error::InvalidMagic)
259}
260
261fn inspect_first_mask_with_info(
262 blob: &[u8],
263 lerc1_shared_mask: Option<&[u8]>,
264 lerc2_shared_mask: Option<&[u8]>,
265) -> Result<(BlobInfo, Option<Vec<u8>>)> {
266 if lerc1::is_lerc1(blob) {
267 return lerc1::inspect_mask(blob, lerc1_shared_mask);
268 }
269 if lerc2::is_lerc2(blob) {
270 return lerc2::inspect_with_mask(blob, lerc2_shared_mask);
271 }
272 Err(Error::InvalidMagic)
273}
274
275fn decode_first_f64(blob: &[u8]) -> Result<DecodedF64> {
276 if lerc1::is_lerc1(blob) {
277 return lerc1::decode_f64(blob, None);
278 }
279 if lerc2::is_lerc2(blob) {
280 return lerc2::decode_f64(blob, None);
281 }
282 Err(Error::InvalidMagic)
283}
284
285fn decode_band_set_owned<T: BandElement>(
286 blob: &[u8],
287 layout: BandLayout,
288) -> Result<(BandSetInfo, Vec<T>)> {
289 let band_info = scan_band_infos(blob)?;
290 decode_band_set_owned_direct(blob, layout, band_info)
291}
292
293fn decode_band_set_into_direct<T: BandElement>(
294 blob: &[u8],
295 layout: BandLayout,
296 out: &mut [T],
297) -> Result<BandSetInfo> {
298 dispatch_band_element!(T, |Concrete| {
299 decode_band_set_into_impl::<Concrete>(blob, layout, cast_slice_mut::<T, Concrete>(out))
300 })
301}
302
303fn decode_band_set_owned_direct<T: BandElement>(
304 blob: &[u8],
305 layout: BandLayout,
306 band_info: BandSetInfo,
307) -> Result<(BandSetInfo, Vec<T>)> {
308 dispatch_band_element!(T, |Concrete| {
309 decode_band_set_owned_direct_impl::<Concrete>(blob, layout, band_info)
310 .map(|(info, values)| (info, cast_vec::<T, Concrete>(values)))
311 })
312}
313
314fn decode_band_set_into_impl<T: Sample + NdArrayElement>(
315 blob: &[u8],
316 layout: BandLayout,
317 out: &mut [T],
318) -> Result<BandSetInfo> {
319 let band_info = scan_band_infos(blob)?;
320 decode_band_set_into_impl_with_info(blob, layout, &band_info, out)?;
321 Ok(band_info)
322}
323
324fn decode_band_set_into_impl_with_info<T: Sample + NdArrayElement>(
325 blob: &[u8],
326 layout: BandLayout,
327 band_info: &BandSetInfo,
328 out: &mut [T],
329) -> Result<()> {
330 let band_count = band_info.band_count();
331 let expected_len = band_info.value_count()?;
332 if out.len() != expected_len {
333 return Err(Error::InvalidBlob(format!(
334 "output slice length {} does not match decoded band set length {}",
335 out.len(),
336 expected_len
337 )));
338 }
339
340 let pixel_count = band_info.bands[0].pixel_count()?;
341 let depth = band_info.depth() as usize;
342 let mut offset = 0usize;
343 let mut band_index = 0usize;
344 let mut lerc1_mask: Option<Vec<u8>> = None;
345 let mut lerc2_mask: Option<Vec<u8>> = None;
346
347 while offset < blob.len() {
348 let slice = &blob[offset..];
349 let is_lerc1 = lerc1::is_lerc1(slice);
350 let mut sink = BandSink::new(
351 out,
352 pixel_count,
353 depth,
354 band_index,
355 band_count,
356 materialize_layout(layout),
357 );
358 let (info, mask) = if is_lerc1 {
359 lerc1::decode_into(slice, lerc1_mask.as_deref(), &mut sink)?
360 } else if lerc2::is_lerc2(slice) {
361 lerc2::decode_into(slice, lerc2_mask.as_deref(), &mut sink)?
362 } else {
363 return Err(Error::InvalidMagic);
364 };
365
366 if is_lerc1 {
367 lerc1_mask = mask;
368 lerc2_mask = None;
369 } else {
370 lerc2_mask = mask;
371 lerc1_mask = None;
372 }
373
374 offset = checked_next_offset(offset, info.blob_size, blob.len())?;
375 band_index += 1;
376 }
377
378 Ok(())
379}
380
381fn decode_band_set_to_f64_info(blob: &[u8], layout: BandLayout) -> Result<(BandSetInfo, Vec<f64>)> {
382 let band_info = scan_band_infos(blob)?;
383 decode_band_set_owned_direct_impl::<f64>(blob, layout, band_info)
384}
385
386fn inspect_band_masks(blob: &[u8]) -> Result<(BandSetInfo, Vec<Option<Vec<u8>>>)> {
387 let mut offset = 0usize;
388 let mut infos = Vec::new();
389 let mut band_masks = Vec::new();
390 let mut lerc1_mask: Option<Vec<u8>> = None;
391 let mut lerc2_mask: Option<Vec<u8>> = None;
392
393 while offset < blob.len() {
394 let slice = &blob[offset..];
395 let is_lerc1 = lerc1::is_lerc1(slice);
396 let (info, mask) =
397 inspect_first_mask_with_info(slice, lerc1_mask.as_deref(), lerc2_mask.as_deref())?;
398
399 if is_lerc1 {
400 lerc1_mask = mask.clone();
401 lerc2_mask = None;
402 } else {
403 lerc2_mask = mask.clone();
404 lerc1_mask = None;
405 }
406
407 offset = checked_next_offset(offset, info.blob_size, blob.len())?;
408 infos.push(info);
409 band_masks.push(mask);
410 }
411
412 Ok((BandSetInfo::new(infos)?, band_masks))
413}
414
415fn scan_band_infos(blob: &[u8]) -> Result<BandSetInfo> {
416 let mut offset = 0usize;
417 let mut infos = Vec::new();
418 let mut lerc1_mask: Option<Vec<u8>> = None;
419
420 while offset < blob.len() {
421 let slice = &blob[offset..];
422 let (info, next_lerc1_mask) = if lerc1::is_lerc1(slice) {
423 let parsed = lerc1::parse(slice, lerc1_mask.as_deref())?;
424 let info = parsed.info;
425 let next_mask = parsed.mask;
426 (info, next_mask)
427 } else if lerc2::is_lerc2(slice) {
428 let (info, _) = lerc2::parse(slice)?;
429 (info, None)
430 } else {
431 return Err(Error::InvalidMagic);
432 };
433
434 offset = checked_next_offset(offset, info.blob_size, blob.len())?;
435 lerc1_mask = next_lerc1_mask;
436 infos.push(info);
437 }
438
439 BandSetInfo::new(infos)
440}
441
442fn ensure_single_blob_consumed(
443 blob_len: usize,
444 decoded_len: usize,
445 strict_api: &str,
446 permissive_api: &str,
447) -> Result<()> {
448 if blob_len == decoded_len {
449 return Ok(());
450 }
451 Err(Error::InvalidBlob(format!(
452 "{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",
453 blob_len - decoded_len
454 )))
455}
456
457fn checked_next_offset(offset: usize, next_len: usize, total_len: usize) -> Result<usize> {
458 let next = offset
459 .checked_add(next_len)
460 .ok_or_else(|| Error::InvalidBlob("band offset overflow".into()))?;
461 if next <= offset || next > total_len {
462 return Err(Error::InvalidBlob(
463 "invalid concatenated band blob size".into(),
464 ));
465 }
466 Ok(next)
467}
468
469fn materialize_layout(layout: BandLayout) -> MaterializeLayout {
470 match layout {
471 BandLayout::Interleaved => MaterializeLayout::Interleaved,
472 BandLayout::Bsq => MaterializeLayout::Bsq,
473 }
474}
475
476fn materialize_error(err: lerc_band_materialize::MaterializeError) -> Error {
477 Error::InvalidBlob(err.to_string())
478}
479
480fn mask_to_ndarray(info: &BlobInfo, mask: Option<Vec<u8>>) -> Result<Option<ArrayD<u8>>> {
481 let shape = info.mask_ndarray_shape();
482 mask.map(|mask| {
483 ArrayD::from_shape_vec(ndarray::IxDyn(&shape), mask).map_err(|e| {
484 Error::InvalidBlob(format!("failed to build ndarray from decoded mask: {e}"))
485 })
486 })
487 .transpose()
488}
489
490fn decode_band_set_owned_direct_impl<T: Sample + NdArrayElement + Copy + Default>(
491 blob: &[u8],
492 layout: BandLayout,
493 band_info: BandSetInfo,
494) -> Result<(BandSetInfo, Vec<T>)> {
495 let expected_len = band_info.value_count()?;
496 if expected_len == 0 {
497 return Ok((band_info, Vec::new()));
498 }
499
500 let pixel_count = band_info.bands[0].pixel_count()?;
501 let depth = band_info.depth() as usize;
502 let band_count = band_info.band_count();
503 let mut materializer =
504 BandMaterializer::new(pixel_count, depth, band_count, materialize_layout(layout))
505 .map_err(materialize_error)?;
506 let mut offset = 0usize;
507 let mut band_index = 0usize;
508 let mut lerc1_mask: Option<Vec<u8>> = None;
509 let mut lerc2_mask: Option<Vec<u8>> = None;
510
511 while offset < blob.len() {
512 let slice = &blob[offset..];
513 let is_lerc1 = lerc1::is_lerc1(slice);
514 let (info, mask) = {
515 let mut writer = materializer
516 .band_writer(band_index)
517 .map_err(materialize_error)?;
518 let decoded = if is_lerc1 {
519 lerc1::decode_into(slice, lerc1_mask.as_deref(), &mut writer)?
520 } else if lerc2::is_lerc2(slice) {
521 lerc2::decode_into(slice, lerc2_mask.as_deref(), &mut writer)?
522 } else {
523 return Err(Error::InvalidMagic);
524 };
525 writer.finish().map_err(materialize_error)?;
526 decoded
527 };
528
529 if is_lerc1 {
530 lerc1_mask = mask;
531 lerc2_mask = None;
532 } else {
533 lerc2_mask = mask;
534 lerc1_mask = None;
535 }
536
537 offset = checked_next_offset(offset, info.blob_size, blob.len())?;
538 band_index += 1;
539 }
540
541 Ok((band_info, materializer.finish().map_err(materialize_error)?))
542}
543
544fn cast_slice_mut<T, U>(slice: &mut [T]) -> &mut [U] {
545 unsafe { &mut *(slice as *mut [T] as *mut [U]) }
546}
547
548fn cast_vec<T, U>(values: Vec<U>) -> Vec<T> {
549 let len = values.len();
550 let cap = values.capacity();
551 let ptr = values.as_ptr() as *mut T;
552 std::mem::forget(values);
553 unsafe { Vec::from_raw_parts(ptr, len, cap) }
554}