Skip to main content

hdf5_reader/messages/
attribute_info.rs

1//! HDF5 Attribute Info message (type 0x0015).
2//!
3//! Provides information about how attributes are stored on an object:
4//! compactly (inline) or densely (in a fractal heap + v2 B-tree).
5
6use crate::error::{Error, Result};
7use crate::io::Cursor;
8
9/// Parsed attribute info message.
10#[derive(Debug, Clone)]
11pub struct AttributeInfoMessage {
12    /// Whether attribute creation order is tracked.
13    pub creation_order_tracked: bool,
14    /// Whether attribute creation order is indexed.
15    pub creation_order_indexed: bool,
16    /// Maximum creation order index, if tracked.
17    pub max_creation_index: Option<u64>,
18    /// Address of the fractal heap for attribute data.
19    pub fractal_heap_address: u64,
20    /// Address of the v2 B-tree for name-indexed lookups.
21    pub btree_name_index_address: u64,
22    /// Address of the v2 B-tree for creation-order lookups, if indexed.
23    pub btree_creation_order_address: Option<u64>,
24}
25
26/// Parse an attribute info message.
27pub fn parse(
28    cursor: &mut Cursor<'_>,
29    offset_size: u8,
30    _length_size: u8,
31    msg_size: usize,
32) -> Result<AttributeInfoMessage> {
33    let start = cursor.position();
34    let version = cursor.read_u8()?;
35
36    if version != 0 {
37        return Err(Error::InvalidData(format!(
38            "unsupported attribute info version: {}",
39            version
40        )));
41    }
42
43    let flags = cursor.read_u8()?;
44    let creation_order_tracked = (flags & 0x01) != 0;
45    let creation_order_indexed = (flags & 0x02) != 0;
46
47    let max_creation_index = if creation_order_tracked {
48        Some(cursor.read_u16_le()? as u64)
49    } else {
50        None
51    };
52
53    let fractal_heap_address = cursor.read_offset(offset_size)?;
54    let btree_name_index_address = cursor.read_offset(offset_size)?;
55
56    let btree_creation_order_address = if creation_order_indexed {
57        Some(cursor.read_offset(offset_size)?)
58    } else {
59        None
60    };
61
62    let consumed = (cursor.position() - start) as usize;
63    if consumed < msg_size {
64        cursor.skip(msg_size - consumed)?;
65    }
66
67    Ok(AttributeInfoMessage {
68        creation_order_tracked,
69        creation_order_indexed,
70        max_creation_index,
71        fractal_heap_address,
72        btree_name_index_address,
73        btree_creation_order_address,
74    })
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_parse_attr_info_simple() {
83        let mut data = vec![
84            0x00, // version
85            0x00, // flags
86        ];
87        data.extend_from_slice(&0xF000u64.to_le_bytes());
88        data.extend_from_slice(&0xF100u64.to_le_bytes());
89
90        let mut cursor = Cursor::new(&data);
91        let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
92        assert!(!msg.creation_order_tracked);
93        assert_eq!(msg.fractal_heap_address, 0xF000);
94        assert_eq!(msg.btree_name_index_address, 0xF100);
95    }
96}