use crate::metadata::tables::{CodedIndexType, TableId, TableInfoRef};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HeapType {
String,
Blob,
Guid,
}
#[derive(Debug, Clone, Copy)]
pub struct HeapFieldDescriptor {
pub offset: usize,
pub size: usize,
pub heap_type: HeapType,
}
#[must_use]
pub fn get_heap_fields(table_id: TableId, table_info: &TableInfoRef) -> Vec<HeapFieldDescriptor> {
let str_size = if table_info.is_large_str() { 4 } else { 2 };
let blob_size = if table_info.is_large_blob() { 4 } else { 2 };
let guid_size = if table_info.is_large_guid() { 4 } else { 2 };
let mut fields = Vec::new();
match table_id {
TableId::Module => {
let mut offset = 2; fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: guid_size,
heap_type: HeapType::Guid,
});
offset += guid_size;
fields.push(HeapFieldDescriptor {
offset,
size: guid_size,
heap_type: HeapType::Guid,
});
offset += guid_size;
fields.push(HeapFieldDescriptor {
offset,
size: guid_size,
heap_type: HeapType::Guid,
});
}
TableId::TypeRef => {
let coded_size = table_info.coded_index_bytes(CodedIndexType::ResolutionScope) as usize;
let mut offset = coded_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::TypeDef => {
let mut offset = 4; fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::Field | TableId::Property => {
let mut offset = 2; fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::MethodDef => {
let mut offset = 8; fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::Param | TableId::LocalVariable => {
fields.push(HeapFieldDescriptor {
offset: 4,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::MemberRef => {
let coded_size = table_info.coded_index_bytes(CodedIndexType::MemberRefParent) as usize;
let mut offset = coded_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::Constant => {
let coded_size = table_info.coded_index_bytes(CodedIndexType::HasConstant) as usize;
fields.push(HeapFieldDescriptor {
offset: 2 + coded_size,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::CustomAttribute => {
let parent_size =
table_info.coded_index_bytes(CodedIndexType::HasCustomAttribute) as usize;
let type_size =
table_info.coded_index_bytes(CodedIndexType::CustomAttributeType) as usize;
fields.push(HeapFieldDescriptor {
offset: parent_size + type_size,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::FieldMarshal => {
let coded_size = table_info.coded_index_bytes(CodedIndexType::HasFieldMarshal) as usize;
fields.push(HeapFieldDescriptor {
offset: coded_size,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::DeclSecurity => {
let coded_size = table_info.coded_index_bytes(CodedIndexType::HasDeclSecurity) as usize;
fields.push(HeapFieldDescriptor {
offset: 2 + coded_size,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::StandAloneSig | TableId::TypeSpec => {
fields.push(HeapFieldDescriptor {
offset: 0,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::Event => {
fields.push(HeapFieldDescriptor {
offset: 2,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::ModuleRef => {
fields.push(HeapFieldDescriptor {
offset: 0,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::ImplMap => {
let coded_size = table_info.coded_index_bytes(CodedIndexType::MemberForwarded) as usize;
fields.push(HeapFieldDescriptor {
offset: 2 + coded_size,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::Assembly => {
let mut offset = 16; fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
offset += blob_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::AssemblyRef => {
let mut offset = 12; fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
offset += blob_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::File => {
let mut offset = 4; fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::ExportedType => {
let mut offset = 8; fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::ManifestResource => {
fields.push(HeapFieldDescriptor {
offset: 8,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::GenericParam => {
let coded_size = table_info.coded_index_bytes(CodedIndexType::TypeOrMethodDef) as usize;
fields.push(HeapFieldDescriptor {
offset: 4 + coded_size,
size: str_size,
heap_type: HeapType::String,
});
}
TableId::MethodSpec => {
let coded_size = table_info.coded_index_bytes(CodedIndexType::MethodDefOrRef) as usize;
fields.push(HeapFieldDescriptor {
offset: coded_size,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::Document => {
let mut offset = 0;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
offset += blob_size;
fields.push(HeapFieldDescriptor {
offset,
size: guid_size,
heap_type: HeapType::Guid,
});
offset += guid_size;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
offset += blob_size;
fields.push(HeapFieldDescriptor {
offset,
size: guid_size,
heap_type: HeapType::Guid,
});
}
TableId::LocalConstant => {
let mut offset = 0;
fields.push(HeapFieldDescriptor {
offset,
size: str_size,
heap_type: HeapType::String,
});
offset += str_size;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::CustomDebugInformation => {
let coded_size =
table_info.coded_index_bytes(CodedIndexType::HasCustomDebugInformation) as usize;
let mut offset = coded_size;
fields.push(HeapFieldDescriptor {
offset,
size: guid_size,
heap_type: HeapType::Guid,
});
offset += guid_size;
fields.push(HeapFieldDescriptor {
offset,
size: blob_size,
heap_type: HeapType::Blob,
});
}
TableId::FieldPtr
| TableId::MethodPtr
| TableId::ParamPtr
| TableId::InterfaceImpl
| TableId::ClassLayout
| TableId::FieldLayout
| TableId::EventMap
| TableId::EventPtr
| TableId::PropertyMap
| TableId::PropertyPtr
| TableId::MethodSemantics
| TableId::MethodImpl
| TableId::FieldRVA
| TableId::EncLog
| TableId::EncMap
| TableId::AssemblyProcessor
| TableId::AssemblyOS
| TableId::AssemblyRefProcessor
| TableId::AssemblyRefOS
| TableId::NestedClass
| TableId::GenericParamConstraint
| TableId::MethodDebugInformation
| TableId::LocalScope
| TableId::StateMachineMethod
| TableId::ImportScope => {
}
}
fields
}
#[cfg(test)]
mod tests {
use super::*;
fn mock_table_info_small() -> TableInfoRef {
TableInfoRef::default()
}
#[test]
fn test_module_heap_fields() {
let info = mock_table_info_small();
let fields = get_heap_fields(TableId::Module, &info);
assert_eq!(fields.len(), 4);
assert_eq!(fields[0].offset, 2);
assert_eq!(fields[0].heap_type, HeapType::String);
assert_eq!(fields[1].heap_type, HeapType::Guid);
assert_eq!(fields[2].heap_type, HeapType::Guid);
assert_eq!(fields[3].heap_type, HeapType::Guid);
}
#[test]
fn test_typedef_heap_fields() {
let info = mock_table_info_small();
let fields = get_heap_fields(TableId::TypeDef, &info);
assert_eq!(fields.len(), 2);
assert_eq!(fields[0].offset, 4); assert_eq!(fields[0].heap_type, HeapType::String); assert_eq!(fields[1].heap_type, HeapType::String); }
#[test]
fn test_methoddef_heap_fields() {
let info = mock_table_info_small();
let fields = get_heap_fields(TableId::MethodDef, &info);
assert_eq!(fields.len(), 2);
assert_eq!(fields[0].offset, 8); assert_eq!(fields[0].heap_type, HeapType::String); assert_eq!(fields[1].heap_type, HeapType::Blob); }
#[test]
fn test_table_without_heap_refs() {
let info = mock_table_info_small();
let fields = get_heap_fields(TableId::InterfaceImpl, &info);
assert!(fields.is_empty());
let fields = get_heap_fields(TableId::NestedClass, &info);
assert!(fields.is_empty());
}
}