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> {
760 if self.dtype == dtype {
761 return Ok(self.data.len());
762 }
763 let z = match dtype {
764 BinaryDataArrayType::Float32 => {
765 let view = self.to_f32()?;
766 #[cfg(target_endian = "big")]
767 {
768 let mut recast = to_bytes(&view);
769 dtype.swap_bytes(&mut recast)?;
770 self.dtype = dtype;
771 self.set_buffer_of_type(recast)
772 }
773 #[cfg(not(target_endian = "big"))]
774 {
775 let recast = to_bytes(&view);
776 self.dtype = dtype;
777 self.set_buffer_of_type(recast)
778 }
779
780 }
781 BinaryDataArrayType::Float64 => {
782 let view = self.to_f64()?;
783 #[cfg(target_endian = "big")]
784 {
785 let mut recast = to_bytes(&view);
786 dtype.swap_bytes(&mut recast)?;
787 self.dtype = dtype;
788 self.set_buffer_of_type(recast)
789 }
790 #[cfg(not(target_endian = "big"))]
791 {
792 let recast = to_bytes(&view);
793 self.dtype = dtype;
794 self.set_buffer_of_type(recast)
795 }
796 }
797 BinaryDataArrayType::Int32 => {
798 let view = self.to_i32()?;
799 #[cfg(target_endian = "big")]
800 {
801 let mut recast = to_bytes(&view);
802 dtype.swap_bytes(&mut recast)?;
803 self.dtype = dtype;
804 self.set_buffer_of_type(recast)
805 }
806 #[cfg(not(target_endian = "big"))]
807 {
808 let recast = to_bytes(&view);
809 self.dtype = dtype;
810 self.set_buffer_of_type(recast)
811 }
812 }
813 BinaryDataArrayType::Int64 => {
814 let view = self.to_i64()?;
815 #[cfg(target_endian = "big")]
816 {
817 let mut recast = to_bytes(&view);
818 dtype.swap_bytes(&mut recast)?;
819 self.dtype = dtype;
820 self.set_buffer_of_type(recast)
821 }
822 #[cfg(not(target_endian = "big"))]
823 {
824 let recast = to_bytes(&view);
825 self.dtype = dtype;
826 self.set_buffer_of_type(recast)
827 }
828 }
829 _ => Ok(0),
830 }?;
831 self.compression = BinaryCompressionType::Decoded;
832 Ok(z)
833 }
834
835 pub const fn is_ion_mobility(&self) -> bool {
840 self.name.is_ion_mobility()
841 }
842
843 pub fn raw_len(&self) -> usize {
845 self.data.len()
846 }
847
848 pub fn data_processing_reference(&self) -> Option<&str> {
850 self.data_processing_reference.as_deref()
851 }
852
853 pub fn set_data_processing_reference(&mut self, data_processing_reference: Option<Box<str>>) {
855 self.data_processing_reference = data_processing_reference;
856 }
857}
858
859
860impl DataArray {
865
866 pub(crate) fn compress_zlib(bytestring: &[u8]) -> Bytes {
868 let result = Bytes::new();
869 let mut compressor = ZlibEncoder::new(result, Compression::best());
870 compressor.write_all(bytestring).expect("Error compressing");
871 compressor.finish().expect("Error compressing")
872 }
873
874 pub(crate) fn decompress_zlib(bytestring: &[u8]) -> Bytes {
879 let result = Bytes::new();
880 let mut decompressor = ZlibDecoder::new(result);
881 decompressor
882 .write_all(bytestring)
883 .unwrap_or_else(|e| panic!("Decompression error: {}", e));
884 let buf = decompressor
885 .finish()
886 .unwrap_or_else(|e| panic!("Decompression error: {}", e));
887 buf
888 }
889
890 #[cfg(feature = "numpress")]
891 pub(crate) fn compress_numpress_linear(data: &[f64]) -> Result<Bytes, ArrayRetrievalError> {
896 if data.is_empty() {
897 return Ok(Bytes::new());
898 }
899 let scaling = numpress::optimal_scaling(data);
900 match numpress::numpress_compress(data, scaling) {
901 Ok(data) => Ok(data),
902 Err(e) => Err(ArrayRetrievalError::DecompressionError(e.to_string())),
903 }
904 }
905
906 #[cfg(feature = "numpress")]
907 pub(crate) fn compress_numpress_slof<T: numpress::AsFloat64>(data: &[T]) -> Result<Bytes, ArrayRetrievalError> {
908 let scaling = numpress::optimal_slof_fixed_point(data);
909 let mut buf = Bytes::new();
910 match numpress::encode_slof(data, &mut buf, scaling) {
911 Ok(_) => Ok(buf),
912 Err(e) => Err(ArrayRetrievalError::DecompressionError(e.to_string())),
913 }
914 }
915
916 #[cfg(feature = "numpress")]
917 pub(crate) fn decompress_numpress_linear(data: &[u8]) -> Result<Vec<f64>, ArrayRetrievalError> {
919 if data.is_empty() {
920 return Ok(Vec::new())
921 }
922 match numpress::numpress_decompress(data) {
923 Ok(data) => Ok(data),
924 Err(e) => Err(ArrayRetrievalError::DecompressionError(e.to_string())),
925 }
926 }
927
928 #[cfg(feature = "numpress")]
929 pub(crate) fn decompress_numpress_slof(data: &[u8], dtype: BinaryDataArrayType) -> Result<Cow<'static, [u8]>, ArrayRetrievalError> {
930 use log::trace;
931
932 let mut buf = Vec::new();
933 let decoded = match numpress::decode_slof(data, &mut buf) {
934 Ok(_) => buf,
935 Err(e) => return Err(ArrayRetrievalError::DecompressionError(e.to_string())),
936 };
937 trace!("Numpress SLOF decoded to {} points", decoded.len());
938 match dtype {
939 BinaryDataArrayType::Float64 => {
940 let view = vec_as_bytes(decoded);
941 Ok(Cow::Owned(view))
942 },
943 BinaryDataArrayType::Float32 => {
944 let n = decoded.len() * BinaryDataArrayType::Float32.size_of();
945 trace!("Mapping to {n} bytes for f32 storage");
946 let mut view: Vec<u8> = Vec::with_capacity(n);
947 for val in decoded {
948 let val = val as f32;
949 view.extend(bytemuck::bytes_of(&val))
950 }
951 Ok(Cow::Owned(view))
952 },
953 _ => {
954 Err(ArrayRetrievalError::DecompressionError(
955 BinaryCompressionType::NumpressSLOF.unsupported_msg(Some(
956 format!("Not compatible with {:?}", dtype).as_str(),
957 )),
958 ))
959 }
960 }
961 }
962
963 #[cfg(feature = "zstd")]
964 pub(crate) fn compress_zstd(
969 bytestring: &[u8],
970 dtype: BinaryDataArrayType,
971 shuffle: bool,
972 ) -> Bytes {
973 let level: i32 = std::env::var("MZDATA_ZSTD_LEVEL")
974 .map(|v| v.parse())
975 .unwrap_or(Ok(zstd::DEFAULT_COMPRESSION_LEVEL))
976 .unwrap_or(zstd::DEFAULT_COMPRESSION_LEVEL);
977 if !shuffle {
978 return zstd::bulk::compress(bytestring, level).unwrap();
979 }
980 match dtype {
981 BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => {
982 zstd::bulk::compress(bytestring, level).unwrap()
983 }
984 BinaryDataArrayType::Float64 => {
985 zstd::bulk::compress(&transpose_f64(bytemuck::cast_slice(bytestring)), level)
986 .unwrap()
987 }
988 BinaryDataArrayType::Float32 => {
989 zstd::bulk::compress(&transpose_f32(bytemuck::cast_slice(bytestring)), level)
990 .unwrap()
991 }
992 BinaryDataArrayType::Int64 => {
993 zstd::bulk::compress(&transpose_i64(bytemuck::cast_slice(bytestring)), level)
994 .unwrap()
995 }
996 BinaryDataArrayType::Int32 => {
997 zstd::bulk::compress(&transpose_i32(bytemuck::cast_slice(bytestring)), level)
998 .unwrap()
999 }
1000 }
1001 }
1002
1003 #[cfg(feature = "zstd")]
1004 pub(crate) fn compress_delta_zstd(
1005 bytestring: &[u8],
1006 dtype: BinaryDataArrayType,
1007 shuffle: bool,
1008 ) -> Bytes {
1009 use bytemuck::cast_slice;
1010
1011 use super::delta_encoding;
1012
1013 match dtype {
1014 BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => {
1015 Self::compress_zstd(bytestring, dtype, shuffle)
1016 }
1017 BinaryDataArrayType::Float64 => {
1018 let mut buf = cast_slice::<_, f64>(bytestring).to_vec();
1019 delta_encoding(&mut buf);
1020 Self::compress_zstd(cast_slice(&buf), dtype, shuffle)
1021 }
1022 BinaryDataArrayType::Float32 => {
1023 let mut buf = cast_slice::<_, f32>(bytestring).to_vec();
1024 delta_encoding(&mut buf);
1025 Self::compress_zstd(cast_slice(&buf), dtype, shuffle)
1026 }
1027 BinaryDataArrayType::Int64 => {
1028 let mut buf = cast_slice::<_, i64>(bytestring).to_vec();
1029 delta_encoding(&mut buf);
1030 Self::compress_zstd(cast_slice(&buf), dtype, shuffle)
1031 }
1032 BinaryDataArrayType::Int32 => {
1033 let mut buf = cast_slice::<_, i32>(bytestring).to_vec();
1034 delta_encoding(&mut buf);
1035 Self::compress_zstd(cast_slice(&buf), dtype, shuffle)
1036 }
1037 }
1038 }
1039
1040 #[cfg(feature = "zstd")]
1041 pub(crate) fn compress_dict_zstd(bytestring: &[u8], dtype: BinaryDataArrayType) -> Bytes {
1042 use super::encodings::dictionary_encoding;
1043 log::trace!("Dictionary encoding {} bytes as {dtype}", bytestring.len());
1044 if bytestring.is_empty() {
1045 return Self::compress_zstd(&bytestring, dtype, false);
1046 }
1047 match dtype {
1048 BinaryDataArrayType::Float64 => {
1049 let compressed =
1050 dictionary_encoding(bytemuck::cast_slice::<u8, f64>(&bytestring))
1051 .unwrap();
1052 let compressed = Self::compress_zstd(&compressed, dtype, false);
1053 compressed
1054 }
1055 BinaryDataArrayType::Float32 => {
1056 let compressed =
1057 dictionary_encoding(bytemuck::cast_slice::<u8, f32>(&bytestring))
1058 .unwrap();
1059 let compressed = Self::compress_zstd(&compressed, dtype, false);
1060 compressed
1061 }
1062 BinaryDataArrayType::Int64 => {
1063 let compressed =
1064 dictionary_encoding(bytemuck::cast_slice::<u8, i64>(&bytestring))
1065 .unwrap();
1066 let compressed = Self::compress_zstd(&compressed, dtype, false);
1067 compressed
1068 }
1069 BinaryDataArrayType::Int32 => {
1070 let compressed =
1071 dictionary_encoding(bytemuck::cast_slice::<u8, i32>(&bytestring))
1072 .unwrap();
1073 let compressed = Self::compress_zstd(&compressed, dtype, false);
1074 compressed
1075 }
1076 _ => {
1077 let compressed =
1078 dictionary_encoding(bytemuck::cast_slice::<u8, u8>(&bytestring))
1079 .unwrap();
1080 let compressed = Self::compress_zstd(&compressed, dtype, false);
1081 compressed
1082 }
1083 }
1084 }
1085
1086 #[cfg(feature = "zstd")]
1087 pub(crate) fn decompress_zstd(data: &[u8], dtype: BinaryDataArrayType, shuffle: bool) -> Bytes {
1088 let mut decoder = zstd::Decoder::new(std::io::Cursor::new(data)).unwrap();
1089 let mut buf = Vec::new();
1090 decoder.read_to_end(&mut buf).unwrap();
1091 if !shuffle {
1092 return buf;
1093 }
1094 match dtype {
1095 BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => buf,
1096 BinaryDataArrayType::Float64 => reverse_transpose_f64(&buf),
1097 BinaryDataArrayType::Float32 => reverse_transpose_f32(&buf),
1098 BinaryDataArrayType::Int64 => reverse_transpose_i64(&buf),
1099 BinaryDataArrayType::Int32 => reverse_transpose_i32(&buf),
1100 }
1101 }
1102
1103 #[cfg(feature = "zstd")]
1104 pub(crate) fn decompress_delta_zstd(
1105 data: &[u8],
1106 dtype: BinaryDataArrayType,
1107 shuffle: bool,
1108 ) -> Bytes {
1109 use super::delta_decoding;
1110
1111 let mut delta = Self::decompress_zstd(data, dtype, shuffle);
1112 match dtype {
1113 BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => delta,
1114 BinaryDataArrayType::Float64 => {
1115 let buf = bytemuck::cast_slice_mut::<_, f64>(&mut delta);
1116 delta_decoding(buf);
1117 delta
1118 }
1119 BinaryDataArrayType::Float32 => {
1120 let buf = bytemuck::cast_slice_mut::<_, f32>(&mut delta);
1121 delta_decoding(buf);
1122 delta
1123 }
1124 BinaryDataArrayType::Int64 => {
1125 let buf = bytemuck::cast_slice_mut::<_, i64>(&mut delta);
1126 delta_decoding(buf);
1127 delta
1128 }
1129 BinaryDataArrayType::Int32 => {
1130 let buf = bytemuck::cast_slice_mut::<_, i32>(&mut delta);
1131 delta_decoding(buf);
1132 delta
1133 }
1134 }
1135 }
1136
1137 #[cfg(feature = "zstd")]
1138 pub(crate) fn decompress_dict_zstd(bytestring: &[u8], dtype: BinaryDataArrayType) -> Bytes {
1139 use super::encodings::dictionary_decoding;
1140
1141 let data = Self::decompress_zstd(bytestring, dtype, false);
1142 match dtype {
1143 BinaryDataArrayType::ASCII | BinaryDataArrayType::Unknown => dictionary_decoding(&data).unwrap(),
1144 BinaryDataArrayType::Float64 => {
1145 to_bytes(&dictionary_decoding::<f64>(&data).unwrap())
1146 },
1147 BinaryDataArrayType::Float32 => {
1148 to_bytes(&dictionary_decoding::<f32>(&data).unwrap())
1149 },
1150 BinaryDataArrayType::Int64 => {
1151 to_bytes(&dictionary_decoding::<i64>(&data).unwrap())
1152 },
1153 BinaryDataArrayType::Int32 => {
1154 to_bytes(&dictionary_decoding::<i32>(&data).unwrap())
1155 },
1156 }
1157 }
1158}
1159
1160impl<'transient, 'lifespan: 'transient> ByteArrayView<'transient, 'lifespan> for DataArray {
1161 fn view(&'lifespan self) -> Result<Cow<'lifespan, [u8]>, ArrayRetrievalError> {
1162 self.decode()
1163 }
1164
1165 fn dtype(&self) -> BinaryDataArrayType {
1166 self.dtype
1167 }
1168
1169 fn data_len(&'lifespan self) -> Result<usize, ArrayRetrievalError> {
1170 if let Some(z) = self.item_count {
1171 Ok(z.get())
1172 } else {
1173 let view = self.view()?;
1174 let n = view.len();
1175 Ok(n / self.dtype().size_of())
1176 }
1177 }
1178
1179 fn unit(&self) -> Unit {
1180 self.unit
1181 }
1182
1183 fn data_processing_reference(&self) -> Option<&str> {
1184 self.data_processing_reference()
1185 }
1186
1187 fn name(&self) -> &ArrayType {
1188 &self.name
1189 }
1190}
1191
1192impl<'transient, 'lifespan: 'transient> ByteArrayViewMut<'transient, 'lifespan> for DataArray {
1193 fn view_mut(&'transient mut self) -> Result<&'transient mut Bytes, ArrayRetrievalError> {
1194 self.decode_mut()
1195 }
1196
1197 fn unit_mut(&mut self) -> &mut Unit {
1198 &mut self.unit
1199 }
1200
1201 fn set_data_processing_reference(&mut self, data_processing_reference: Option<Box<str>>) {
1202 self.set_data_processing_reference(data_processing_reference);
1203 }
1204}
1205
1206mzdata_param::impl_param_described_deferred!(DataArray);
1207
1208#[derive(Clone, Debug)]
1212pub struct DataArraySlice<'a> {
1213 source: &'a DataArray,
1214 pub start: usize,
1216 pub end: usize,
1218}
1219
1220impl<'a> DataArraySlice<'a> {
1221 pub fn new(source: &'a DataArray, mut start: usize, mut end: usize) -> Self {
1223 if start > end {
1224 mem::swap(&mut start, &mut end);
1225 }
1226 Self { source, start, end }
1227 }
1228
1229 pub fn decode(&'a self) -> Result<Cow<'a, [u8]>, ArrayRetrievalError> {
1236 self.source.decoded_slice(self.start, self.end)
1237 }
1238
1239 pub const fn is_ion_mobility(&self) -> bool {
1244 self.source.is_ion_mobility()
1245 }
1246
1247 pub fn source(&self) -> &'a DataArray {
1249 self.source
1250 }
1251}
1252
1253impl<'transient, 'lifespan: 'transient> ByteArrayView<'transient, 'lifespan>
1254 for DataArraySlice<'lifespan>
1255{
1256 fn view(&'lifespan self) -> Result<Cow<'lifespan, [u8]>, ArrayRetrievalError> {
1257 self.decode()
1258 }
1259
1260 fn dtype(&self) -> BinaryDataArrayType {
1261 self.source.dtype()
1262 }
1263
1264 fn unit(&self) -> Unit {
1265 self.source.unit
1266 }
1267
1268 fn name(&self) -> &ArrayType {
1269 self.source.name()
1270 }
1271
1272 fn data_processing_reference(&self) -> Option<&str> {
1273 self.source.data_processing_reference()
1274 }
1275}
1276
1277#[cfg(test)]
1278mod test {
1279 use super::*;
1280 use std::fs;
1281 use std::io;
1282
1283 use super::DataArray;
1284
1285 fn make_array_from_file() -> io::Result<DataArray> {
1286 let mut fh = fs::File::open("../../test/data/mz_f64_zlib_bas64.txt")?;
1287 let mut buf = String::new();
1288 fh.read_to_string(&mut buf)?;
1289 let bytes: Vec<u8> = buf.into();
1290 let mut da = DataArray::wrap(&ArrayType::MZArray, BinaryDataArrayType::Float64, bytes);
1291 da.compression = BinaryCompressionType::Zlib;
1292 *da.unit_mut() = Unit::MZ;
1293 assert_eq!(da.unit(), Unit::MZ);
1294 assert!(!da.is_ion_mobility());
1295 assert_eq!(da.name(), &ArrayType::MZArray);
1296 Ok(da)
1297 }
1298
1299 #[cfg(feature = "zstd")]
1300 fn make_array_from_file_im_zstd() -> io::Result<DataArray> {
1301 let mut fh = fs::File::open("../../test/data/im_f64_zstd_base64.txt")?;
1302 let mut buf = String::new();
1303 fh.read_to_string(&mut buf)?;
1304 let bytes: Vec<u8> = buf.into();
1305 let mut da = DataArray::wrap(&ArrayType::MeanInverseReducedIonMobilityArray, BinaryDataArrayType::Float64, bytes);
1306 da.compression = BinaryCompressionType::Zstd;
1307 *da.unit_mut() = Unit::VoltSecondPerSquareCentimeter;
1308 assert_eq!(da.unit(), Unit::VoltSecondPerSquareCentimeter);
1309 assert!(da.is_ion_mobility());
1310 assert_eq!(da.name(), &ArrayType::MeanInverseReducedIonMobilityArray);
1311 Ok(da)
1312 }
1313
1314 #[test]
1315 fn test_decode() -> io::Result<()> {
1316 let mut da = make_array_from_file()?;
1317 da.decode_and_store()?;
1318 let view = da.to_f64()?;
1319 assert_eq!(view.len(), 19800);
1320 Ok(())
1321 }
1322
1323 #[test]
1324 fn test_decode_store() -> io::Result<()> {
1325 let mut da = make_array_from_file()?;
1326 da.decode_and_store()?;
1327 let back = da.clone();
1328 da.store_as(BinaryDataArrayType::Float32)?;
1329 let view = da.to_f64()?;
1330 assert_eq!(view.len(), 19800);
1331 for (a, b) in back.iter_f64()?.zip(view.iter().copied()) {
1332 let err = (a - b).abs();
1333 assert!((a - b).abs() < 1e-3, "{} - {} = {}", a, b, err);
1334 }
1335 for (a, b) in back.iter_f64()?.zip(da.iter_f32()?.map(|x| x as f64)) {
1336 let err = (a - b).abs();
1337 assert!((a - b).abs() < 1e-3, "{} - {} = {}", a, b, err);
1338 }
1339 da.store_as(BinaryDataArrayType::Float64)?;
1340 let view = da.to_f64()?;
1341 assert_eq!(view.len(), 19800);
1342 for (a, b) in back.iter_f64()?.zip(view.iter().copied()) {
1343 let err = (a - b).abs();
1344 assert!((a - b).abs() < 1e-3, "{} - {} = {}", a, b, err);
1345 }
1346 Ok(())
1347 }
1348
1349 #[cfg(feature = "zstd")]
1350 #[test]
1351 fn test_decode_delta_zstd() {
1352 let points: Vec<f64> = (0..200_000usize).map(|i| i as f64 * 0.01 + 100.0).collect();
1353 let mut da = DataArray::from_name(&ArrayType::MZArray);
1354 da.extend(&points).unwrap();
1355
1356 let decoded_len = da.data.len();
1357 da.store_compressed(BinaryCompressionType::ShuffleZstd)
1358 .unwrap();
1359 let zstd_len = da.data.len();
1360
1361 da.store_compressed(BinaryCompressionType::Zlib).unwrap();
1362 let zlib_len = da.data.len();
1363
1364 da.decode_and_store().unwrap();
1365
1366 da.store_compressed(BinaryCompressionType::DeltaShuffleZstd)
1367 .unwrap();
1368 let delta_zstd_len = da.data.len();
1369 eprintln!("decoded: {decoded_len};\nzlib: {zlib_len};\nzstd: {zstd_len};\ndelta-zstd: {delta_zstd_len}");
1370 da.decode_and_store().unwrap();
1371 let view = da.to_f64().unwrap();
1372 let err: f64 = points
1373 .iter()
1374 .zip(view.iter())
1375 .map(|(a, b)| {
1376 assert!((a - b).abs() < 1e-3, "{a} - {b} = {}", a - b);
1377 (a - b).abs()
1378 })
1379 .sum();
1380 let mean_err = err / (points.len() as f64);
1381 eprintln!("mean abs error: {mean_err:0.8}")
1382 }
1383
1384 #[cfg(feature = "zstd")]
1385 #[test]
1386 fn test_decode_zstd() -> io::Result<()> {
1387 let mut da = make_array_from_file()?;
1388 let zlib_len = da.data.len();
1389 da.decode_and_store()?;
1390
1391 let decoded_len = da.data.len();
1392
1393 da.store_compressed(BinaryCompressionType::ShuffleZstd)?;
1394
1395 let zstd_len = da.data.len();
1396
1397 eprintln!("zlib: {zlib_len};\ndecoded: {decoded_len};\nzstd: {zstd_len}");
1398 da.decode_and_store()?;
1399
1400 let mut da_ref = make_array_from_file()?;
1401 da_ref.decode_and_store()?;
1402 assert_eq!(da.data, da_ref.data);
1403 Ok(())
1404 }
1405
1406 #[cfg(feature = "numpress")]
1407 #[test]
1408 fn test_numpress_linear() -> io::Result<()> {
1409 let mut da = make_array_from_file()?;
1410 let zlib_len = da.data.len();
1411 da.decode_and_store()?;
1412
1413 let decoded_len = da.data.len();
1414
1415 da.store_compressed(BinaryCompressionType::NumpressLinear)?;
1416 let numpress_len = da.data.len();
1417
1418 eprintln!("zlib: {zlib_len};\ndecoded: {decoded_len};\nnumpress: {numpress_len}");
1419 da.decode_and_store()?;
1420
1421 let mut da_ref = make_array_from_file()?;
1422 da_ref.decode_and_store()?;
1423 for (a, b) in da.iter_f64()?.zip(da_ref.iter_f64()?) {
1424 assert!((a - b).abs() < 1e-3, "{a} - {b} = {} which is too large a deviation", (a - b).abs())
1425 }
1426
1427 Ok(())
1428 }
1429
1430 #[test]
1431 fn test_decode_roundtrip() -> io::Result<()> {
1432 let mut da = make_array_from_file()?;
1433 let compressed_size = da.data_len()?;
1434 da.decode_and_store()?;
1435 let view = da.to_f64()?;
1436 assert_eq!(view.len(), 19800);
1437 drop(view);
1438 da.store_compressed(BinaryCompressionType::Zlib)?;
1439 assert_eq!(da.compression, BinaryCompressionType::Zlib);
1440 assert_eq!(da.data_len()?, compressed_size);
1441 let view = da.to_f64()?;
1442 assert_eq!(view.len(), 19800);
1443 Ok(())
1444 }
1445
1446 #[test]
1447 fn test_decode_empty() {
1448 let mut da = DataArray::wrap(
1449 &ArrayType::MZArray,
1450 BinaryDataArrayType::Float64,
1451 Vec::new(),
1452 );
1453 da.compression = BinaryCompressionType::Zlib;
1454
1455 assert_eq!(da.data.len(), 0);
1456 assert_eq!(da.data_len().unwrap(), 0);
1457 assert_eq!(da.decode().unwrap().len(), 0);
1458 assert_eq!(da.to_f64().unwrap().len(), 0);
1459 }
1460
1461
1462 #[cfg(feature = "zstd")]
1463 #[test]
1464 fn test_dict_from_base64() -> io::Result<()> {
1465 let mut da = make_array_from_file_im_zstd()?;
1466
1467 da.decode_and_store()?;
1468 assert_eq!(da.data_len()?, 221);
1469
1470 da.store_compressed(BinaryCompressionType::ZstdDict)?;
1471 assert_eq!(da.data_len()?, 221);
1472
1473 da.store_compressed(BinaryCompressionType::ShuffleZstd)?;
1474 assert_eq!(da.data_len()?, 221);
1475
1476 Ok(())
1477 }
1478}