Skip to main content

netcdf_reader/nc4/
types.rs

1//! Map HDF5 datatypes to NetCDF-4 types.
2//!
3//! HDF5 has a richer type system than NetCDF-4. This module maps the subset
4//! of HDF5 types that are valid in NetCDF-4 to `NcType`:
5//!
6//! | HDF5 Datatype                 | NcType   |
7//! |-------------------------------|----------|
8//! | FixedPoint { size=1, signed } | Byte     |
9//! | FixedPoint { size=1, !signed} | UByte    |
10//! | FixedPoint { size=2, signed } | Short    |
11//! | FixedPoint { size=2, !signed} | UShort   |
12//! | FixedPoint { size=4, signed } | Int      |
13//! | FixedPoint { size=4, !signed} | UInt     |
14//! | FixedPoint { size=8, signed } | Int64    |
15//! | FixedPoint { size=8, !signed} | UInt64   |
16//! | FloatingPoint { size=4 }      | Float    |
17//! | FloatingPoint { size=8 }      | Double   |
18//! | String (any)                  | String   |
19//! | Enum { base=byte }            | Byte*    |
20//! | Compound { .. }               | Compound |
21//! | Opaque { .. }                 | Opaque   |
22//! | Array { base, dims }          | Array    |
23//! | VarLen { base=u8 }            | String*  |
24//! | VarLen { base!=u8 }           | VLen     |
25//!
26//! * Enums are mapped to their base integer type.
27//! * NetCDF-4 `NC_STRING` is stored as HDF5 variable-length bytes.
28
29use hdf5_reader::messages::datatype::Datatype;
30
31use crate::error::{Error, Result};
32use crate::types::{NcCompoundField, NcType};
33
34/// Map an HDF5 datatype to a NetCDF type.
35pub fn hdf5_to_nc_type(dtype: &Datatype) -> Result<NcType> {
36    match dtype {
37        Datatype::FixedPoint { size, signed, .. } => match (size, signed) {
38            (1, true) => Ok(NcType::Byte),
39            (1, false) => Ok(NcType::UByte),
40            (2, true) => Ok(NcType::Short),
41            (2, false) => Ok(NcType::UShort),
42            (4, true) => Ok(NcType::Int),
43            (4, false) => Ok(NcType::UInt),
44            (8, true) => Ok(NcType::Int64),
45            (8, false) => Ok(NcType::UInt64),
46            _ => Err(Error::InvalidData(format!(
47                "unsupported HDF5 integer size {} for NetCDF-4",
48                size
49            ))),
50        },
51        Datatype::FloatingPoint { size, .. } => match size {
52            4 => Ok(NcType::Float),
53            8 => Ok(NcType::Double),
54            _ => Err(Error::InvalidData(format!(
55                "unsupported HDF5 float size {} for NetCDF-4",
56                size
57            ))),
58        },
59        Datatype::String { .. } => Ok(NcType::String),
60        Datatype::Enum { base, .. } => {
61            // Enums map to their base integer type.
62            hdf5_to_nc_type(base)
63        }
64        Datatype::Compound { size, fields } => {
65            let mut nc_fields = Vec::with_capacity(fields.len());
66            for f in fields {
67                nc_fields.push(NcCompoundField {
68                    name: f.name.clone(),
69                    offset: f.byte_offset as u64,
70                    dtype: hdf5_to_nc_type(&f.datatype)?,
71                });
72            }
73            Ok(NcType::Compound {
74                size: *size,
75                fields: nc_fields,
76            })
77        }
78        Datatype::Opaque { size, tag } => Ok(NcType::Opaque {
79            size: *size,
80            tag: tag.clone(),
81        }),
82        Datatype::Array { base, dims } => {
83            let base_nc = hdf5_to_nc_type(base)?;
84            Ok(NcType::Array {
85                base: Box::new(base_nc),
86                dims: dims.clone(),
87            })
88        }
89        Datatype::VarLen { base }
90            if matches!(
91                base.as_ref(),
92                Datatype::FixedPoint {
93                    size: 1,
94                    signed: false,
95                    ..
96                }
97            ) =>
98        {
99            Ok(NcType::String)
100        }
101        Datatype::VarLen { base } => {
102            let base_nc = hdf5_to_nc_type(base)?;
103            Ok(NcType::VLen {
104                base: Box::new(base_nc),
105            })
106        }
107        _ => Err(Error::InvalidData(format!(
108            "HDF5 datatype {:?} has no NetCDF-4 equivalent",
109            dtype
110        ))),
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use hdf5_reader::error::ByteOrder;
118
119    #[test]
120    fn test_integer_types() {
121        let bo = ByteOrder::LittleEndian;
122        assert_eq!(
123            hdf5_to_nc_type(&Datatype::FixedPoint {
124                size: 1,
125                signed: true,
126                byte_order: bo
127            })
128            .unwrap(),
129            NcType::Byte
130        );
131        assert_eq!(
132            hdf5_to_nc_type(&Datatype::FixedPoint {
133                size: 1,
134                signed: false,
135                byte_order: bo
136            })
137            .unwrap(),
138            NcType::UByte
139        );
140        assert_eq!(
141            hdf5_to_nc_type(&Datatype::FixedPoint {
142                size: 4,
143                signed: true,
144                byte_order: bo
145            })
146            .unwrap(),
147            NcType::Int
148        );
149        assert_eq!(
150            hdf5_to_nc_type(&Datatype::FixedPoint {
151                size: 8,
152                signed: false,
153                byte_order: bo
154            })
155            .unwrap(),
156            NcType::UInt64
157        );
158    }
159
160    #[test]
161    fn test_float_types() {
162        let bo = ByteOrder::LittleEndian;
163        assert_eq!(
164            hdf5_to_nc_type(&Datatype::FloatingPoint {
165                size: 4,
166                byte_order: bo
167            })
168            .unwrap(),
169            NcType::Float
170        );
171        assert_eq!(
172            hdf5_to_nc_type(&Datatype::FloatingPoint {
173                size: 8,
174                byte_order: bo
175            })
176            .unwrap(),
177            NcType::Double
178        );
179    }
180
181    #[test]
182    fn test_varlen_u8_maps_to_string() {
183        let bo = ByteOrder::LittleEndian;
184        assert_eq!(
185            hdf5_to_nc_type(&Datatype::VarLen {
186                base: Box::new(Datatype::FixedPoint {
187                    size: 1,
188                    signed: false,
189                    byte_order: bo,
190                }),
191            })
192            .unwrap(),
193            NcType::String
194        );
195    }
196}