use crate::{
metadata::tables::{
implmap::ImplMapRaw,
types::{CodedIndexType, RowWritable, TableId, TableInfoRef},
},
utils::{write_le_at, write_le_at_dyn},
Result,
};
impl RowWritable for ImplMapRaw {
fn row_write(
&self,
data: &mut [u8],
offset: &mut usize,
_rid: u32,
sizes: &TableInfoRef,
) -> Result<()> {
write_le_at(
data,
offset,
u16::try_from(self.mapping_flags).map_err(|_| {
malformed_error!("ImplMap mapping flags out of range: {}", self.mapping_flags)
})?,
)?;
let member_forwarded_value = sizes.encode_coded_index(
self.member_forwarded.tag,
self.member_forwarded.row,
CodedIndexType::MemberForwarded,
)?;
write_le_at_dyn(
data,
offset,
member_forwarded_value,
sizes.coded_index_bits(CodedIndexType::MemberForwarded) > 16,
)?;
write_le_at_dyn(data, offset, self.import_name, sizes.is_large_str())?;
write_le_at_dyn(
data,
offset,
self.import_scope,
sizes.is_large(TableId::ModuleRef),
)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::metadata::tables::{
implmap::ImplMapRaw,
types::{
CodedIndex, CodedIndexType, RowReadable, RowWritable, TableId, TableInfo, TableRow,
},
};
use crate::metadata::token::Token;
#[test]
fn test_implmap_row_size() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::Field, 100),
(TableId::MethodDef, 50),
(TableId::ModuleRef, 10),
],
false,
false,
false,
));
let expected_size = 2 + 2 + 2 + 2; assert_eq!(<ImplMapRaw as TableRow>::row_size(&sizes), expected_size);
let sizes_large = Arc::new(TableInfo::new_test(
&[
(TableId::Field, 0x10000),
(TableId::MethodDef, 0x10000),
(TableId::ModuleRef, 0x10000),
],
true,
true,
true,
));
let expected_size_large = 2 + 4 + 4 + 4; assert_eq!(
<ImplMapRaw as TableRow>::row_size(&sizes_large),
expected_size_large
);
}
#[test]
fn test_implmap_row_write_small() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::Field, 100),
(TableId::MethodDef, 50),
(TableId::ModuleRef, 10),
],
false,
false,
false,
));
let impl_map = ImplMapRaw {
rid: 1,
token: Token::new(0x1C000001),
offset: 0,
mapping_flags: 0x0101,
member_forwarded: CodedIndex::new(TableId::Field, 1, CodedIndexType::MemberForwarded), import_name: 0x0303,
import_scope: 0x0404,
};
let mut buffer = vec![0u8; <ImplMapRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
impl_map
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x01, 0x02, 0x00, 0x03, 0x03, 0x04, 0x04, ];
assert_eq!(buffer, expected);
assert_eq!(offset, expected.len());
}
#[test]
fn test_implmap_row_write_large() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::Field, 0x10000),
(TableId::MethodDef, 0x10000),
(TableId::ModuleRef, 0x10000),
],
true,
true,
true,
));
let impl_map = ImplMapRaw {
rid: 1,
token: Token::new(0x1C000001),
offset: 0,
mapping_flags: 0x0101,
member_forwarded: CodedIndex::new(TableId::Field, 1, CodedIndexType::MemberForwarded), import_name: 0x03030303,
import_scope: 0x04040404,
};
let mut buffer = vec![0u8; <ImplMapRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
impl_map
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x01, 0x02, 0x00, 0x00,
0x00, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, ];
assert_eq!(buffer, expected);
assert_eq!(offset, expected.len());
}
#[test]
fn test_implmap_round_trip() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::Field, 100),
(TableId::MethodDef, 50),
(TableId::ModuleRef, 10),
],
false,
false,
false,
));
let original = ImplMapRaw {
rid: 42,
token: Token::new(0x1C00002A),
offset: 0,
mapping_flags: 0x0001, member_forwarded: CodedIndex::new(
TableId::MethodDef,
25,
CodedIndexType::MemberForwarded,
), import_name: 128,
import_scope: 5,
};
let mut buffer = vec![0u8; <ImplMapRaw 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 = ImplMapRaw::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.mapping_flags, read_back.mapping_flags);
assert_eq!(original.member_forwarded, read_back.member_forwarded);
assert_eq!(original.import_name, read_back.import_name);
assert_eq!(original.import_scope, read_back.import_scope);
}
#[test]
fn test_implmap_different_member_types() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::Field, 100),
(TableId::MethodDef, 50),
(TableId::ModuleRef, 10),
],
false,
false,
false,
));
let test_cases = vec![
(TableId::Field, 1, "Field reference"),
(TableId::MethodDef, 1, "MethodDef reference"),
(TableId::Field, 50, "Different field"),
(TableId::MethodDef, 25, "Different method"),
];
for (member_tag, member_row, _description) in test_cases {
let impl_map = ImplMapRaw {
rid: 1,
token: Token::new(0x1C000001),
offset: 0,
mapping_flags: 0x0001,
member_forwarded: CodedIndex::new(
member_tag,
member_row,
CodedIndexType::MemberForwarded,
),
import_name: 100,
import_scope: 3,
};
let mut buffer = vec![0u8; <ImplMapRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
impl_map
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let mut read_offset = 0;
let read_back = ImplMapRaw::row_read(&buffer, &mut read_offset, 1, &sizes).unwrap();
assert_eq!(impl_map.member_forwarded, read_back.member_forwarded);
assert_eq!(impl_map.import_name, read_back.import_name);
assert_eq!(impl_map.import_scope, read_back.import_scope);
}
}
#[test]
fn test_implmap_pinvoke_flags() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::Field, 100),
(TableId::MethodDef, 50),
(TableId::ModuleRef, 10),
],
false,
false,
false,
));
let flag_cases = vec![
(0x0000, "Default"),
(0x0001, "NoMangle"),
(0x0002, "CharSetAnsi"),
(0x0004, "CharSetUnicode"),
(0x0006, "CharSetAuto"),
(0x0010, "SupportsLastError"),
(0x0100, "CallConvWinapi"),
(0x0200, "CallConvCdecl"),
(0x0300, "CallConvStdcall"),
(0x0400, "CallConvThiscall"),
(0x0500, "CallConvFastcall"),
];
for (flags, _description) in flag_cases {
let impl_map = ImplMapRaw {
rid: 1,
token: Token::new(0x1C000001),
offset: 0,
mapping_flags: flags,
member_forwarded: CodedIndex::new(
TableId::MethodDef,
1,
CodedIndexType::MemberForwarded,
),
import_name: 50,
import_scope: 2,
};
let mut buffer = vec![0u8; <ImplMapRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
impl_map
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let written_flags = u16::from_le_bytes([buffer[0], buffer[1]]);
assert_eq!(written_flags as u32, flags);
}
}
#[test]
fn test_implmap_edge_cases() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::Field, 100),
(TableId::MethodDef, 50),
(TableId::ModuleRef, 10),
],
false,
false,
false,
));
let zero_implmap = ImplMapRaw {
rid: 1,
token: Token::new(0x1C000001),
offset: 0,
mapping_flags: 0,
member_forwarded: CodedIndex::new(TableId::Field, 0, CodedIndexType::MemberForwarded), import_name: 0,
import_scope: 0,
};
let mut buffer = vec![0u8; <ImplMapRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
zero_implmap
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
assert_eq!(buffer, expected);
let max_implmap = ImplMapRaw {
rid: 1,
token: Token::new(0x1C000001),
offset: 0,
mapping_flags: 0xFFFF,
member_forwarded: CodedIndex::new(
TableId::MethodDef,
0x7FFF,
CodedIndexType::MemberForwarded,
), import_name: 0xFFFF,
import_scope: 0xFFFF,
};
let mut buffer = vec![0u8; <ImplMapRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
max_implmap
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
assert_eq!(buffer.len(), 8); }
#[test]
fn test_implmap_known_binary_format() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::ImplMap, 1),
(TableId::Field, 10),
(TableId::MethodDef, 10),
(TableId::ModuleRef, 10),
],
false,
false,
false,
));
let impl_map = ImplMapRaw {
rid: 1,
token: Token::new(0x1C000001),
offset: 0,
mapping_flags: 0x0101,
member_forwarded: CodedIndex::new(TableId::Field, 1, CodedIndexType::MemberForwarded), import_name: 0x0303,
import_scope: 0x0404,
};
let mut buffer = vec![0u8; <ImplMapRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
impl_map
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x01, 0x02, 0x00, 0x03, 0x03, 0x04, 0x04, ];
assert_eq!(buffer, expected);
}
}