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 }               | VLen     |
24//!
25//! * Enums are mapped to their base integer type.
26
27use hdf5_reader::messages::datatype::Datatype;
28
29use crate::error::{Error, Result};
30use crate::types::{NcCompoundField, NcType};
31
32/// Map an HDF5 datatype to a NetCDF type.
33pub fn hdf5_to_nc_type(dtype: &Datatype) -> Result<NcType> {
34    match dtype {
35        Datatype::FixedPoint { size, signed, .. } => match (size, signed) {
36            (1, true) => Ok(NcType::Byte),
37            (1, false) => Ok(NcType::UByte),
38            (2, true) => Ok(NcType::Short),
39            (2, false) => Ok(NcType::UShort),
40            (4, true) => Ok(NcType::Int),
41            (4, false) => Ok(NcType::UInt),
42            (8, true) => Ok(NcType::Int64),
43            (8, false) => Ok(NcType::UInt64),
44            _ => Err(Error::InvalidData(format!(
45                "unsupported HDF5 integer size {} for NetCDF-4",
46                size
47            ))),
48        },
49        Datatype::FloatingPoint { size, .. } => match size {
50            4 => Ok(NcType::Float),
51            8 => Ok(NcType::Double),
52            _ => Err(Error::InvalidData(format!(
53                "unsupported HDF5 float size {} for NetCDF-4",
54                size
55            ))),
56        },
57        Datatype::String { .. } => Ok(NcType::String),
58        Datatype::Enum { base, .. } => {
59            // Enums map to their base integer type.
60            hdf5_to_nc_type(base)
61        }
62        Datatype::Compound { size, fields } => {
63            let mut nc_fields = Vec::with_capacity(fields.len());
64            for f in fields {
65                nc_fields.push(NcCompoundField {
66                    name: f.name.clone(),
67                    offset: f.byte_offset as u64,
68                    dtype: hdf5_to_nc_type(&f.datatype)?,
69                });
70            }
71            Ok(NcType::Compound {
72                size: *size,
73                fields: nc_fields,
74            })
75        }
76        Datatype::Opaque { size, tag } => Ok(NcType::Opaque {
77            size: *size,
78            tag: tag.clone(),
79        }),
80        Datatype::Array { base, dims } => {
81            let base_nc = hdf5_to_nc_type(base)?;
82            Ok(NcType::Array {
83                base: Box::new(base_nc),
84                dims: dims.clone(),
85            })
86        }
87        Datatype::VarLen { base } => {
88            let base_nc = hdf5_to_nc_type(base)?;
89            Ok(NcType::VLen {
90                base: Box::new(base_nc),
91            })
92        }
93        _ => Err(Error::InvalidData(format!(
94            "HDF5 datatype {:?} has no NetCDF-4 equivalent",
95            dtype
96        ))),
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use hdf5_reader::error::ByteOrder;
104
105    #[test]
106    fn test_integer_types() {
107        let bo = ByteOrder::LittleEndian;
108        assert_eq!(
109            hdf5_to_nc_type(&Datatype::FixedPoint {
110                size: 1,
111                signed: true,
112                byte_order: bo
113            })
114            .unwrap(),
115            NcType::Byte
116        );
117        assert_eq!(
118            hdf5_to_nc_type(&Datatype::FixedPoint {
119                size: 1,
120                signed: false,
121                byte_order: bo
122            })
123            .unwrap(),
124            NcType::UByte
125        );
126        assert_eq!(
127            hdf5_to_nc_type(&Datatype::FixedPoint {
128                size: 4,
129                signed: true,
130                byte_order: bo
131            })
132            .unwrap(),
133            NcType::Int
134        );
135        assert_eq!(
136            hdf5_to_nc_type(&Datatype::FixedPoint {
137                size: 8,
138                signed: false,
139                byte_order: bo
140            })
141            .unwrap(),
142            NcType::UInt64
143        );
144    }
145
146    #[test]
147    fn test_float_types() {
148        let bo = ByteOrder::LittleEndian;
149        assert_eq!(
150            hdf5_to_nc_type(&Datatype::FloatingPoint {
151                size: 4,
152                byte_order: bo
153            })
154            .unwrap(),
155            NcType::Float
156        );
157        assert_eq!(
158            hdf5_to_nc_type(&Datatype::FloatingPoint {
159                size: 8,
160                byte_order: bo
161            })
162            .unwrap(),
163            NcType::Double
164        );
165    }
166}