hdf5_metno_types/
h5type.rs

1use std::fmt::{self, Display};
2use std::mem;
3use std::os::raw::c_void;
4
5use crate::array::VarLenArray;
6use crate::references::Reference;
7use crate::string::{FixedAscii, FixedUnicode, VarLenAscii, VarLenUnicode};
8
9#[allow(non_camel_case_types)]
10#[repr(C)]
11#[derive(Copy, Clone)]
12pub(crate) struct hvl_t {
13    pub len: usize,
14    pub ptr: *mut c_void,
15}
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
18pub enum IntSize {
19    U1 = 1,
20    U2 = 2,
21    U4 = 4,
22    U8 = 8,
23}
24
25impl IntSize {
26    pub const fn from_int(size: usize) -> Option<Self> {
27        if size == 1 {
28            Some(Self::U1)
29        } else if size == 2 {
30            Some(Self::U2)
31        } else if size == 4 {
32            Some(Self::U4)
33        } else if size == 8 {
34            Some(Self::U8)
35        } else {
36            None
37        }
38    }
39}
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
42pub enum FloatSize {
43    #[cfg(feature = "f16")]
44    U2 = 2,
45    U4 = 4,
46    U8 = 8,
47}
48
49impl FloatSize {
50    pub const fn from_int(size: usize) -> Option<Self> {
51        #[cfg(feature = "f16")]
52        {
53            if size == 2 {
54                return Some(Self::U2);
55            }
56        }
57        match size {
58            4 => Some(Self::U4),
59            8 => Some(Self::U8),
60            _ => None,
61        }
62    }
63}
64
65#[derive(Clone, Debug, PartialEq, Eq)]
66pub struct EnumMember {
67    pub name: String,
68    pub value: u64,
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
72pub struct EnumType {
73    pub size: IntSize,
74    pub signed: bool,
75    pub members: Vec<EnumMember>,
76}
77
78impl EnumType {
79    #[inline]
80    pub fn base_type(&self) -> TypeDescriptor {
81        if self.signed {
82            TypeDescriptor::Integer(self.size)
83        } else {
84            TypeDescriptor::Unsigned(self.size)
85        }
86    }
87}
88
89#[derive(Clone, Debug, PartialEq, Eq)]
90pub struct CompoundField {
91    pub name: String,
92    pub ty: TypeDescriptor,
93    pub offset: usize,
94    pub index: usize,
95}
96
97impl CompoundField {
98    pub fn new(name: &str, ty: TypeDescriptor, offset: usize, index: usize) -> Self {
99        Self { name: name.to_owned(), ty, offset, index }
100    }
101
102    pub fn typed<T: H5Type>(name: &str, offset: usize, index: usize) -> Self {
103        Self::new(name, T::type_descriptor(), offset, index)
104    }
105}
106
107#[derive(Clone, Debug, PartialEq, Eq)]
108pub struct CompoundType {
109    pub fields: Vec<CompoundField>,
110    pub size: usize,
111}
112
113impl CompoundType {
114    pub fn to_c_repr(&self) -> Self {
115        let mut layout = self.clone();
116        layout.fields.sort_by_key(|f| f.index);
117        let mut offset = 0;
118        let mut max_align = 1;
119        for f in &mut layout.fields {
120            f.ty = f.ty.to_c_repr();
121            let align = f.ty.c_alignment();
122            while offset % align != 0 {
123                offset += 1;
124            }
125            f.offset = offset;
126            max_align = max_align.max(align);
127            offset += f.ty.size();
128            layout.size = offset;
129            while layout.size % max_align != 0 {
130                layout.size += 1;
131            }
132        }
133        layout
134    }
135
136    pub fn to_packed_repr(&self) -> Self {
137        let mut layout = self.clone();
138        layout.fields.sort_by_key(|f| f.index);
139        layout.size = 0;
140        for f in &mut layout.fields {
141            f.ty = f.ty.to_packed_repr();
142            f.offset = layout.size;
143            layout.size += f.ty.size();
144        }
145        layout
146    }
147}
148
149#[derive(Clone, Debug, PartialEq, Eq)]
150pub enum TypeDescriptor {
151    Integer(IntSize),
152    Unsigned(IntSize),
153    Float(FloatSize),
154    Boolean,
155    Enum(EnumType),
156    Compound(CompoundType),
157    FixedArray(Box<Self>, usize),
158    FixedAscii(usize),
159    FixedUnicode(usize),
160    VarLenArray(Box<Self>),
161    VarLenAscii,
162    VarLenUnicode,
163    Reference(Reference),
164}
165
166impl Display for TypeDescriptor {
167    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168        match self {
169            TypeDescriptor::Integer(IntSize::U1) => write!(f, "int8"),
170            TypeDescriptor::Integer(IntSize::U2) => write!(f, "int16"),
171            TypeDescriptor::Integer(IntSize::U4) => write!(f, "int32"),
172            TypeDescriptor::Integer(IntSize::U8) => write!(f, "int64"),
173            TypeDescriptor::Unsigned(IntSize::U1) => write!(f, "uint8"),
174            TypeDescriptor::Unsigned(IntSize::U2) => write!(f, "uint16"),
175            TypeDescriptor::Unsigned(IntSize::U4) => write!(f, "uint32"),
176            TypeDescriptor::Unsigned(IntSize::U8) => write!(f, "uint64"),
177            #[cfg(feature = "f16")]
178            TypeDescriptor::Float(FloatSize::U2) => write!(f, "float16"),
179            TypeDescriptor::Float(FloatSize::U4) => write!(f, "float32"),
180            TypeDescriptor::Float(FloatSize::U8) => write!(f, "float64"),
181            TypeDescriptor::Boolean => write!(f, "bool"),
182            TypeDescriptor::Enum(ref tp) => write!(f, "enum ({})", tp.base_type()),
183            TypeDescriptor::Compound(ref tp) => write!(f, "compound ({} fields)", tp.fields.len()),
184            TypeDescriptor::FixedArray(ref tp, n) => write!(f, "[{tp}; {n}]"),
185            TypeDescriptor::FixedAscii(n) => write!(f, "string (len {n})"),
186            TypeDescriptor::FixedUnicode(n) => write!(f, "unicode (len {n})"),
187            TypeDescriptor::VarLenArray(ref tp) => write!(f, "[{tp}] (var len)"),
188            TypeDescriptor::VarLenAscii => write!(f, "string (var len)"),
189            TypeDescriptor::VarLenUnicode => write!(f, "unicode (var len)"),
190            TypeDescriptor::Reference(Reference::Object) => write!(f, "reference (object)"),
191            TypeDescriptor::Reference(Reference::Region) => write!(f, "reference (region)"),
192            #[cfg(feature = "1.12.0")]
193            TypeDescriptor::Reference(Reference::Std) => write!(f, "reference"),
194        }
195    }
196}
197
198impl TypeDescriptor {
199    /// Returns the size of the [`TypeDescriptor`] variant in bytes
200    pub fn size(&self) -> usize {
201        match *self {
202            Self::Integer(size) | Self::Unsigned(size) => size as _,
203            Self::Float(size) => size as _,
204            Self::Boolean => 1,
205            Self::Enum(ref enum_type) => enum_type.size as _,
206            Self::Compound(ref compound) => compound.size,
207            Self::FixedArray(ref ty, len) => ty.size() * len,
208            Self::FixedAscii(len) | Self::FixedUnicode(len) => len,
209            Self::VarLenArray(_) => mem::size_of::<hvl_t>(),
210            Self::VarLenAscii | Self::VarLenUnicode => mem::size_of::<*const u8>(),
211            Self::Reference(reftyp) => reftyp.size(),
212        }
213    }
214
215    fn c_alignment(&self) -> usize {
216        match *self {
217            Self::Compound(ref compound) => {
218                compound.fields.iter().map(|f| f.ty.c_alignment()).max().unwrap_or(1)
219            }
220            Self::FixedArray(ref ty, _) => ty.c_alignment(),
221            Self::FixedAscii(_) | Self::FixedUnicode(_) => 1,
222            Self::VarLenArray(_) => mem::size_of::<usize>(),
223            _ => self.size(),
224        }
225    }
226
227    pub fn to_c_repr(&self) -> Self {
228        match *self {
229            Self::Compound(ref compound) => Self::Compound(compound.to_c_repr()),
230            Self::FixedArray(ref ty, size) => Self::FixedArray(Box::new(ty.to_c_repr()), size),
231            Self::VarLenArray(ref ty) => Self::VarLenArray(Box::new(ty.to_c_repr())),
232            _ => self.clone(),
233        }
234    }
235
236    pub fn to_packed_repr(&self) -> Self {
237        match *self {
238            Self::Compound(ref compound) => Self::Compound(compound.to_packed_repr()),
239            Self::FixedArray(ref ty, size) => Self::FixedArray(Box::new(ty.to_packed_repr()), size),
240            Self::VarLenArray(ref ty) => Self::VarLenArray(Box::new(ty.to_packed_repr())),
241            _ => self.clone(),
242        }
243    }
244}
245
246pub unsafe trait H5Type: 'static {
247    fn type_descriptor() -> TypeDescriptor;
248}
249
250macro_rules! impl_h5type {
251    ($ty:ty, $variant:ident, $size:expr) => {
252        unsafe impl H5Type for $ty {
253            #[inline]
254            fn type_descriptor() -> TypeDescriptor {
255                $crate::h5type::TypeDescriptor::$variant($size)
256            }
257        }
258    };
259}
260
261impl_h5type!(i8, Integer, IntSize::U1);
262impl_h5type!(i16, Integer, IntSize::U2);
263impl_h5type!(i32, Integer, IntSize::U4);
264impl_h5type!(i64, Integer, IntSize::U8);
265impl_h5type!(u8, Unsigned, IntSize::U1);
266impl_h5type!(u16, Unsigned, IntSize::U2);
267impl_h5type!(u32, Unsigned, IntSize::U4);
268impl_h5type!(u64, Unsigned, IntSize::U8);
269#[cfg(feature = "f16")]
270impl_h5type!(::half::f16, Float, FloatSize::U2);
271impl_h5type!(f32, Float, FloatSize::U4);
272impl_h5type!(f64, Float, FloatSize::U8);
273
274#[cfg(target_pointer_width = "32")]
275impl_h5type!(isize, Integer, IntSize::U4);
276#[cfg(target_pointer_width = "32")]
277impl_h5type!(usize, Unsigned, IntSize::U4);
278
279#[cfg(target_pointer_width = "64")]
280impl_h5type!(isize, Integer, IntSize::U8);
281#[cfg(target_pointer_width = "64")]
282impl_h5type!(usize, Unsigned, IntSize::U8);
283
284unsafe impl H5Type for bool {
285    #[inline]
286    fn type_descriptor() -> TypeDescriptor {
287        TypeDescriptor::Boolean
288    }
289}
290
291// macro_rules! impl_tuple {
292//     (@second $a:tt $b:tt) => ($b);
293
294//     (@parse_fields [$($s:ident)*] $origin:ident $fields:ident | $t:ty $(,$tt:ty)*) => (
295//         let &$($s)*(.., ref f, $(impl_tuple!(@second $tt _),)*) = unsafe { &*$origin };
296//         let index = $fields.len();
297//         $fields.push(CompoundField {
298//             name: format!("{}", index),
299//             ty: <$t as H5Type>::type_descriptor(),
300//             offset: f as *const _ as _,
301//             index,
302//         });
303//         impl_tuple!(@parse_fields [$($s)*] $origin $fields | $($tt),*);
304//     );
305
306//     (@parse_fields [$($s:ident)*] $origin:ident $fields:ident |) => ();
307
308//     ($t:ident) => (
309//         unsafe impl<$t> H5Type for ($t,) where $t: H5Type {
310//             #[inline]
311//             fn type_descriptor() -> TypeDescriptor {
312//                 let size = mem::size_of::<($t,)>();
313//                 assert_eq!(size, mem::size_of::<$t>());
314//                 TypeDescriptor::Compound(CompoundType {
315//                     fields: vec![CompoundField::typed::<$t>("0", 0, 0)],
316//                     size,
317//                 })
318//             }
319//         }
320//     );
321
322//     ($t:ident, $($tt:ident),*) => (
323//         #[allow(dead_code, unused_variables)]
324//         unsafe impl<$t, $($tt),*> H5Type for ($t, $($tt),*)
325//             where $t: H5Type, $($tt: H5Type),*
326//         {
327//             fn type_descriptor() -> TypeDescriptor {
328//                 let origin: *const Self = std::ptr::null();
329//                 let mut fields = Vec::new();
330//                 impl_tuple!(@parse_fields [] origin fields | $t, $($tt),*);
331//                 let size = mem::size_of::<Self>();
332//                 fields.sort_by_key(|f| f.offset);
333//                 TypeDescriptor::Compound(CompoundType { fields, size })
334//             }
335//         }
336
337//         impl_tuple!($($tt),*);
338//     );
339// }
340
341// impl_tuple! { A, B, C, D, E, F, G, H, I, J, K, L }
342
343unsafe impl<T: H5Type, const N: usize> H5Type for [T; N] {
344    #[inline]
345    fn type_descriptor() -> TypeDescriptor {
346        TypeDescriptor::FixedArray(Box::new(<T as H5Type>::type_descriptor()), N)
347    }
348}
349
350unsafe impl<T: H5Type + Copy> H5Type for VarLenArray<T> {
351    #[inline]
352    fn type_descriptor() -> TypeDescriptor {
353        TypeDescriptor::VarLenArray(Box::new(<T as H5Type>::type_descriptor()))
354    }
355}
356
357unsafe impl<const N: usize> H5Type for FixedAscii<N> {
358    #[inline]
359    fn type_descriptor() -> TypeDescriptor {
360        TypeDescriptor::FixedAscii(N)
361    }
362}
363
364unsafe impl<const N: usize> H5Type for FixedUnicode<N> {
365    #[inline]
366    fn type_descriptor() -> TypeDescriptor {
367        TypeDescriptor::FixedUnicode(N)
368    }
369}
370
371unsafe impl H5Type for VarLenAscii {
372    #[inline]
373    fn type_descriptor() -> TypeDescriptor {
374        TypeDescriptor::VarLenAscii
375    }
376}
377
378unsafe impl H5Type for VarLenUnicode {
379    #[inline]
380    fn type_descriptor() -> TypeDescriptor {
381        TypeDescriptor::VarLenUnicode
382    }
383}
384
385#[cfg(test)]
386pub mod tests {
387    use super::TypeDescriptor as TD;
388    use super::{hvl_t, FloatSize, H5Type, IntSize};
389    use crate::array::VarLenArray;
390    use crate::string::{FixedAscii, FixedUnicode, VarLenAscii, VarLenUnicode};
391    use std::mem;
392
393    #[test]
394    pub fn test_scalar_types() {
395        assert_eq!(bool::type_descriptor(), TD::Boolean);
396        assert_eq!(i8::type_descriptor(), TD::Integer(IntSize::U1));
397        assert_eq!(i16::type_descriptor(), TD::Integer(IntSize::U2));
398        assert_eq!(i32::type_descriptor(), TD::Integer(IntSize::U4));
399        assert_eq!(i64::type_descriptor(), TD::Integer(IntSize::U8));
400        assert_eq!(u8::type_descriptor(), TD::Unsigned(IntSize::U1));
401        assert_eq!(u16::type_descriptor(), TD::Unsigned(IntSize::U2));
402        assert_eq!(u32::type_descriptor(), TD::Unsigned(IntSize::U4));
403        assert_eq!(u64::type_descriptor(), TD::Unsigned(IntSize::U8));
404        assert_eq!(f32::type_descriptor(), TD::Float(FloatSize::U4));
405        assert_eq!(f64::type_descriptor(), TD::Float(FloatSize::U8));
406
407        assert_eq!(bool::type_descriptor().size(), 1);
408        assert_eq!(i16::type_descriptor().size(), 2);
409        assert_eq!(u32::type_descriptor().size(), 4);
410        assert_eq!(f64::type_descriptor().size(), 8);
411    }
412
413    #[test]
414    #[cfg(target_pointer_width = "32")]
415    pub fn test_ptr_sized_ints() {
416        assert_eq!(isize::type_descriptor(), TD::Integer(IntSize::U4));
417        assert_eq!(usize::type_descriptor(), TD::Unsigned(IntSize::U4));
418
419        assert_eq!(usize::type_descriptor().size(), 4);
420    }
421
422    #[test]
423    #[cfg(target_pointer_width = "64")]
424    pub fn test_ptr_sized_ints() {
425        assert_eq!(isize::type_descriptor(), TD::Integer(IntSize::U8));
426        assert_eq!(usize::type_descriptor(), TD::Unsigned(IntSize::U8));
427
428        assert_eq!(usize::type_descriptor().size(), 8);
429    }
430
431    #[test]
432    pub fn test_fixed_array() {
433        type S = [T; 4];
434        type T = [u32; 256];
435        assert_eq!(T::type_descriptor(), TD::FixedArray(Box::new(TD::Unsigned(IntSize::U4)), 256));
436        assert_eq!(S::type_descriptor(), TD::FixedArray(Box::new(T::type_descriptor()), 4));
437    }
438
439    #[test]
440    pub fn test_varlen_array() {
441        type S = VarLenArray<u16>;
442        assert_eq!(S::type_descriptor(), TD::VarLenArray(Box::new(u16::type_descriptor())));
443        assert_eq!(mem::size_of::<VarLenArray<u8>>(), mem::size_of::<hvl_t>());
444    }
445
446    #[test]
447    pub fn test_string_types() {
448        type FA = FixedAscii<16>;
449        type FU = FixedUnicode<32>;
450        assert_eq!(FA::type_descriptor(), TD::FixedAscii(16));
451        assert_eq!(FU::type_descriptor(), TD::FixedUnicode(32));
452        assert_eq!(VarLenAscii::type_descriptor(), TD::VarLenAscii);
453        assert_eq!(VarLenUnicode::type_descriptor(), TD::VarLenUnicode);
454    }
455
456    // #[test]
457    // pub fn test_tuples() {
458    //     type T1 = (u16,);
459    //     let td = T1::type_descriptor();
460    //     assert_eq!(
461    //         td,
462    //         TD::Compound(CompoundType {
463    //             fields: vec![CompoundField::typed::<u16>("0", 0, 0),],
464    //             size: 2,
465    //         })
466    //     );
467    //     assert_eq!(td.size(), 2);
468    //     assert_eq!(mem::size_of::<T1>(), 2);
469
470    //     type T2 = (i32, f32, (u64,));
471    //     let td = T2::type_descriptor();
472    //     assert_eq!(
473    //         td,
474    //         TD::Compound(CompoundType {
475    //             fields: vec![
476    //                 CompoundField::typed::<i32>("0", 0, 0),
477    //                 CompoundField::typed::<f32>("1", 4, 1),
478    //                 CompoundField::new(
479    //                     "2",
480    //                     TD::Compound(CompoundType {
481    //                         fields: vec![CompoundField::typed::<u64>("0", 0, 0),],
482    //                         size: 8,
483    //                     }),
484    //                     8,
485    //                     2
486    //                 ),
487    //             ],
488    //             size: 16,
489    //         })
490    //     );
491    //     assert_eq!(td.size(), 16);
492    //     assert_eq!(mem::size_of::<T2>(), 16);
493    // }
494
495    // #[test]
496    // pub fn test_tuple_various_reprs() {
497    //     type T = (i8, u64, f32, bool);
498    //     assert_eq!(mem::size_of::<T>(), 16);
499
500    //     let td = T::type_descriptor();
501    //     assert_eq!(
502    //         td,
503    //         TD::Compound(CompoundType {
504    //             fields: vec![
505    //                 CompoundField::typed::<u64>("1", 0, 1),
506    //                 CompoundField::typed::<f32>("2", 8, 2),
507    //                 CompoundField::typed::<i8>("0", 12, 0),
508    //                 CompoundField::typed::<bool>("3", 13, 3),
509    //             ],
510    //             size: 16,
511    //         })
512    //     );
513    //     assert_eq!(td.size(), 16);
514
515    //     let td = T::type_descriptor().to_c_repr();
516    //     assert_eq!(
517    //         td,
518    //         TD::Compound(CompoundType {
519    //             fields: vec![
520    //                 CompoundField::typed::<i8>("0", 0, 0),
521    //                 CompoundField::typed::<u64>("1", 8, 1),
522    //                 CompoundField::typed::<f32>("2", 16, 2),
523    //                 CompoundField::typed::<bool>("3", 20, 3),
524    //             ],
525    //             size: 24,
526    //         })
527    //     );
528    //     assert_eq!(td.size(), 24);
529
530    //     let td = T::type_descriptor().to_packed_repr();
531    //     assert_eq!(
532    //         td,
533    //         TD::Compound(CompoundType {
534    //             fields: vec![
535    //                 CompoundField::typed::<i8>("0", 0, 0),
536    //                 CompoundField::typed::<u64>("1", 1, 1),
537    //                 CompoundField::typed::<f32>("2", 9, 2),
538    //                 CompoundField::typed::<bool>("3", 13, 3),
539    //             ],
540    //             size: 14,
541    //         })
542    //     );
543    //     assert_eq!(td.size(), 14);
544    // }
545}