Skip to main content

mzdata_bindata/
traits.rs

1use std::marker::PhantomData;
2use std::mem;
3use std::borrow::Cow;
4
5use bytemuck::Pod;
6use num_traits::{AsPrimitive, Num};
7use mzdata_param::Unit;
8
9use super::encodings::{ArrayRetrievalError, BinaryDataArrayType, Bytes};
10use super::ArrayType;
11
12/// A byte array that may be encoded and compressed as [`BinaryCompressionType`], or unpacked in
13/// memory and re-interpreted as native types appropriate to [`BinaryDataArrayType`].
14///
15/// The raw bytes must be made available through the [`ByteArrayView::view`] method,
16/// but all other behavior is added on top of that
17pub trait ByteArrayView<'transient, 'lifespan: 'transient> {
18    /// The core method that exposes the decoded byte array that the type *must* provide.
19    fn view(&'lifespan self) -> Result<Cow<'lifespan, [u8]>, ArrayRetrievalError>;
20
21    /// This is a helper method for the various `to_<type>` methods like [`Self::to_f64`]. It should not
22    /// need to be called directly. This method relies on [`bytemuck::try_cast_slice`] to do the heavy lifting.
23    fn coerce_from<T: Pod>(
24        buffer: Cow<'transient, [u8]>,
25    ) -> Result<Cow<'transient, [T]>, ArrayRetrievalError> {
26        let n = buffer.len();
27        if n == 0 {
28            return Ok(Cow::Owned(Vec::new()))
29        }
30        let z = mem::size_of::<T>();
31        if n % z != 0 {
32            return Err(ArrayRetrievalError::DataTypeSizeMismatch);
33        }
34        match buffer {
35            Cow::Borrowed(c) => {
36                Ok(Cow::Borrowed(bytemuck::try_cast_slice(c)?))
37            },
38            Cow::Owned(v) => {
39                let size_type = n / z;
40                let mut buf = Vec::with_capacity(size_type);
41                v.chunks_exact(z).try_for_each(|c| {
42                    buf.extend(bytemuck::try_cast_slice(c)?);
43                    Ok::<(), bytemuck::PodCastError>(())
44                })?;
45                Ok(Cow::Owned(buf))
46            },
47        }
48    }
49
50    /// This is a helper method for the various `to_<type>` methods like [`Self::to_f64`]. It should not
51    /// need to be called directly. This calls into [`Self::coerce_from`] internally after decoding.
52    fn coerce<T: Pod>(
53        &'lifespan self,
54    ) -> Result<Cow<'transient, [T]>, ArrayRetrievalError> {
55        match self.view() {
56            Ok(data) => {
57                #[cfg(target_endian = "big")]
58                {
59                    let mut data: Cow<'_, [u8]> = Cow::Owned(data.to_vec());
60                    self.dtype().swap_bytes(data.to_mut())?;
61                    Self::coerce_from(data)
62                }
63                #[cfg(not(target_endian = "big"))]
64                {
65                    Self::coerce_from(data)
66                }
67            },
68            Err(err) => Err(err),
69        }
70    }
71
72    /// Decode the array, then copy it to a new array, converting each element from type `D` to to type `S`
73    ///
74    /// This makes a copy of the data, forcibly coercing the values using [`AsPrimitive`]
75    fn convert<S: Num + Clone + AsPrimitive<D> + Pod, D: Num + Clone + Copy + 'static>(
76        &'lifespan self,
77    ) -> Result<Cow<'transient, [D]>, ArrayRetrievalError> {
78        match self.coerce::<S>() {
79            Ok(view) => {
80                match view {
81                    Cow::Borrowed(view) => {
82                        Ok(Cow::Owned(view.iter().map(|a| a.as_()).collect()))
83                    }
84                    Cow::Owned(owned) => {
85                        let res = owned.iter().map(|a| a.as_()).collect();
86                        Ok(Cow::Owned(res))
87                    }
88                }
89            }
90            Err(err) => Err(err),
91        }
92    }
93
94    /// The kind of array this is
95    fn name(&self) -> &ArrayType;
96
97    /// The real data type encoded in bytes
98    fn dtype(&self) -> BinaryDataArrayType;
99
100    /// The unit of measurement each data point is in
101    fn unit(&self) -> Unit;
102
103    /// Get the identifier referencing a [`DataProcessing`](crate::meta::DataProcessing)
104    fn data_processing_reference(&self) -> Option<&str> {
105        None
106    }
107
108    /// Get a view of the data as [`f32`]. If the data are stored that way already, no
109    /// copy is required.
110    fn to_f32(&'lifespan self) -> Result<Cow<'transient, [f32]>, ArrayRetrievalError> {
111        type D = f32;
112        match self.dtype() {
113            BinaryDataArrayType::Float32 | BinaryDataArrayType::ASCII => self.coerce::<D>(),
114            BinaryDataArrayType::Float64 => {
115                type S = f64;
116                self.convert::<S, D>()
117            }
118            BinaryDataArrayType::Int32 => {
119                type S = i32;
120                self.convert::<S, D>()
121            }
122            BinaryDataArrayType::Int64 => {
123                type S = i64;
124                self.convert::<S, D>()
125            }
126            _ => Err(ArrayRetrievalError::DataTypeSizeMismatch),
127        }
128    }
129
130    /// Get a view of the data as [`f64`]. If the data are stored that way already, no
131    /// copy is required.
132    fn to_f64(&'lifespan self) -> Result<Cow<'transient, [f64]>, ArrayRetrievalError> {
133        type D = f64;
134        match self.dtype() {
135            BinaryDataArrayType::Float32 => {
136                type S = f32;
137                self.convert::<S, D>()
138            }
139            BinaryDataArrayType::Float64 | BinaryDataArrayType::ASCII => self.coerce(),
140            BinaryDataArrayType::Int32 => {
141                type S = i32;
142                self.convert::<S, D>()
143            }
144            BinaryDataArrayType::Int64 => {
145                type S = i64;
146                self.convert::<S, D>()
147            }
148            _ => Err(ArrayRetrievalError::DataTypeSizeMismatch),
149        }
150    }
151
152    /// Get a view of the data as [`i32`]. If the data are stored that way already, no
153    /// copy is required.
154    fn to_i32(&'lifespan self) -> Result<Cow<'transient, [i32]>, ArrayRetrievalError> {
155        type D = i32;
156        match self.dtype() {
157            BinaryDataArrayType::Float32 => {
158                type S = f32;
159                self.convert::<S, D>()
160            }
161            BinaryDataArrayType::Float64 => {
162                type S = f64;
163                self.convert::<S, D>()
164            }
165            BinaryDataArrayType::Int32 | BinaryDataArrayType::ASCII => self.coerce::<D>(),
166            BinaryDataArrayType::Int64 => {
167                type S = i64;
168                self.convert::<S, D>()
169            }
170            _ => Err(ArrayRetrievalError::DataTypeSizeMismatch),
171        }
172    }
173
174    /// Get a view of the data as [`i64`]. If the data are stored that way already, no
175    /// copy is required.
176    fn to_i64(&'lifespan self) -> Result<Cow<'transient, [i64]>, ArrayRetrievalError> {
177        type D = i64;
178        match self.dtype() {
179            BinaryDataArrayType::Float32 => {
180                type S = f32;
181                self.convert::<S, D>()
182            }
183            BinaryDataArrayType::Float64 => {
184                type S = f64;
185                self.convert::<S, D>()
186            }
187            BinaryDataArrayType::Int64 | BinaryDataArrayType::ASCII => self.coerce::<D>(),
188            BinaryDataArrayType::Int32 => {
189                type S = i32;
190                self.convert::<S, D>()
191            }
192            _ => Err(ArrayRetrievalError::DataTypeSizeMismatch),
193        }
194    }
195
196    /// The size of encoded array in terms of # of elements of the [`BinaryDataArrayType`] given by [`ByteArrayView::dtype`]
197    fn data_len(&'lifespan self) -> Result<usize, ArrayRetrievalError> {
198        let view = self.view()?;
199        let n = view.len();
200        Ok(n / self.dtype().size_of())
201    }
202
203    fn iter_type<T: Pod>(&'lifespan self) -> Result<DataSliceIter<'lifespan, T>, ArrayRetrievalError> {
204        Ok(DataSliceIter::new(self.view()?))
205    }
206
207    fn iter_u8(&'lifespan self) -> Result<DataSliceIter<'lifespan, u8>, ArrayRetrievalError> {
208        Ok(DataSliceIter::new(self.view()?))
209    }
210
211    fn iter_f32(&'lifespan self) -> Result<DataSliceIter<'lifespan, f32>, ArrayRetrievalError> {
212        Ok(DataSliceIter::new(self.view()?))
213    }
214
215    fn iter_f64(&'lifespan self) -> Result<DataSliceIter<'lifespan, f64>, ArrayRetrievalError> {
216        Ok(DataSliceIter::new(self.view()?))
217    }
218
219    fn iter_i32(&'lifespan self) -> Result<DataSliceIter<'lifespan, i32>, ArrayRetrievalError> {
220        Ok(DataSliceIter::new(self.view()?))
221    }
222
223    fn iter_i64(&'lifespan self) -> Result<DataSliceIter<'lifespan, i64>, ArrayRetrievalError> {
224        Ok(DataSliceIter::new(self.view()?))
225    }
226}
227
228/// A mutable byte array
229pub trait ByteArrayViewMut<'transient, 'lifespan: 'transient>:
230    ByteArrayView<'transient, 'lifespan>
231{
232
233    /// Specify the unit of the data array
234    fn unit_mut(&mut self) -> &mut Unit;
235
236    /// Get a mutable view of the bytes backing this data array.
237    ///
238    /// This is in turn used by [`ByteArrayViewMut::coerce_mut`] to produce a typed array
239    fn view_mut(&'transient mut self) -> Result<&'transient mut Bytes, ArrayRetrievalError>;
240
241    /// Reinterpret decoded bytes as a mutable slice in native byte order.
242    ///
243    /// The returned slice borrows the input buffer. `T` must be [`Pod`] so that
244    /// all bit patterns are valid and writing values cannot introduce padding.
245    /// Empty buffers return an empty slice.
246    ///
247    /// # Errors
248    /// Returns [`ArrayRetrievalError::DataTypeSizeMismatch`] if the buffer is
249    /// not aligned for `T` or its length is not a multiple of the element size.
250    /// A nonempty buffer cannot be cast to a zero-sized type.
251    ///
252    /// The returned reference cannot outlive its buffer:
253    ///
254    /// ```compile_fail
255    /// use mzdata_bindata::{ByteArrayViewMut, DataArray};
256    ///
257    /// fn escape_buffer() -> &'static mut [u8] {
258    ///     let mut bytes = vec![1u8, 2, 3];
259    ///     <DataArray as ByteArrayViewMut<'static, 'static>>::coerce_from_mut::<u8>(
260    ///         &mut bytes,
261    ///     ).unwrap()
262    /// }
263    /// ```
264    ///
265    /// Types such as `bool` cannot represent arbitrary bytes:
266    ///
267    /// ```compile_fail
268    /// use mzdata_bindata::{ByteArrayViewMut, DataArray};
269    ///
270    /// let mut bytes = [0u8];
271    /// let _ = <DataArray as ByteArrayViewMut>::coerce_from_mut::<bool>(&mut bytes);
272    /// ```
273    fn coerce_from_mut<T: Pod>(
274        buffer: &'transient mut [u8],
275    ) -> Result<&'transient mut [T], ArrayRetrievalError> {
276        if buffer.is_empty() {
277            return Ok(&mut []);
278        }
279        Ok(bytemuck::try_cast_slice_mut(buffer)?)
280    }
281
282    fn coerce_mut<T: Pod>(
283        &'lifespan mut self,
284    ) -> Result<&'transient mut [T], ArrayRetrievalError> {
285        let view = self.view_mut()?;
286        #[cfg(target_endian = "big")]
287        {
288            log::error!("Mutable view of raw bytes on big endian system is only partially supported.")
289        }
290        Self::coerce_from_mut(view)
291    }
292
293    #[allow(unused)]
294    /// Set the identifier referencing a [`DataProcessing`](crate::meta::DataProcessing)
295    fn set_data_processing_reference(&mut self, data_processing_reference: Option<Box<str>>) {}
296}
297
298#[derive(Debug)]
299pub struct DataSliceIter<'a, T: Pod> {
300    buffer: Cow<'a, [u8]>,
301    i: usize,
302    _t: PhantomData<T>
303}
304
305impl<T: Pod> ExactSizeIterator for DataSliceIter<'_, T> {
306    fn len(&self) -> usize {
307        let z = mem::size_of::<T>();
308        self.buffer.len() / z - self.i
309    }
310}
311
312impl<'a, T: Pod> DataSliceIter<'a, T> {
313    pub fn new(buffer: Cow<'a, [u8]>) -> Self {
314        Self { buffer, i: 0, _t: PhantomData }
315    }
316
317    pub fn next_value(&mut self) -> Option<T> {
318        let z = mem::size_of::<T>();
319        let offset = z * self.i;
320        if (offset + z) > self.buffer.len() {
321            None
322        } else {
323            let data = &self.buffer[offset..offset + z];
324            #[cfg(target_endian = "big")]
325            {
326                let mut data = data.to_vec();
327                data.reverse();
328                let val = bytemuck::pod_read_unaligned(&data);
329                self.i += 1;
330                Some(val)
331            }
332            #[cfg(not(target_endian = "big"))]
333            {
334                let val = bytemuck::pod_read_unaligned(data);
335                self.i += 1;
336                Some(val)
337            }
338        }
339    }
340}
341
342impl<T: Pod> Iterator for DataSliceIter<'_, T> {
343    type Item = T;
344
345    fn next(&mut self) -> Option<Self::Item> {
346        self.next_value()
347    }
348
349    fn size_hint(&self) -> (usize, Option<usize>) {
350        if mem::size_of::<T>() == 0 {
351            return (0, None);
352        }
353        let n = self.len();
354        (n, Some(n))
355    }
356}
357
358#[cfg(test)]
359mod test {
360    use super::*;
361
362    fn check_remaining<T: Pod + PartialEq + std::fmt::Debug>(values: &[T]) {
363        let mut it = DataSliceIter::<T>::new(Cow::Borrowed(bytemuck::cast_slice(values)));
364        for (i, value) in values.iter().enumerate() {
365            let n = values.len() - i;
366            assert_eq!(it.len(), n);
367            assert_eq!(it.size_hint(), (n, Some(n)));
368            assert_eq!(it.next(), Some(*value));
369        }
370        assert_eq!(it.len(), 0);
371        assert_eq!(it.size_hint(), (0, Some(0)));
372        assert_eq!(it.next(), None);
373        assert_eq!(it.next(), None);
374    }
375
376    #[test]
377    fn test_iterator_remaining() {
378        check_remaining::<u8>(&[]);
379        check_remaining(&[1u8]);
380        check_remaining(&[1u8, 2, 3]);
381        check_remaining(&[1i32, -2, 3]);
382        check_remaining(&[1i64, -2, 3]);
383        check_remaining(&[1.25f32, -2.5, 3.0]);
384        check_remaining(&[1.25f64, -2.5, 3.0]);
385
386        let units = DataSliceIter::<()>::new(Cow::Borrowed(&[]));
387        assert_eq!(units.take(3).collect::<Vec<_>>(), vec![(); 3]);
388    }
389}