Skip to main content

hdf5_reader/messages/
shared.rs

1//! HDF5 shared object-header message references.
2//!
3//! A shared message indicates that the actual message content is stored
4//! elsewhere — either in another object header or in the shared message
5//! heap (SOHM).
6
7use crate::error::{Error, Result};
8use crate::io::Cursor;
9
10/// Where the shared message is stored.
11#[derive(Debug, Clone)]
12pub enum SharedMessage {
13    /// Shared in another object header at the given address.
14    SharedInOhdr {
15        /// Original object-header message type from the shared message envelope.
16        message_type: u16,
17        /// Object header that contains the shared message.
18        address: u64,
19    },
20    /// Shared in the SOHM fractal heap, identified by a heap ID.
21    SharedInSohm {
22        /// Original object-header message type from the shared message envelope.
23        message_type: u16,
24        /// Fractal heap ID in the file's SOHM heap.
25        heap_id: Vec<u8>,
26    },
27}
28
29/// Parse a shared message wrapper.
30///
31/// `message_type` is the type ID from the containing object-header message
32/// envelope. The shared-message payload itself only stores where the actual
33/// message lives, so callers must preserve this type to decode SOHM payloads.
34pub fn parse(
35    cursor: &mut Cursor<'_>,
36    message_type: u16,
37    offset_size: u8,
38    _length_size: u8,
39    msg_size: usize,
40) -> Result<SharedMessage> {
41    let start = cursor.position();
42    let version = cursor.read_u8()?;
43
44    let msg = match version {
45        1 => {
46            // Version 1: type byte + reserved(6) + address
47            let _type_field = cursor.read_u8()?;
48            let _reserved = cursor.read_bytes(6)?;
49            let address = cursor.read_offset(offset_size)?;
50            SharedMessage::SharedInOhdr {
51                message_type,
52                address,
53            }
54        }
55        2 => {
56            let type_field = cursor.read_u8()?;
57            cursor.skip(2)?;
58            match type_field {
59                0 => {
60                    let address = cursor.read_offset(offset_size)?;
61                    SharedMessage::SharedInOhdr {
62                        message_type,
63                        address,
64                    }
65                }
66                t => {
67                    return Err(Error::InvalidData(format!(
68                        "unknown shared message v2 type: {}",
69                        t
70                    )));
71                }
72            }
73        }
74        3 => {
75            let type_field = cursor.read_u8()?;
76            cursor.skip(2)?;
77            match type_field {
78                1 => {
79                    let heap_id = cursor.read_bytes(8)?.to_vec();
80                    SharedMessage::SharedInSohm {
81                        message_type,
82                        heap_id,
83                    }
84                }
85                2 => {
86                    let address = cursor.read_offset(offset_size)?;
87                    SharedMessage::SharedInOhdr {
88                        message_type,
89                        address,
90                    }
91                }
92                t => {
93                    return Err(Error::InvalidData(format!(
94                        "unsupported shared message v3 type: {}",
95                        t
96                    )));
97                }
98            }
99        }
100        v => {
101            return Err(Error::InvalidData(format!(
102                "unsupported shared message version: {}",
103                v
104            )));
105        }
106    };
107
108    let consumed = (cursor.position() - start) as usize;
109    if consumed < msg_size {
110        cursor.skip(msg_size - consumed)?;
111    }
112
113    Ok(msg)
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use crate::messages::MSG_DATATYPE;
120
121    #[test]
122    fn parses_v2_object_header_reference_with_padding() {
123        let mut bytes = Vec::new();
124        bytes.push(2);
125        bytes.push(0);
126        bytes.extend_from_slice(&[0, 0]);
127        bytes.extend_from_slice(&0x1234u64.to_le_bytes());
128
129        let mut cursor = Cursor::new(&bytes);
130        let message = parse(&mut cursor, MSG_DATATYPE, 8, 8, bytes.len()).unwrap();
131        match message {
132            SharedMessage::SharedInOhdr {
133                message_type,
134                address,
135            } => {
136                assert_eq!(message_type, MSG_DATATYPE);
137                assert_eq!(address, 0x1234);
138            }
139            other => panic!("expected OHDR reference, got {:?}", other),
140        }
141    }
142
143    #[test]
144    fn parses_v3_sohm_heap_id_with_padding() {
145        let mut bytes = Vec::new();
146        bytes.push(3);
147        bytes.push(1);
148        bytes.extend_from_slice(&[0, 0]);
149        bytes.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
150
151        let mut cursor = Cursor::new(&bytes);
152        let message = parse(&mut cursor, MSG_DATATYPE, 8, 8, bytes.len()).unwrap();
153        match message {
154            SharedMessage::SharedInSohm {
155                message_type,
156                heap_id,
157            } => {
158                assert_eq!(message_type, MSG_DATATYPE);
159                assert_eq!(heap_id, vec![1, 2, 3, 4, 5, 6, 7, 8]);
160            }
161            other => panic!("expected SOHM reference, got {:?}", other),
162        }
163    }
164}