Skip to main content

hdf5_reader/messages/
dataspace.rs

1//! HDF5 Dataspace message (type 0x0001).
2//!
3//! A dataspace describes the shape of a dataset: scalar, null, or simple
4//! (one or more dimensions with current and optional maximum sizes).
5
6use crate::error::{Error, Result};
7use crate::io::Cursor;
8pub use hdf5_core::{DataspaceMessage, DataspaceType, UNLIMITED};
9
10/// Parse a dataspace message.
11///
12/// `length_size` is needed for version 1 where dimensions are stored using
13/// the file-global length size.
14pub fn parse(
15    cursor: &mut Cursor<'_>,
16    _offset_size: u8,
17    length_size: u8,
18    msg_size: usize,
19) -> Result<DataspaceMessage> {
20    let start = cursor.position();
21    let version = cursor.read_u8()?;
22
23    match version {
24        1 => parse_v1(cursor, length_size),
25        2 => parse_v2(cursor, length_size),
26        v => Err(Error::UnsupportedDataspaceVersion(v)),
27    }
28    .and_then(|msg| {
29        // Advance past any remaining bytes in the message
30        let consumed = (cursor.position() - start) as usize;
31        if consumed < msg_size {
32            cursor.skip(msg_size - consumed)?;
33        }
34        Ok(msg)
35    })
36}
37
38/// Version 1 dataspace message.
39fn parse_v1(cursor: &mut Cursor<'_>, length_size: u8) -> Result<DataspaceMessage> {
40    let rank = cursor.read_u8()?;
41    let flags = cursor.read_u8()?;
42    let _reserved = cursor.read_u8()?; // reserved
43    let _reserved2 = cursor.read_u32_le()?; // reserved
44
45    let has_max_dims = (flags & 0x01) != 0;
46    // Bit 1 was "permutation index present" in v1 but is never actually set
47    // in practice. We skip it if the flag is set.
48    let has_permutation = (flags & 0x02) != 0;
49
50    let dataspace_type = if rank == 0 {
51        DataspaceType::Scalar
52    } else {
53        DataspaceType::Simple
54    };
55
56    let mut dims = Vec::with_capacity(rank as usize);
57    for _ in 0..rank {
58        dims.push(cursor.read_length(length_size)?);
59    }
60
61    let max_dims = if has_max_dims {
62        let mut md = Vec::with_capacity(rank as usize);
63        for _ in 0..rank {
64            md.push(cursor.read_length(length_size)?);
65        }
66        Some(md)
67    } else {
68        None
69    };
70
71    if has_permutation {
72        // Skip permutation indices — each is `length_size` bytes.
73        for _ in 0..rank {
74            cursor.read_length(length_size)?;
75        }
76    }
77
78    Ok(DataspaceMessage {
79        rank,
80        dims,
81        max_dims,
82        dataspace_type,
83    })
84}
85
86/// Version 2 dataspace message.
87fn parse_v2(cursor: &mut Cursor<'_>, length_size: u8) -> Result<DataspaceMessage> {
88    let rank = cursor.read_u8()?;
89    let flags = cursor.read_u8()?;
90    let ds_type_byte = cursor.read_u8()?;
91
92    let has_max_dims = (flags & 0x01) != 0;
93
94    let dataspace_type = match ds_type_byte {
95        0 => DataspaceType::Scalar,
96        1 => DataspaceType::Simple,
97        2 => DataspaceType::Null,
98        _ => {
99            return Err(Error::InvalidData(format!(
100                "unknown dataspace type: {}",
101                ds_type_byte
102            )))
103        }
104    };
105
106    let mut dims = Vec::with_capacity(rank as usize);
107    for _ in 0..rank {
108        dims.push(cursor.read_length(length_size)?);
109    }
110
111    let max_dims = if has_max_dims {
112        let mut md = Vec::with_capacity(rank as usize);
113        for _ in 0..rank {
114            md.push(cursor.read_length(length_size)?);
115        }
116        Some(md)
117    } else {
118        None
119    };
120
121    Ok(DataspaceMessage {
122        rank,
123        dims,
124        max_dims,
125        dataspace_type,
126    })
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn parse_v1_scalar() {
135        // Version 1, rank=0 (scalar), flags=0, reserved bytes
136        let data = [
137            0x01, // version
138            0x00, // rank
139            0x00, // flags
140            0x00, // reserved
141            0x00, 0x00, 0x00, 0x00, // reserved u32
142        ];
143        let mut cursor = Cursor::new(&data);
144        let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
145        assert_eq!(msg.rank, 0);
146        assert_eq!(msg.dataspace_type, DataspaceType::Scalar);
147        assert!(msg.dims.is_empty());
148        assert!(msg.max_dims.is_none());
149        assert_eq!(msg.num_elements().unwrap(), 1);
150    }
151
152    #[test]
153    fn parse_v1_simple_2d() {
154        // Version 1, rank=2, flags=0x01 (has max dims), 8-byte lengths
155        let mut data = vec![
156            0x01, // version
157            0x02, // rank
158            0x01, // flags (has max dims)
159            0x00, // reserved
160            0x00, 0x00, 0x00, 0x00, // reserved u32
161        ];
162        // dim[0] = 10
163        data.extend_from_slice(&10u64.to_le_bytes());
164        // dim[1] = 20
165        data.extend_from_slice(&20u64.to_le_bytes());
166        // max_dim[0] = 100
167        data.extend_from_slice(&100u64.to_le_bytes());
168        // max_dim[1] = UNLIMITED
169        data.extend_from_slice(&u64::MAX.to_le_bytes());
170
171        let mut cursor = Cursor::new(&data);
172        let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
173        assert_eq!(msg.rank, 2);
174        assert_eq!(msg.dims, vec![10, 20]);
175        assert_eq!(msg.max_dims.as_ref().unwrap(), &vec![100, UNLIMITED]);
176        assert_eq!(msg.dataspace_type, DataspaceType::Simple);
177        assert_eq!(msg.num_elements().unwrap(), 200);
178    }
179
180    #[test]
181    fn parse_v2_simple_1d() {
182        // Version 2, rank=1, flags=0x00, type=1 (simple), 4-byte lengths
183        let mut data = vec![
184            0x02, // version
185            0x01, // rank
186            0x00, // flags
187            0x01, // type = simple
188        ];
189        // dim[0] = 42
190        data.extend_from_slice(&42u32.to_le_bytes());
191
192        let mut cursor = Cursor::new(&data);
193        let msg = parse(&mut cursor, 4, 4, data.len()).unwrap();
194        assert_eq!(msg.rank, 1);
195        assert_eq!(msg.dims, vec![42]);
196        assert!(msg.max_dims.is_none());
197        assert_eq!(msg.dataspace_type, DataspaceType::Simple);
198    }
199
200    #[test]
201    fn parse_v2_null() {
202        let data = [
203            0x02, // version
204            0x00, // rank
205            0x00, // flags
206            0x02, // type = null
207        ];
208        let mut cursor = Cursor::new(&data);
209        let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
210        assert_eq!(msg.dataspace_type, DataspaceType::Null);
211        assert_eq!(msg.num_elements().unwrap(), 0);
212    }
213
214    #[test]
215    fn parse_v2_with_max_dims() {
216        let mut data = vec![
217            0x02, // version
218            0x03, // rank = 3
219            0x01, // flags = has max dims
220            0x01, // type = simple
221        ];
222        // current dims: 5, 10, 15
223        for &d in &[5u64, 10, 15] {
224            data.extend_from_slice(&d.to_le_bytes());
225        }
226        // max dims: 50, 100, UNLIMITED
227        for &d in &[50u64, 100, u64::MAX] {
228            data.extend_from_slice(&d.to_le_bytes());
229        }
230
231        let mut cursor = Cursor::new(&data);
232        let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
233        assert_eq!(msg.rank, 3);
234        assert_eq!(msg.dims, vec![5, 10, 15]);
235        let md = msg.max_dims.clone().unwrap();
236        assert_eq!(md, vec![50, 100, UNLIMITED]);
237        assert_eq!(msg.num_elements().unwrap(), 750);
238    }
239
240    #[test]
241    fn unsupported_version() {
242        let data = [0x03, 0x00, 0x00, 0x00];
243        let mut cursor = Cursor::new(&data);
244        assert!(parse(&mut cursor, 8, 8, data.len()).is_err());
245    }
246
247    #[test]
248    fn num_elements_rejects_overflow() {
249        let msg = DataspaceMessage {
250            rank: 2,
251            dims: vec![u64::MAX, 2],
252            max_dims: None,
253            dataspace_type: DataspaceType::Simple,
254        };
255
256        let err = msg.num_elements().unwrap_err();
257        assert!(err.to_string().contains("element count"));
258    }
259}