Skip to main content

hdf5_reader/messages/
group_info.rs

1//! HDF5 Group Info message (type 0x000A).
2//!
3//! Contains hints for the library about group storage thresholds and
4//! estimated entry counts. Used for v2-style groups.
5
6use crate::error::{Error, Result};
7use crate::io::Cursor;
8
9/// Parsed group info message.
10#[derive(Debug, Clone)]
11pub struct GroupInfoMessage {
12    /// Maximum number of links to store compactly (in header messages).
13    pub max_compact_links: Option<u16>,
14    /// Minimum number of links before switching to dense storage.
15    pub min_dense_links: Option<u16>,
16    /// Estimated number of entries in this group.
17    pub est_num_entries: Option<u16>,
18    /// Estimated average name length for entries.
19    pub est_name_len: Option<u16>,
20}
21
22/// Parse a group info message.
23pub fn parse(
24    cursor: &mut Cursor<'_>,
25    _offset_size: u8,
26    _length_size: u8,
27    msg_size: usize,
28) -> Result<GroupInfoMessage> {
29    let start = cursor.position();
30    let version = cursor.read_u8()?;
31
32    if version != 0 {
33        return Err(Error::InvalidData(format!(
34            "unsupported group info version: {}",
35            version
36        )));
37    }
38
39    let flags = cursor.read_u8()?;
40
41    // Bit 0: link phase change values present
42    let (max_compact_links, min_dense_links) = if (flags & 0x01) != 0 {
43        let max_compact = cursor.read_u16_le()?;
44        let min_dense = cursor.read_u16_le()?;
45        (Some(max_compact), Some(min_dense))
46    } else {
47        (None, None)
48    };
49
50    // Bit 1: estimated entry information present
51    let (est_num_entries, est_name_len) = if (flags & 0x02) != 0 {
52        let num = cursor.read_u16_le()?;
53        let len = cursor.read_u16_le()?;
54        (Some(num), Some(len))
55    } else {
56        (None, None)
57    };
58
59    let consumed = (cursor.position() - start) as usize;
60    if consumed < msg_size {
61        cursor.skip(msg_size - consumed)?;
62    }
63
64    Ok(GroupInfoMessage {
65        max_compact_links,
66        min_dense_links,
67        est_num_entries,
68        est_name_len,
69    })
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_parse_group_info_all_present() {
78        let mut data = vec![
79            0x00, // version
80            0x03, // flags: both bits set
81        ];
82        data.extend_from_slice(&8u16.to_le_bytes()); // max compact
83        data.extend_from_slice(&6u16.to_le_bytes()); // min dense
84        data.extend_from_slice(&4u16.to_le_bytes()); // est entries
85        data.extend_from_slice(&18u16.to_le_bytes()); // est name len
86
87        let mut cursor = Cursor::new(&data);
88        let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
89        assert_eq!(msg.max_compact_links, Some(8));
90        assert_eq!(msg.min_dense_links, Some(6));
91        assert_eq!(msg.est_num_entries, Some(4));
92        assert_eq!(msg.est_name_len, Some(18));
93    }
94
95    #[test]
96    fn test_parse_group_info_empty() {
97        let data = vec![0x00, 0x00];
98        let mut cursor = Cursor::new(&data);
99        let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
100        assert!(msg.max_compact_links.is_none());
101        assert!(msg.est_num_entries.is_none());
102    }
103}