openigtlink_rust/protocol/
extended_header.rs

1//! OpenIGTLink Version 3 Extended Header implementation
2//!
3//! The Extended Header is an optional component in OpenIGTLink Version 3 messages
4//! that provides additional metadata and message identification capabilities.
5
6use crate::error::{IgtlError, Result};
7use bytes::{Buf, BufMut};
8
9/// OpenIGTLink Version 3 Extended Header structure
10///
11/// The Extended Header is a fixed 12-byte structure (minimum) that appears
12/// immediately after the standard 58-byte header when version >= 3.
13///
14/// # Structure (12 bytes minimum)
15/// - extended_header_size (2 bytes) - Total size including this field and any additional fields
16/// - metadata_header_size (2 bytes) - Number of metadata key-value pairs
17/// - metadata_size (4 bytes) - Total metadata size in bytes
18/// - message_id (4 bytes) - Unique message identifier
19/// - additional_fields (variable) - Optional implementation-specific data
20///
21/// # C++ Compatibility
22/// This structure matches the igtlMessageHeader.h ExtendedHeader format
23/// from the OpenIGTLink C++ reference implementation.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct ExtendedHeader {
26    /// Total size of extended header in bytes (including this field)
27    /// Minimum value: 12 (for standard Extended Header)
28    /// Larger values indicate additional implementation-specific fields
29    pub extended_header_size: u16,
30
31    /// Number of metadata key-value pairs that follow the message content
32    /// This is a count, not a size in bytes
33    pub metadata_header_size: u16,
34
35    /// Total size of metadata section in bytes (not including this Extended Header)
36    /// The metadata section appears at the end of the message body
37    pub metadata_size: u32,
38
39    /// Unique message identifier for this message
40    /// Can be used for:
41    /// - Request/response correlation
42    /// - Message tracking and debugging
43    /// - Transfer checkpointing and resumption
44    pub message_id: u32,
45
46    /// Additional implementation-specific fields (if extended_header_size > 12)
47    /// These are optional and can contain custom protocol extensions
48    pub additional_fields: Vec<u8>,
49}
50
51impl ExtendedHeader {
52    /// Minimum Extended Header size (standard fields only)
53    pub const MIN_SIZE: usize = 12;
54
55    /// Create a new Extended Header with default values
56    ///
57    /// # Returns
58    /// Extended Header with:
59    /// - extended_header_size = 12 (minimum)
60    /// - metadata_header_size = 0
61    /// - metadata_size = 0
62    /// - message_id = 0
63    /// - no additional fields
64    pub fn new() -> Self {
65        ExtendedHeader {
66            extended_header_size: Self::MIN_SIZE as u16,
67            metadata_header_size: 0,
68            metadata_size: 0,
69            message_id: 0,
70            additional_fields: Vec::new(),
71        }
72    }
73
74    /// Create an Extended Header with metadata information
75    ///
76    /// # Arguments
77    /// * `metadata_count` - Number of metadata key-value pairs
78    /// * `metadata_size` - Total metadata size in bytes
79    ///
80    /// # Returns
81    /// Extended Header configured for metadata
82    pub fn with_metadata(metadata_count: u16, metadata_size: u32) -> Self {
83        ExtendedHeader {
84            extended_header_size: Self::MIN_SIZE as u16,
85            metadata_header_size: metadata_count,
86            metadata_size,
87            message_id: 0,
88            additional_fields: Vec::new(),
89        }
90    }
91
92    /// Create an Extended Header with a message ID
93    ///
94    /// # Arguments
95    /// * `message_id` - Unique message identifier
96    ///
97    /// # Returns
98    /// Extended Header with the specified message ID
99    pub fn with_message_id(message_id: u32) -> Self {
100        ExtendedHeader {
101            extended_header_size: Self::MIN_SIZE as u16,
102            metadata_header_size: 0,
103            metadata_size: 0,
104            message_id,
105            additional_fields: Vec::new(),
106        }
107    }
108
109    /// Add additional implementation-specific fields
110    ///
111    /// # Arguments
112    /// * `data` - Additional field data
113    ///
114    /// # Notes
115    /// This automatically updates extended_header_size to include the additional data
116    pub fn set_additional_fields(&mut self, data: Vec<u8>) {
117        self.extended_header_size = (Self::MIN_SIZE + data.len()) as u16;
118        self.additional_fields = data;
119    }
120
121    /// Encode the Extended Header to bytes
122    ///
123    /// # Returns
124    /// Byte vector containing the encoded Extended Header
125    pub fn encode(&self) -> Vec<u8> {
126        let mut buf = Vec::with_capacity(self.extended_header_size as usize);
127
128        // Extended header size (2 bytes, big-endian)
129        buf.put_u16(self.extended_header_size);
130
131        // Metadata header size / count (2 bytes, big-endian)
132        buf.put_u16(self.metadata_header_size);
133
134        // Metadata size in bytes (4 bytes, big-endian)
135        buf.put_u32(self.metadata_size);
136
137        // Message ID (4 bytes, big-endian)
138        buf.put_u32(self.message_id);
139
140        // Additional fields (if any)
141        buf.extend_from_slice(&self.additional_fields);
142
143        buf
144    }
145
146    /// Decode an Extended Header from bytes
147    ///
148    /// # Arguments
149    /// * `data` - Byte slice containing at least 12 bytes
150    ///
151    /// # Returns
152    /// Decoded Extended Header or error
153    ///
154    /// # Errors
155    /// Returns error if data is less than 12 bytes or if extended_header_size
156    /// is less than 12 or larger than available data
157    pub fn decode(data: &[u8]) -> Result<Self> {
158        if data.len() < Self::MIN_SIZE {
159            return Err(IgtlError::InvalidSize {
160                expected: Self::MIN_SIZE,
161                actual: data.len(),
162            });
163        }
164
165        let mut cursor = std::io::Cursor::new(data);
166
167        // Read extended header size (2 bytes, big-endian)
168        let extended_header_size = cursor.get_u16();
169
170        if (extended_header_size as usize) < Self::MIN_SIZE {
171            return Err(IgtlError::InvalidHeader(format!(
172                "Extended header size {} is less than minimum {}",
173                extended_header_size,
174                Self::MIN_SIZE
175            )));
176        }
177
178        if (extended_header_size as usize) > data.len() {
179            return Err(IgtlError::InvalidSize {
180                expected: extended_header_size as usize,
181                actual: data.len(),
182            });
183        }
184
185        // Read metadata header size / count (2 bytes, big-endian)
186        let metadata_header_size = cursor.get_u16();
187
188        // Read metadata size (4 bytes, big-endian)
189        let metadata_size = cursor.get_u32();
190
191        // Read message ID (4 bytes, big-endian)
192        let message_id = cursor.get_u32();
193
194        // Read additional fields (if any)
195        let additional_size = extended_header_size as usize - Self::MIN_SIZE;
196        let mut additional_fields = vec![0u8; additional_size];
197        if additional_size > 0 {
198            cursor.copy_to_slice(&mut additional_fields);
199        }
200
201        Ok(ExtendedHeader {
202            extended_header_size,
203            metadata_header_size,
204            metadata_size,
205            message_id,
206            additional_fields,
207        })
208    }
209
210    /// Get the total size of this Extended Header
211    pub fn size(&self) -> usize {
212        self.extended_header_size as usize
213    }
214
215    /// Check if this Extended Header indicates metadata is present
216    pub fn has_metadata(&self) -> bool {
217        self.metadata_size > 0
218    }
219
220    /// Get the metadata size in bytes (size of metadata data)
221    pub fn get_metadata_size(&self) -> usize {
222        self.metadata_size as usize
223    }
224
225    /// Get the metadata header size in bytes (size of metadata structure definitions)
226    pub fn get_metadata_header_size(&self) -> usize {
227        self.metadata_header_size as usize
228    }
229
230    /// Get the number of metadata entries (deprecated - metadata_header_size is not entry count)
231    #[deprecated(
232        note = "Use get_metadata_header_size() instead - metadata_header_size is size in bytes, not count"
233    )]
234    pub fn get_metadata_count(&self) -> usize {
235        self.metadata_header_size as usize
236    }
237
238    /// Get the message ID
239    pub fn get_message_id(&self) -> u32 {
240        self.message_id
241    }
242}
243
244impl Default for ExtendedHeader {
245    fn default() -> Self {
246        Self::new()
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253
254    #[test]
255    fn test_new_extended_header() {
256        let ext_header = ExtendedHeader::new();
257        assert_eq!(ext_header.extended_header_size, 12);
258        assert_eq!(ext_header.metadata_header_size, 0);
259        assert_eq!(ext_header.metadata_size, 0);
260        assert_eq!(ext_header.message_id, 0);
261        assert!(ext_header.additional_fields.is_empty());
262    }
263
264    #[test]
265    fn test_with_metadata() {
266        let ext_header = ExtendedHeader::with_metadata(5, 128);
267        assert_eq!(ext_header.metadata_header_size, 5);
268        assert_eq!(ext_header.metadata_size, 128);
269        assert!(ext_header.has_metadata());
270        assert_eq!(ext_header.get_metadata_header_size(), 5);
271        assert_eq!(ext_header.get_metadata_size(), 128);
272    }
273
274    #[test]
275    fn test_with_message_id() {
276        let ext_header = ExtendedHeader::with_message_id(12345);
277        assert_eq!(ext_header.message_id, 12345);
278        assert_eq!(ext_header.get_message_id(), 12345);
279    }
280
281    #[test]
282    fn test_encode_decode_roundtrip() {
283        let original = ExtendedHeader {
284            extended_header_size: 12,
285            metadata_header_size: 3,
286            metadata_size: 256,
287            message_id: 98765,
288            additional_fields: Vec::new(),
289        };
290
291        let encoded = original.encode();
292        assert_eq!(encoded.len(), 12);
293
294        let decoded = ExtendedHeader::decode(&encoded).unwrap();
295        assert_eq!(decoded, original);
296    }
297
298    #[test]
299    fn test_encode_decode_with_additional_fields() {
300        let mut original = ExtendedHeader::new();
301        original.set_additional_fields(vec![0xAA, 0xBB, 0xCC, 0xDD]);
302
303        assert_eq!(original.extended_header_size, 16); // 12 + 4
304
305        let encoded = original.encode();
306        assert_eq!(encoded.len(), 16);
307
308        let decoded = ExtendedHeader::decode(&encoded).unwrap();
309        assert_eq!(decoded, original);
310        assert_eq!(decoded.additional_fields, vec![0xAA, 0xBB, 0xCC, 0xDD]);
311    }
312
313    #[test]
314    fn test_decode_too_small() {
315        let data = vec![0u8; 10];
316        let result = ExtendedHeader::decode(&data);
317        assert!(matches!(result, Err(IgtlError::InvalidSize { .. })));
318    }
319
320    #[test]
321    fn test_decode_invalid_size() {
322        let mut data = vec![0u8; 12];
323        // Set extended_header_size to 8 (less than minimum 12)
324        data[0] = 0;
325        data[1] = 8;
326
327        let result = ExtendedHeader::decode(&data);
328        assert!(matches!(result, Err(IgtlError::InvalidHeader(_))));
329    }
330
331    #[test]
332    fn test_size_methods() {
333        let ext_header = ExtendedHeader::with_metadata(2, 64);
334        assert_eq!(ext_header.size(), 12);
335        assert!(ext_header.has_metadata());
336    }
337
338    #[test]
339    fn test_big_endian_encoding() {
340        let ext_header = ExtendedHeader {
341            extended_header_size: 0x1234,
342            metadata_header_size: 0x5678,
343            metadata_size: 0x9ABCDEF0,
344            message_id: 0x11223344,
345            additional_fields: Vec::new(),
346        };
347
348        let encoded = ext_header.encode();
349
350        // Verify big-endian encoding
351        assert_eq!(encoded[0], 0x12);
352        assert_eq!(encoded[1], 0x34);
353        assert_eq!(encoded[2], 0x56);
354        assert_eq!(encoded[3], 0x78);
355        assert_eq!(encoded[4], 0x9A);
356        assert_eq!(encoded[5], 0xBC);
357        assert_eq!(encoded[6], 0xDE);
358        assert_eq!(encoded[7], 0xF0);
359        assert_eq!(encoded[8], 0x11);
360        assert_eq!(encoded[9], 0x22);
361        assert_eq!(encoded[10], 0x33);
362        assert_eq!(encoded[11], 0x44);
363    }
364
365    #[test]
366    fn test_real_world_example() {
367        // Example from actual OpenIGTLink message
368        let data = vec![
369            0x00, 0x0C, // extended_header_size = 12
370            0x00, 0x01, // metadata_header_size = 1 (1 entry)
371            0x00, 0x00, 0x00, 0x14, // metadata_size = 20 bytes
372            0x00, 0x00, 0x00, 0x00, // message_id = 0
373        ];
374
375        let ext_header = ExtendedHeader::decode(&data).unwrap();
376        assert_eq!(ext_header.extended_header_size, 12);
377        assert_eq!(ext_header.metadata_header_size, 1);
378        assert_eq!(ext_header.metadata_size, 20);
379        assert_eq!(ext_header.message_id, 0);
380        assert!(ext_header.has_metadata());
381    }
382}