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