use crate::{
metadata::tables::{
param::ParamRaw,
types::{RowWritable, TableInfoRef},
},
utils::{write_le_at, write_le_at_dyn},
Error, Result,
};
impl RowWritable for ParamRaw {
fn row_write(
&self,
data: &mut [u8],
offset: &mut usize,
_rid: u32,
sizes: &TableInfoRef,
) -> Result<()> {
let flags_u16 = u16::try_from(self.flags).map_err(|_| {
Error::LayoutFailed("Parameter flags value exceeds u16 range".to_string())
})?;
write_le_at(data, offset, flags_u16)?;
let sequence_u16 = u16::try_from(self.sequence).map_err(|_| {
Error::LayoutFailed("Parameter sequence value exceeds u16 range".to_string())
})?;
write_le_at(data, offset, sequence_u16)?;
write_le_at_dyn(data, offset, self.name, sizes.is_large_str())?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
metadata::tables::types::{RowReadable, TableInfo, TableRow},
metadata::token::Token,
};
use std::sync::Arc;
#[test]
fn test_row_size() {
let table_info = Arc::new(TableInfo::new_test(&[], false, false, false));
let size = <ParamRaw as TableRow>::row_size(&table_info);
assert_eq!(size, 6);
let table_info_large = Arc::new(TableInfo::new_test(&[], true, false, false));
let size_large = <ParamRaw as TableRow>::row_size(&table_info_large);
assert_eq!(size_large, 8);
}
#[test]
fn test_round_trip_serialization() {
let original_row = ParamRaw {
rid: 1,
token: Token::new(0x08000001),
offset: 0,
flags: 0x0101,
sequence: 0x0202,
name: 0x0303,
};
let table_info = Arc::new(TableInfo::new_test(&[], false, false, false));
let row_size = <ParamRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
original_row
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Serialization should succeed");
let mut read_offset = 0;
let deserialized_row = ParamRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.expect("Deserialization should succeed");
assert_eq!(deserialized_row.rid, original_row.rid);
assert_eq!(deserialized_row.flags, original_row.flags);
assert_eq!(deserialized_row.sequence, original_row.sequence);
assert_eq!(deserialized_row.name, original_row.name);
assert_eq!(offset, row_size, "Offset should match expected row size");
}
#[test]
fn test_known_binary_format_small_heap() {
let data = vec![
0x01, 0x01, 0x02, 0x02, 0x03, 0x03, ];
let table_info = Arc::new(TableInfo::new_test(&[], false, false, false));
let mut read_offset = 0;
let reference_row = ParamRaw::row_read(&data, &mut read_offset, 1, &table_info)
.expect("Reading reference data should succeed");
let mut buffer = vec![0u8; data.len()];
let mut write_offset = 0;
reference_row
.row_write(&mut buffer, &mut write_offset, 1, &table_info)
.expect("Serialization should succeed");
assert_eq!(
buffer, data,
"Serialized data should match original binary format"
);
}
#[test]
fn test_known_binary_format_large_heap() {
let data = vec![
0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, ];
let table_info = Arc::new(TableInfo::new_test(&[], true, false, false));
let mut read_offset = 0;
let reference_row = ParamRaw::row_read(&data, &mut read_offset, 1, &table_info)
.expect("Reading reference data should succeed");
let mut buffer = vec![0u8; data.len()];
let mut write_offset = 0;
reference_row
.row_write(&mut buffer, &mut write_offset, 1, &table_info)
.expect("Serialization should succeed");
assert_eq!(
buffer, data,
"Serialized data should match original binary format"
);
}
#[test]
fn test_parameter_attributes() {
let test_cases = vec![
(0x0000, "None"),
(0x0001, "In"),
(0x0002, "Out"),
(0x0003, "In|Out"),
(0x0010, "Optional"),
(0x1000, "HasDefault"),
(0x2000, "HasFieldMarshal"),
(0x3011, "In|Optional|HasDefault|HasFieldMarshal"), ];
let table_info = Arc::new(TableInfo::new_test(&[], false, false, false));
for (flags, description) in test_cases {
let param_row = ParamRaw {
rid: 1,
token: Token::new(0x08000001),
offset: 0,
flags,
sequence: 1,
name: 0x100,
};
let row_size = <ParamRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
param_row
.row_write(&mut buffer, &mut offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Serialization should succeed for {description}"));
let mut read_offset = 0;
let deserialized_row = ParamRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Deserialization should succeed for {description}"));
assert_eq!(
deserialized_row.flags, param_row.flags,
"Flags should match for {description}"
);
}
}
#[test]
fn test_sequence_numbers() {
let test_cases = vec![
(0, "Return type parameter"),
(1, "First parameter"),
(2, "Second parameter"),
(10, "Tenth parameter"),
(255, "Max 8-bit parameter"),
(65535, "Max 16-bit parameter"),
];
let table_info = Arc::new(TableInfo::new_test(&[], false, false, false));
for (sequence, description) in test_cases {
let param_row = ParamRaw {
rid: 1,
token: Token::new(0x08000001),
offset: 0,
flags: 0x0001, sequence,
name: 0x100,
};
let row_size = <ParamRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
param_row
.row_write(&mut buffer, &mut offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Serialization should succeed for {description}"));
let mut read_offset = 0;
let deserialized_row = ParamRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Deserialization should succeed for {description}"));
assert_eq!(
deserialized_row.sequence, param_row.sequence,
"Sequence should match for {description}"
);
}
}
#[test]
fn test_large_heap_serialization() {
let original_row = ParamRaw {
rid: 1,
token: Token::new(0x08000001),
offset: 0,
flags: 0x3011, sequence: 255,
name: 0x123456,
};
let table_info = Arc::new(TableInfo::new_test(&[], true, false, false));
let row_size = <ParamRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
original_row
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Large heap serialization should succeed");
let mut read_offset = 0;
let deserialized_row = ParamRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.expect("Large heap deserialization should succeed");
assert_eq!(deserialized_row.flags, original_row.flags);
assert_eq!(deserialized_row.sequence, original_row.sequence);
assert_eq!(deserialized_row.name, original_row.name);
}
#[test]
fn test_edge_cases() {
let unnamed_param = ParamRaw {
rid: 1,
token: Token::new(0x08000001),
offset: 0,
flags: 0, sequence: 0, name: 0, };
let table_info = Arc::new(TableInfo::new_test(&[], false, false, false));
let row_size = <ParamRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
unnamed_param
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Unnamed parameter serialization should succeed");
let mut read_offset = 0;
let deserialized_row = ParamRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.expect("Unnamed parameter deserialization should succeed");
assert_eq!(deserialized_row.flags, unnamed_param.flags);
assert_eq!(deserialized_row.sequence, unnamed_param.sequence);
assert_eq!(deserialized_row.name, unnamed_param.name);
}
#[test]
fn test_flags_range_validation() {
let large_flags_row = ParamRaw {
rid: 1,
token: Token::new(0x08000001),
offset: 0,
flags: 0x12345678, sequence: 0x87654321, name: 0x100,
};
let table_info = Arc::new(TableInfo::new_test(&[], false, false, false));
let row_size = <ParamRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
let result = large_flags_row.row_write(&mut buffer, &mut offset, 1, &table_info);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Parameter flags value exceeds u16 range"));
}
}