use crate::{
metadata::tables::{
customdebuginformation::CustomDebugInformationRaw,
types::{CodedIndexType, RowWritable, TableInfoRef},
},
utils::write_le_at_dyn,
Result,
};
impl RowWritable for CustomDebugInformationRaw {
fn row_write(
&self,
data: &mut [u8],
offset: &mut usize,
_rid: u32,
sizes: &TableInfoRef,
) -> Result<()> {
let parent_value = sizes.encode_coded_index(
self.parent.tag,
self.parent.row,
CodedIndexType::HasCustomDebugInformation,
)?;
write_le_at_dyn(
data,
offset,
parent_value,
sizes.coded_index_bits(CodedIndexType::HasCustomDebugInformation) > 16,
)?;
write_le_at_dyn(data, offset, self.kind, sizes.is_large_guid())?;
write_le_at_dyn(data, offset, self.value, sizes.is_large_blob())?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
metadata::tables::types::{CodedIndex, CodedIndexType, RowReadable, TableInfo, TableRow},
metadata::{tables::TableId, token::Token},
};
#[test]
fn test_round_trip_serialization_small_heaps() {
let original_row = CustomDebugInformationRaw {
rid: 1,
token: Token::new(0x3700_0001),
offset: 0,
parent: CodedIndex::new(
TableId::MethodDef,
42,
CodedIndexType::HasCustomDebugInformation,
),
kind: 15,
value: 200,
};
let table_info = std::sync::Arc::new(TableInfo::new_test(
&[
(TableId::CustomDebugInformation, 100),
(TableId::MethodDef, 1000),
],
false,
false,
false,
));
let row_size = <CustomDebugInformationRaw 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 =
CustomDebugInformationRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.expect("Deserialization should succeed");
assert_eq!(original_row.parent.tag, deserialized_row.parent.tag);
assert_eq!(original_row.parent.row, deserialized_row.parent.row);
assert_eq!(original_row.kind, deserialized_row.kind);
assert_eq!(original_row.value, deserialized_row.value);
assert_eq!(offset, row_size, "Offset should match expected row size");
assert_eq!(
read_offset, row_size,
"Read offset should match expected row size"
);
}
#[test]
fn test_round_trip_serialization_large_heaps() {
let original_row = CustomDebugInformationRaw {
rid: 2,
token: Token::new(0x3700_0002),
offset: 0,
parent: CodedIndex::new(
TableId::TypeDef,
12345,
CodedIndexType::HasCustomDebugInformation,
),
kind: 0x12345,
value: 0x54321,
};
let table_info = std::sync::Arc::new(TableInfo::new_test(
&[
(TableId::CustomDebugInformation, 10000),
(TableId::TypeDef, 100000),
(TableId::MethodDef, 100000),
],
true,
true,
true,
));
let row_size = <CustomDebugInformationRaw 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, 2, &table_info)
.expect("Serialization should succeed");
let mut read_offset = 0;
let deserialized_row =
CustomDebugInformationRaw::row_read(&buffer, &mut read_offset, 2, &table_info)
.expect("Deserialization should succeed");
assert_eq!(original_row.parent.tag, deserialized_row.parent.tag);
assert_eq!(original_row.parent.row, deserialized_row.parent.row);
assert_eq!(original_row.kind, deserialized_row.kind);
assert_eq!(original_row.value, deserialized_row.value);
assert_eq!(offset, row_size, "Offset should match expected row size");
assert_eq!(
read_offset, row_size,
"Read offset should match expected row size"
);
}
#[test]
fn test_known_binary_format_small_heaps() {
let custom_debug_info = CustomDebugInformationRaw {
rid: 1,
token: Token::new(0x3700_0001),
offset: 0,
parent: CodedIndex::new(
TableId::MemberRef,
0,
CodedIndexType::HasCustomDebugInformation,
),
kind: 0x0001,
value: 0x000A,
};
let table_info = std::sync::Arc::new(TableInfo::new_test(
&[
(TableId::CustomDebugInformation, 100),
(TableId::MethodDef, 1000),
(TableId::MemberRef, 1000),
],
false,
false,
false,
));
let row_size = <CustomDebugInformationRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
custom_debug_info
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Serialization should succeed");
assert_eq!(row_size, 6, "Row size should be 6 bytes for small heaps");
assert_eq!(buffer[0], 0x06);
assert_eq!(buffer[1], 0x00);
assert_eq!(buffer[2], 0x01);
assert_eq!(buffer[3], 0x00);
assert_eq!(buffer[4], 0x0A);
assert_eq!(buffer[5], 0x00);
}
#[test]
fn test_known_binary_format_large_heaps() {
let custom_debug_info = CustomDebugInformationRaw {
rid: 1,
token: Token::new(0x3700_0001),
offset: 0,
parent: CodedIndex::new(
TableId::MemberRef,
8,
CodedIndexType::HasCustomDebugInformation,
),
kind: 0x00000101,
value: 0x0000020A,
};
let table_info = std::sync::Arc::new(TableInfo::new_test(
&[
(TableId::CustomDebugInformation, 10000),
(TableId::MethodDef, 100000),
(TableId::MemberRef, 100000),
],
true,
true,
true,
));
let row_size = <CustomDebugInformationRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
custom_debug_info
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Serialization should succeed");
assert_eq!(row_size, 12, "Row size should be 12 bytes for large heaps");
assert_eq!(buffer[0], 0x06);
assert_eq!(buffer[1], 0x01);
assert_eq!(buffer[2], 0x00);
assert_eq!(buffer[3], 0x00);
assert_eq!(buffer[4], 0x01);
assert_eq!(buffer[5], 0x01);
assert_eq!(buffer[6], 0x00);
assert_eq!(buffer[7], 0x00);
assert_eq!(buffer[8], 0x0A);
assert_eq!(buffer[9], 0x02);
assert_eq!(buffer[10], 0x00);
assert_eq!(buffer[11], 0x00);
}
#[test]
fn test_various_coded_index_types() {
let test_cases = vec![
(TableId::MethodDef, 1), (TableId::TypeDef, 5), (TableId::Field, 10), (TableId::Property, 15), (TableId::Event, 20), ];
for (table_id, row) in test_cases {
let custom_debug_info = CustomDebugInformationRaw {
rid: 1,
token: Token::new(0x3700_0001),
offset: 0,
parent: CodedIndex::new(table_id, row, CodedIndexType::HasCustomDebugInformation),
kind: 100,
value: 200,
};
let table_info = std::sync::Arc::new(TableInfo::new_test(
&[
(TableId::CustomDebugInformation, 100),
(TableId::MethodDef, 1000),
(TableId::TypeDef, 1000),
(TableId::Field, 1000),
(TableId::Property, 1000),
(TableId::Event, 1000),
],
false,
false,
false,
));
let row_size = <CustomDebugInformationRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
custom_debug_info
.row_write(&mut buffer, &mut offset, 1, &table_info)
.expect("Serialization should succeed");
let mut read_offset = 0;
let deserialized_row =
CustomDebugInformationRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.expect("Deserialization should succeed");
assert_eq!(custom_debug_info.parent.tag, deserialized_row.parent.tag);
assert_eq!(custom_debug_info.parent.row, deserialized_row.parent.row);
assert_eq!(custom_debug_info.kind, deserialized_row.kind);
assert_eq!(custom_debug_info.value, deserialized_row.value);
}
}
#[test]
fn test_common_debug_info_scenarios() {
let test_cases = vec![
("Source Link", 1, 100), ("Embedded Source", 2, 500), ("Dynamic Locals", 3, 50), ("State Machine Scopes", 4, 150), ("Edit and Continue", 5, 25), ];
for (name, kind, value) in test_cases {
let custom_debug_info = CustomDebugInformationRaw {
rid: 1,
token: Token::new(0x3700_0001),
offset: 0,
parent: CodedIndex::new(
TableId::MethodDef,
100,
CodedIndexType::HasCustomDebugInformation,
),
kind,
value,
};
let table_info = std::sync::Arc::new(TableInfo::new_test(
&[
(TableId::CustomDebugInformation, 100),
(TableId::MethodDef, 1000),
],
false,
false,
false,
));
let row_size = <CustomDebugInformationRaw as TableRow>::row_size(&table_info) as usize;
let mut buffer = vec![0u8; row_size];
let mut offset = 0;
custom_debug_info
.row_write(&mut buffer, &mut offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Serialization should succeed for {name}"));
let mut read_offset = 0;
let deserialized_row =
CustomDebugInformationRaw::row_read(&buffer, &mut read_offset, 1, &table_info)
.unwrap_or_else(|_| panic!("Deserialization should succeed for {name}"));
assert_eq!(
custom_debug_info.kind, deserialized_row.kind,
"Kind mismatch for {name}"
);
assert_eq!(
custom_debug_info.value, deserialized_row.value,
"Value mismatch for {name}"
);
}
}
}