Skip to main content

rust_hdf5/format/messages/
continuation.rs

1//! Object header continuation message (type 0x10).
2//!
3//! Binary layout:
4//!   offset: sizeof_addr bytes (LE) — address of continuation block
5//!   length: sizeof_size bytes (LE) — length of continuation block
6
7use crate::format::bytes::read_le_uint as read_uint;
8use crate::format::{FormatContext, FormatError, FormatResult};
9
10/// Object header continuation message payload.
11#[derive(Debug, Clone, PartialEq)]
12pub struct ContinuationMessage {
13    /// File offset of the continuation block.
14    pub offset: u64,
15    /// Length of the continuation block in bytes.
16    pub length: u64,
17}
18
19impl ContinuationMessage {
20    pub fn new(offset: u64, length: u64) -> Self {
21        Self { offset, length }
22    }
23
24    // ------------------------------------------------------------------ encode
25
26    pub fn encode(&self, ctx: &FormatContext) -> Vec<u8> {
27        let sa = ctx.sizeof_addr as usize;
28        let ss = ctx.sizeof_size as usize;
29        let mut buf = Vec::with_capacity(sa + ss);
30        buf.extend_from_slice(&self.offset.to_le_bytes()[..sa]);
31        buf.extend_from_slice(&self.length.to_le_bytes()[..ss]);
32        buf
33    }
34
35    // ------------------------------------------------------------------ decode
36
37    pub fn decode(buf: &[u8], ctx: &FormatContext) -> FormatResult<(Self, usize)> {
38        let sa = ctx.sizeof_addr as usize;
39        let ss = ctx.sizeof_size as usize;
40        let needed = sa + ss;
41        if buf.len() < needed {
42            return Err(FormatError::BufferTooShort {
43                needed,
44                available: buf.len(),
45            });
46        }
47
48        let offset = read_uint(&buf[0..], sa);
49        let length = read_uint(&buf[sa..], ss);
50
51        Ok((Self { offset, length }, needed))
52    }
53}
54
55// ======================================================================= tests
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    fn ctx8() -> FormatContext {
62        FormatContext {
63            sizeof_addr: 8,
64            sizeof_size: 8,
65        }
66    }
67
68    fn ctx4() -> FormatContext {
69        FormatContext {
70            sizeof_addr: 4,
71            sizeof_size: 4,
72        }
73    }
74
75    #[test]
76    fn roundtrip_ctx8() {
77        let msg = ContinuationMessage::new(0x1000, 256);
78        let encoded = msg.encode(&ctx8());
79        assert_eq!(encoded.len(), 16);
80        let (decoded, consumed) = ContinuationMessage::decode(&encoded, &ctx8()).unwrap();
81        assert_eq!(consumed, 16);
82        assert_eq!(decoded, msg);
83    }
84
85    #[test]
86    fn roundtrip_ctx4() {
87        let msg = ContinuationMessage::new(0x800, 128);
88        let encoded = msg.encode(&ctx4());
89        assert_eq!(encoded.len(), 8);
90        let (decoded, consumed) = ContinuationMessage::decode(&encoded, &ctx4()).unwrap();
91        assert_eq!(consumed, 8);
92        assert_eq!(decoded, msg);
93    }
94
95    #[test]
96    fn roundtrip_zero() {
97        let msg = ContinuationMessage::new(0, 0);
98        let encoded = msg.encode(&ctx8());
99        let (decoded, _) = ContinuationMessage::decode(&encoded, &ctx8()).unwrap();
100        assert_eq!(decoded, msg);
101    }
102
103    #[test]
104    fn roundtrip_large_values() {
105        let msg = ContinuationMessage::new(0xDEAD_BEEF_CAFE_0000, 0x0000_FFFF_0000_1234);
106        let encoded = msg.encode(&ctx8());
107        let (decoded, _) = ContinuationMessage::decode(&encoded, &ctx8()).unwrap();
108        assert_eq!(decoded, msg);
109    }
110
111    #[test]
112    fn decode_buffer_too_short() {
113        let buf = [0u8; 4];
114        let err = ContinuationMessage::decode(&buf, &ctx8()).unwrap_err();
115        match err {
116            FormatError::BufferTooShort {
117                needed: 16,
118                available: 4,
119            } => {}
120            other => panic!("unexpected error: {:?}", other),
121        }
122    }
123
124    #[test]
125    fn decode_buffer_too_short_ctx4() {
126        let buf = [0u8; 6];
127        let err = ContinuationMessage::decode(&buf, &ctx4()).unwrap_err();
128        match err {
129            FormatError::BufferTooShort {
130                needed: 8,
131                available: 6,
132            } => {}
133            other => panic!("unexpected error: {:?}", other),
134        }
135    }
136
137    #[test]
138    fn encode_size() {
139        let msg = ContinuationMessage::new(1, 2);
140        assert_eq!(msg.encode(&ctx8()).len(), 16);
141        assert_eq!(msg.encode(&ctx4()).len(), 8);
142    }
143}