1use std::borrow::Cow;
2use std::fmt::{self, Formatter};
3use std::io::prelude::*;
4use std::mem;
5use std::num;
6
7use base64_simd;
8use bytemuck::Pod;
9use flate2::write::{ZlibDecoder, ZlibEncoder};
10use flate2::Compression;
11use num_traits::ToBytes;
12
13use mzdata_param::{ParamList, Unit};
14
15#[allow(unused)]
16use crate::encodings::{
17 to_bytes, vec_as_bytes, ArrayRetrievalError, ArrayType, BinaryCompressionType, BinaryDataArrayType,
18 Bytes,
19};
20use crate::traits::{ByteArrayView, ByteArrayViewMut};
21
22#[allow(unused)]
23use super::encodings::{
24 reverse_transpose_f32, reverse_transpose_f64, reverse_transpose_i32, reverse_transpose_i64,
25 transpose_f32, transpose_f64, transpose_i32, transpose_i64,
26};
27
28#[derive(Default, Clone)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct DataArray {
49 pub data: Bytes,
51 pub dtype: BinaryDataArrayType,
53 pub compression: BinaryCompressionType,
55 pub name: ArrayType,
57 pub params: Option<Box<ParamList>>,
59 pub unit: Unit,
61 item_count: Option<num::NonZero<usize>>,
63 data_processing_reference: Option<Box<str>>,
65}
66
67impl core::fmt::Debug for DataArray {
68 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
69 f.debug_struct("DataArray")
70 .field("name", &self.name)
71 .field("data size", &self.data.len())
72 .field("dtype", &self.dtype)
73 .field("compression", &self.compression)
74 .field("params", &self.params)
75 .field("unit", &self.unit)
76 .field("data_processing_ref", &self.data_processing_reference)
77 .finish()
78 }
79}
80
81const EMPTY_BUFFER: [u8; 0] = [];
82
83
84impl<'transient, 'lifespan: 'transient> DataArray {
85 pub fn new() -> DataArray {
88 DataArray {
89 ..Default::default()
90 }
91 }
92
93 pub fn from_name(name: &ArrayType) -> DataArray {
96 DataArray {
97 dtype: name.preferred_dtype(),
98 name: name.clone(),
99 compression: BinaryCompressionType::Decoded,
100 unit: name.preferred_unit().unwrap_or_default(),
101 ..Default::default()
102 }
103 }
104
105 pub fn from_name_and_type(name: &ArrayType, dtype: BinaryDataArrayType) -> DataArray {
108 DataArray {
109 dtype,
110 name: name.clone(),
111 compression: BinaryCompressionType::Decoded,
112 unit: name.preferred_unit().unwrap_or_default(),
113 ..Default::default()
114 }
115 }
116
117 pub fn from_name_type_size(
121 name: &ArrayType,
122 dtype: BinaryDataArrayType,
123 size: usize,
124 ) -> DataArray {
125 DataArray {
126 dtype,
127 name: name.clone(),
128 data: Bytes::with_capacity(size),
129 compression: BinaryCompressionType::Decoded,
130 unit: name.preferred_unit().unwrap_or_default(),
131 ..Default::default()
132 }
133 }
134
135 pub fn slice(&self, start: usize, end: usize) -> Result<DataArray, ArrayRetrievalError> {
152 if end < start || (end - start) % self.dtype.size_of() != 0 {
153 Err(ArrayRetrievalError::DataTypeSizeMismatch)
154 } else {
155 let data = self.decode()?;
156 let slice = data[start..end].to_vec();
157 let subset = Self::wrap(&self.name, self.dtype, slice);
158 Ok(subset)
159 }
160 }
161
162 pub fn slice_buffer(
166 &self,
167 start: usize,
168 end: usize,
169 ) -> Result<Cow<'_, [u8]>, ArrayRetrievalError> {
170 if end < start || (end - start) % self.dtype.size_of() != 0 {
171 Err(ArrayRetrievalError::DataTypeSizeMismatch)
172 } else {
173 let data = self.decode()?;
174 match data {
175 Cow::Borrowed(view) => Ok(Cow::Borrowed(&view[start..end])),
176 Cow::Owned(view) => {
177 let data = &view[start..end];
178 Ok(Cow::Owned(data.to_owned()))
179 }
180 }
181 }
182 }
183
184 pub fn wrap(name: &ArrayType, dtype: BinaryDataArrayType, data: Bytes) -> DataArray {
200 DataArray {
201 dtype,
202 name: name.clone(),
203 data,
204 compression: BinaryCompressionType::Decoded,
205 ..Default::default()
206 }
207 }
208
209 fn set_buffer_of_type(&mut self, data_buffer: Vec<u8>) -> Result<usize, ArrayRetrievalError> {
211 if data_buffer.is_empty() {
212 self.item_count = None;
213 } else {
214 self.item_count = num::NonZero::try_from(data_buffer.len() / self.dtype().size_of()).ok();
215 }
216 self.data = data_buffer;
217 Ok(self.data.len())
218 }
219
220 pub fn update_buffer<T: Pod + ToBytes>(
225 &mut self,
226 data_buffer: &[T],
227 ) -> Result<usize, ArrayRetrievalError> {
228 if self.dtype.size_of() != mem::size_of::<T>() {
229 Err(ArrayRetrievalError::DataTypeSizeMismatch)
230 } else {
231 let n = data_buffer.len();
232 self.item_count = num::NonZero::try_from(n).ok();
233 self.data = to_bytes(data_buffer);
234 Ok(self.data.len())
235 }
236 }
237
238 pub fn push<T: Pod>(&mut self, value: T) -> Result<(), ArrayRetrievalError> {
255 if !matches!(self.compression, BinaryCompressionType::Decoded) {
256 self.decode_and_store()?;
257 };
258 if self.dtype.size_of() != mem::size_of::<T>() {
259 Err(ArrayRetrievalError::DataTypeSizeMismatch)
260 } else {
261 let data = bytemuck::bytes_of(&value);
262 self.data.extend_from_slice(data);
263 self.item_count = self.item_count.map(|i| i.saturating_add(1));
264 Ok(())
265 }
266 }
267
268 pub fn extend<T: Pod>(&mut self, values: &[T]) -> Result<(), ArrayRetrievalError> {
284 if !matches!(self.compression, BinaryCompressionType::Decoded) {
285 self.decode_and_store()?;
286 };
287 if self.dtype.size_of() != mem::size_of::<T>() {
288 Err(ArrayRetrievalError::DataTypeSizeMismatch)
289 } else {
290 self.item_count = self.item_count.map(|i| i.saturating_add(values.len()));
291 let data = bytemuck::cast_slice(values);
292 self.data.extend_from_slice(data);
293 Ok(())
294 }
295 }
296
297 pub fn extend_raw(&mut self, values: &[u8]) -> Result<(), ArrayRetrievalError> {
302 if values.len() % self.dtype.size_of() != 0 {
303 Err(ArrayRetrievalError::DataTypeSizeMismatch)
304 } else {
305 self.data.extend_from_slice(values);
306 Ok(())
307 }
308 }
309
310 pub fn extend_iter<T: Pod>(&mut self, iter: impl IntoIterator<Item=T>) -> Result<(), ArrayRetrievalError> {
318 if mem::size_of::<T>() != self.dtype.size_of() {
319 return Err(ArrayRetrievalError::DataTypeSizeMismatch)
320 }
321 for val in iter.into_iter() {
322 let vb = bytemuck::bytes_of(&val);
323 self.data.extend_from_slice(vb);
324 }
325 Ok(())
326 }
327
328 pub fn encode_bytestring(&self, compression: BinaryCompressionType) -> Bytes {
345 if self.compression == compression {
346 log::trace!("Fast-path encoding {}:{}", self.name, self.dtype);
347 return self.data.clone();
348 }
349 let bytestring = match self.compression {
350 BinaryCompressionType::Decoded => Cow::Borrowed(self.data.as_slice()),
351 _ => self.decode().expect("Failed to decode binary data"),
352 };
353 match compression {
354 BinaryCompressionType::Decoded => panic!("Should never happen"),
355 BinaryCompressionType::Zlib => {
356 let compressed = Self::compress_zlib(&bytestring);
357 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
358 }
359 BinaryCompressionType::NoCompression => {
360 base64_simd::STANDARD.encode_type::<Bytes>(bytestring.as_ref())
361 }
362 #[cfg(feature = "numpress")]
363 BinaryCompressionType::NumpressLinear => {
364 if self.dtype != BinaryDataArrayType::Float64 {
365 panic!("Cannot Numpress non-float64 data!");
366 }
367 if bytestring.is_empty() {
368 return base64_simd::STANDARD.encode_type::<Bytes>(&bytestring)
369 }
370 let compressed =
371 Self::compress_numpress_linear(bytemuck::cast_slice(&bytestring)).unwrap();
372 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
373 }
374 #[cfg(feature = "numpress")]
375 BinaryCompressionType::NumpressSLOF => {
376 let compressed = match self.dtype {
377 BinaryDataArrayType::Float32 => {
378 Self::compress_numpress_slof(bytemuck::cast_slice::<u8, f32>(&bytestring)).unwrap()
379 },
380 BinaryDataArrayType::Float64 => {
381 Self::compress_numpress_slof(bytemuck::cast_slice::<u8, f64>(&bytestring)).unwrap()
382 },
383 _ => {
384 panic!("Cannot Numpress non-float data!");
385 }
386 };
387 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
388 }
389 #[cfg(feature = "numpress")]
390 BinaryCompressionType::NumpressLinearZlib => {
391 if self.dtype != BinaryDataArrayType::Float64 {
392 panic!("Cannot Numpress non-float64 data!");
393 }
394 if bytestring.is_empty() {
395 let compressed = Self::compress_zlib(&bytestring);
396 return base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
397 }
398 let compressed = Self::compress_numpress_linear(bytemuck::cast_slice(&bytestring))
399 .inspect_err(|e| {
400 log::error!("Failed to compress buffer with numpress: {e}");
401 })
402 .unwrap();
403 let compressed = Self::compress_zlib(&compressed);
404 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
405 }
406 #[cfg(all(feature = "numpress", feature = "zstd"))]
407 BinaryCompressionType::NumpressLinearZstd => {
408 if self.dtype != BinaryDataArrayType::Float64 {
409 panic!("Cannot Numpress non-float64 data!");
410 }
411 if bytestring.is_empty() {
412 let compressed = Self::compress_zstd(&bytestring, BinaryDataArrayType::Unknown, false);
413 return base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
414 }
415 let compressed = Self::compress_numpress_linear(bytemuck::cast_slice(&bytestring))
416 .inspect_err(|e| {
417 log::error!("Failed to compress buffer with numpress: {e}");
418 })
419 .unwrap();
420 let compressed = Self::compress_zstd(&compressed, BinaryDataArrayType::Unknown, false);
421 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
422 }
423 #[cfg(feature = "numpress")]
424 BinaryCompressionType::NumpressSLOFZlib => {
425 if bytestring.is_empty() {
426 let bytestring = Self::compress_zlib(&bytestring);
427 return base64_simd::STANDARD.encode_type::<Bytes>(&bytestring)
428 }
429 let compressed = match self.dtype {
430 BinaryDataArrayType::Float32 => {
431 Self::compress_numpress_slof(bytemuck::cast_slice::<u8, f32>(&bytestring)).unwrap()
432 },
433 BinaryDataArrayType::Float64 => {
434 Self::compress_numpress_slof(bytemuck::cast_slice::<u8, f64>(&bytestring)).unwrap()
435 },
436 _ => {
437 panic!("Cannot Numpress non-float data!");
438 }
439 };
440 let bytestring = Self::compress_zlib(&compressed);
441 base64_simd::STANDARD.encode_type::<Bytes>(&bytestring)
442 }
443 #[cfg(all(feature = "numpress", feature = "zstd"))]
444 BinaryCompressionType::NumpressSLOFZstd => {
445 if bytestring.is_empty() {
446 let bytestring = Self::compress_zstd(&bytestring, BinaryDataArrayType::Unknown, false);
447 return base64_simd::STANDARD.encode_type::<Bytes>(&bytestring)
448 }
449 let compressed = match self.dtype {
450 BinaryDataArrayType::Float32 => {
451 Self::compress_numpress_slof(bytemuck::cast_slice::<u8, f32>(&bytestring)).unwrap()
452 },
453 BinaryDataArrayType::Float64 => {
454 Self::compress_numpress_slof(bytemuck::cast_slice::<u8, f64>(&bytestring)).unwrap()
455 },
456 _ => {
457 panic!("Cannot Numpress non-float data!");
458 }
459 };
460 let bytestring = Self::compress_zstd(&compressed, BinaryDataArrayType::Unknown, false);
461 base64_simd::STANDARD.encode_type::<Bytes>(&bytestring)
462 }
463 #[cfg(feature = "zstd")]
464 BinaryCompressionType::Zstd => {
465 let compressed = Self::compress_zstd(&bytestring, self.dtype, false);
466 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
467 }
468 #[cfg(feature = "zstd")]
469 BinaryCompressionType::ShuffleZstd => {
470 let compressed = Self::compress_zstd(&bytestring, self.dtype, true);
471 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
472 }
473 #[cfg(feature = "zstd")]
474 BinaryCompressionType::DeltaShuffleZstd => {
475 let compressed = Self::compress_delta_zstd(&bytestring, self.dtype, true);
476 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
477 }
478 #[cfg(feature = "zstd")]
479 BinaryCompressionType::ZstdDict => {
480 let compressed = Self::compress_dict_zstd(&bytestring, self.dtype);
481 base64_simd::STANDARD.encode_type::<Bytes>(&compressed)
482 }
483 _ => {
484 panic!("Compresion type {:?} is unsupported", compression)
485 }
486 }
487 }
488
489 pub fn decode_and_store(&mut self) -> Result<BinaryCompressionType, ArrayRetrievalError> {
512 match self.decode() {
513 Ok(data) => {
514 match data {
515 Cow::Borrowed(_view) => Ok(self.compression),
518 Cow::Owned(buffer) => {
519 let n = buffer.len() / self.dtype.size_of();
520 self.item_count = n.try_into().ok();
521 self.data = buffer;
522 self.compression = BinaryCompressionType::Decoded;
523 Ok(self.compression)
524 }
525 }
526 }
527 Err(err) => Err(err),
528 }
529 }
530
531 pub fn decode(&'lifespan self) -> Result<Cow<'lifespan, [u8]>, ArrayRetrievalError> {
540 if self.data.is_empty() {
541 return Ok(Cow::Borrowed(&EMPTY_BUFFER));
542 }
543
544 macro_rules! base64_decode {
545 () => {
546 base64_simd::STANDARD
547 .decode_type::<Bytes>(&self.data)
548 .map_err(|_| ArrayRetrievalError::MalformedBase64Encoding)?
549 };
550 }
551
552 match self.compression {
553 BinaryCompressionType::Decoded => Ok(Cow::Borrowed(self.data.as_slice())),
554 BinaryCompressionType::NoCompression => {
555 let bytestring = base64_decode!();
556 Ok(Cow::Owned(bytestring))
557 }
558 BinaryCompressionType::Zlib => {
559 let mut bytestring = base64_decode!();
560 bytestring = Self::decompress_zlib(&bytestring);
561 Ok(Cow::Owned(bytestring))
562 }
563 #[cfg(feature = "zstd")]
564 BinaryCompressionType::Zstd => {
565 let bytestring = base64_decode!();
566 Ok(Cow::Owned(Self::decompress_zstd(
567 &bytestring,
568 self.dtype,
569 false,
570 )))
571 }
572 #[cfg(feature = "zstd")]
573 BinaryCompressionType::ShuffleZstd => {
574 let bytestring = base64_decode!();
575 Ok(Cow::Owned(Self::decompress_zstd(
576 &bytestring,
577 self.dtype,
578 true,
579 )))
580 }
581 #[cfg(feature = "zstd")]
582 BinaryCompressionType::DeltaShuffleZstd => {
583 let bytestring = base64_decode!();
584 Ok(Cow::Owned(Self::decompress_delta_zstd(
585 &bytestring,
586 self.dtype,
587 true,
588 )))
589 }
590
591 #[cfg(feature = "zstd")]
592 BinaryCompressionType::ZstdDict => {
593 let bytestring = base64_decode!();
594 Ok(Cow::Owned(Self::decompress_dict_zstd(
595 &bytestring,
596 self.dtype,
597 )))
598 }
599 #[cfg(feature = "numpress")]
600 BinaryCompressionType::NumpressLinear => match self.dtype {
601 BinaryDataArrayType::Float64 => {
602 let bytestring = base64_decode!();
603 let decoded = Self::decompress_numpress_linear(&bytestring)?;
604 let view = vec_as_bytes(decoded);
605 Ok(Cow::Owned(view))
606 }
607 _ => Err(ArrayRetrievalError::DecompressionError(
608 self.compression.unsupported_msg(Some(
609 format!("Not compatible with {:?}", self.dtype).as_str(),
610 )),
611 )),
612 },
613
614 #[cfg(feature = "numpress")]
615 BinaryCompressionType::NumpressSLOF => {
616 let bytestring = base64_decode!();
617 Self::decompress_numpress_slof(&bytestring, self.dtype)
618 }
619
620 #[cfg(feature = "numpress")]
621 BinaryCompressionType::NumpressLinearZlib => match self.dtype {
622 BinaryDataArrayType::Float64 => {
623 let bytestring = base64_decode!();
624 let bytestring = Self::decompress_zlib(&bytestring);
625 let decoded = Self::decompress_numpress_linear(&bytestring)?;
626 let view = vec_as_bytes(decoded);
627 Ok(Cow::Owned(view))
628 }
629 _ => Err(ArrayRetrievalError::DecompressionError(
630 self.compression.unsupported_msg(Some(
631 format!("Not compatible with {:?}", self.dtype).as_str(),
632 )),
633 )),
634 },
635
636 #[cfg(feature = "numpress")]
637 BinaryCompressionType::NumpressSLOFZlib => {
638 let bytestring = base64_decode!();
639 let bytestring = Self::decompress_zlib(&bytestring);
640 Self::decompress_numpress_slof(&bytestring, self.dtype)
641 }
642
643 #[cfg(all(feature = "numpress", feature = "zstd"))]
644 BinaryCompressionType::NumpressLinearZstd => match self.dtype {
645 BinaryDataArrayType::Float64 => {
646 let bytestring = base64_decode!();
647 let bytestring = Self::decompress_zstd(
648 &bytestring,
649 BinaryDataArrayType::Unknown,
650 false,
651 );
652 let decoded = Self::decompress_numpress_linear(&bytestring)?;
653 let view = vec_as_bytes(decoded);
654 Ok(Cow::Owned(view))
655 }
656 _ => Err(ArrayRetrievalError::DecompressionError(
657 self.compression.unsupported_msg(Some(
658 format!("Not compatible with {:?}", self.dtype).as_str(),
659 )),
660 )),
661 },
662
663 #[cfg(all(feature = "numpress", feature = "zstd"))]
664 BinaryCompressionType::NumpressSLOFZstd => {
665 let bytestring = base64_decode!();
666 let bytestring = Self::decompress_zstd(&bytestring, BinaryDataArrayType::Unknown, false);
667 Self::decompress_numpress_slof(&bytestring, self.dtype)
668 }
669
670 mode => Err(ArrayRetrievalError::DecompressionError(format!(
671 "Cannot decode array encoded with {:?}",
672 mode
673 ))),
674 }
675 }
676
677 pub(crate) fn decoded_slice(
683 &'lifespan self,
684 start: usize,
685 end: usize,
686 ) -> Result<Cow<'lifespan, [u8]>, ArrayRetrievalError> {
687 if start > end || (end - start) % self.dtype.size_of() != 0 {
688 return Err(ArrayRetrievalError::DataTypeSizeMismatch);
689 }
690 match self.compression {
691 BinaryCompressionType::Decoded => Ok(Cow::Borrowed(&self.data.as_slice()[start..end])),
692 _ => {
693 Ok(Cow::Owned(self.decode()?[start..end].to_vec()))
694 }
695 }
696 }
697
698 pub fn decode_mut(&'transient mut self) -> Result<&'transient mut Bytes, ArrayRetrievalError> {
708 if self.data.is_empty() || matches!(self.compression, BinaryCompressionType::Decoded) {
709 Ok(&mut self.data)
710 } else {
711 match self.decode()? {
712 Cow::Borrowed(_) => {
713 Ok(&mut self.data)
714 },
715 Cow::Owned(owned) => {
716 self.data = owned;
717 self.compression = BinaryCompressionType::Decoded;
718 Ok(&mut self.data)
719 },
720 }
721 }
722
723 }
724
725 pub fn clear(&mut self) {
728 self.data.clear();
729 self.params = None;
730 self.item_count = None;
731 }
732
733 pub fn store_compressed(
736 &mut self,
737 compression: BinaryCompressionType,
738 ) -> Result<(), ArrayRetrievalError> {
739 if self.compression == compression {
740 Ok(())
741 } else {
742 self.item_count = self.data_len().ok().and_then(|n| n.try_into().ok());
743 let bytes = self.encode_bytestring(compression);
744 self.data = bytes;
745 self.compression = compression;
746 Ok(())
747 }
748 }
749
750 pub fn store_as(&mut self, dtype: BinaryDataArrayType) -> Result<usize, ArrayRetrievalError> {
758 if self.dtype == dtype {
759 return Ok(self.data.len());
760 }
761 match dtype {
762 BinaryDataArrayType::Float32 => {
763 let view = self.to_f32()?;
764 #[cfg(target_endian = "big")]
765 {
766 let mut recast = to_bytes(&view);
767 dtype.swap_bytes(&mut recast)?;
768 self.dtype = dtype;
769 self.set_buffer_of_type(recast)
770 }
771 #[cfg(not(target_endian = "big"))]
772 {
773 let recast = to_bytes(&view);
774 self.dtype = dtype;
775 self.set_buffer_of_type(recast)
776 }
777
778 }
779 BinaryDataArrayType::Float64 => {
780 let view = self.to_f64()?;
781 #[cfg(target_endian = "big")]
782 {
783 let mut recast = to_bytes(&view);
784 dtype.swap_bytes(&mut recast)?;
785 self.dtype = dtype;
786 self.set_buffer_of_type(recast)
787 }
788 #[cfg(not(target_endian = "big"))]
789 {
790 let recast = to_bytes(&view);
791 self.dtype = dtype;
792 self.set_buffer_of_type(recast)
793 }
794 }
795 BinaryDataArrayType::Int32 => {
796 let view = self.to_i32()?;
797 #[cfg(target_endian = "big")]
798 {
799 let mut recast = to_bytes(&view);
800 dtype.swap_bytes(&mut recast)?;
801 self.dtype = dtype;
802 self.set_buffer_of_type(recast)
803 }
804 #[cfg(not(target_endian = "big"))]
805 {
806 let recast = to_bytes(&view);
807 self.dtype = dtype;
808 self.set_buffer_of_type(recast)
809 }
810 }
811 BinaryDataArrayType::Int64 => {
812 let view = self.to_i64()?;
813 #[cfg(target_endian = "big")]
814 {
815 let mut recast = to_bytes(&view);
816 dtype.swap_bytes(&mut recast)?;
817 self.dtype = dtype;
818 self.set_buffer_of_type(recast)
819 }
820 #[cfg(not(target_endian = "big"))]
821 {
822 let recast = to_bytes(&view);
823 self.dtype = dtype;
824 self.set_buffer_of_type(recast)
825 }
826 }
827 _ => Ok(0),
828 }
829 }
830
831 pub const fn is_ion_mobility(&self) -> bool {
836 self.name.is_ion_mobility()
837 }
838
839 pub fn raw_len(&self) -> usize {
841 self.data.len()
842 }
843
844 pub fn data_processing_reference(&self) -> Option<&str> {
846 self.data_processing_reference.as_deref()
847 }
848
849 pub fn set_data_processing_reference(&mut self, data_processing_reference: Option<Box<str>>) {
851 self.data_processing_reference = data_processing_reference;
852 }
853}
854
855
856impl DataArray {
861
862 pub(crate) fn compress_zlib(bytestring: &[u8]) -> Bytes {
864 let result = Bytes::new();
865 let mut compressor = ZlibEncoder::new(result, Compression::best());
866 compressor.write_all(bytestring).expect("Error compressing");
867 compressor.finish().expect("Error compressing")
868 }
869
870 pub(crate) fn decompress_zlib(bytestring: &[u8]) -> Bytes {
875 let result = Bytes::new();
876 let mut decompressor = ZlibDecoder::new(result);
877 decompressor
878 .write_all(bytestring)
879 .unwrap_or_else(|e| panic!("Decompression error: {}", e));
880 let buf = decompressor
881 .finish()
882 .unwrap_or_else(|e| panic!("Decompression error: {}", e));
883 buf
884 }
885
886 #[cfg(feature = "numpress")]
887 pub(crate) fn compress_numpress_linear(data: &[f64]) -> Result<Bytes, ArrayRetrievalError> {
892 if data.is_empty() {
893 return Ok(Bytes::new());
894 }
895 let scaling = numpress::optimal_scaling(data);
896 match numpress::numpress_compress(data, scaling) {
897 Ok(data) => Ok(data),
898 Err(e) => Err(ArrayRetrievalError::DecompressionError(e.to_string())),
899 }
900 }
901
902 #[cfg(feature = "numpress")]
903 pub(crate) fn compress_numpress_slof<T: numpress::AsFloat64>(data: &[T]) -> Result<Bytes, ArrayRetrievalError> {
904 let scaling = numpress::optimal_slof_fixed_point(data);
905 let mut buf = Bytes::new();
906 match numpress::encode_slof(data, &mut buf, scaling) {
907 Ok(_) => Ok(buf),
908 Err(e) => Err(ArrayRetrievalError::DecompressionError(e.to_string())),
909 }
910 }
911
912 #[cfg(feature = "numpress")]
913 pub(crate) fn decompress_numpress_linear(data: &[u8]) -> Result<Vec<f64>, ArrayRetrievalError> {
915 if data.is_empty() {
916 return Ok(Vec::new())
917 }
918 match numpress::numpress_decompress(data) {
919 Ok(data) => Ok(data),
920 Err(e) => Err(ArrayRetrievalError::DecompressionError(e.to_string())),
921 }
922 }
923
924 #[cfg(feature = "numpress")]
925 pub(crate) fn decompress_numpress_slof(data: &[u8], dtype: BinaryDataArrayType) -> Result<Cow<'static, [u8]>, ArrayRetrievalError> {
926 use log::trace;
927
928 let mut buf = Vec::new();
929 let decoded = match numpress::decode_slof(data, &mut buf) {
930 Ok(_) => buf,
931 Err(e) => return Err(ArrayRetrievalError::DecompressionError(e.to_string())),
932 };
933 trace!("Numpress SLOF decoded to {} points", decoded.len());
934 match dtype {
935 BinaryDataArrayType::Float64 => {
936 let view = vec_as_bytes(decoded);
937 Ok(Cow::Owned(view))
938 },
939 BinaryDataArrayType::Float32 => {
940 let n = decoded.len() * BinaryDataArrayType::Float32.size_of();
941 trace!("Mapping to {n} bytes for f32 storage");
942 let mut view: Vec<u8> = Vec::with_capacity(n);
943 for val in decoded {
944 let val = val as f32;
945 view.extend(bytemuck::bytes_of(&val))
946 }
947 Ok(Cow::Owned(view))
948 },
949 _ => {
950 Err(ArrayRetrievalError::DecompressionError(
951 BinaryCompressionType::NumpressSLOF.unsupported_msg(Some(
952 format!("Not compatible with {:?}", dtype).as_str(),
953 )),
954 ))
955 }
956 }
957 }
958
959 #[cfg(feature = "zstd")]
960 pub(crate) fn compress_zstd(
965 bytestring: &[u8],
966 dtype: BinaryDataArrayType,
967 shuffle: bool,
968 ) -> Bytes {
969 let level: i32 = std::env::var("MZDATA_ZSTD_LEVEL")
970 .map(|v| v.parse())
971 .unwrap_or(Ok(zstd::DEFAULT_COMPRESSION_LEVEL))
972 .unwrap_or(zstd::DEFAULT_COMPRESSION_LEVEL);
973 if !shuffle {
974 return zstd::bulk::compress(bytestring, level).unwrap();
975 }
976 match dtype {
977 BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => {
978 zstd::bulk::compress(bytestring, level).unwrap()
979 }
980 BinaryDataArrayType::Float64 => {
981 zstd::bulk::compress(&transpose_f64(bytemuck::cast_slice(bytestring)), level)
982 .unwrap()
983 }
984 BinaryDataArrayType::Float32 => {
985 zstd::bulk::compress(&transpose_f32(bytemuck::cast_slice(bytestring)), level)
986 .unwrap()
987 }
988 BinaryDataArrayType::Int64 => {
989 zstd::bulk::compress(&transpose_i64(bytemuck::cast_slice(bytestring)), level)
990 .unwrap()
991 }
992 BinaryDataArrayType::Int32 => {
993 zstd::bulk::compress(&transpose_i32(bytemuck::cast_slice(bytestring)), level)
994 .unwrap()
995 }
996 }
997 }
998
999 #[cfg(feature = "zstd")]
1000 pub(crate) fn compress_delta_zstd(
1001 bytestring: &[u8],
1002 dtype: BinaryDataArrayType,
1003 shuffle: bool,
1004 ) -> Bytes {
1005 use bytemuck::cast_slice;
1006
1007 use super::delta_encoding;
1008
1009 match dtype {
1010 BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => {
1011 Self::compress_zstd(bytestring, dtype, shuffle)
1012 }
1013 BinaryDataArrayType::Float64 => {
1014 let mut buf = cast_slice::<_, f64>(bytestring).to_vec();
1015 delta_encoding(&mut buf);
1016 Self::compress_zstd(cast_slice(&buf), dtype, shuffle)
1017 }
1018 BinaryDataArrayType::Float32 => {
1019 let mut buf = cast_slice::<_, f32>(bytestring).to_vec();
1020 delta_encoding(&mut buf);
1021 Self::compress_zstd(cast_slice(&buf), dtype, shuffle)
1022 }
1023 BinaryDataArrayType::Int64 => {
1024 let mut buf = cast_slice::<_, i64>(bytestring).to_vec();
1025 delta_encoding(&mut buf);
1026 Self::compress_zstd(cast_slice(&buf), dtype, shuffle)
1027 }
1028 BinaryDataArrayType::Int32 => {
1029 let mut buf = cast_slice::<_, i32>(bytestring).to_vec();
1030 delta_encoding(&mut buf);
1031 Self::compress_zstd(cast_slice(&buf), dtype, shuffle)
1032 }
1033 }
1034 }
1035
1036 #[cfg(feature = "zstd")]
1037 pub(crate) fn compress_dict_zstd(bytestring: &[u8], dtype: BinaryDataArrayType) -> Bytes {
1038 use super::encodings::dictionary_encoding;
1039 log::trace!("Dictionary encoding {} bytes as {dtype}", bytestring.len());
1040 if bytestring.is_empty() {
1041 return Self::compress_zstd(&bytestring, dtype, false);
1042 }
1043 match dtype {
1044 BinaryDataArrayType::Float64 => {
1045 let compressed =
1046 dictionary_encoding(bytemuck::cast_slice::<u8, f64>(&bytestring))
1047 .unwrap();
1048 let compressed = Self::compress_zstd(&compressed, dtype, false);
1049 compressed
1050 }
1051 BinaryDataArrayType::Float32 => {
1052 let compressed =
1053 dictionary_encoding(bytemuck::cast_slice::<u8, f32>(&bytestring))
1054 .unwrap();
1055 let compressed = Self::compress_zstd(&compressed, dtype, false);
1056 compressed
1057 }
1058 BinaryDataArrayType::Int64 => {
1059 let compressed =
1060 dictionary_encoding(bytemuck::cast_slice::<u8, i64>(&bytestring))
1061 .unwrap();
1062 let compressed = Self::compress_zstd(&compressed, dtype, false);
1063 compressed
1064 }
1065 BinaryDataArrayType::Int32 => {
1066 let compressed =
1067 dictionary_encoding(bytemuck::cast_slice::<u8, i32>(&bytestring))
1068 .unwrap();
1069 let compressed = Self::compress_zstd(&compressed, dtype, false);
1070 compressed
1071 }
1072 _ => {
1073 let compressed =
1074 dictionary_encoding(bytemuck::cast_slice::<u8, u8>(&bytestring))
1075 .unwrap();
1076 let compressed = Self::compress_zstd(&compressed, dtype, false);
1077 compressed
1078 }
1079 }
1080 }
1081
1082 #[cfg(feature = "zstd")]
1083 pub(crate) fn decompress_zstd(data: &[u8], dtype: BinaryDataArrayType, shuffle: bool) -> Bytes {
1084 let mut decoder = zstd::Decoder::new(std::io::Cursor::new(data)).unwrap();
1085 let mut buf = Vec::new();
1086 decoder.read_to_end(&mut buf).unwrap();
1087 if !shuffle {
1088 return buf;
1089 }
1090 match dtype {
1091 BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => buf,
1092 BinaryDataArrayType::Float64 => reverse_transpose_f64(&buf),
1093 BinaryDataArrayType::Float32 => reverse_transpose_f32(&buf),
1094 BinaryDataArrayType::Int64 => reverse_transpose_i64(&buf),
1095 BinaryDataArrayType::Int32 => reverse_transpose_i32(&buf),
1096 }
1097 }
1098
1099 #[cfg(feature = "zstd")]
1100 pub(crate) fn decompress_delta_zstd(
1101 data: &[u8],
1102 dtype: BinaryDataArrayType,
1103 shuffle: bool,
1104 ) -> Bytes {
1105 use super::delta_decoding;
1106
1107 let mut delta = Self::decompress_zstd(data, dtype, shuffle);
1108 match dtype {
1109 BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => delta,
1110 BinaryDataArrayType::Float64 => {
1111 let buf = bytemuck::cast_slice_mut::<_, f64>(&mut delta);
1112 delta_decoding(buf);
1113 delta
1114 }
1115 BinaryDataArrayType::Float32 => {
1116 let buf = bytemuck::cast_slice_mut::<_, f32>(&mut delta);
1117 delta_decoding(buf);
1118 delta
1119 }
1120 BinaryDataArrayType::Int64 => {
1121 let buf = bytemuck::cast_slice_mut::<_, i64>(&mut delta);
1122 delta_decoding(buf);
1123 delta
1124 }
1125 BinaryDataArrayType::Int32 => {
1126 let buf = bytemuck::cast_slice_mut::<_, i32>(&mut delta);
1127 delta_decoding(buf);
1128 delta
1129 }
1130 }
1131 }
1132
1133 #[cfg(feature = "zstd")]
1134 pub(crate) fn decompress_dict_zstd(bytestring: &[u8], dtype: BinaryDataArrayType) -> Bytes {
1135 use super::encodings::dictionary_decoding;
1136
1137 let data = Self::decompress_zstd(bytestring, dtype, false);
1138 match dtype {
1139 BinaryDataArrayType::ASCII | BinaryDataArrayType::Unknown => dictionary_decoding(&data).unwrap(),
1140 BinaryDataArrayType::Float64 => {
1141 to_bytes(&dictionary_decoding::<f64>(&data).unwrap())
1142 },
1143 BinaryDataArrayType::Float32 => {
1144 to_bytes(&dictionary_decoding::<f32>(&data).unwrap())
1145 },
1146 BinaryDataArrayType::Int64 => {
1147 to_bytes(&dictionary_decoding::<i64>(&data).unwrap())
1148 },
1149 BinaryDataArrayType::Int32 => {
1150 to_bytes(&dictionary_decoding::<i32>(&data).unwrap())
1151 },
1152 }
1153 }
1154}
1155
1156impl<'transient, 'lifespan: 'transient> ByteArrayView<'transient, 'lifespan> for DataArray {
1157 fn view(&'lifespan self) -> Result<Cow<'lifespan, [u8]>, ArrayRetrievalError> {
1158 self.decode()
1159 }
1160
1161 fn dtype(&self) -> BinaryDataArrayType {
1162 self.dtype
1163 }
1164
1165 fn data_len(&'lifespan self) -> Result<usize, ArrayRetrievalError> {
1166 if let Some(z) = self.item_count {
1167 Ok(z.get())
1168 } else {
1169 let view = self.view()?;
1170 let n = view.len();
1171 Ok(n / self.dtype().size_of())
1172 }
1173 }
1174
1175 fn unit(&self) -> Unit {
1176 self.unit
1177 }
1178
1179 fn data_processing_reference(&self) -> Option<&str> {
1180 self.data_processing_reference()
1181 }
1182
1183 fn name(&self) -> &ArrayType {
1184 &self.name
1185 }
1186}
1187
1188impl<'transient, 'lifespan: 'transient> ByteArrayViewMut<'transient, 'lifespan> for DataArray {
1189 fn view_mut(&'transient mut self) -> Result<&'transient mut Bytes, ArrayRetrievalError> {
1190 self.decode_mut()
1191 }
1192
1193 fn unit_mut(&mut self) -> &mut Unit {
1194 &mut self.unit
1195 }
1196
1197 fn set_data_processing_reference(&mut self, data_processing_reference: Option<Box<str>>) {
1198 self.set_data_processing_reference(data_processing_reference);
1199 }
1200}
1201
1202mzdata_param::impl_param_described_deferred!(DataArray);
1203
1204#[derive(Clone, Debug)]
1208pub struct DataArraySlice<'a> {
1209 source: &'a DataArray,
1210 pub start: usize,
1212 pub end: usize,
1214}
1215
1216impl<'a> DataArraySlice<'a> {
1217 pub fn new(source: &'a DataArray, mut start: usize, mut end: usize) -> Self {
1219 if start > end {
1220 mem::swap(&mut start, &mut end);
1221 }
1222 Self { source, start, end }
1223 }
1224
1225 pub fn decode(&'a self) -> Result<Cow<'a, [u8]>, ArrayRetrievalError> {
1232 self.source.decoded_slice(self.start, self.end)
1233 }
1234
1235 pub const fn is_ion_mobility(&self) -> bool {
1240 self.source.is_ion_mobility()
1241 }
1242
1243 pub fn source(&self) -> &'a DataArray {
1245 self.source
1246 }
1247}
1248
1249impl<'transient, 'lifespan: 'transient> ByteArrayView<'transient, 'lifespan>
1250 for DataArraySlice<'lifespan>
1251{
1252 fn view(&'lifespan self) -> Result<Cow<'lifespan, [u8]>, ArrayRetrievalError> {
1253 self.decode()
1254 }
1255
1256 fn dtype(&self) -> BinaryDataArrayType {
1257 self.source.dtype()
1258 }
1259
1260 fn unit(&self) -> Unit {
1261 self.source.unit
1262 }
1263
1264 fn name(&self) -> &ArrayType {
1265 self.source.name()
1266 }
1267
1268 fn data_processing_reference(&self) -> Option<&str> {
1269 self.source.data_processing_reference()
1270 }
1271}
1272
1273#[cfg(test)]
1274mod test {
1275 use super::*;
1276 use std::fs;
1277 use std::io;
1278
1279 use super::DataArray;
1280
1281 fn make_array_from_file() -> io::Result<DataArray> {
1282 let mut fh = fs::File::open("../../test/data/mz_f64_zlib_bas64.txt")?;
1283 let mut buf = String::new();
1284 fh.read_to_string(&mut buf)?;
1285 let bytes: Vec<u8> = buf.into();
1286 let mut da = DataArray::wrap(&ArrayType::MZArray, BinaryDataArrayType::Float64, bytes);
1287 da.compression = BinaryCompressionType::Zlib;
1288 *da.unit_mut() = Unit::MZ;
1289 assert_eq!(da.unit(), Unit::MZ);
1290 assert!(!da.is_ion_mobility());
1291 assert_eq!(da.name(), &ArrayType::MZArray);
1292 Ok(da)
1293 }
1294
1295 #[cfg(feature = "zstd")]
1296 fn make_array_from_file_im_zstd() -> io::Result<DataArray> {
1297 let mut fh = fs::File::open("../../test/data/im_f64_zstd_base64.txt")?;
1298 let mut buf = String::new();
1299 fh.read_to_string(&mut buf)?;
1300 let bytes: Vec<u8> = buf.into();
1301 let mut da = DataArray::wrap(&ArrayType::MeanInverseReducedIonMobilityArray, BinaryDataArrayType::Float64, bytes);
1302 da.compression = BinaryCompressionType::Zstd;
1303 *da.unit_mut() = Unit::VoltSecondPerSquareCentimeter;
1304 assert_eq!(da.unit(), Unit::VoltSecondPerSquareCentimeter);
1305 assert!(da.is_ion_mobility());
1306 assert_eq!(da.name(), &ArrayType::MeanInverseReducedIonMobilityArray);
1307 Ok(da)
1308 }
1309
1310 #[test]
1311 fn test_decode() -> io::Result<()> {
1312 let mut da = make_array_from_file()?;
1313 da.decode_and_store()?;
1314 let view = da.to_f64()?;
1315 assert_eq!(view.len(), 19800);
1316 Ok(())
1317 }
1318
1319 #[test]
1320 fn test_decode_store() -> io::Result<()> {
1321 let mut da = make_array_from_file()?;
1322 da.decode_and_store()?;
1323 let back = da.clone();
1324 da.store_as(BinaryDataArrayType::Float32)?;
1325 let view = da.to_f64()?;
1326 assert_eq!(view.len(), 19800);
1327 for (a, b) in back.iter_f64()?.zip(view.iter().copied()) {
1328 let err = (a - b).abs();
1329 assert!((a - b).abs() < 1e-3, "{} - {} = {}", a, b, err);
1330 }
1331 for (a, b) in back.iter_f64()?.zip(da.iter_f32()?.map(|x| x as f64)) {
1332 let err = (a - b).abs();
1333 assert!((a - b).abs() < 1e-3, "{} - {} = {}", a, b, err);
1334 }
1335 da.store_as(BinaryDataArrayType::Float64)?;
1336 let view = da.to_f64()?;
1337 assert_eq!(view.len(), 19800);
1338 for (a, b) in back.iter_f64()?.zip(view.iter().copied()) {
1339 let err = (a - b).abs();
1340 assert!((a - b).abs() < 1e-3, "{} - {} = {}", a, b, err);
1341 }
1342 Ok(())
1343 }
1344
1345 #[cfg(feature = "zstd")]
1346 #[test]
1347 fn test_decode_delta_zstd() {
1348 let points: Vec<f64> = (0..200_000usize).map(|i| i as f64 * 0.01 + 100.0).collect();
1349 let mut da = DataArray::from_name(&ArrayType::MZArray);
1350 da.extend(&points).unwrap();
1351
1352 let decoded_len = da.data.len();
1353 da.store_compressed(BinaryCompressionType::ShuffleZstd)
1354 .unwrap();
1355 let zstd_len = da.data.len();
1356
1357 da.store_compressed(BinaryCompressionType::Zlib).unwrap();
1358 let zlib_len = da.data.len();
1359
1360 da.decode_and_store().unwrap();
1361
1362 da.store_compressed(BinaryCompressionType::DeltaShuffleZstd)
1363 .unwrap();
1364 let delta_zstd_len = da.data.len();
1365 eprintln!("decoded: {decoded_len};\nzlib: {zlib_len};\nzstd: {zstd_len};\ndelta-zstd: {delta_zstd_len}");
1366 da.decode_and_store().unwrap();
1367 let view = da.to_f64().unwrap();
1368 let err: f64 = points
1369 .iter()
1370 .zip(view.iter())
1371 .map(|(a, b)| {
1372 assert!((a - b).abs() < 1e-3, "{a} - {b} = {}", a - b);
1373 (a - b).abs()
1374 })
1375 .sum();
1376 let mean_err = err / (points.len() as f64);
1377 eprintln!("mean abs error: {mean_err:0.8}")
1378 }
1379
1380 #[cfg(feature = "zstd")]
1381 #[test]
1382 fn test_decode_zstd() -> io::Result<()> {
1383 let mut da = make_array_from_file()?;
1384 let zlib_len = da.data.len();
1385 da.decode_and_store()?;
1386
1387 let decoded_len = da.data.len();
1388
1389 da.store_compressed(BinaryCompressionType::ShuffleZstd)?;
1390
1391 let zstd_len = da.data.len();
1392
1393 eprintln!("zlib: {zlib_len};\ndecoded: {decoded_len};\nzstd: {zstd_len}");
1394 da.decode_and_store()?;
1395
1396 let mut da_ref = make_array_from_file()?;
1397 da_ref.decode_and_store()?;
1398 assert_eq!(da.data, da_ref.data);
1399 Ok(())
1400 }
1401
1402 #[cfg(feature = "numpress")]
1403 #[test]
1404 fn test_numpress_linear() -> io::Result<()> {
1405 let mut da = make_array_from_file()?;
1406 let zlib_len = da.data.len();
1407 da.decode_and_store()?;
1408
1409 let decoded_len = da.data.len();
1410
1411 da.store_compressed(BinaryCompressionType::NumpressLinear)?;
1412 let numpress_len = da.data.len();
1413
1414 eprintln!("zlib: {zlib_len};\ndecoded: {decoded_len};\nnumpress: {numpress_len}");
1415 da.decode_and_store()?;
1416
1417 let mut da_ref = make_array_from_file()?;
1418 da_ref.decode_and_store()?;
1419 for (a, b) in da.iter_f64()?.zip(da_ref.iter_f64()?) {
1420 assert!((a - b).abs() < 1e-3, "{a} - {b} = {} which is too large a deviation", (a - b).abs())
1421 }
1422
1423 Ok(())
1424 }
1425
1426 #[test]
1427 fn test_decode_roundtrip() -> io::Result<()> {
1428 let mut da = make_array_from_file()?;
1429 let compressed_size = da.data_len()?;
1430 da.decode_and_store()?;
1431 let view = da.to_f64()?;
1432 assert_eq!(view.len(), 19800);
1433 drop(view);
1434 da.store_compressed(BinaryCompressionType::Zlib)?;
1435 assert_eq!(da.compression, BinaryCompressionType::Zlib);
1436 assert_eq!(da.data_len()?, compressed_size);
1437 let view = da.to_f64()?;
1438 assert_eq!(view.len(), 19800);
1439 Ok(())
1440 }
1441
1442 #[test]
1443 fn test_decode_empty() {
1444 let mut da = DataArray::wrap(
1445 &ArrayType::MZArray,
1446 BinaryDataArrayType::Float64,
1447 Vec::new(),
1448 );
1449 da.compression = BinaryCompressionType::Zlib;
1450
1451 assert_eq!(da.data.len(), 0);
1452 assert_eq!(da.data_len().unwrap(), 0);
1453 assert_eq!(da.decode().unwrap().len(), 0);
1454 assert_eq!(da.to_f64().unwrap().len(), 0);
1455 }
1456
1457
1458 #[cfg(feature = "zstd")]
1459 #[test]
1460 fn test_dict_from_base64() -> io::Result<()> {
1461 let mut da = make_array_from_file_im_zstd()?;
1462
1463 da.decode_and_store()?;
1464 assert_eq!(da.data_len()?, 221);
1465
1466 da.store_compressed(BinaryCompressionType::ZstdDict)?;
1467 assert_eq!(da.data_len()?, 221);
1468
1469 da.store_compressed(BinaryCompressionType::ShuffleZstd)?;
1470 assert_eq!(da.data_len()?, 221);
1471
1472 Ok(())
1473 }
1474}