use crate::error::{IgtlError, Result};
use bytes::{Buf, BufMut};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExtendedHeader {
pub extended_header_size: u16,
pub metadata_header_size: u16,
pub metadata_size: u32,
pub message_id: u32,
pub additional_fields: Vec<u8>,
}
impl ExtendedHeader {
pub const MIN_SIZE: usize = 12;
pub fn new() -> Self {
ExtendedHeader {
extended_header_size: Self::MIN_SIZE as u16,
metadata_header_size: 0,
metadata_size: 0,
message_id: 0,
additional_fields: Vec::new(),
}
}
pub fn with_metadata(metadata_count: u16, metadata_size: u32) -> Self {
ExtendedHeader {
extended_header_size: Self::MIN_SIZE as u16,
metadata_header_size: metadata_count,
metadata_size,
message_id: 0,
additional_fields: Vec::new(),
}
}
pub fn with_message_id(message_id: u32) -> Self {
ExtendedHeader {
extended_header_size: Self::MIN_SIZE as u16,
metadata_header_size: 0,
metadata_size: 0,
message_id,
additional_fields: Vec::new(),
}
}
pub fn set_additional_fields(&mut self, data: Vec<u8>) {
self.extended_header_size = (Self::MIN_SIZE + data.len()) as u16;
self.additional_fields = data;
}
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.extended_header_size as usize);
buf.put_u16(self.extended_header_size);
buf.put_u16(self.metadata_header_size);
buf.put_u32(self.metadata_size);
buf.put_u32(self.message_id);
buf.extend_from_slice(&self.additional_fields);
buf
}
pub fn decode(data: &[u8]) -> Result<Self> {
if data.len() < Self::MIN_SIZE {
return Err(IgtlError::InvalidSize {
expected: Self::MIN_SIZE,
actual: data.len(),
});
}
let mut cursor = std::io::Cursor::new(data);
let extended_header_size = cursor.get_u16();
if (extended_header_size as usize) < Self::MIN_SIZE {
return Err(IgtlError::InvalidHeader(format!(
"Extended header size {} is less than minimum {}",
extended_header_size,
Self::MIN_SIZE
)));
}
if (extended_header_size as usize) > data.len() {
return Err(IgtlError::InvalidSize {
expected: extended_header_size as usize,
actual: data.len(),
});
}
let metadata_header_size = cursor.get_u16();
let metadata_size = cursor.get_u32();
let message_id = cursor.get_u32();
let additional_size = extended_header_size as usize - Self::MIN_SIZE;
let mut additional_fields = vec![0u8; additional_size];
if additional_size > 0 {
cursor.copy_to_slice(&mut additional_fields);
}
Ok(ExtendedHeader {
extended_header_size,
metadata_header_size,
metadata_size,
message_id,
additional_fields,
})
}
pub fn size(&self) -> usize {
self.extended_header_size as usize
}
pub fn has_metadata(&self) -> bool {
self.metadata_size > 0
}
pub fn get_metadata_size(&self) -> usize {
self.metadata_size as usize
}
pub fn get_metadata_header_size(&self) -> usize {
self.metadata_header_size as usize
}
#[deprecated(
note = "Use get_metadata_header_size() instead - metadata_header_size is size in bytes, not count"
)]
pub fn get_metadata_count(&self) -> usize {
self.metadata_header_size as usize
}
pub fn get_message_id(&self) -> u32 {
self.message_id
}
}
impl Default for ExtendedHeader {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_extended_header() {
let ext_header = ExtendedHeader::new();
assert_eq!(ext_header.extended_header_size, 12);
assert_eq!(ext_header.metadata_header_size, 0);
assert_eq!(ext_header.metadata_size, 0);
assert_eq!(ext_header.message_id, 0);
assert!(ext_header.additional_fields.is_empty());
}
#[test]
fn test_with_metadata() {
let ext_header = ExtendedHeader::with_metadata(5, 128);
assert_eq!(ext_header.metadata_header_size, 5);
assert_eq!(ext_header.metadata_size, 128);
assert!(ext_header.has_metadata());
assert_eq!(ext_header.get_metadata_header_size(), 5);
assert_eq!(ext_header.get_metadata_size(), 128);
}
#[test]
fn test_with_message_id() {
let ext_header = ExtendedHeader::with_message_id(12345);
assert_eq!(ext_header.message_id, 12345);
assert_eq!(ext_header.get_message_id(), 12345);
}
#[test]
fn test_encode_decode_roundtrip() {
let original = ExtendedHeader {
extended_header_size: 12,
metadata_header_size: 3,
metadata_size: 256,
message_id: 98765,
additional_fields: Vec::new(),
};
let encoded = original.encode();
assert_eq!(encoded.len(), 12);
let decoded = ExtendedHeader::decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn test_encode_decode_with_additional_fields() {
let mut original = ExtendedHeader::new();
original.set_additional_fields(vec![0xAA, 0xBB, 0xCC, 0xDD]);
assert_eq!(original.extended_header_size, 16);
let encoded = original.encode();
assert_eq!(encoded.len(), 16);
let decoded = ExtendedHeader::decode(&encoded).unwrap();
assert_eq!(decoded, original);
assert_eq!(decoded.additional_fields, vec![0xAA, 0xBB, 0xCC, 0xDD]);
}
#[test]
fn test_decode_too_small() {
let data = vec![0u8; 10];
let result = ExtendedHeader::decode(&data);
assert!(matches!(result, Err(IgtlError::InvalidSize { .. })));
}
#[test]
fn test_decode_invalid_size() {
let mut data = vec![0u8; 12];
data[0] = 0;
data[1] = 8;
let result = ExtendedHeader::decode(&data);
assert!(matches!(result, Err(IgtlError::InvalidHeader(_))));
}
#[test]
fn test_size_methods() {
let ext_header = ExtendedHeader::with_metadata(2, 64);
assert_eq!(ext_header.size(), 12);
assert!(ext_header.has_metadata());
}
#[test]
fn test_big_endian_encoding() {
let ext_header = ExtendedHeader {
extended_header_size: 0x1234,
metadata_header_size: 0x5678,
metadata_size: 0x9ABCDEF0,
message_id: 0x11223344,
additional_fields: Vec::new(),
};
let encoded = ext_header.encode();
assert_eq!(encoded[0], 0x12);
assert_eq!(encoded[1], 0x34);
assert_eq!(encoded[2], 0x56);
assert_eq!(encoded[3], 0x78);
assert_eq!(encoded[4], 0x9A);
assert_eq!(encoded[5], 0xBC);
assert_eq!(encoded[6], 0xDE);
assert_eq!(encoded[7], 0xF0);
assert_eq!(encoded[8], 0x11);
assert_eq!(encoded[9], 0x22);
assert_eq!(encoded[10], 0x33);
assert_eq!(encoded[11], 0x44);
}
#[test]
fn test_real_world_example() {
let data = vec![
0x00, 0x0C, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, ];
let ext_header = ExtendedHeader::decode(&data).unwrap();
assert_eq!(ext_header.extended_header_size, 12);
assert_eq!(ext_header.metadata_header_size, 1);
assert_eq!(ext_header.metadata_size, 20);
assert_eq!(ext_header.message_id, 0);
assert!(ext_header.has_metadata());
}
}