Skip to main content

mzdata_bindata/
encodings.rs

1use bytemuck::{self, Pod};
2
3use std::{
4    fmt::Display,
5    io,
6    ops::{AddAssign, Mul},
7};
8
9use thiserror::{self, Error};
10use num_traits::{Num, ToBytes};
11
12#[cfg(feature = "numpress")]
13use numpress;
14
15use mzdata_param::{curie, Param, ControlledVocabulary, ParamCow, Unit, CURIE, ValueRef};
16
17pub type Bytes = Vec<u8>;
18
19/// Convert the values in `data` into an owned buffer of little-endian bytes
20pub fn to_bytes<T: Pod + ToBytes>(data: &[T]) -> Bytes {
21    let n = data.len();
22    let mut buf = Vec::with_capacity(n * size_of::<T>());
23    for v in data {
24        buf.extend_from_slice(v.to_le_bytes().as_ref());
25    }
26    buf
27}
28
29pub fn as_bytes<T: Pod>(data: &[T]) -> &[u8] {
30    bytemuck::cast_slice(data)
31}
32
33pub fn vec_as_bytes<T: Pod>(data: Vec<T>) -> Bytes {
34    let mut buf = Bytes::with_capacity(data.len() * std::mem::size_of::<T>());
35    for val in data {
36        buf.extend_from_slice(bytemuck::bytes_of(&val));
37    }
38    buf
39}
40
41mod byte_rotation {
42    use super::*;
43
44    pub fn transpose_bytes_into<T: Pod, const N: usize>(data: &[T], buffer: &mut Vec<u8>) {
45        assert_eq!(core::mem::size_of::<T>(), N);
46        let bytes = bytemuck::cast_slice::<T, [u8; N]>(data);
47        buffer.clear();
48        let delta = (data.len() * N).saturating_sub(buffer.capacity());
49        if delta > 0 {
50            buffer.reserve(delta);
51        }
52
53        #[cfg(target_endian = "little")]
54        {
55            for i in 0..N {
56                buffer.extend(bytes.iter().map(|b| b[i]))
57            }
58        }
59        #[cfg(target_endian = "big")]
60        {
61            for i in (0..N).rev() {
62                buffer.extend(bytes.iter().map(|b| b[i]))
63            }
64        }
65    }
66
67    pub fn transpose_bytes<T: Pod, const N: usize>(data: &[T]) -> Bytes {
68        let mut result = Bytes::with_capacity(data.len() * N);
69        transpose_bytes_into::<T, N>(data, &mut result);
70        result
71    }
72
73    pub fn transpose_4bytes<T: Pod>(data: &[T]) -> Bytes {
74        assert_eq!(std::mem::size_of::<T>(), 4);
75        transpose_bytes::<_, 4>(data)
76    }
77
78    pub fn transpose_8bytes<T: Pod>(data: &[T]) -> Bytes {
79        assert_eq!(std::mem::size_of::<T>(), 8);
80        transpose_bytes::<_, 8>(data)
81    }
82
83    pub fn transpose_i32(data: &[i32]) -> Bytes {
84        transpose_4bytes(data)
85    }
86
87    pub fn transpose_f32(data: &[f32]) -> Bytes {
88        transpose_4bytes(data)
89    }
90
91    pub fn transpose_i64(data: &[i64]) -> Bytes {
92        transpose_8bytes(data)
93    }
94
95    pub fn transpose_f64(data: &[f64]) -> Bytes {
96        transpose_8bytes(data)
97    }
98
99    pub fn reverse_transpose_bytes_into<const N: usize>(data: &[u8], buffer: &mut Vec<u8>) {
100        let rem = data.len() % N;
101        assert_eq!(rem, 0);
102        let n_entries = data.len() / N;
103        buffer.clear();
104        buffer.resize(data.len(), 0);
105
106        #[cfg(target_endian = "little")]
107        {
108            for (i, band) in data.chunks_exact(n_entries).enumerate() {
109                for (j, byte) in band.iter().copied().enumerate() {
110                    bytemuck::cast_slice_mut::<_, [u8; N]>(buffer)[j][i] = byte;
111                }
112            }
113        }
114        #[cfg(target_endian = "big")]
115        {
116            for (i, band) in data.chunks_exact(n_entries).enumerate() {
117                for (j, byte) in band.iter().copied().enumerate() {
118                    bytemuck::cast_slice_mut::<_, [u8; N]>(buffer)[j][(N - 1) - i] = byte;
119                }
120            }
121        }
122    }
123
124    pub fn reverse_transpose_bytes<const N: usize>(data: &[u8]) -> Bytes {
125        let mut result: Bytes = vec![0; data.len()];
126        reverse_transpose_bytes_into::<N>(data, &mut result);
127        result
128    }
129
130    pub fn reverse_transpose_4bytes<T: Pod>(data: &[u8]) -> Bytes {
131        assert_eq!(std::mem::size_of::<T>(), 4);
132        reverse_transpose_bytes::<4>(data)
133    }
134
135    pub fn reverse_transpose_8bytes<T: Pod>(data: &[u8]) -> Bytes {
136        assert_eq!(std::mem::size_of::<T>(), 8);
137        reverse_transpose_bytes::<8>(data)
138    }
139
140    pub fn reverse_transpose_i32(data: &[u8]) -> Vec<u8> {
141        reverse_transpose_4bytes::<i32>(data)
142    }
143
144    pub fn reverse_transpose_f32(data: &[u8]) -> Vec<u8> {
145        reverse_transpose_4bytes::<f32>(data)
146    }
147
148    pub fn reverse_transpose_i64(data: &[u8]) -> Vec<u8> {
149        reverse_transpose_8bytes::<i64>(data)
150    }
151
152    pub fn reverse_transpose_f64(data: &[u8]) -> Vec<u8> {
153        reverse_transpose_8bytes::<f64>(data)
154    }
155}
156
157mod dictionary_encoding {
158    use super::*;
159    use io::prelude::*;
160    use num_traits::ops::bytes::{FromBytes, ToBytes};
161    use std::{
162        borrow::Cow,
163        collections::{HashMap, HashSet},
164        hash::Hash,
165        io::BufWriter,
166    };
167
168    trait DictValue<const W: usize>:
169        Pod + ToBytes<Bytes = [u8; W]> + Hash + Eq + Ord + FromBytes<Bytes = [u8; W]>
170    {
171    }
172
173    macro_rules! impl_dict_value {
174        ($val:ty, $size:literal) => {
175            impl DictValue<$size> for $val {}
176        };
177    }
178
179    impl_dict_value!(u8, 1);
180    impl_dict_value!(u16, 2);
181    impl_dict_value!(u32, 4);
182    impl_dict_value!(u64, 8);
183
184    trait DictIndex<const W: usize>:
185        Pod + ToBytes<Bytes = [u8; W]> + FromBytes<Bytes = [u8; W]>
186    {
187        fn from_usize(index: usize) -> Self;
188        fn to_usize(&self) -> usize;
189    }
190
191    macro_rules! impl_dict_index {
192        ($idx:ty, $size:literal) => {
193            impl DictIndex<$size> for $idx {
194                fn from_usize(index: usize) -> Self {
195                    index as Self
196                }
197
198                fn to_usize(&self) -> usize {
199                    *self as usize
200                }
201            }
202        };
203    }
204
205    impl_dict_index!(u8, 1);
206    impl_dict_index!(u16, 2);
207    impl_dict_index!(u32, 4);
208    impl_dict_index!(u64, 8);
209
210    #[derive(Default, Debug)]
211    pub struct DictionaryEncoder {
212        shuffle: bool,
213        buffer: Vec<u8>,
214    }
215
216    impl DictionaryEncoder {
217        pub fn new(shuffle: bool) -> Self {
218            Self {
219                shuffle,
220                buffer: Vec::new(),
221            }
222        }
223
224        fn build_value_map<T: Pod, const W1: usize, V: DictValue<W1>>(
225            &self,
226            data: &[T],
227        ) -> (Vec<V>, HashMap<V, usize>) {
228            debug_assert_eq!(core::mem::size_of::<T>(), core::mem::size_of::<V>());
229            debug_assert_eq!(core::mem::size_of::<T>(), W1);
230            let mut value_codes = HashSet::new();
231            for v in data {
232                let k: V = *bytemuck::from_bytes(bytemuck::bytes_of(v));
233                value_codes.insert(k);
234            }
235
236            let mut value_codes: Vec<_> = value_codes.into_iter().collect();
237            value_codes.sort();
238
239            let byte_map: HashMap<V, usize> = value_codes
240                .iter()
241                .enumerate()
242                .map(|(i, k)| (*k, i))
243                .collect();
244            (value_codes, byte_map)
245        }
246
247        fn create_writer<
248            T: Pod,
249            const W1: usize,
250            V: Pod + Ord + Hash + ToBytes<Bytes = [u8; W1]> + Eq,
251            const W2: usize,
252            K: DictIndex<W2>,
253        >(
254            &self,
255            data: &[T],
256            value_codes: &[V],
257        ) -> BufWriter<Vec<u8>> {
258            let data_offset = 16 + std::mem::size_of_val(value_codes);
259            let dict_buffer: Vec<u8> =
260                Vec::with_capacity(data_offset + data.len() * core::mem::size_of::<K>());
261            BufWriter::new(dict_buffer)
262        }
263
264        fn encode_dict_indices<
265            T: Pod,
266            const W1: usize,
267            V: Pod + Ord + Hash + ToBytes<Bytes = [u8; W1]> + Eq,
268            const W2: usize,
269            K: DictIndex<W2>,
270        >(
271            &mut self,
272            data: &[T],
273            value_codes: &[V],
274            byte_map: HashMap<V, usize>,
275        ) -> io::Result<Vec<u8>> {
276            let data_offset = 16 + std::mem::size_of_val(value_codes);
277            let mut writer = self.create_writer::<T, W1, V, W2, K>(data, value_codes);
278            writer.write_all(&(data_offset as u64).to_le_bytes())?;
279            writer.write_all(&(value_codes.len() as u64).to_le_bytes())?;
280
281            if self.shuffle {
282                // This isn't endian-correct yet, see note in `transpose_bytes_into`
283                byte_rotation::transpose_bytes_into::<V, W1>(value_codes, &mut self.buffer);
284                writer.write_all(&self.buffer)?;
285                // for v in self.buffer.chunks(W1) {
286                //     writer.write_all(v)?;
287                // }
288            } else {
289                for v in value_codes.iter() {
290                    let bts = v.to_le_bytes();
291                    writer.write_all(&bts)?;
292                }
293            }
294
295            if self.shuffle {
296                let mut buf = Vec::with_capacity(data.len());
297                for v in data {
298                    let i = *byte_map
299                        .get(bytemuck::from_bytes(bytemuck::bytes_of(v)))
300                        .unwrap();
301                    let ik: K = K::from_usize(i);
302                    buf.push(ik);
303                }
304                // This isn't endian-correct
305                byte_rotation::transpose_bytes_into::<K, W2>(&buf, &mut self.buffer);
306                writer.write_all(&self.buffer)?;
307            } else {
308                for v in data {
309                    let i = *byte_map
310                        .get(bytemuck::from_bytes(bytemuck::bytes_of(v)))
311                        .unwrap();
312                    let ik: K = K::from_usize(i);
313                    writer.write_all(&ik.to_le_bytes())?;
314                }
315            }
316
317            writer.flush()?;
318            let val = writer.into_inner().unwrap();
319            Ok(val)
320        }
321
322        fn encode_values<T: Pod, const W1: usize, V: DictValue<W1>>(
323            &mut self,
324            data: &[T],
325        ) -> Result<Vec<u8>, io::Error> {
326            let (value_codes, byte_map) = self.build_value_map(data);
327            let n_value_codes = value_codes.len();
328
329            if n_value_codes <= 2usize.pow(8) {
330                self.encode_dict_indices::<T, W1, V, 1, u8>(data, &value_codes, byte_map)
331            } else if n_value_codes <= 2usize.pow(16) {
332                self.encode_dict_indices::<T, W1, V, 2, u16>(data, &value_codes, byte_map)
333            } else if n_value_codes <= 2usize.pow(32) {
334                self.encode_dict_indices::<T, W1, V, 4, u32>(data, &value_codes, byte_map)
335            } else if n_value_codes <= 2usize.pow(64) {
336                self.encode_dict_indices::<T, W1, V, 8, u64>(data, &value_codes, byte_map)
337            } else {
338                Err(io::Error::new(
339                    io::ErrorKind::Unsupported,
340                    "Cannot encode a dictionary with more than 2 ** 64 values",
341                ))
342            }
343        }
344
345        pub fn encode<T: Pod>(&mut self, data: &[T]) -> io::Result<Bytes> {
346            if data.is_empty() {
347                return Ok(Vec::new());
348            }
349            let z_val = core::mem::size_of::<T>();
350            if z_val <= 1 {
351                self.encode_values::<T, 1, u8>(data)
352            } else if z_val <= 2 {
353                self.encode_values::<T, 2, u16>(data)
354            } else if z_val <= 4 {
355                self.encode_values::<T, 4, u32>(data)
356            } else if z_val <= 8 {
357                self.encode_values::<T, 8, u64>(data)
358            } else {
359                Err(io::Error::new(
360                    io::ErrorKind::Unsupported,
361                    "Cannot encode a dictionary with more than 2 ** 64 keys",
362                ))
363            }
364        }
365    }
366
367    #[derive(Default, Debug)]
368    pub struct DictionaryDecoder {
369        shuffle: bool,
370        buffer: Vec<u8>,
371    }
372
373    impl DictionaryDecoder {
374        pub fn new(shuffle: bool) -> Self {
375            Self {
376                shuffle,
377                buffer: Default::default(),
378            }
379        }
380
381        fn make_reader<'a>(&self, buffer: &'a [u8]) -> io::BufReader<&'a [u8]> {
382            io::BufReader::new(buffer)
383        }
384
385        fn decode_value_buffer<T: Pod, const W1: usize, V: DictValue<W1>>(
386            &mut self,
387            buffer: &[u8],
388            n_values: usize,
389        ) -> Vec<T> {
390            macro_rules! decode_chunk {
391                ($chunk:ident) => {{
392                    let chunk_a: [u8; W1] = $chunk.try_into().unwrap();
393                    let val = V::from_le_bytes(&chunk_a);
394                    let val: T = *bytemuck::from_bytes(bytemuck::bytes_of(&val));
395                    val
396                }};
397            }
398            let mut value_buffer = Vec::with_capacity(n_values);
399            if self.shuffle {
400                let blocks = match core::mem::size_of::<T>() {
401                    1 => Cow::Borrowed(buffer),
402                    2 => {
403                        byte_rotation::reverse_transpose_bytes_into::<2>(buffer, &mut self.buffer);
404                        Cow::Borrowed(self.buffer.as_slice())
405                    }
406                    4 => {
407                        byte_rotation::reverse_transpose_bytes_into::<4>(buffer, &mut self.buffer);
408                        Cow::Borrowed(self.buffer.as_slice())
409                    }
410                    8 => {
411                        byte_rotation::reverse_transpose_bytes_into::<8>(buffer, &mut self.buffer);
412                        Cow::Borrowed(self.buffer.as_slice())
413                    }
414                    x => {
415                        panic!("Unsupported size {x}");
416                    }
417                };
418                for chunk in blocks.chunks_exact(W1) {
419                    let val = decode_chunk!(chunk);
420                    value_buffer.push(val);
421                }
422            } else {
423                for chunk in buffer.chunks_exact(W1) {
424                    let val = decode_chunk!(chunk);
425                    value_buffer.push(val);
426                }
427            };
428            value_buffer
429        }
430
431        fn decode_index_buffer<T: Pod, const W2: usize, K: DictIndex<W2>>(
432            &mut self,
433            value_codes: &[T],
434            index_buffer: &[u8],
435        ) -> Vec<T> {
436            let mut result = Vec::with_capacity(index_buffer.len() / W2);
437            if self.shuffle {
438                byte_rotation::reverse_transpose_bytes_into::<W2>(index_buffer, &mut self.buffer);
439                for chunk in self.buffer.chunks_exact(W2) {
440                    let b: [u8; W2] = chunk.try_into().unwrap();
441                    let k: usize = K::from_le_bytes(&b).to_usize();
442                    result.push(value_codes[k])
443                }
444            } else {
445                for chunk in index_buffer.chunks_exact(W2) {
446                    let b: [u8; W2] = chunk.try_into().unwrap();
447                    let k: usize = K::from_le_bytes(&b).to_usize();
448                    result.push(value_codes[k])
449                }
450            }
451            result
452        }
453
454        pub fn decode<T: Pod>(&mut self, buffer: &[u8]) -> io::Result<Vec<T>> {
455            if buffer.is_empty() {
456                return Ok(Vec::new());
457            }
458            let mut reader = self.make_reader(buffer);
459            let mut z_buf = [0u8; 8];
460            reader.read_exact(&mut z_buf)?;
461            let data_offset = u64::from_le_bytes(z_buf);
462            if data_offset == 0 {
463                return Ok(Vec::new());
464            }
465            let mut z_buf = [0u8; 8];
466            reader.read_exact(&mut z_buf)?;
467            let n_value_codes = u64::from_le_bytes(z_buf);
468            if n_value_codes == 0 {
469                return Ok(Vec::new());
470            }
471            let value_buffer = &buffer[16..(data_offset as usize)];
472            let value_width = (data_offset - 16) / n_value_codes;
473            let index_buffer = &buffer[data_offset as usize..];
474
475            let n_value_codes = n_value_codes as usize;
476
477            macro_rules! decode_indices {
478                ($values:ident) => {
479                    if n_value_codes <= 2usize.pow(8) {
480                        self.decode_index_buffer::<T, 1, u8>(&$values, index_buffer)
481                    } else if n_value_codes <= 2usize.pow(16) {
482                        self.decode_index_buffer::<T, 2, u16>(&$values, index_buffer)
483                    } else if n_value_codes <= 2usize.pow(32) {
484                        self.decode_index_buffer::<T, 4, u32>(&$values, index_buffer)
485                    } else if n_value_codes <= 2usize.pow(64) {
486                        self.decode_index_buffer::<T, 8, u64>(&$values, index_buffer)
487                    } else {
488                        return Err(io::Error::new(
489                            io::ErrorKind::Unsupported,
490                            "Cannot decode a dictionary with more than 2 ** 64 indices",
491                        ));
492                    }
493                };
494            }
495
496            let values = if value_width <= 1 {
497                let values = self.decode_value_buffer::<T, 1, u8>(value_buffer, n_value_codes);
498                decode_indices!(values)
499            } else if value_width <= 2 {
500                let values = self.decode_value_buffer::<T, 2, u16>(value_buffer, n_value_codes);
501                decode_indices!(values)
502            } else if value_width <= 4 {
503                let values = self.decode_value_buffer::<T, 4, u32>(value_buffer, n_value_codes);
504                decode_indices!(values)
505            } else if value_width <= 8 {
506                let values = self.decode_value_buffer::<T, 8, u64>(value_buffer, n_value_codes);
507                decode_indices!(values)
508            } else {
509                return Err(io::Error::new(
510                    io::ErrorKind::Unsupported,
511                    "Cannot decode dictionary with value byte width greater than 8",
512                ));
513            };
514
515            Ok(values)
516        }
517    }
518
519    pub fn dictionary_encoding<T: Pod>(data: &[T]) -> Result<Vec<u8>, io::Error> {
520        let mut encoder = DictionaryEncoder::new(true);
521        encoder.encode(data)
522    }
523
524    pub fn dictionary_decoding<T: Pod>(buffer: &[u8]) -> io::Result<Vec<T>> {
525        let mut decoder = DictionaryDecoder::new(true);
526        decoder.decode(buffer)
527    }
528}
529
530pub use byte_rotation::*;
531
532pub use dictionary_encoding::{dictionary_decoding, dictionary_encoding};
533
534/// The kinds of data arrays found in mass spectrometry data files governed
535/// by the PSI-MS controlled vocabulary.
536#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord, Default)]
537#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
538pub enum ArrayType {
539    #[default]
540    Unknown,
541    MZArray,
542    IntensityArray,
543    ChargeArray,
544    SignalToNoiseArray,
545    TimeArray,
546    WavelengthArray,
547
548    IonMobilityArray,
549    MeanIonMobilityArray,
550    MeanDriftTimeArray,
551    MeanInverseReducedIonMobilityArray,
552    RawIonMobilityArray,
553    RawDriftTimeArray,
554    RawInverseReducedIonMobilityArray,
555    DeconvolutedIonMobilityArray,
556    DeconvolutedDriftTimeArray,
557    DeconvolutedInverseReducedIonMobilityArray,
558
559    ScanningQuadrupolePositionLowerBoundMZ,
560    ScanningQuadrupolePositionUpperBoundMZ,
561
562    IndexArray,
563
564    BaselineArray,
565    ResolutionArray,
566    PressureArray,
567    TemperatureArray,
568    FlowRateArray,
569    NonStandardDataArray {
570        name: Box<String>,
571    },
572}
573
574impl Display for ArrayType {
575    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576        write!(f, "{:?}", self)
577    }
578}
579
580impl ArrayType {
581    /// Get the data type that the array is compatible with in the
582    /// `mzdata` type expectations.
583    ///
584    /// By default, the m/z array is encoded using `Float64`,
585    /// the charge state array is encoded using `Int32`, and
586    /// all other arrays are encoded using `Float32`.
587    pub const fn preferred_dtype(&self) -> BinaryDataArrayType {
588        match self {
589            ArrayType::MZArray => BinaryDataArrayType::Float64,
590            ArrayType::IntensityArray => BinaryDataArrayType::Float32,
591            ArrayType::ChargeArray => BinaryDataArrayType::Int32,
592            ArrayType::IndexArray => BinaryDataArrayType::Int64,
593            _ => BinaryDataArrayType::Float32,
594        }
595    }
596
597    /// Convert an ion mobility array to its mean variant
598    pub const fn as_mean_ion_mobility(&self) -> Option<ArrayType> {
599        Some(match self {
600            Self::RawDriftTimeArray
601            | Self::DeconvolutedDriftTimeArray
602            | Self::MeanDriftTimeArray => Self::MeanDriftTimeArray,
603            Self::RawInverseReducedIonMobilityArray
604            | Self::DeconvolutedInverseReducedIonMobilityArray
605            | Self::MeanInverseReducedIonMobilityArray => Self::MeanInverseReducedIonMobilityArray,
606            Self::RawIonMobilityArray
607            | Self::DeconvolutedIonMobilityArray
608            | Self::MeanIonMobilityArray => Self::MeanIonMobilityArray,
609            _ => return None,
610        })
611    }
612
613    /// Convert an ion mobility array to its raw variant
614    pub const fn as_raw_ion_mobility(&self) -> Option<ArrayType> {
615        Some(match self {
616            Self::RawDriftTimeArray
617            | Self::DeconvolutedDriftTimeArray
618            | Self::MeanDriftTimeArray => Self::RawDriftTimeArray,
619            Self::RawInverseReducedIonMobilityArray
620            | Self::DeconvolutedInverseReducedIonMobilityArray
621            | Self::MeanInverseReducedIonMobilityArray => Self::RawInverseReducedIonMobilityArray,
622            Self::RawIonMobilityArray
623            | Self::DeconvolutedIonMobilityArray
624            | Self::MeanIonMobilityArray => Self::RawIonMobilityArray,
625            _ => return None,
626        })
627    }
628
629    /// Convert an ion mobility array to its deconvoluted variant
630    pub const fn as_deconvoluted_ion_mobility(&self) -> Option<ArrayType> {
631        Some(match self {
632            Self::RawDriftTimeArray
633            | Self::DeconvolutedDriftTimeArray
634            | Self::MeanDriftTimeArray => Self::DeconvolutedDriftTimeArray,
635            Self::RawInverseReducedIonMobilityArray
636            | Self::DeconvolutedInverseReducedIonMobilityArray
637            | Self::MeanInverseReducedIonMobilityArray => {
638                Self::DeconvolutedInverseReducedIonMobilityArray
639            }
640            Self::RawIonMobilityArray
641            | Self::DeconvolutedIonMobilityArray
642            | Self::MeanIonMobilityArray => Self::DeconvolutedIonMobilityArray,
643            _ => return None,
644        })
645    }
646
647    /// Create a [`ArrayType::NonStandardDataArray`] with the provided name.
648    pub fn nonstandard<S: ToString>(name: S) -> ArrayType {
649        ArrayType::NonStandardDataArray {
650            name: name.to_string().into(),
651        }
652    }
653
654    /// Test if the the array describes an ion mobility quantity.
655    pub const fn is_ion_mobility(&self) -> bool {
656        matches!(
657            self,
658            Self::IonMobilityArray
659                | Self::MeanIonMobilityArray
660                | Self::MeanDriftTimeArray
661                | Self::MeanInverseReducedIonMobilityArray
662                | Self::DeconvolutedIonMobilityArray
663                | Self::DeconvolutedDriftTimeArray
664                | Self::DeconvolutedInverseReducedIonMobilityArray
665                | Self::RawIonMobilityArray
666                | Self::RawDriftTimeArray
667                | Self::RawInverseReducedIonMobilityArray,
668        )
669    }
670
671    /// Create a [`Param`] for this array type.
672    ///
673    /// If a unit is provided, that unit will be specified, otherwise a default unit may
674    /// be used instead.
675    pub fn as_param(&self, unit: Option<Unit>) -> Param {
676        const CV: ControlledVocabulary = ControlledVocabulary::MS;
677        match self {
678            ArrayType::MZArray => CV
679                .const_param_ident_unit("m/z array", 1000514, unit.unwrap_or(Unit::MZ))
680                .into(),
681            ArrayType::IntensityArray => CV
682                .const_param_ident_unit(
683                    "intensity array",
684                    1000515,
685                    unit.unwrap_or(Unit::DetectorCounts),
686                )
687                .into(),
688            ArrayType::ChargeArray => CV.const_param_ident("charge array", 1000516).into(),
689            ArrayType::TimeArray => CV
690                .const_param_ident_unit("time array", 1000595, unit.unwrap_or(Unit::Minute))
691                .into(),
692            ArrayType::WavelengthArray => CV
693                .const_param_ident_unit("wavelength array", 1000617, Unit::Nanometer)
694                .into(),
695            ArrayType::SignalToNoiseArray => CV
696                .const_param_ident("signal to noise array", 1000517)
697                .into(),
698            ArrayType::IonMobilityArray => CV
699                .const_param_ident_unit("ion mobility array", 1002893, unit.unwrap_or_default())
700                .into(),
701
702            ArrayType::RawDriftTimeArray => CV
703                .const_param_ident_unit(
704                    "raw ion mobility drift time array",
705                    1003153,
706                    unit.unwrap_or_default(),
707                )
708                .into(),
709            ArrayType::RawInverseReducedIonMobilityArray => CV
710                .const_param_ident_unit(
711                    "raw inverse reduced ion mobility array",
712                    1003008,
713                    unit.unwrap_or_default(),
714                )
715                .into(),
716            ArrayType::RawIonMobilityArray => CV
717                .const_param_ident_unit("raw ion mobility array", 1003007, unit.unwrap_or_default())
718                .into(),
719
720            ArrayType::MeanIonMobilityArray => CV
721                .const_param_ident_unit(
722                    "mean ion mobility array",
723                    1002816,
724                    unit.unwrap_or_default(),
725                )
726                .into(),
727            ArrayType::MeanDriftTimeArray => CV
728                .const_param_ident_unit(
729                    "mean ion mobility drift time array",
730                    1002477,
731                    unit.unwrap_or_default(),
732                )
733                .into(),
734            ArrayType::MeanInverseReducedIonMobilityArray => CV
735                .const_param_ident_unit(
736                    "mean inverse reduced ion mobility array",
737                    1003006,
738                    unit.unwrap_or_default(),
739                )
740                .into(),
741
742            ArrayType::DeconvolutedIonMobilityArray => CV
743                .const_param_ident_unit(
744                    "deconvoluted ion mobility array",
745                    1003154,
746                    unit.unwrap_or_default(),
747                )
748                .into(),
749            ArrayType::DeconvolutedDriftTimeArray => CV
750                .const_param_ident_unit(
751                    "deconvoluted ion mobility drift time array",
752                    1003156,
753                    unit.unwrap_or_default(),
754                )
755                .into(),
756            ArrayType::DeconvolutedInverseReducedIonMobilityArray => CV
757                .const_param_ident_unit(
758                    "deconvoluted inverse reduced ion mobility array",
759                    1003155,
760                    unit.unwrap_or_default(),
761                )
762                .into(),
763
764            ArrayType::NonStandardDataArray { name } => {
765                let mut p = CV.param_val(1000786, "non-standard data array", name.to_string());
766                p.unit = unit.unwrap_or_default();
767                p
768            }
769            ArrayType::BaselineArray => CV.const_param_ident("baseline array", 1002530).into(),
770            ArrayType::ResolutionArray => CV.const_param_ident("resolution array", 1002529).into(),
771            ArrayType::PressureArray => {
772                let mut p = CV.const_param_ident("pressure array", 1000821);
773                p.unit = unit.unwrap_or_default();
774                p.into()
775            }
776            ArrayType::TemperatureArray => {
777                let mut p = CV.const_param_ident("temperature array", 1000822);
778                p.unit = unit.unwrap_or_default();
779                p.into()
780            }
781            ArrayType::FlowRateArray => {
782                let mut p = CV.const_param_ident("flow rate array", 1000820);
783                p.unit = unit.unwrap_or_default();
784                p.into()
785            }
786            ArrayType::ScanningQuadrupolePositionLowerBoundMZ => {
787                let mut p = CV.const_param_ident("scanning quadrupole position lower bound m/z array", 1003157);
788                p.unit = unit.unwrap_or_default();
789                p.into()
790            }
791            ArrayType::ScanningQuadrupolePositionUpperBoundMZ => {
792                let mut p = CV.const_param_ident("scanning quadrupole position upper bound m/z array", 1003158);
793                p.unit = unit.unwrap_or_default();
794                p.into()
795            }
796            ArrayType::IndexArray => {
797                CV.const_param_ident("index array", 1003870).into()
798            }
799            _ => {
800                panic!("Could not determine how to name for array {}", self);
801            }
802        }
803    }
804
805    /// Create a [`ParamCow`] for this array type in a `const` context using the default unit.
806    ///
807    /// **NOTE**: If this is a [`Self::NonStandardDataArray`], this function *will* panic as
808    /// this variant requires allocation.
809    pub const fn as_param_const(&self) -> ParamCow<'static> {
810        const CV: ControlledVocabulary = ControlledVocabulary::MS;
811        match self {
812            ArrayType::MZArray => CV.const_param_ident_unit("m/z array", 1000514, Unit::MZ),
813            ArrayType::IntensityArray => {
814                CV.const_param_ident_unit("intensity array", 1000515, Unit::DetectorCounts)
815            }
816            ArrayType::ChargeArray => CV.const_param_ident("charge array", 1000516),
817            ArrayType::TimeArray => CV.const_param_ident_unit("time array", 1000595, Unit::Minute),
818            ArrayType::WavelengthArray => {
819                CV.const_param_ident_unit("wavelength array", 1000617, Unit::Nanometer)
820            }
821            ArrayType::SignalToNoiseArray => CV.const_param_ident("signal to noise array", 1000517),
822            ArrayType::IonMobilityArray => CV.const_param_ident("ion mobility array", 1002893),
823            ArrayType::RawIonMobilityArray => {
824                CV.const_param_ident("raw ion mobility array", 1003007)
825            }
826            ArrayType::MeanIonMobilityArray => {
827                CV.const_param_ident("mean ion mobility array", 1002816)
828            }
829            ArrayType::DeconvolutedIonMobilityArray => {
830                CV.const_param_ident("deconvoluted ion mobility array", 1003154)
831            }
832            ArrayType::RawDriftTimeArray => CV.const_param_ident_unit(
833                "raw ion mobility drift time array",
834                1003153,
835                Unit::Unknown,
836            ),
837            ArrayType::RawInverseReducedIonMobilityArray => CV.const_param_ident_unit(
838                "raw inverse reduced ion mobility array",
839                1003008,
840                Unit::VoltSecondPerSquareCentimeter,
841            ),
842
843            ArrayType::MeanDriftTimeArray => CV.const_param_ident_unit(
844                "mean ion mobility drift time array",
845                1002477,
846                Unit::Unknown,
847            ),
848            ArrayType::MeanInverseReducedIonMobilityArray => CV.const_param_ident_unit(
849                "mean inverse reduced ion mobility array",
850                1003006,
851                Unit::VoltSecondPerSquareCentimeter,
852            ),
853
854            ArrayType::DeconvolutedDriftTimeArray => CV.const_param_ident_unit(
855                "deconvoluted ion mobility drift time array",
856                1003156,
857                Unit::Unknown,
858            ),
859            ArrayType::DeconvolutedInverseReducedIonMobilityArray => CV.const_param_ident_unit(
860                "deconvoluted inverse reduced ion mobility array",
861                1003155,
862                Unit::VoltSecondPerSquareCentimeter,
863            ),
864
865            ArrayType::NonStandardDataArray { name: _name } => {
866                panic!(
867                    "Cannot format NonStandardDataArray in a const context, please use `as_param`"
868                );
869            }
870            ArrayType::BaselineArray => CV.const_param_ident("baseline array", 1002530),
871            ArrayType::ResolutionArray => CV.const_param_ident("resolution array", 1002529),
872            ArrayType::PressureArray => CV.const_param_ident_unit("pressure array", 1000821, Unit::Pascal),
873            ArrayType::TemperatureArray => CV.const_param_ident("temperature array", 1000822),
874            ArrayType::FlowRateArray => CV.const_param_ident_unit("flow rate array", 1000820, Unit::MicrolitersPerMinute),
875            ArrayType::ScanningQuadrupolePositionLowerBoundMZ => {
876                let mut p = CV.const_param_ident("scanning quadrupole position lower bound m/z array", 1003157);
877                p.unit = Unit::MZ;
878                p
879            }
880            ArrayType::IndexArray => {
881                CV.const_param_ident("index array", 1003870)
882            }
883            ArrayType::ScanningQuadrupolePositionUpperBoundMZ => {
884                let mut p = CV.const_param_ident("scanning quadrupole position upper bound m/z array", 1003158);
885                p.unit = Unit::MZ;
886                p
887            }
888            _ => {
889                panic!("Could not determine how to name for array");
890            }
891        }
892    }
893
894    /// Create a [`ParamCow`] for this array type in a `const` context using the provided unit.
895    ///
896    /// **NOTE**: If this is a [`Self::NonStandardDataArray`], this function *will* panic as
897    /// this variant requires allocation.
898    pub const fn as_param_with_unit_const(&self, unit: Unit) -> ParamCow<'static> {
899        const CV: ControlledVocabulary = ControlledVocabulary::MS;
900        match self {
901            ArrayType::MZArray => CV.const_param_ident_unit("m/z array", 1000514, unit),
902            ArrayType::IntensityArray => {
903                CV.const_param_ident_unit("intensity array", 1000515, unit)
904            }
905            ArrayType::ChargeArray => CV.const_param_ident_unit("charge array", 1000516, unit),
906            ArrayType::TimeArray => CV.const_param_ident_unit("time array", 1000595, unit),
907            ArrayType::RawIonMobilityArray => {
908                CV.const_param_ident_unit("raw ion mobility array", 1003007, unit)
909            }
910            ArrayType::MeanIonMobilityArray => {
911                CV.const_param_ident_unit("mean ion mobility array", 1002816, unit)
912            }
913            ArrayType::DeconvolutedIonMobilityArray => {
914                CV.const_param_ident_unit("deconvoluted ion mobility array", 1003154, unit)
915            }
916            ArrayType::NonStandardDataArray { name: _name } => {
917                panic!(
918                    "Cannot format NonStandardDataArray in a const context, please use `as_param`"
919                );
920            }
921
922            ArrayType::RawDriftTimeArray => {
923                CV.const_param_ident_unit("raw ion mobility drift time array", 1003153, unit)
924            }
925            ArrayType::RawInverseReducedIonMobilityArray => {
926                CV.const_param_ident_unit("raw inverse reduced ion mobility array", 1003008, unit)
927            }
928
929            ArrayType::MeanDriftTimeArray => {
930                CV.const_param_ident_unit("mean ion mobility drift time array", 1002477, unit)
931            }
932            ArrayType::MeanInverseReducedIonMobilityArray => {
933                CV.const_param_ident_unit("mean inverse reduced ion mobility array", 1003006, unit)
934            }
935
936            ArrayType::DeconvolutedDriftTimeArray => CV.const_param_ident_unit(
937                "deconvoluted ion mobility drift time array",
938                1003156,
939                unit,
940            ),
941            ArrayType::DeconvolutedInverseReducedIonMobilityArray => CV.const_param_ident_unit(
942                "deconvoluted inverse reduced ion mobility array",
943                1003155,
944                unit,
945            ),
946
947            ArrayType::BaselineArray => CV.const_param_ident_unit("baseline array", 1002530, unit),
948            ArrayType::ResolutionArray => {
949                CV.const_param_ident_unit("resolution array", 1002529, unit)
950            }
951            ArrayType::PressureArray => CV.const_param_ident_unit("pressure array", 1000821, unit),
952            ArrayType::TemperatureArray => {
953                CV.const_param_ident_unit("temperature array", 1000822, unit)
954            }
955            ArrayType::FlowRateArray => CV.const_param_ident_unit("flow rate array", 1000820, unit),
956            ArrayType::ScanningQuadrupolePositionLowerBoundMZ => {
957                CV.const_param_ident_unit("scanning quadrupole position lower bound m/z array", 1003157, unit)
958            }
959            ArrayType::ScanningQuadrupolePositionUpperBoundMZ => {
960                CV.const_param_ident_unit("scanning quadrupole position upper bound m/z array", 1003158, unit)
961            }
962            ArrayType::SignalToNoiseArray => {
963                CV.const_param_ident_unit("signal to noise array", 1000517, unit)
964            }
965            ArrayType::WavelengthArray => {
966                CV.const_param_ident_unit("wavelength array", 1000617, unit)
967            }
968            ArrayType::IonMobilityArray => {
969                CV.const_param_ident_unit("ion mobility array", 1002893, unit)
970            }
971            ArrayType::IndexArray => {
972                CV.const_param_ident_unit("index array", 1003870, unit)
973            }
974            _ => {
975                panic!("Could not determine how to name for array");
976            }
977        }
978    }
979
980    /// Translate from [`CURIE`] to an [`ArrayType`]
981    pub fn from_accession(x: CURIE) -> Option<Self> {
982        let tp = if x == Self::MZArray.as_param_const().curie().unwrap() {
983            Self::MZArray
984        } else if x == Self::IntensityArray.as_param_const().curie().unwrap() {
985            Self::IntensityArray
986        } else if x == Self::ChargeArray.as_param_const().curie().unwrap() {
987            Self::ChargeArray
988        } else if x == Self::SignalToNoiseArray.as_param_const().curie().unwrap() {
989            Self::SignalToNoiseArray
990        } else if x == Self::TimeArray.as_param_const().curie().unwrap() {
991            Self::TimeArray
992        } else if x == Self::WavelengthArray.as_param_const().curie().unwrap() {
993            Self::WavelengthArray
994        } else if x == Self::IonMobilityArray.as_param_const().curie().unwrap() {
995            Self::IonMobilityArray
996        } else if x == Self::MeanIonMobilityArray.as_param_const().curie().unwrap() {
997            Self::MeanIonMobilityArray
998        } else if x == Self::MeanDriftTimeArray.as_param_const().curie().unwrap() {
999            Self::MeanDriftTimeArray
1000        } else if x
1001            == Self::MeanInverseReducedIonMobilityArray
1002                .as_param_const()
1003                .curie()
1004                .unwrap()
1005        {
1006            Self::MeanInverseReducedIonMobilityArray
1007        } else if x == Self::RawIonMobilityArray.as_param_const().curie().unwrap() {
1008            Self::RawIonMobilityArray
1009        } else if x == Self::RawDriftTimeArray.as_param_const().curie().unwrap() {
1010            Self::RawDriftTimeArray
1011        } else if x
1012            == Self::RawInverseReducedIonMobilityArray
1013                .as_param_const()
1014                .curie()
1015                .unwrap()
1016        {
1017            Self::RawInverseReducedIonMobilityArray
1018        } else if x
1019            == Self::DeconvolutedIonMobilityArray
1020                .as_param_const()
1021                .curie()
1022                .unwrap()
1023        {
1024            Self::DeconvolutedIonMobilityArray
1025        } else if x
1026            == Self::DeconvolutedDriftTimeArray
1027                .as_param_const()
1028                .curie()
1029                .unwrap()
1030        {
1031            Self::DeconvolutedDriftTimeArray
1032        } else if x
1033            == Self::DeconvolutedInverseReducedIonMobilityArray
1034                .as_param_const()
1035                .curie()
1036                .unwrap()
1037        {
1038            Self::DeconvolutedInverseReducedIonMobilityArray
1039        } else if x == Self::BaselineArray.as_param_const().curie().unwrap() {
1040            Self::BaselineArray
1041        } else if x == Self::ResolutionArray.as_param_const().curie().unwrap() {
1042            Self::ResolutionArray
1043        } else if x == Self::PressureArray.as_param_const().curie().unwrap() {
1044            Self::PressureArray
1045        } else if x == Self::TemperatureArray.as_param_const().curie().unwrap() {
1046            Self::TemperatureArray
1047        } else if x == Self::FlowRateArray.as_param_const().curie().unwrap() {
1048            Self::FlowRateArray
1049        } else if x == Self::ScanningQuadrupolePositionLowerBoundMZ.as_param_const().curie().unwrap() {
1050            Self::ScanningQuadrupolePositionLowerBoundMZ
1051        } else if x == Self::ScanningQuadrupolePositionUpperBoundMZ.as_param_const().curie().unwrap() {
1052            Self::ScanningQuadrupolePositionUpperBoundMZ
1053        } else if x == Self::IndexArray.as_param_const().curie().unwrap() {
1054            Self::IndexArray
1055        }
1056        else if x
1057            == (Self::NonStandardDataArray {
1058                name: "".to_string().into(),
1059            })
1060            .as_param(None)
1061            .curie()
1062            .unwrap()
1063        {
1064            Self::NonStandardDataArray {
1065                name: "".to_string().into(),
1066            }
1067        } else {
1068            return None;
1069        };
1070        Some(tp)
1071    }
1072}
1073
1074/// The canonical primitive data types found in MS data file formats
1075/// supported by the PSI-MS controlled vocabulary
1076#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, Default)]
1077#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1078pub enum BinaryDataArrayType {
1079    #[default]
1080    Unknown,
1081    Float64,
1082    Float32,
1083    Int64,
1084    Int32,
1085    ASCII,
1086}
1087
1088impl Display for BinaryDataArrayType {
1089    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1090        write!(f, "{:?}", self)
1091    }
1092}
1093
1094impl BinaryDataArrayType {
1095    /// Get the size in bytes of a single value of this type
1096    pub const fn size_of(&self) -> usize {
1097        match self {
1098            BinaryDataArrayType::Unknown | BinaryDataArrayType::ASCII => 1,
1099            BinaryDataArrayType::Float32 | BinaryDataArrayType::Int32 => 4,
1100            BinaryDataArrayType::Float64 | BinaryDataArrayType::Int64 => 8,
1101        }
1102    }
1103
1104    /// Convert the data type to a [`ParamCow`] if possible.
1105    pub const fn as_param_const(&self) -> Option<ParamCow<'static>> {
1106        let name = match self {
1107            BinaryDataArrayType::Unknown => return None,
1108            BinaryDataArrayType::Float64 => "64-bit float",
1109            BinaryDataArrayType::Float32 => "32-bit float",
1110            BinaryDataArrayType::Int64 => "64-bit integer",
1111            BinaryDataArrayType::Int32 => "32-bit integer",
1112            BinaryDataArrayType::ASCII => "null-terminated ASCII string",
1113        };
1114        if let Some(curie) = self.curie() {
1115            Some(ParamCow::const_new(
1116                name,
1117                ValueRef::Empty,
1118                Some(curie.accession),
1119                Some(curie.controlled_vocabulary),
1120                Unit::Unknown,
1121            ))
1122        } else {
1123            None
1124        }
1125    }
1126
1127    /// Convert the data type to a [`CURIE`] if one exists for it
1128    pub const fn curie(&self) -> Option<CURIE> {
1129        match self {
1130            Self::Float32 => Some(curie!(MS:1000521)),
1131            Self::Float64 => Some(curie!(MS:1000523)),
1132            Self::Int32 => Some(curie!(MS:1000519)),
1133            Self::Int64 => Some(curie!(MS:1000522)),
1134            Self::ASCII => Some(curie!(MS:1001479)),
1135            _ => None,
1136        }
1137    }
1138
1139    pub fn from_accession(accession: CURIE) -> Option<Self> {
1140        match accession {
1141            x if Some(x) == Self::Float32.curie() => Some(Self::Float32),
1142            x if Some(x) == Self::Float64.curie() => Some(Self::Float64),
1143            x if Some(x) == Self::Int32.curie() => Some(Self::Int32),
1144            x if Some(x) == Self::Int64.curie() => Some(Self::Int64),
1145            x if Some(x) == Self::ASCII.curie() => Some(Self::ASCII),
1146            _ => None,
1147        }
1148    }
1149
1150    /// Byte order swap the data stored in
1151    pub fn swap_bytes(&self, data: &mut [u8]) -> Result<(), ArrayRetrievalError> {
1152        let z = self.size_of();
1153        if !(data.len() % z == 0) {
1154            return Err(ArrayRetrievalError::DataTypeSizeMismatch)
1155        }
1156        match z {
1157            1 => {
1158                data.reverse();
1159            }
1160            4 => {
1161                data.as_chunks_mut::<4>().0.into_iter().for_each(|c| {
1162                    *c = u32::from_ne_bytes(*c).swap_bytes().to_ne_bytes();
1163                });
1164            }
1165            8 => {
1166                data.as_chunks_mut::<8>().0.into_iter().for_each(|c| {
1167                    *c = u64::from_ne_bytes(*c).swap_bytes().to_ne_bytes();
1168                });
1169                // data.chunks_exact_mut(4).for_each(|c| {
1170                //     c.swap(0, 7);
1171                //     c.swap(1, 6);
1172                //     c.swap(2, 5);
1173                //     c.swap(3, 4);
1174                // });
1175            }
1176            x => {
1177                data.chunks_exact_mut(x).for_each(|c| c.reverse());
1178            }
1179        }
1180        Ok(())
1181    }
1182}
1183
1184/// The range of compression and encoding states that a raw byte buffer
1185/// might be in during different stages of decoding. Other than `Decoded`,
1186/// these states may or may not include intermediate base64 encoding.
1187#[derive(Debug, Clone, Copy, PartialEq, Hash, Default)]
1188#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1189pub enum BinaryCompressionType {
1190    #[default]
1191    NoCompression,
1192    Zlib,
1193    NumpressLinear,
1194    NumpressSLOF,
1195    NumpressPIC,
1196    NumpressLinearZlib,
1197    NumpressSLOFZlib,
1198    NumpressPICZlib,
1199    LinearPrediction,
1200    DeltaPrediction,
1201    Decoded,
1202    Zstd,
1203    ShuffleZstd,
1204    DeltaShuffleZstd,
1205    ZstdDict,
1206    NumpressLinearZstd,
1207    NumpressSLOFZstd,
1208    NumpressPICZstd,
1209}
1210
1211impl BinaryCompressionType {
1212    pub const COMPRESSION_METHODS: &[Self] = &[
1213        Self::NoCompression,
1214        Self::Zlib,
1215
1216        #[cfg(feature = "numpress")]
1217        Self::NumpressLinear,
1218        #[cfg(feature = "numpress")]
1219        Self::NumpressLinearZlib,
1220        #[cfg(feature = "numpress")]
1221        Self::NumpressSLOF,
1222        #[cfg(feature = "numpress")]
1223        Self::NumpressSLOFZlib,
1224
1225        #[cfg(feature = "zstd")]
1226        Self::Zstd,
1227        #[cfg(feature = "zstd")]
1228        Self::ShuffleZstd,
1229        #[cfg(feature = "zstd")]
1230        Self::DeltaShuffleZstd,
1231        #[cfg(feature = "zstd")]
1232        Self::ZstdDict,
1233
1234        #[cfg(all(feature = "zstd", feature = "numpress"))]
1235        Self::NumpressLinearZstd,
1236        #[cfg(all(feature = "zstd", feature = "numpress"))]
1237        Self::NumpressSLOFZstd,
1238    ];
1239
1240    /// Generate a user-understandable message about why a compression conversion operation failed
1241    pub fn unsupported_msg(&self, context: Option<&str>) -> String {
1242        match context {
1243            Some(ctx) => format!("Cannot decode array compressed with {:?} ({})", self, ctx),
1244            None => format!("Cannot decode array compressed with {:?}", self),
1245        }
1246    }
1247
1248    /// Check if the compressor handles endianess during decoding. Otherwise the data needs to be
1249    /// byteswapped to little endianess on big endian systems before use
1250    pub const fn is_endian_aware(&self) -> bool {
1251        match self {
1252            BinaryCompressionType::Zlib |
1253            BinaryCompressionType::NoCompression |
1254            BinaryCompressionType::DeltaPrediction |
1255            BinaryCompressionType::LinearPrediction |
1256            BinaryCompressionType::Zstd => false,
1257            _ => true,
1258
1259        }
1260    }
1261
1262    pub const fn accession(&self) -> Option<u32> {
1263        let acc = match self {
1264            BinaryCompressionType::NoCompression => 1000576,
1265            BinaryCompressionType::Zlib => 1000574,
1266            BinaryCompressionType::NumpressLinear => 1002312,
1267            BinaryCompressionType::NumpressSLOF => 1002314,
1268            BinaryCompressionType::NumpressPIC => 1002313,
1269            BinaryCompressionType::NumpressLinearZlib => 1002746,
1270            BinaryCompressionType::NumpressSLOFZlib => 1002748,
1271            BinaryCompressionType::NumpressPICZlib => 1002747,
1272            BinaryCompressionType::DeltaPrediction => 1003089,
1273            BinaryCompressionType::LinearPrediction => 1003090,
1274            BinaryCompressionType::NumpressSLOFZstd => 1003785,
1275            BinaryCompressionType::NumpressLinearZstd => 1003783,
1276            BinaryCompressionType::NumpressPICZstd => 1003784,
1277            BinaryCompressionType::ZstdDict => 1003782,
1278            BinaryCompressionType::Zstd => 1003780,
1279            BinaryCompressionType::ShuffleZstd => 1003781,
1280            BinaryCompressionType::DeltaShuffleZstd => 9999999,
1281            BinaryCompressionType::Decoded => return None,
1282        };
1283        Some(acc)
1284    }
1285
1286    /// Convert a [`CURIE`] into a compression method, if one exists.
1287    pub fn from_accession(accession: CURIE) -> Option<Self> {
1288        match accession {
1289            CURIE {
1290                controlled_vocabulary: ControlledVocabulary::MS,
1291                accession: 1000576,
1292            } => Some(Self::NoCompression),
1293            CURIE {
1294                controlled_vocabulary: ControlledVocabulary::MS,
1295                accession: 1000574,
1296            } => Some(BinaryCompressionType::Zlib),
1297            CURIE {
1298                controlled_vocabulary: ControlledVocabulary::MS,
1299                accession: 1002312,
1300            } => Some(BinaryCompressionType::NumpressLinear),
1301            CURIE {
1302                controlled_vocabulary: ControlledVocabulary::MS,
1303                accession: 1002314,
1304            } => Some(BinaryCompressionType::NumpressSLOF),
1305            CURIE {
1306                controlled_vocabulary: ControlledVocabulary::MS,
1307                accession: 1002313,
1308            } => Some(BinaryCompressionType::NumpressPIC),
1309            CURIE {
1310                controlled_vocabulary: ControlledVocabulary::MS,
1311                accession: 1002746,
1312            } => Some(BinaryCompressionType::NumpressLinearZlib),
1313            CURIE {
1314                controlled_vocabulary: ControlledVocabulary::MS,
1315                accession: 1002748,
1316            } => Some(BinaryCompressionType::NumpressSLOFZlib),
1317            CURIE {
1318                controlled_vocabulary: ControlledVocabulary::MS,
1319                accession: 1002747,
1320            } => Some(BinaryCompressionType::NumpressPICZlib),
1321            CURIE {
1322                controlled_vocabulary: ControlledVocabulary::MS,
1323                accession: 1003089,
1324            } => Some(BinaryCompressionType::DeltaPrediction),
1325            CURIE {
1326                controlled_vocabulary: ControlledVocabulary::MS,
1327                accession: 1003090,
1328            } => Some(BinaryCompressionType::LinearPrediction),
1329            x if x
1330                == CURIE {
1331                    controlled_vocabulary: ControlledVocabulary::MS,
1332                    accession: BinaryCompressionType::NumpressSLOFZstd.accession().unwrap(),
1333                } =>
1334            {
1335                Some(BinaryCompressionType::NumpressSLOFZstd)
1336            }
1337            x if x
1338                == CURIE {
1339                    controlled_vocabulary: ControlledVocabulary::MS,
1340                    accession: BinaryCompressionType::NumpressPICZstd.accession().unwrap(),
1341                } =>
1342            {
1343                Some(BinaryCompressionType::NumpressPICZstd)
1344            }
1345            x if x
1346                == CURIE {
1347                    controlled_vocabulary: ControlledVocabulary::MS,
1348                    accession: BinaryCompressionType::NumpressLinearZstd
1349                        .accession()
1350                        .unwrap(),
1351                } =>
1352            {
1353                Some(BinaryCompressionType::NumpressLinearZstd)
1354            }
1355            x if x
1356                == CURIE {
1357                    controlled_vocabulary: ControlledVocabulary::MS,
1358                    accession: BinaryCompressionType::ZstdDict.accession().unwrap(),
1359                } =>
1360            {
1361                Some(BinaryCompressionType::ZstdDict)
1362            }
1363            x if x
1364                == CURIE {
1365                    controlled_vocabulary: ControlledVocabulary::MS,
1366                    accession: BinaryCompressionType::Zstd.accession().unwrap(),
1367                } =>
1368            {
1369                Some(BinaryCompressionType::Zstd)
1370            }
1371            x if x
1372                == CURIE {
1373                    controlled_vocabulary: ControlledVocabulary::MS,
1374                    accession: BinaryCompressionType::ShuffleZstd.accession().unwrap(),
1375                } =>
1376            {
1377                Some(BinaryCompressionType::ShuffleZstd)
1378            }
1379            x if x
1380                == CURIE {
1381                    controlled_vocabulary: ControlledVocabulary::MS,
1382                    accession: BinaryCompressionType::DeltaShuffleZstd.accession().unwrap(),
1383                } =>
1384            {
1385                Some(BinaryCompressionType::DeltaShuffleZstd)
1386            }
1387            _ => None,
1388        }
1389    }
1390
1391    /// Convert the compression type to a [`ParamCow`].
1392    ///
1393    /// Most compression methods have a controlled vocabulary
1394    /// term.
1395    pub const fn as_param(&self) -> Option<ParamCow<'static>> {
1396        let (name, accession) = match self {
1397            BinaryCompressionType::Decoded => return None,
1398            BinaryCompressionType::NoCompression => ("no compression", self.accession()),
1399            BinaryCompressionType::Zlib => ("zlib compression", self.accession()),
1400            BinaryCompressionType::NumpressLinear => (
1401                "MS-Numpress linear prediction compression",
1402                self.accession(),
1403            ),
1404            BinaryCompressionType::NumpressSLOF => (
1405                "MS-Numpress short logged float compression",
1406                self.accession(),
1407            ),
1408            BinaryCompressionType::NumpressPIC => {
1409                ("MS-Numpress positive integer compression", self.accession())
1410            }
1411            BinaryCompressionType::NumpressLinearZlib => (
1412                "MS-Numpress linear prediction compression followed by zlib compression",
1413                self.accession(),
1414            ),
1415            BinaryCompressionType::NumpressSLOFZlib => (
1416                "MS-Numpress short logged float compression followed by zlib compression",
1417                self.accession(),
1418            ),
1419            BinaryCompressionType::NumpressPICZlib => (
1420                "MS-Numpress positive integer compression followed by zlib compression",
1421                self.accession(),
1422            ),
1423            BinaryCompressionType::DeltaPrediction => (
1424                "truncation, delta prediction and zlib compression",
1425                self.accession(),
1426            ),
1427            BinaryCompressionType::LinearPrediction => (
1428                "truncation, linear prediction and zlib compression",
1429                self.accession(),
1430            ),
1431            BinaryCompressionType::NumpressSLOFZstd => {
1432                return Some(ParamCow::const_new(
1433                    "MS-Numpress short logged float compression followed by zstd compression",
1434                    ValueRef::Empty,
1435                    self.accession(),
1436                    Some(ControlledVocabulary::MS),
1437                    Unit::Unknown,
1438                ))
1439            }
1440            BinaryCompressionType::NumpressLinearZstd => {
1441                return Some(ParamCow::const_new(
1442                    "MS-Numpress linear prediction compression followed by zstd compression",
1443                    ValueRef::Empty,
1444                    self.accession(),
1445                    Some(ControlledVocabulary::MS),
1446                    Unit::Unknown,
1447                ))
1448            }
1449            BinaryCompressionType::NumpressPICZstd => {
1450                return Some(ParamCow::const_new(
1451                    "MS-Numpress positive integer compression followed by zstd compression",
1452                    ValueRef::Empty,
1453                    self.accession(),
1454                    Some(ControlledVocabulary::MS),
1455                    Unit::Unknown,
1456                ))
1457            }
1458            BinaryCompressionType::ZstdDict => {
1459                return Some(ParamCow::const_new(
1460                    "dict-zstd compression",
1461                    ValueRef::Empty,
1462                    self.accession(),
1463                    Some(ControlledVocabulary::MS),
1464                    Unit::Unknown,
1465                ))
1466            }
1467            BinaryCompressionType::Zstd => {
1468                return Some(ParamCow::const_new(
1469                    "zstd compression",
1470                    ValueRef::Empty,
1471                    self.accession(),
1472                    Some(ControlledVocabulary::MS),
1473                    Unit::Unknown,
1474                ))
1475            }
1476            BinaryCompressionType::ShuffleZstd => {
1477                return Some(ParamCow::const_new(
1478                    "byte-shuffle-zstd compression",
1479                    ValueRef::Empty,
1480                    self.accession(),
1481                    Some(ControlledVocabulary::MS),
1482                    Unit::Unknown,
1483                ))
1484            }
1485            BinaryCompressionType::DeltaShuffleZstd => {
1486                return Some(ParamCow::const_new(
1487                    "delta-byte-shuffle-zstd compression",
1488                    ValueRef::Empty,
1489                    self.accession(),
1490                    Some(ControlledVocabulary::MS),
1491                    Unit::Unknown,
1492                ))
1493            }
1494        };
1495        Some(
1496            ControlledVocabulary::MS
1497                .const_param_ident(name, unsafe { accession.unwrap_unchecked() }),
1498        )
1499    }
1500}
1501
1502impl Display for BinaryCompressionType {
1503    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1504        write!(f, "{:?}", self)
1505    }
1506}
1507
1508/// A high level set of failure modes that an operation to retrieve a typed memory buffer
1509/// from a [`BinaryArrayMap`](crate::BinaryArrayMap) might encounter. May also be used to represented conversion
1510/// during reading or writing.
1511#[derive(Debug, Clone, Error, PartialEq)]
1512pub enum ArrayRetrievalError {
1513    #[error("Array type {0:?} not found")]
1514    NotFound(ArrayType),
1515    #[error("An error occurred while decompressing: {0}")]
1516    DecompressionError(String),
1517    #[error("The requested data type does not match the number of bytes available in the buffer")]
1518    DataTypeSizeMismatch,
1519}
1520
1521impl From<bytemuck::PodCastError> for ArrayRetrievalError {
1522    fn from(value: bytemuck::PodCastError) -> Self {
1523        match value {
1524            bytemuck::PodCastError::TargetAlignmentGreaterAndInputNotAligned => {
1525                Self::DataTypeSizeMismatch
1526            }
1527            bytemuck::PodCastError::OutputSliceWouldHaveSlop => Self::DataTypeSizeMismatch,
1528            bytemuck::PodCastError::SizeMismatch => Self::DataTypeSizeMismatch,
1529            bytemuck::PodCastError::AlignmentMismatch => Self::DataTypeSizeMismatch,
1530        }
1531    }
1532}
1533
1534impl From<ArrayRetrievalError> for io::Error {
1535    fn from(value: ArrayRetrievalError) -> Self {
1536        match value {
1537            ArrayRetrievalError::NotFound(_) => io::Error::new(io::ErrorKind::NotFound, value),
1538            ArrayRetrievalError::DecompressionError(e) => {
1539                io::Error::new(io::ErrorKind::InvalidData, e)
1540            }
1541            ArrayRetrievalError::DataTypeSizeMismatch => {
1542                io::Error::new(io::ErrorKind::InvalidData, value)
1543            }
1544        }
1545    }
1546}
1547
1548#[cfg(feature = "numpress")]
1549impl From<numpress::Error> for ArrayRetrievalError {
1550    fn from(value: numpress::Error) -> Self {
1551        ArrayRetrievalError::DecompressionError(value.to_string())
1552    }
1553}
1554
1555pub fn linear_prediction_decoding<F: Num + Copy + Mul + AddAssign>(values: &mut [F]) -> &mut [F] {
1556    if values.len() < 2 {
1557        return values;
1558    }
1559
1560    let two = F::one() + F::one();
1561
1562    let prev2 = values[1];
1563    let prev1 = values[2];
1564    let offset = values[1];
1565
1566    values
1567        .iter_mut()
1568        .skip(2)
1569        .fold((prev1, prev2), |(prev1, prev2), current| {
1570            let tmp = *current + two * prev1 - prev2 - offset;
1571            let prev1 = *current;
1572            let prev2 = prev1;
1573            *current = tmp;
1574            (prev1, prev2)
1575        });
1576
1577    for i in 0..values.len() {
1578        if i < 2 {
1579            continue;
1580        }
1581        let v = values[i] + two * values[i - 1] - values[i - 2] - values[1];
1582        values[i] = v;
1583    }
1584    values
1585}
1586
1587pub fn linear_prediction_encoding<F: Num + Copy + Mul<F> + AddAssign>(
1588    values: &mut [F],
1589) -> &mut [F] {
1590    let n = values.len();
1591    if n < 3 {
1592        return values;
1593    }
1594    let offset = values[1];
1595    let prev2 = values[0];
1596    let prev1 = values[1];
1597    let two = F::one() + F::one();
1598
1599    values
1600        .iter_mut()
1601        .fold((prev1, prev2), |(prev1, prev2), val| {
1602            *val += offset - two * prev1 + prev2;
1603            let tmp = prev1;
1604            let prev1 = *val + two * prev1 - prev2 - offset;
1605            let prev2 = tmp;
1606            (prev1, prev2)
1607        });
1608    values
1609}
1610
1611pub fn delta_decoding<F: Num + Copy + Mul + AddAssign>(values: &mut [F]) -> &mut [F] {
1612    if values.len() < 2 {
1613        return values;
1614    }
1615
1616    let offset = values[0];
1617    let prev = values[1];
1618
1619    values.iter_mut().skip(2).fold(prev, |prev, current| {
1620        *current += prev - offset;
1621        *current
1622    });
1623    values
1624}
1625
1626pub fn delta_encoding<F: Num + Copy + Mul + AddAssign>(values: &mut [F]) -> &mut [F] {
1627    let n = values.len();
1628    if n < 2 {
1629        return values;
1630    }
1631    let prev = values[0];
1632    let offset = values[0];
1633
1634    let it = values.iter_mut();
1635    it.skip(1).fold(prev, |prev, current| {
1636        let tmp = *current;
1637        *current += offset - prev;
1638        tmp
1639    });
1640    values
1641}
1642
1643#[cfg(test)]
1644mod test {
1645    use super::*;
1646
1647    #[test]
1648    fn test_dtype_size() {
1649        assert_eq!(BinaryDataArrayType::ASCII.size_of(), 1);
1650        assert_eq!(BinaryDataArrayType::Float32.size_of(), 4);
1651        assert_eq!(BinaryDataArrayType::Int32.size_of(), 4);
1652        assert_eq!(BinaryDataArrayType::Float64.size_of(), 8);
1653        assert_eq!(BinaryDataArrayType::Int64.size_of(), 8);
1654    }
1655
1656    #[test]
1657    fn test_array_type_param() {
1658        let array_types = [
1659            ArrayType::MZArray,
1660            ArrayType::IntensityArray,
1661            ArrayType::ChargeArray,
1662            ArrayType::SignalToNoiseArray,
1663            ArrayType::TimeArray,
1664            ArrayType::WavelengthArray,
1665            ArrayType::IonMobilityArray,
1666            ArrayType::MeanIonMobilityArray,
1667            ArrayType::RawIonMobilityArray,
1668            ArrayType::DeconvolutedIonMobilityArray,
1669        ];
1670
1671        for at in array_types {
1672            assert_eq!(at.as_param_const().name, at.as_param(None).name)
1673        }
1674    }
1675
1676    #[test]
1677    fn test_binary_encoding_conv() {
1678        let encodings = [
1679            BinaryCompressionType::Decoded,
1680            BinaryCompressionType::NoCompression,
1681            BinaryCompressionType::NumpressLinear,
1682            BinaryCompressionType::NumpressLinearZlib,
1683            BinaryCompressionType::NumpressPIC,
1684            BinaryCompressionType::NumpressPICZlib,
1685            BinaryCompressionType::NumpressSLOF,
1686            BinaryCompressionType::NumpressSLOFZlib,
1687            BinaryCompressionType::Zlib,
1688        ];
1689
1690        for enc in encodings {
1691            let reps = match enc {
1692                BinaryCompressionType::NoCompression => ("no compression", 1000576),
1693                BinaryCompressionType::Zlib => ("zlib compression", 1000574),
1694                BinaryCompressionType::NumpressLinear => {
1695                    ("MS-Numpress linear prediction compression", 1002312)
1696                }
1697                BinaryCompressionType::NumpressSLOF => {
1698                    ("MS-Numpress short logged float compression", 1002314)
1699                }
1700                BinaryCompressionType::NumpressPIC => {
1701                    ("MS-Numpress positive integer compression", 1002313)
1702                }
1703                BinaryCompressionType::NumpressLinearZlib => (
1704                    "MS-Numpress linear prediction compression followed by zlib compression",
1705                    1002746,
1706                ),
1707                BinaryCompressionType::NumpressPICZlib => (
1708                    "MS-Numpress positive integer compression followed by zlib compression",
1709                    1002747,
1710                ),
1711                BinaryCompressionType::NumpressSLOFZlib => (
1712                    "MS-Numpress short logged float compression followed by zlib compression",
1713                    1002748,
1714                ),
1715                _ => ("", 0),
1716            };
1717            if let Some(p) = enc.as_param() {
1718                assert_eq!(p.name, reps.0);
1719                assert_eq!(p.accession.unwrap(), reps.1);
1720            }
1721        }
1722    }
1723
1724    #[test]
1725    fn test_transpose() {
1726        let data: Vec<_> = (0..128i32).map(|i| i.pow(2u32) as f64).collect();
1727        let flip = transpose_f64(&data);
1728        let rev = reverse_transpose_f64(&flip);
1729        let rev_cast: &[f64] = bytemuck::cast_slice(&rev);
1730        assert_eq!(data, rev_cast);
1731    }
1732
1733    #[test]
1734    fn test_dict() {
1735        let data: Vec<_> = (0..127i32).map(|i| i.pow(2u32) as f64).collect();
1736        let encoded = dictionary_encoding(&data).unwrap();
1737        let decoded: Vec<f64> = dictionary_decoding(&encoded).unwrap();
1738
1739        assert_eq!(data, decoded);
1740    }
1741
1742    #[test]
1743    fn test_byteswap() {
1744        let x = 42u32;
1745        let mut bytes_of = x.to_le_bytes();
1746        BinaryDataArrayType::Int32.swap_bytes(&mut bytes_of).unwrap();
1747        let y1 = u32::from_le_bytes(bytes_of);
1748        let y2 = x.swap_bytes();
1749        assert_eq!(y1, y2);
1750
1751        bytes_of = x.to_be_bytes();
1752        BinaryDataArrayType::Int32.swap_bytes(&mut bytes_of).unwrap();
1753        let y1 = u32::from_le_bytes(bytes_of);
1754        assert_eq!(x, y1);
1755    }
1756}