use crate::{
metadata::tables::{
methodspec::MethodSpecRaw,
types::{CodedIndexType, RowWritable, TableInfoRef},
},
utils::write_le_at_dyn,
Result,
};
impl RowWritable for MethodSpecRaw {
fn row_write(
&self,
data: &mut [u8],
offset: &mut usize,
_rid: u32,
sizes: &TableInfoRef,
) -> Result<()> {
let method_value = sizes.encode_coded_index(
self.method.tag,
self.method.row,
CodedIndexType::MethodDefOrRef,
)?;
write_le_at_dyn(
data,
offset,
method_value,
sizes.coded_index_bits(CodedIndexType::MethodDefOrRef) > 16,
)?;
write_le_at_dyn(data, offset, self.instantiation, sizes.is_large_blob())?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::metadata::tables::{
methodspec::MethodSpecRaw,
types::{
CodedIndex, CodedIndexType, RowReadable, RowWritable, TableId, TableInfo, TableRow,
},
};
use crate::metadata::token::Token;
#[test]
fn test_methodspec_row_size() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let expected_size = 2 + 2; assert_eq!(<MethodSpecRaw as TableRow>::row_size(&sizes), expected_size);
let sizes_large = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 0x10000), (TableId::MemberRef, 0x10000)],
false,
true,
false,
));
let expected_size_large = 4 + 4; assert_eq!(
<MethodSpecRaw as TableRow>::row_size(&sizes_large),
expected_size_large
);
}
#[test]
fn test_methodspec_row_write_small() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let method_spec = MethodSpecRaw {
rid: 1,
token: Token::new(0x2B000001),
offset: 0,
method: CodedIndex::new(TableId::MemberRef, 0, CodedIndexType::MethodDefOrRef), instantiation: 0x0202,
};
let mut buffer = vec![0u8; <MethodSpecRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
method_spec
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x00, 0x02, 0x02, ];
assert_eq!(buffer, expected);
assert_eq!(offset, expected.len());
}
#[test]
fn test_methodspec_row_write_large() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 0x10000), (TableId::MemberRef, 0x10000)],
false,
true,
false,
));
let method_spec = MethodSpecRaw {
rid: 1,
token: Token::new(0x2B000001),
offset: 0,
method: CodedIndex::new(TableId::MemberRef, 0, CodedIndexType::MethodDefOrRef), instantiation: 0x02020202,
};
let mut buffer = vec![0u8; <MethodSpecRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
method_spec
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, ];
assert_eq!(buffer, expected);
assert_eq!(offset, expected.len());
}
#[test]
fn test_methodspec_round_trip() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let original = MethodSpecRaw {
rid: 42,
token: Token::new(0x2B00002A),
offset: 0,
method: CodedIndex::new(TableId::MethodDef, 25, CodedIndexType::MethodDefOrRef), instantiation: 128, };
let mut buffer = vec![0u8; <MethodSpecRaw 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 = MethodSpecRaw::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.method, read_back.method);
assert_eq!(original.instantiation, read_back.instantiation);
}
#[test]
fn test_methodspec_different_method_types() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let test_cases = vec![
(TableId::MethodDef, 1, 100, "Internal generic method"),
(TableId::MemberRef, 1, 200, "External generic method"),
(TableId::MethodDef, 50, 300, "Different internal method"),
(TableId::MemberRef, 25, 400, "Different external method"),
(TableId::MethodDef, 10, 500, "Generic constructor"),
];
for (method_tag, method_row, blob_index, _description) in test_cases {
let method_spec = MethodSpecRaw {
rid: 1,
token: Token::new(0x2B000001),
offset: 0,
method: CodedIndex::new(method_tag, method_row, CodedIndexType::MethodDefOrRef),
instantiation: blob_index,
};
let mut buffer = vec![0u8; <MethodSpecRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
method_spec
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let mut read_offset = 0;
let read_back = MethodSpecRaw::row_read(&buffer, &mut read_offset, 1, &sizes).unwrap();
assert_eq!(method_spec.method, read_back.method);
assert_eq!(method_spec.instantiation, read_back.instantiation);
}
}
#[test]
fn test_methodspec_generic_scenarios() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let scenarios = vec![
(TableId::MethodDef, 1, 100, "List<T>.Add<int>()"),
(
TableId::MemberRef,
2,
200,
"Dictionary<TKey,TValue>.TryGetValue<string,object>()",
),
(
TableId::MethodDef,
3,
300,
"Array.ConvertAll<TInput,TOutput>()",
),
(
TableId::MemberRef,
4,
400,
"Enumerable.Select<TSource,TResult>()",
),
(TableId::MethodDef, 5, 500, "Task.FromResult<T>()"),
(TableId::MemberRef, 6, 600, "Activator.CreateInstance<T>()"),
];
for (method_tag, method_row, blob_index, _description) in scenarios {
let method_spec = MethodSpecRaw {
rid: method_row,
token: Token::new(0x2B000000 + method_row),
offset: 0,
method: CodedIndex::new(method_tag, method_row, CodedIndexType::MethodDefOrRef),
instantiation: blob_index,
};
let mut buffer = vec![0u8; <MethodSpecRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
method_spec
.row_write(&mut buffer, &mut offset, method_row, &sizes)
.unwrap();
let mut read_offset = 0;
let read_back =
MethodSpecRaw::row_read(&buffer, &mut read_offset, method_row, &sizes).unwrap();
assert_eq!(method_spec.method, read_back.method);
assert_eq!(method_spec.instantiation, read_back.instantiation);
}
}
#[test]
fn test_methodspec_edge_cases() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let zero_spec = MethodSpecRaw {
rid: 1,
token: Token::new(0x2B000001),
offset: 0,
method: CodedIndex::new(TableId::MethodDef, 0, CodedIndexType::MethodDefOrRef), instantiation: 0,
};
let mut buffer = vec![0u8; <MethodSpecRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
zero_spec
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x00, 0x00, 0x00, 0x00, ];
assert_eq!(buffer, expected);
let max_spec = MethodSpecRaw {
rid: 1,
token: Token::new(0x2B000001),
offset: 0,
method: CodedIndex::new(TableId::MemberRef, 0x7FFF, CodedIndexType::MethodDefOrRef), instantiation: 0xFFFF,
};
let mut buffer = vec![0u8; <MethodSpecRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
max_spec
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
assert_eq!(buffer.len(), 4); }
#[test]
fn test_methodspec_instantiation_signatures() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
false,
false,
));
let signature_cases = vec![
(TableId::MethodDef, 1, 1, "Single type argument"),
(TableId::MemberRef, 2, 100, "Multiple type arguments"),
(TableId::MethodDef, 3, 200, "Complex generic types"),
(TableId::MemberRef, 4, 300, "Nested generic arguments"),
(TableId::MethodDef, 5, 400, "Value type arguments"),
(TableId::MemberRef, 6, 500, "Reference type arguments"),
(TableId::MethodDef, 7, 600, "Array type arguments"),
(TableId::MemberRef, 8, 700, "Pointer type arguments"),
];
for (method_tag, method_row, blob_index, _description) in signature_cases {
let method_spec = MethodSpecRaw {
rid: 1,
token: Token::new(0x2B000001),
offset: 0,
method: CodedIndex::new(method_tag, method_row, CodedIndexType::MethodDefOrRef),
instantiation: blob_index,
};
let mut buffer = vec![0u8; <MethodSpecRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
method_spec
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let written_blob = u16::from_le_bytes([buffer[2], buffer[3]]);
assert_eq!(written_blob as u32, blob_index);
}
}
#[test]
fn test_methodspec_heap_sizes() {
let configurations = vec![
(false, 2), (true, 4), ];
for (large_blob, expected_blob_size) in configurations {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 100), (TableId::MemberRef, 50)],
false,
large_blob,
false,
));
let method_spec = MethodSpecRaw {
rid: 1,
token: Token::new(0x2B000001),
offset: 0,
method: CodedIndex::new(TableId::MethodDef, 1, CodedIndexType::MethodDefOrRef),
instantiation: 0x12345678,
};
let expected_total_size = 2 + expected_blob_size; assert_eq!(
<MethodSpecRaw as TableRow>::row_size(&sizes) as usize,
expected_total_size
);
let mut buffer = vec![0u8; expected_total_size];
let mut offset = 0;
method_spec
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
assert_eq!(buffer.len(), expected_total_size);
assert_eq!(offset, expected_total_size);
}
}
#[test]
fn test_methodspec_known_binary_format() {
let sizes = Arc::new(TableInfo::new_test(
&[(TableId::MethodDef, 10), (TableId::MemberRef, 10)],
false,
false,
false,
));
let method_spec = MethodSpecRaw {
rid: 1,
token: Token::new(0x2B000001),
offset: 0,
method: CodedIndex::new(TableId::MemberRef, 0, CodedIndexType::MethodDefOrRef), instantiation: 0x0202,
};
let mut buffer = vec![0u8; <MethodSpecRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
method_spec
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x00, 0x02, 0x02, ];
assert_eq!(buffer, expected);
}
}