1use std::ffi::c_longlong;
26
27pub const MAX_COMPONENTS: usize = lc_framework_sys::MAX_STAGES;
29
30pub const MAX_BYTES: usize = const {
32 #[allow(clippy::cast_possible_truncation)]
33 if std::mem::size_of::<c_longlong>() <= std::mem::size_of::<usize>() {
34 c_longlong::MAX as usize
35 } else {
36 usize::MAX
37 }
38};
39
40pub fn compress(
52 preprocessors: &[Preprocessor],
53 components: &[Component],
54 input: &[u8],
55) -> Result<Vec<u8>, Error> {
56 let mut preprocessor_ids = Vec::with_capacity(preprocessors.len());
57 let mut preprocessor_params = Vec::new();
58 let mut preprocessor_params_num = Vec::with_capacity(preprocessors.len());
59 for preprocessor in preprocessors {
60 preprocessor_ids.push(preprocessor.as_id());
61 let preprocessor_nparams_sum = preprocessor_params.len();
62 preprocessor.push_params(&mut preprocessor_params);
63 preprocessor_params_num.push(preprocessor_params.len() - preprocessor_nparams_sum);
64 }
65
66 if components.is_empty() {
67 return Err(Error::TooFewComponents);
68 }
69
70 if components.len() > MAX_COMPONENTS {
71 return Err(Error::TooManyComponents);
72 }
73
74 let component_ids = components
75 .iter()
76 .copied()
77 .map(Component::as_id)
78 .collect::<Vec<_>>();
79
80 let input_size: c_longlong = input
81 .len()
82 .try_into()
83 .map_err(|_| Error::ExcessiveInputData)?;
84
85 let mut encoded_ptr = std::ptr::null_mut();
86 let mut encoded_size = 0;
87
88 #[expect(unsafe_code)]
89 let status = unsafe {
91 lc_framework_sys::lc_compress(
92 preprocessor_ids.len(),
93 preprocessor_ids.as_ptr(),
94 preprocessor_params_num.as_ptr(),
95 preprocessor_params.as_ptr(),
96 component_ids.len(),
97 component_ids.as_ptr(),
98 input.as_ptr(),
99 input_size,
100 &raw mut encoded_ptr,
101 &raw mut encoded_size,
102 )
103 };
104
105 if status != 0 {
106 return Err(Error::CompressionFailed);
107 }
108
109 let encoded_len: usize = encoded_size
110 .try_into()
111 .map_err(|_| Error::ExcessiveCompressedData)?;
112
113 #[expect(unsafe_code)]
114 let encoded = unsafe {
116 let mut encoded = Vec::with_capacity(encoded_len);
117 std::ptr::copy_nonoverlapping(encoded_ptr.cast_const(), encoded.as_mut_ptr(), encoded_len);
118 encoded.set_len(encoded_len);
119 encoded
120 };
121
122 #[expect(unsafe_code)]
123 unsafe {
125 lc_framework_sys::lc_free_bytes(encoded_ptr);
126 }
127
128 Ok(encoded)
129}
130
131pub fn decompress(
147 preprocessors: &[Preprocessor],
148 components: &[Component],
149 compressed: &[u8],
150) -> Result<Vec<u8>, Error> {
151 let encoded = compressed;
152
153 let mut preprocessor_ids = Vec::with_capacity(preprocessors.len());
154 let mut preprocessor_params = Vec::new();
155 let mut preprocessor_params_num = Vec::with_capacity(preprocessors.len());
156 for preprocessor in preprocessors {
157 preprocessor_ids.push(preprocessor.as_id());
158 let preprocessor_nparams_sum = preprocessor_params.len();
159 preprocessor.push_params(&mut preprocessor_params);
160 preprocessor_params_num.push(preprocessor_params.len() - preprocessor_nparams_sum);
161 }
162
163 if components.is_empty() {
164 return Err(Error::TooFewComponents);
165 }
166
167 if components.len() > MAX_COMPONENTS {
168 return Err(Error::TooManyComponents);
169 }
170
171 let component_ids = components
172 .iter()
173 .copied()
174 .map(Component::as_id)
175 .collect::<Vec<_>>();
176
177 let encoded_size: c_longlong = encoded
178 .len()
179 .try_into()
180 .map_err(|_| Error::ExcessiveCompressedData)?;
181
182 let mut decoded_ptr = std::ptr::null_mut();
183 let mut decoded_size = 0;
184
185 #[expect(unsafe_code)]
186 let status = unsafe {
188 lc_framework_sys::lc_decompress(
189 preprocessor_ids.len(),
190 preprocessor_ids.as_ptr(),
191 preprocessor_params_num.as_ptr(),
192 preprocessor_params.as_ptr(),
193 component_ids.len(),
194 component_ids.as_ptr(),
195 encoded.as_ptr(),
196 encoded_size,
197 &raw mut decoded_ptr,
198 &raw mut decoded_size,
199 )
200 };
201
202 if status != 0 {
203 return Err(Error::DecompressionFailed);
204 }
205
206 let decoded_len: usize = decoded_size
207 .try_into()
208 .map_err(|_| Error::ExcessiveDecompressedData)?;
209
210 #[expect(unsafe_code)]
211 let decoded = unsafe {
213 let mut decoded = Vec::with_capacity(decoded_len);
214 std::ptr::copy_nonoverlapping(decoded_ptr.cast_const(), decoded.as_mut_ptr(), decoded_len);
215 decoded.set_len(decoded_len);
216 decoded
217 };
218
219 #[expect(unsafe_code)]
220 unsafe {
222 lc_framework_sys::lc_free_bytes(decoded_ptr);
223 }
224
225 Ok(decoded)
226}
227
228#[derive(Debug, thiserror::Error)]
229pub enum Error {
231 #[error("at least one component must be given")]
233 TooFewComponents,
234 #[error("at most {MAX_COMPONENTS} components must be given")]
236 TooManyComponents,
237 #[error("input data must not exceed {MAX_BYTES} bytes")]
239 ExcessiveInputData,
240 #[error("internal compression error")]
242 CompressionFailed,
243 #[error("compressed data must not exceed {MAX_BYTES} bytes")]
245 ExcessiveCompressedData,
246 #[error("internal decompression error")]
248 DecompressionFailed,
249 #[error("decompressed data must not exceed {MAX_BYTES} bytes")]
251 ExcessiveDecompressedData,
252}
253
254#[expect(missing_docs)]
255#[derive(Clone, Debug, PartialEq)]
256pub enum Preprocessor {
258 Noop,
259 Lorenzo1D {
260 dtype: LorenzoDtype,
261 },
262 QuantizeErrorBound {
263 dtype: QuantizeDType,
264 kind: ErrorKind,
265 error_bound: f64,
266 threshold: Option<f64>,
267 decorrelation: Decorrelation,
268 },
269}
270
271impl Preprocessor {
272 const fn as_id(&self) -> lc_framework_sys::LC_CPUpreprocessor {
273 match self {
274 Self::Noop => lc_framework_sys::LC_CPUpreprocessor_NUL_CPUpreprocessor,
275 Self::Lorenzo1D {
276 dtype: LorenzoDtype::I32,
277 } => lc_framework_sys::LC_CPUpreprocessor_LOR1D_i32,
278 Self::QuantizeErrorBound {
279 dtype: QuantizeDType::F32,
280 kind: ErrorKind::Abs,
281 error_bound: _,
282 threshold: _,
283 decorrelation: Decorrelation::Zero,
284 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_ABS_0_f32,
285 Self::QuantizeErrorBound {
286 dtype: QuantizeDType::F32,
287 kind: ErrorKind::Abs,
288 error_bound: _,
289 threshold: _,
290 decorrelation: Decorrelation::Random,
291 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_ABS_R_f32,
292 Self::QuantizeErrorBound {
293 dtype: QuantizeDType::F32,
294 kind: ErrorKind::Noa,
295 error_bound: _,
296 threshold: _,
297 decorrelation: Decorrelation::Zero,
298 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_NOA_0_f32,
299 Self::QuantizeErrorBound {
300 dtype: QuantizeDType::F32,
301 kind: ErrorKind::Noa,
302 error_bound: _,
303 threshold: _,
304 decorrelation: Decorrelation::Random,
305 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_NOA_R_f32,
306 Self::QuantizeErrorBound {
307 dtype: QuantizeDType::F32,
308 kind: ErrorKind::Rel,
309 error_bound: _,
310 threshold: _,
311 decorrelation: Decorrelation::Zero,
312 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_REL_0_f32,
313 Self::QuantizeErrorBound {
314 dtype: QuantizeDType::F32,
315 kind: ErrorKind::Rel,
316 error_bound: _,
317 threshold: _,
318 decorrelation: Decorrelation::Random,
319 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_REL_R_f32,
320 Self::QuantizeErrorBound {
321 dtype: QuantizeDType::F64,
322 kind: ErrorKind::Abs,
323 error_bound: _,
324 threshold: _,
325 decorrelation: Decorrelation::Zero,
326 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_ABS_0_f64,
327 Self::QuantizeErrorBound {
328 dtype: QuantizeDType::F64,
329 kind: ErrorKind::Abs,
330 error_bound: _,
331 threshold: _,
332 decorrelation: Decorrelation::Random,
333 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_ABS_R_f64,
334 Self::QuantizeErrorBound {
335 dtype: QuantizeDType::F64,
336 kind: ErrorKind::Noa,
337 error_bound: _,
338 threshold: _,
339 decorrelation: Decorrelation::Zero,
340 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_NOA_0_f64,
341 Self::QuantizeErrorBound {
342 dtype: QuantizeDType::F64,
343 kind: ErrorKind::Noa,
344 error_bound: _,
345 threshold: _,
346 decorrelation: Decorrelation::Random,
347 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_NOA_R_f64,
348 Self::QuantizeErrorBound {
349 dtype: QuantizeDType::F64,
350 kind: ErrorKind::Rel,
351 error_bound: _,
352 threshold: _,
353 decorrelation: Decorrelation::Zero,
354 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_REL_0_f64,
355 Self::QuantizeErrorBound {
356 dtype: QuantizeDType::F64,
357 kind: ErrorKind::Rel,
358 error_bound: _,
359 threshold: _,
360 decorrelation: Decorrelation::Random,
361 } => lc_framework_sys::LC_CPUpreprocessor_QUANT_REL_R_f64,
362 }
363 }
364
365 fn push_params(&self, params: &mut Vec<f64>) {
366 match self {
367 Self::Noop | Self::Lorenzo1D { dtype: _ } => (),
368 Self::QuantizeErrorBound {
369 dtype: _,
370 kind: _,
371 error_bound,
372 threshold,
373 decorrelation: _,
374 } => {
375 params.push(*error_bound);
376 if let Some(threshold) = threshold {
377 params.push(*threshold);
378 }
379 }
380 }
381 }
382}
383
384#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
385pub enum ErrorKind {
387 Abs,
389 Noa,
391 Rel,
393}
394
395#[expect(missing_docs)]
396#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
397pub enum Decorrelation {
399 Zero,
400 Random,
401}
402
403#[expect(missing_docs)]
404#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
405pub enum LorenzoDtype {
407 I32,
408}
409
410#[expect(missing_docs)]
411#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
412pub enum QuantizeDType {
414 F32,
415 F64,
416}
417
418#[expect(missing_docs)]
419#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
420pub enum Component {
422 Noop,
423 TwosComplementToSignMagnitude { size: ElemSize },
425 TwosComplementToNegaBinary { size: ElemSize },
426 DebiasedExponentFractionSign { size: FloatSize },
427 DebiasedExponentSignFraction { size: FloatSize },
428 BitShuffle { size: ElemSize },
430 Tuple { size: TupleSize },
431 Delta { size: ElemSize },
433 DeltaAsSignMagnitude { size: ElemSize },
434 DeltaAsNegaBinary { size: ElemSize },
435 Clog { size: ElemSize },
437 HClog { size: ElemSize },
438 Rare { size: ElemSize },
439 Raze { size: ElemSize },
440 RunLengthEncoding { size: ElemSize },
441 RepetitionRunBitmapEncoding { size: ElemSize },
442 ZeroRunBitmapEncoding { size: ElemSize },
443}
444
445impl Component {
446 #[expect(clippy::too_many_lines)]
447 const fn as_id(self) -> lc_framework_sys::LC_CPUcomponents {
448 match self {
449 Self::Noop => lc_framework_sys::LC_CPUcomponents_NUL_CPUcomponents,
450 Self::TwosComplementToSignMagnitude { size: ElemSize::S1 } => {
452 lc_framework_sys::LC_CPUcomponents_TCMS_1
453 }
454 Self::TwosComplementToSignMagnitude { size: ElemSize::S2 } => {
455 lc_framework_sys::LC_CPUcomponents_TCMS_2
456 }
457 Self::TwosComplementToSignMagnitude { size: ElemSize::S4 } => {
458 lc_framework_sys::LC_CPUcomponents_TCMS_4
459 }
460 Self::TwosComplementToSignMagnitude { size: ElemSize::S8 } => {
461 lc_framework_sys::LC_CPUcomponents_TCMS_8
462 }
463 Self::TwosComplementToNegaBinary { size: ElemSize::S1 } => {
464 lc_framework_sys::LC_CPUcomponents_TCNB_1
465 }
466 Self::TwosComplementToNegaBinary { size: ElemSize::S2 } => {
467 lc_framework_sys::LC_CPUcomponents_TCNB_2
468 }
469 Self::TwosComplementToNegaBinary { size: ElemSize::S4 } => {
470 lc_framework_sys::LC_CPUcomponents_TCNB_4
471 }
472 Self::TwosComplementToNegaBinary { size: ElemSize::S8 } => {
473 lc_framework_sys::LC_CPUcomponents_TCNB_8
474 }
475 Self::DebiasedExponentFractionSign {
476 size: FloatSize::S4,
477 } => lc_framework_sys::LC_CPUcomponents_DBEFS_4,
478 Self::DebiasedExponentFractionSign {
479 size: FloatSize::S8,
480 } => lc_framework_sys::LC_CPUcomponents_DBEFS_8,
481 Self::DebiasedExponentSignFraction {
482 size: FloatSize::S4,
483 } => lc_framework_sys::LC_CPUcomponents_DBESF_4,
484 Self::DebiasedExponentSignFraction {
485 size: FloatSize::S8,
486 } => lc_framework_sys::LC_CPUcomponents_DBESF_8,
487 Self::BitShuffle { size: ElemSize::S1 } => lc_framework_sys::LC_CPUcomponents_BIT_1,
489 Self::BitShuffle { size: ElemSize::S2 } => lc_framework_sys::LC_CPUcomponents_BIT_2,
490 Self::BitShuffle { size: ElemSize::S4 } => lc_framework_sys::LC_CPUcomponents_BIT_4,
491 Self::BitShuffle { size: ElemSize::S8 } => lc_framework_sys::LC_CPUcomponents_BIT_8,
492 Self::Tuple {
493 size: TupleSize::S1x2,
494 } => lc_framework_sys::LC_CPUcomponents_TUPL2_1,
495 Self::Tuple {
496 size: TupleSize::S1x3,
497 } => lc_framework_sys::LC_CPUcomponents_TUPL3_1,
498 Self::Tuple {
499 size: TupleSize::S1x4,
500 } => lc_framework_sys::LC_CPUcomponents_TUPL4_1,
501 Self::Tuple {
502 size: TupleSize::S1x6,
503 } => lc_framework_sys::LC_CPUcomponents_TUPL6_1,
504 Self::Tuple {
505 size: TupleSize::S1x8,
506 } => lc_framework_sys::LC_CPUcomponents_TUPL8_1,
507 Self::Tuple {
508 size: TupleSize::S1x12,
509 } => lc_framework_sys::LC_CPUcomponents_TUPL12_1,
510 Self::Tuple {
511 size: TupleSize::S2x2,
512 } => lc_framework_sys::LC_CPUcomponents_TUPL2_2,
513 Self::Tuple {
514 size: TupleSize::S2x3,
515 } => lc_framework_sys::LC_CPUcomponents_TUPL3_2,
516 Self::Tuple {
517 size: TupleSize::S2x4,
518 } => lc_framework_sys::LC_CPUcomponents_TUPL4_2,
519 Self::Tuple {
520 size: TupleSize::S2x6,
521 } => lc_framework_sys::LC_CPUcomponents_TUPL6_2,
522 Self::Tuple {
523 size: TupleSize::S4x2,
524 } => lc_framework_sys::LC_CPUcomponents_TUPL2_4,
525 Self::Tuple {
526 size: TupleSize::S4x6,
527 } => lc_framework_sys::LC_CPUcomponents_TUPL6_4,
528 Self::Tuple {
529 size: TupleSize::S8x3,
530 } => lc_framework_sys::LC_CPUcomponents_TUPL3_8,
531 Self::Tuple {
532 size: TupleSize::S8x6,
533 } => lc_framework_sys::LC_CPUcomponents_TUPL6_8,
534 Self::Delta { size: ElemSize::S1 } => lc_framework_sys::LC_CPUcomponents_DIFF_1,
536 Self::Delta { size: ElemSize::S2 } => lc_framework_sys::LC_CPUcomponents_DIFF_2,
537 Self::Delta { size: ElemSize::S4 } => lc_framework_sys::LC_CPUcomponents_DIFF_4,
538 Self::Delta { size: ElemSize::S8 } => lc_framework_sys::LC_CPUcomponents_DIFF_8,
539 Self::DeltaAsSignMagnitude { size: ElemSize::S1 } => {
540 lc_framework_sys::LC_CPUcomponents_DIFFMS_1
541 }
542 Self::DeltaAsSignMagnitude { size: ElemSize::S2 } => {
543 lc_framework_sys::LC_CPUcomponents_DIFFMS_2
544 }
545 Self::DeltaAsSignMagnitude { size: ElemSize::S4 } => {
546 lc_framework_sys::LC_CPUcomponents_DIFFMS_4
547 }
548 Self::DeltaAsSignMagnitude { size: ElemSize::S8 } => {
549 lc_framework_sys::LC_CPUcomponents_DIFFMS_8
550 }
551 Self::DeltaAsNegaBinary { size: ElemSize::S1 } => {
552 lc_framework_sys::LC_CPUcomponents_DIFFNB_1
553 }
554 Self::DeltaAsNegaBinary { size: ElemSize::S2 } => {
555 lc_framework_sys::LC_CPUcomponents_DIFFNB_2
556 }
557 Self::DeltaAsNegaBinary { size: ElemSize::S4 } => {
558 lc_framework_sys::LC_CPUcomponents_DIFFNB_4
559 }
560 Self::DeltaAsNegaBinary { size: ElemSize::S8 } => {
561 lc_framework_sys::LC_CPUcomponents_DIFFNB_8
562 }
563 Self::Clog { size: ElemSize::S1 } => lc_framework_sys::LC_CPUcomponents_CLOG_1,
565 Self::Clog { size: ElemSize::S2 } => lc_framework_sys::LC_CPUcomponents_CLOG_2,
566 Self::Clog { size: ElemSize::S4 } => lc_framework_sys::LC_CPUcomponents_CLOG_4,
567 Self::Clog { size: ElemSize::S8 } => lc_framework_sys::LC_CPUcomponents_CLOG_8,
568 Self::HClog { size: ElemSize::S1 } => lc_framework_sys::LC_CPUcomponents_HCLOG_1,
569 Self::HClog { size: ElemSize::S2 } => lc_framework_sys::LC_CPUcomponents_HCLOG_2,
570 Self::HClog { size: ElemSize::S4 } => lc_framework_sys::LC_CPUcomponents_HCLOG_4,
571 Self::HClog { size: ElemSize::S8 } => lc_framework_sys::LC_CPUcomponents_HCLOG_8,
572 Self::Rare { size: ElemSize::S1 } => lc_framework_sys::LC_CPUcomponents_RARE_1,
573 Self::Rare { size: ElemSize::S2 } => lc_framework_sys::LC_CPUcomponents_RARE_2,
574 Self::Rare { size: ElemSize::S4 } => lc_framework_sys::LC_CPUcomponents_RARE_4,
575 Self::Rare { size: ElemSize::S8 } => lc_framework_sys::LC_CPUcomponents_RARE_8,
576 Self::Raze { size: ElemSize::S1 } => lc_framework_sys::LC_CPUcomponents_RAZE_1,
577 Self::Raze { size: ElemSize::S2 } => lc_framework_sys::LC_CPUcomponents_RAZE_2,
578 Self::Raze { size: ElemSize::S4 } => lc_framework_sys::LC_CPUcomponents_RAZE_4,
579 Self::Raze { size: ElemSize::S8 } => lc_framework_sys::LC_CPUcomponents_RAZE_8,
580 Self::RunLengthEncoding { size: ElemSize::S1 } => {
581 lc_framework_sys::LC_CPUcomponents_RLE_1
582 }
583 Self::RunLengthEncoding { size: ElemSize::S2 } => {
584 lc_framework_sys::LC_CPUcomponents_RLE_2
585 }
586 Self::RunLengthEncoding { size: ElemSize::S4 } => {
587 lc_framework_sys::LC_CPUcomponents_RLE_4
588 }
589 Self::RunLengthEncoding { size: ElemSize::S8 } => {
590 lc_framework_sys::LC_CPUcomponents_RLE_8
591 }
592 Self::RepetitionRunBitmapEncoding { size: ElemSize::S1 } => {
593 lc_framework_sys::LC_CPUcomponents_RRE_1
594 }
595 Self::RepetitionRunBitmapEncoding { size: ElemSize::S2 } => {
596 lc_framework_sys::LC_CPUcomponents_RRE_2
597 }
598 Self::RepetitionRunBitmapEncoding { size: ElemSize::S4 } => {
599 lc_framework_sys::LC_CPUcomponents_RRE_4
600 }
601 Self::RepetitionRunBitmapEncoding { size: ElemSize::S8 } => {
602 lc_framework_sys::LC_CPUcomponents_RRE_8
603 }
604 Self::ZeroRunBitmapEncoding { size: ElemSize::S1 } => {
605 lc_framework_sys::LC_CPUcomponents_RZE_1
606 }
607 Self::ZeroRunBitmapEncoding { size: ElemSize::S2 } => {
608 lc_framework_sys::LC_CPUcomponents_RZE_2
609 }
610 Self::ZeroRunBitmapEncoding { size: ElemSize::S4 } => {
611 lc_framework_sys::LC_CPUcomponents_RZE_4
612 }
613 Self::ZeroRunBitmapEncoding { size: ElemSize::S8 } => {
614 lc_framework_sys::LC_CPUcomponents_RZE_8
615 }
616 }
617 }
618}
619
620#[expect(missing_docs)]
621#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
622pub enum ElemSize {
624 S1,
625 S2,
626 S4,
627 S8,
628}
629
630#[expect(missing_docs)]
631#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
632pub enum FloatSize {
634 S4,
635 S8,
636}
637
638#[expect(missing_docs)]
639#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
640pub enum TupleSize {
642 S1x2,
643 S1x3,
644 S1x4,
645 S1x6,
646 S1x8,
647 S1x12,
648 S2x2,
649 S2x3,
650 S2x4,
651 S2x6,
652 S4x2,
653 S4x6,
654 S8x3,
655 S8x6,
656}
657
658#[cfg(test)]
659#[allow(clippy::unwrap_used)]
660mod tests {
661 use super::*;
662
663 #[test]
664 fn bit4_rle4() {
665 let preprocessors = &[];
666 let components = &[
667 Component::BitShuffle { size: ElemSize::S4 },
668 Component::RunLengthEncoding { size: ElemSize::S4 },
669 ];
670
671 let data = b"abcd";
672
673 let compressed = compress(preprocessors, components, data).unwrap();
674 let decompressed = decompress(preprocessors, components, &compressed).unwrap();
675
676 assert_eq!(decompressed, data);
677 }
678
679 #[test]
680 fn abs_error() {
681 let data = (0..100_u16)
682 .map(|x| f32::from(x) / 100.0)
683 .map(|x| std::f32::consts::PI * x)
684 .map(f32::cos)
685 .collect::<Vec<_>>();
686 #[expect(unsafe_code)]
687 let data_bytes = unsafe {
691 std::slice::from_raw_parts(data.as_ptr().cast(), std::mem::size_of_val(data.as_slice()))
692 };
693
694 let error_bound = 0.1;
695
696 let preprocessors = &[Preprocessor::QuantizeErrorBound {
697 dtype: QuantizeDType::F32,
698 kind: ErrorKind::Abs,
699 error_bound,
700 threshold: None,
701 decorrelation: Decorrelation::Zero,
702 }];
703 let components = &[
704 Component::BitShuffle { size: ElemSize::S4 },
705 Component::RunLengthEncoding { size: ElemSize::S4 },
706 ];
707
708 let compressed = compress(preprocessors, components, data_bytes).unwrap();
709
710 for i in 0..std::mem::size_of::<c_longlong>() {
711 let mut compressed_unaligned = Vec::with_capacity(compressed.len() + i);
712 compressed_unaligned.extend(std::iter::repeat_n(b'\0', i));
713 compressed_unaligned.extend_from_slice(&compressed);
714
715 let decompressed_bytes = decompress(
716 preprocessors,
717 components,
718 compressed_unaligned.get(i..).unwrap(),
719 )
720 .unwrap();
721 assert_eq!(decompressed_bytes.len(), data_bytes.len());
722
723 #[expect(unsafe_code)]
724 let decompressed = unsafe {
726 let mut decompressed = Vec::<f32>::with_capacity(data.len());
727 std::ptr::copy_nonoverlapping(
728 decompressed_bytes.as_ptr(),
729 decompressed.as_mut_ptr().cast(),
730 data_bytes.len(),
731 );
732 decompressed.set_len(data.len());
733 decompressed
734 };
735
736 for (o, d) in data.iter().copied().zip(decompressed) {
737 assert!(f64::from((o - d).abs()) <= error_bound);
738 }
739 }
740 }
741}