Skip to main content

rustyhdf5_format/
group_info.rs

1//! HDF5 Group Info message parsing (message type 0x000A).
2
3use crate::error::FormatError;
4
5/// Parsed Group Info message.
6#[derive(Debug, Clone, PartialEq)]
7pub struct GroupInfoMessage {
8    /// Maximum number of links in compact storage before switching to dense.
9    pub max_compact: Option<u16>,
10    /// Minimum number of links in dense storage before switching back to compact.
11    pub min_dense: Option<u16>,
12    /// Estimated number of entries (hint).
13    pub estimated_num_entries: Option<u16>,
14    /// Estimated average link name length (hint).
15    pub estimated_name_length: Option<u16>,
16}
17
18impl GroupInfoMessage {
19    /// Parse a Group Info message from raw message data.
20    pub fn parse(data: &[u8]) -> Result<GroupInfoMessage, FormatError> {
21        if data.len() < 2 {
22            return Err(FormatError::UnexpectedEof {
23                expected: 2,
24                available: data.len(),
25            });
26        }
27
28        let version = data[0];
29        if version != 0 {
30            return Err(FormatError::InvalidGroupInfoVersion(version));
31        }
32
33        let flags = data[1];
34        let has_link_phase = flags & 0x01 != 0;
35        let has_estimated = flags & 0x02 != 0;
36
37        let mut pos = 2;
38
39        let (max_compact, min_dense) = if has_link_phase {
40            if pos + 4 > data.len() {
41                return Err(FormatError::UnexpectedEof {
42                    expected: pos + 4,
43                    available: data.len(),
44                });
45            }
46            let mc = u16::from_le_bytes([data[pos], data[pos + 1]]);
47            let md = u16::from_le_bytes([data[pos + 2], data[pos + 3]]);
48            pos += 4;
49            (Some(mc), Some(md))
50        } else {
51            (None, None)
52        };
53
54        let (estimated_num_entries, estimated_name_length) = if has_estimated {
55            if pos + 4 > data.len() {
56                return Err(FormatError::UnexpectedEof {
57                    expected: pos + 4,
58                    available: data.len(),
59                });
60            }
61            let ne = u16::from_le_bytes([data[pos], data[pos + 1]]);
62            let nl = u16::from_le_bytes([data[pos + 2], data[pos + 3]]);
63            (Some(ne), Some(nl))
64        } else {
65            (None, None)
66        };
67
68        Ok(GroupInfoMessage {
69            max_compact,
70            min_dense,
71            estimated_num_entries,
72            estimated_name_length,
73        })
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn with_link_phase_change() {
83        let data = vec![0, 0x01, 8, 0, 6, 0]; // version=0, flags=1, max_compact=8, min_dense=6
84        let msg = GroupInfoMessage::parse(&data).unwrap();
85        assert_eq!(msg.max_compact, Some(8));
86        assert_eq!(msg.min_dense, Some(6));
87        assert_eq!(msg.estimated_num_entries, None);
88    }
89
90    #[test]
91    fn with_estimated_entry_info() {
92        let data = vec![0, 0x02, 4, 0, 16, 0]; // flags=2
93        let msg = GroupInfoMessage::parse(&data).unwrap();
94        assert_eq!(msg.max_compact, None);
95        assert_eq!(msg.estimated_num_entries, Some(4));
96        assert_eq!(msg.estimated_name_length, Some(16));
97    }
98
99    #[test]
100    fn both_flags_set() {
101        let data = vec![0, 0x03, 8, 0, 6, 0, 10, 0, 32, 0];
102        let msg = GroupInfoMessage::parse(&data).unwrap();
103        assert_eq!(msg.max_compact, Some(8));
104        assert_eq!(msg.min_dense, Some(6));
105        assert_eq!(msg.estimated_num_entries, Some(10));
106        assert_eq!(msg.estimated_name_length, Some(32));
107    }
108
109    #[test]
110    fn no_flags() {
111        let data = vec![0, 0x00];
112        let msg = GroupInfoMessage::parse(&data).unwrap();
113        assert_eq!(msg.max_compact, None);
114        assert_eq!(msg.min_dense, None);
115        assert_eq!(msg.estimated_num_entries, None);
116        assert_eq!(msg.estimated_name_length, None);
117    }
118
119    #[test]
120    fn invalid_version() {
121        let data = vec![1, 0];
122        let err = GroupInfoMessage::parse(&data).unwrap_err();
123        assert_eq!(err, FormatError::InvalidGroupInfoVersion(1));
124    }
125}