use crate::{
metadata::tables::{
customattribute::CustomAttributeRaw,
types::{CodedIndexType, RowWritable, TableInfoRef},
},
utils::write_le_at_dyn,
Result,
};
impl RowWritable for CustomAttributeRaw {
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::HasCustomAttribute,
)?;
write_le_at_dyn(
data,
offset,
parent_value,
sizes.coded_index_bits(CodedIndexType::HasCustomAttribute) > 16,
)?;
let constructor_value = sizes.encode_coded_index(
self.constructor.tag,
self.constructor.row,
CodedIndexType::CustomAttributeType,
)?;
write_le_at_dyn(
data,
offset,
constructor_value,
sizes.coded_index_bits(CodedIndexType::CustomAttributeType) > 16,
)?;
write_le_at_dyn(data, offset, self.value, sizes.is_large_blob())?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::metadata::tables::{
customattribute::CustomAttributeRaw,
types::{
CodedIndex, CodedIndexType, RowReadable, RowWritable, TableId, TableInfo, TableRow,
},
};
use crate::metadata::token::Token;
#[test]
fn test_customattribute_row_size() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let expected_size = 2 + 2 + 2; assert_eq!(
<CustomAttributeRaw as TableRow>::row_size(&sizes),
expected_size
);
let sizes_large = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
true,
true,
true,
));
let expected_size_large = 2 + 2 + 4; assert_eq!(
<CustomAttributeRaw as TableRow>::row_size(&sizes_large),
expected_size_large
);
}
#[test]
fn test_customattribute_row_write_small_heaps() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let custom_attr = CustomAttributeRaw {
rid: 1,
token: Token::new(0x0C000001),
offset: 0,
parent: CodedIndex::new(TableId::TypeDef, 42, CodedIndexType::HasCustomAttribute), constructor: CodedIndex::new(
TableId::MethodDef,
15,
CodedIndexType::CustomAttributeType,
), value: 0x1234,
};
let mut buffer = vec![0u8; <CustomAttributeRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
custom_attr
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x43, 0x05, 0x7A, 0x00, 0x34, 0x12, ];
assert_eq!(buffer, expected);
assert_eq!(offset, expected.len());
}
#[test]
fn test_customattribute_row_write_large_heaps() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
true,
true,
true,
));
let custom_attr = CustomAttributeRaw {
rid: 1,
token: Token::new(0x0C000001),
offset: 0,
parent: CodedIndex::new(TableId::Assembly, 5, CodedIndexType::HasCustomAttribute), constructor: CodedIndex::new(
TableId::MemberRef,
25,
CodedIndexType::CustomAttributeType,
), value: 0x12345678,
};
let mut buffer = vec![0u8; <CustomAttributeRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
custom_attr
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0xAE, 0x00, 0xCB, 0x00, 0x78, 0x56, 0x34, 0x12, ];
assert_eq!(buffer, expected);
assert_eq!(offset, expected.len());
}
#[test]
fn test_customattribute_round_trip() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let original = CustomAttributeRaw {
rid: 42,
token: Token::new(0x0C00002A),
offset: 0,
parent: CodedIndex::new(TableId::Field, 10, CodedIndexType::HasCustomAttribute),
constructor: CodedIndex::new(
TableId::MethodDef,
20,
CodedIndexType::CustomAttributeType,
),
value: 0x5678,
};
let mut buffer = vec![0u8; <CustomAttributeRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
original
.row_write(&mut buffer, &mut offset, 42, &sizes)
.unwrap();
let mut read_offset = 0;
let read_back =
CustomAttributeRaw::row_read(&buffer, &mut read_offset, 42, &sizes).unwrap();
assert_eq!(original.rid, read_back.rid);
assert_eq!(original.token, read_back.token);
assert_eq!(original.parent, read_back.parent);
assert_eq!(original.constructor, read_back.constructor);
assert_eq!(original.value, read_back.value);
}
#[test]
fn test_customattribute_edge_cases() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let zero_attr = CustomAttributeRaw {
rid: 1,
token: Token::new(0x0C000001),
offset: 0,
parent: CodedIndex::new(TableId::Assembly, 0, CodedIndexType::HasCustomAttribute),
constructor: CodedIndex::new(
TableId::MethodDef,
0,
CodedIndexType::CustomAttributeType,
),
value: 0,
};
let mut buffer = vec![0u8; <CustomAttributeRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
zero_attr
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, ];
assert_eq!(buffer, expected);
}
#[test]
fn test_customattribute_different_coded_index_types() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let test_cases = vec![
(TableId::MethodDef, 10, 0), (TableId::Field, 15, 1), (TableId::TypeRef, 20, 2), (TableId::TypeDef, 25, 3), ];
for (table_id, row, expected_tag) in test_cases {
let custom_attr = CustomAttributeRaw {
rid: 1,
token: Token::new(0x0C000001),
offset: 0,
parent: CodedIndex::new(table_id, row, CodedIndexType::HasCustomAttribute),
constructor: CodedIndex::new(
TableId::MethodDef,
5,
CodedIndexType::CustomAttributeType,
),
value: 0x1000,
};
let mut buffer = vec![0u8; <CustomAttributeRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
custom_attr
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected_parent = (row << 5) | expected_tag;
let actual_parent = u16::from_le_bytes([buffer[0], buffer[1]]);
assert_eq!(actual_parent, expected_parent as u16);
}
}
#[test]
fn test_customattribute_known_binary_format() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 1)],
false,
false,
false,
));
let custom_attr = CustomAttributeRaw {
rid: 1,
token: Token::new(0x0C000001),
offset: 0,
parent: CodedIndex::new(TableId::TypeRef, 16, CodedIndexType::HasCustomAttribute), constructor: CodedIndex::new(
TableId::MemberRef,
96,
CodedIndexType::CustomAttributeType,
), value: 0x0404,
};
let mut buffer = vec![0u8; <CustomAttributeRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
custom_attr
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x02, 0x02, 0x03, 0x03, 0x04, 0x04, ];
assert_eq!(buffer, expected);
}
}