use crate::{
metadata::tables::{
genericparamconstraint::GenericParamConstraintRaw,
types::{CodedIndexType, RowWritable, TableId, TableInfoRef},
},
utils::write_le_at_dyn,
Result,
};
impl RowWritable for GenericParamConstraintRaw {
fn row_write(
&self,
data: &mut [u8],
offset: &mut usize,
_rid: u32,
sizes: &TableInfoRef,
) -> Result<()> {
write_le_at_dyn(
data,
offset,
self.owner,
sizes.is_large(TableId::GenericParam),
)?;
let constraint_value = sizes.encode_coded_index(
self.constraint.tag,
self.constraint.row,
CodedIndexType::TypeDefOrRef,
)?;
write_le_at_dyn(
data,
offset,
constraint_value,
sizes.coded_index_bits(CodedIndexType::TypeDefOrRef) > 16,
)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::metadata::tables::{
genericparamconstraint::GenericParamConstraintRaw,
types::{
CodedIndex, CodedIndexType, RowReadable, RowWritable, TableId, TableInfo, TableRow,
},
};
use crate::metadata::token::Token;
#[test]
fn test_genericparamconstraint_row_size() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 100),
(TableId::TypeDef, 50),
(TableId::TypeRef, 25),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let expected_size = 2 + 2; assert_eq!(
<GenericParamConstraintRaw as TableRow>::row_size(&sizes),
expected_size
);
let sizes_large = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 0x10000),
(TableId::TypeDef, 0x10000),
(TableId::TypeRef, 0x10000),
(TableId::TypeSpec, 0x10000),
],
false,
false,
false,
));
let expected_size_large = 4 + 4; assert_eq!(
<GenericParamConstraintRaw as TableRow>::row_size(&sizes_large),
expected_size_large
);
}
#[test]
fn test_genericparamconstraint_row_write_small() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 100),
(TableId::TypeDef, 50),
(TableId::TypeRef, 25),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let constraint = GenericParamConstraintRaw {
rid: 1,
token: Token::new(0x2C000001),
offset: 0,
owner: 0x0101,
constraint: CodedIndex::new(TableId::TypeDef, 2, CodedIndexType::TypeDefOrRef), };
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
constraint
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x01, 0x08, 0x00, ];
assert_eq!(buffer, expected);
assert_eq!(offset, expected.len());
}
#[test]
fn test_genericparamconstraint_row_write_large() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 0x10000),
(TableId::TypeDef, 0x10000),
(TableId::TypeRef, 0x10000),
(TableId::TypeSpec, 0x10000),
],
false,
false,
false,
));
let constraint = GenericParamConstraintRaw {
rid: 1,
token: Token::new(0x2C000001),
offset: 0,
owner: 0x01010101,
constraint: CodedIndex::new(TableId::TypeDef, 2, CodedIndexType::TypeDefOrRef), };
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
constraint
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x01, 0x01, 0x01, 0x08, 0x00, 0x00,
0x00, ];
assert_eq!(buffer, expected);
assert_eq!(offset, expected.len());
}
#[test]
fn test_genericparamconstraint_round_trip() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 100),
(TableId::TypeDef, 50),
(TableId::TypeRef, 25),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let original = GenericParamConstraintRaw {
rid: 42,
token: Token::new(0x2C00002A),
offset: 0,
owner: 25, constraint: CodedIndex::new(TableId::TypeRef, 10, CodedIndexType::TypeDefOrRef), };
let mut buffer =
vec![0u8; <GenericParamConstraintRaw 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 =
GenericParamConstraintRaw::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.owner, read_back.owner);
assert_eq!(original.constraint, read_back.constraint);
}
#[test]
fn test_genericparamconstraint_different_constraint_types() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 100),
(TableId::TypeDef, 50),
(TableId::TypeRef, 25),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let test_cases = vec![
(1, TableId::TypeDef, 1, "Base class constraint"),
(2, TableId::TypeRef, 5, "External interface constraint"),
(3, TableId::TypeSpec, 2, "Generic type constraint"),
(
1,
TableId::TypeDef,
10,
"Multiple constraints on same parameter",
),
(4, TableId::TypeRef, 15, "Different parameter constraint"),
];
for (owner_idx, constraint_tag, constraint_row, _description) in test_cases {
let constraint = GenericParamConstraintRaw {
rid: 1,
token: Token::new(0x2C000001),
offset: 0,
owner: owner_idx,
constraint: CodedIndex::new(
constraint_tag,
constraint_row,
CodedIndexType::TypeDefOrRef,
),
};
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
constraint
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let mut read_offset = 0;
let read_back =
GenericParamConstraintRaw::row_read(&buffer, &mut read_offset, 1, &sizes).unwrap();
assert_eq!(constraint.owner, read_back.owner);
assert_eq!(constraint.constraint, read_back.constraint);
}
}
#[test]
fn test_genericparamconstraint_constraint_scenarios() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 100),
(TableId::TypeDef, 50),
(TableId::TypeRef, 25),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let scenarios = vec![
(1, TableId::TypeDef, 1, "where T : BaseClass"),
(1, TableId::TypeRef, 2, "where T : IInterface"),
(2, TableId::TypeSpec, 1, "where U : IComparable<U>"),
(3, TableId::TypeDef, 5, "where V : Enum"),
(4, TableId::TypeRef, 10, "where W : IDisposable"),
(1, TableId::TypeRef, 15, "T : second interface constraint"),
(2, TableId::TypeDef, 20, "U : class constraint"),
];
for (param_idx, constraint_tag, constraint_row, _description) in scenarios {
let constraint = GenericParamConstraintRaw {
rid: param_idx,
token: Token::new(0x2C000000 + param_idx),
offset: 0,
owner: param_idx,
constraint: CodedIndex::new(
constraint_tag,
constraint_row,
CodedIndexType::TypeDefOrRef,
),
};
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
constraint
.row_write(&mut buffer, &mut offset, param_idx, &sizes)
.unwrap();
let mut read_offset = 0;
let read_back =
GenericParamConstraintRaw::row_read(&buffer, &mut read_offset, param_idx, &sizes)
.unwrap();
assert_eq!(constraint.owner, read_back.owner);
assert_eq!(constraint.constraint, read_back.constraint);
}
}
#[test]
fn test_genericparamconstraint_multiple_constraints() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 100),
(TableId::TypeDef, 50),
(TableId::TypeRef, 25),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let constraints = vec![
(1, TableId::TypeDef, 1), (1, TableId::TypeRef, 2), (1, TableId::TypeRef, 3), (1, TableId::TypeSpec, 1), ];
for (param_idx, constraint_tag, constraint_row) in constraints {
let constraint = GenericParamConstraintRaw {
rid: 1,
token: Token::new(0x2C000001),
offset: 0,
owner: param_idx,
constraint: CodedIndex::new(
constraint_tag,
constraint_row,
CodedIndexType::TypeDefOrRef,
),
};
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
constraint
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let mut read_offset = 0;
let read_back =
GenericParamConstraintRaw::row_read(&buffer, &mut read_offset, 1, &sizes).unwrap();
assert_eq!(constraint.owner, read_back.owner);
assert_eq!(constraint.constraint, read_back.constraint);
}
}
#[test]
fn test_genericparamconstraint_edge_cases() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 100),
(TableId::TypeDef, 50),
(TableId::TypeRef, 25),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let zero_constraint = GenericParamConstraintRaw {
rid: 1,
token: Token::new(0x2C000001),
offset: 0,
owner: 0,
constraint: CodedIndex::new(TableId::TypeDef, 0, CodedIndexType::TypeDefOrRef), };
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
zero_constraint
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x00, 0x00, 0x00, 0x00, ];
assert_eq!(buffer, expected);
let max_constraint = GenericParamConstraintRaw {
rid: 1,
token: Token::new(0x2C000001),
offset: 0,
owner: 0xFFFF,
constraint: CodedIndex::new(TableId::TypeSpec, 0x3FFF, CodedIndexType::TypeDefOrRef), };
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
max_constraint
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
assert_eq!(buffer.len(), 4); }
#[test]
fn test_genericparamconstraint_type_references() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 100),
(TableId::TypeDef, 50),
(TableId::TypeRef, 25),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let type_refs = vec![
(TableId::TypeDef, 1, "Internal class"),
(TableId::TypeDef, 10, "Internal interface"),
(TableId::TypeRef, 1, "External class (System.Object)"),
(TableId::TypeRef, 5, "External interface (IDisposable)"),
(TableId::TypeSpec, 1, "Generic type (IComparable<T>)"),
(TableId::TypeSpec, 3, "Array type (T[])"),
];
for (constraint_tag, constraint_row, _description) in type_refs {
let constraint = GenericParamConstraintRaw {
rid: 1,
token: Token::new(0x2C000001),
offset: 0,
owner: 1,
constraint: CodedIndex::new(
constraint_tag,
constraint_row,
CodedIndexType::TypeDefOrRef,
),
};
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
constraint
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected_constraint_value = match constraint_tag {
TableId::TypeDef => constraint_row << 2,
TableId::TypeRef => (constraint_row << 2) | 1,
TableId::TypeSpec => (constraint_row << 2) | 2,
_ => panic!("Unexpected constraint tag"),
};
let written_constraint = u16::from_le_bytes([buffer[2], buffer[3]]) as u32;
assert_eq!(written_constraint, expected_constraint_value);
}
}
#[test]
fn test_genericparamconstraint_known_binary_format() {
let sizes = Arc::new(TableInfo::new_test(
&[
(TableId::GenericParam, 10),
(TableId::TypeDef, 10),
(TableId::TypeRef, 10),
(TableId::TypeSpec, 10),
],
false,
false,
false,
));
let constraint = GenericParamConstraintRaw {
rid: 1,
token: Token::new(0x2C000001),
offset: 0,
owner: 0x0101,
constraint: CodedIndex::new(TableId::TypeDef, 2, CodedIndexType::TypeDefOrRef), };
let mut buffer =
vec![0u8; <GenericParamConstraintRaw as TableRow>::row_size(&sizes) as usize];
let mut offset = 0;
constraint
.row_write(&mut buffer, &mut offset, 1, &sizes)
.unwrap();
let expected = vec![
0x01, 0x01, 0x08, 0x00, ];
assert_eq!(buffer, expected);
}
}