use std::collections::{HashMap, HashSet};
use crate::{
cilassembly::{AssemblyChanges, TableModifications},
metadata::tables::{
AssemblyOsRaw, AssemblyProcessorRaw, AssemblyRaw, AssemblyRefOsRaw,
AssemblyRefProcessorRaw, AssemblyRefRaw, ClassLayoutRaw, CodedIndex, ConstantRaw,
CustomAttributeRaw, CustomDebugInformationRaw, DeclSecurityRaw, DocumentRaw, EncLogRaw,
EncMapRaw, EventMapRaw, EventPtrRaw, EventRaw, ExportedTypeRaw, FieldLayoutRaw,
FieldMarshalRaw, FieldPtrRaw, FieldRaw, FieldRvaRaw, FileRaw, GenericParamConstraintRaw,
GenericParamRaw, ImplMapRaw, ImportScopeRaw, InterfaceImplRaw, LocalConstantRaw,
LocalScopeRaw, LocalVariableRaw, ManifestResourceRaw, MemberRefRaw,
MethodDebugInformationRaw, MethodDefRaw, MethodImplRaw, MethodPtrRaw, MethodSemanticsRaw,
MethodSpecRaw, ModuleRaw, ModuleRefRaw, NestedClassRaw, ParamPtrRaw, ParamRaw,
PropertyMapRaw, PropertyPtrRaw, PropertyRaw, StandAloneSigRaw, StateMachineMethodRaw,
TableDataOwned, TableId, TypeDefRaw, TypeRefRaw, TypeSpecRaw,
},
};
#[derive(Debug, Default)]
pub struct RidRemapper {
remaps: HashMap<TableId, HashMap<u32, u32>>,
tables_with_deletions: HashSet<TableId>,
deleted_rids: HashMap<TableId, HashSet<u32>>,
}
impl RidRemapper {
#[must_use]
pub fn empty() -> Self {
Self::default()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.tables_with_deletions.is_empty()
}
#[must_use]
pub fn has_deletions(&self, table_id: TableId) -> bool {
self.tables_with_deletions.contains(&table_id)
}
#[must_use]
pub fn from_changes(
changes: &AssemblyChanges,
original_counts: &HashMap<TableId, u32>,
) -> Self {
let mut remaps = HashMap::new();
let mut tables_with_deletions = HashSet::new();
let mut deleted_rids = HashMap::new();
for (table_id, table_mods) in &changes.table_changes {
if let TableModifications::Sparse { deleted_rows, .. } = table_mods {
if deleted_rows.is_empty() {
continue;
}
tables_with_deletions.insert(*table_id);
deleted_rids.insert(*table_id, deleted_rows.clone());
let original_count = original_counts.get(table_id).copied().unwrap_or(0);
let remap = Self::calculate_remapping(deleted_rows, original_count);
if !remap.is_empty() {
remaps.insert(*table_id, remap);
}
}
}
Self {
remaps,
tables_with_deletions,
deleted_rids,
}
}
fn calculate_remapping(deleted_rows: &HashSet<u32>, original_count: u32) -> HashMap<u32, u32> {
let mut remap = HashMap::new();
let mut new_rid = 1u32;
let mut surviving_rids: Vec<(u32, u32)> = Vec::new();
for old_rid in 1..=original_count {
if deleted_rows.contains(&old_rid) {
continue;
}
surviving_rids.push((old_rid, new_rid));
if old_rid != new_rid {
remap.insert(old_rid, new_rid);
}
new_rid += 1;
}
let final_new_rid = new_rid; for old_rid in 1..=original_count {
if !deleted_rows.contains(&old_rid) {
continue; }
let continuation = surviving_rids
.iter()
.find(|(orig, _)| *orig > old_rid)
.map_or(final_new_rid, |(_, new)| *new);
remap.insert(old_rid, continuation);
}
let old_continuation = original_count + 1;
if old_continuation != final_new_rid {
remap.insert(old_continuation, final_new_rid);
}
remap
}
#[must_use]
pub fn remap_rid(&self, target_table: TableId, old_rid: u32) -> u32 {
if old_rid == 0 {
return 0;
}
self.remaps
.get(&target_table)
.and_then(|map| map.get(&old_rid))
.copied()
.unwrap_or(old_rid)
}
#[must_use]
pub fn typedef_remap(&self) -> Option<HashMap<u32, u32>> {
let full_remap = self.remaps.get(&TableId::TypeDef)?;
let deleted = self.deleted_rids.get(&TableId::TypeDef);
let filtered: HashMap<u32, u32> = full_remap
.iter()
.filter(|(old_rid, _)| deleted.is_none_or(|d| !d.contains(old_rid)))
.map(|(&k, &v)| (k, v))
.collect();
if filtered.is_empty() {
None
} else {
Some(filtered)
}
}
#[must_use]
pub fn typeref_remap(&self) -> Option<HashMap<u32, u32>> {
let full_remap = self.remaps.get(&TableId::TypeRef)?;
let deleted = self.deleted_rids.get(&TableId::TypeRef);
let filtered: HashMap<u32, u32> = full_remap
.iter()
.filter(|(old_rid, _)| deleted.is_none_or(|d| !d.contains(old_rid)))
.map(|(&k, &v)| (k, v))
.collect();
if filtered.is_empty() {
None
} else {
Some(filtered)
}
}
pub fn remap_coded_index(&self, coded_index: &mut CodedIndex) {
if coded_index.row == 0 {
return;
}
let table_id = coded_index.tag;
if let Some(table_remap) = self.remaps.get(&table_id) {
if let Some(&new_rid) = table_remap.get(&coded_index.row) {
coded_index.row = new_rid;
coded_index.token = crate::metadata::token::Token::from_parts(table_id, new_rid);
}
}
}
#[must_use]
pub fn table_count(&self) -> usize {
self.remaps.len()
}
#[must_use]
pub fn total_remappings(&self) -> usize {
self.remaps.values().map(HashMap::len).sum()
}
#[must_use]
pub fn build_token_remapping(&self) -> HashMap<u32, u32> {
let mut token_remap = HashMap::new();
for (table_id, rid_map) in &self.remaps {
let table_byte = (*table_id as u32) << 24;
for (&old_rid, &new_rid) in rid_map {
if old_rid == 0 || old_rid == new_rid {
continue;
}
let old_token = table_byte | old_rid;
let new_token = table_byte | new_rid;
token_remap.insert(old_token, new_token);
}
}
token_remap
}
}
pub trait RemapReferences {
fn remap_references(&mut self, remapper: &RidRemapper);
}
impl RemapReferences for TypeRefRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.resolution_scope);
}
}
impl RemapReferences for TypeDefRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.extends);
self.field_list = remapper.remap_rid(TableId::Field, self.field_list);
self.method_list = remapper.remap_rid(TableId::MethodDef, self.method_list);
}
}
impl RemapReferences for MethodDefRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.param_list = remapper.remap_rid(TableId::Param, self.param_list);
}
}
impl RemapReferences for InterfaceImplRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.class = remapper.remap_rid(TableId::TypeDef, self.class);
remapper.remap_coded_index(&mut self.interface);
}
}
impl RemapReferences for MemberRefRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.class);
}
}
impl RemapReferences for ConstantRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.parent);
}
}
impl RemapReferences for CustomAttributeRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.parent);
remapper.remap_coded_index(&mut self.constructor);
}
}
impl RemapReferences for FieldMarshalRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.parent);
}
}
impl RemapReferences for DeclSecurityRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.parent);
}
}
impl RemapReferences for ClassLayoutRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.parent = remapper.remap_rid(TableId::TypeDef, self.parent);
}
}
impl RemapReferences for FieldLayoutRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.field = remapper.remap_rid(TableId::Field, self.field);
}
}
impl RemapReferences for EventMapRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.parent = remapper.remap_rid(TableId::TypeDef, self.parent);
self.event_list = remapper.remap_rid(TableId::Event, self.event_list);
}
}
impl RemapReferences for EventRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.event_type);
}
}
impl RemapReferences for PropertyMapRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.parent = remapper.remap_rid(TableId::TypeDef, self.parent);
self.property_list = remapper.remap_rid(TableId::Property, self.property_list);
}
}
impl RemapReferences for MethodSemanticsRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.method = remapper.remap_rid(TableId::MethodDef, self.method);
remapper.remap_coded_index(&mut self.association);
}
}
impl RemapReferences for MethodImplRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.class = remapper.remap_rid(TableId::TypeDef, self.class);
remapper.remap_coded_index(&mut self.method_body);
remapper.remap_coded_index(&mut self.method_declaration);
}
}
impl RemapReferences for ImplMapRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.member_forwarded);
self.import_scope = remapper.remap_rid(TableId::ModuleRef, self.import_scope);
}
}
impl RemapReferences for FieldRvaRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.field = remapper.remap_rid(TableId::Field, self.field);
}
}
impl RemapReferences for AssemblyRefRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {
}
}
impl RemapReferences for ExportedTypeRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.implementation);
}
}
impl RemapReferences for ManifestResourceRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.implementation);
}
}
impl RemapReferences for NestedClassRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.nested_class = remapper.remap_rid(TableId::TypeDef, self.nested_class);
self.enclosing_class = remapper.remap_rid(TableId::TypeDef, self.enclosing_class);
}
}
impl RemapReferences for GenericParamRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.owner);
}
}
impl RemapReferences for MethodSpecRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
remapper.remap_coded_index(&mut self.method);
}
}
impl RemapReferences for GenericParamConstraintRaw {
fn remap_references(&mut self, remapper: &RidRemapper) {
self.owner = remapper.remap_rid(TableId::GenericParam, self.owner);
remapper.remap_coded_index(&mut self.constraint);
}
}
impl RemapReferences for ModuleRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for FieldPtrRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for FieldRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for MethodPtrRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for ParamPtrRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for ParamRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for StandAloneSigRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for EventPtrRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for PropertyPtrRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for PropertyRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for ModuleRefRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for TypeSpecRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for EncLogRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for EncMapRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for AssemblyRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for AssemblyProcessorRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for AssemblyOsRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for AssemblyRefProcessorRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for AssemblyRefOsRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for FileRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for DocumentRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for MethodDebugInformationRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for LocalScopeRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for LocalVariableRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for LocalConstantRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for ImportScopeRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for StateMachineMethodRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for CustomDebugInformationRaw {
fn remap_references(&mut self, _remapper: &RidRemapper) {}
}
impl RemapReferences for TableDataOwned {
fn remap_references(&mut self, remapper: &RidRemapper) {
match self {
TableDataOwned::TypeRef(r) => r.remap_references(remapper),
TableDataOwned::TypeDef(r) => r.remap_references(remapper),
TableDataOwned::MethodDef(r) => r.remap_references(remapper),
TableDataOwned::InterfaceImpl(r) => r.remap_references(remapper),
TableDataOwned::MemberRef(r) => r.remap_references(remapper),
TableDataOwned::Constant(r) => r.remap_references(remapper),
TableDataOwned::CustomAttribute(r) => r.remap_references(remapper),
TableDataOwned::FieldMarshal(r) => r.remap_references(remapper),
TableDataOwned::DeclSecurity(r) => r.remap_references(remapper),
TableDataOwned::ClassLayout(r) => r.remap_references(remapper),
TableDataOwned::FieldLayout(r) => r.remap_references(remapper),
TableDataOwned::EventMap(r) => r.remap_references(remapper),
TableDataOwned::Event(r) => r.remap_references(remapper),
TableDataOwned::PropertyMap(r) => r.remap_references(remapper),
TableDataOwned::MethodSemantics(r) => r.remap_references(remapper),
TableDataOwned::MethodImpl(r) => r.remap_references(remapper),
TableDataOwned::ImplMap(r) => r.remap_references(remapper),
TableDataOwned::FieldRVA(r) => r.remap_references(remapper),
TableDataOwned::AssemblyRef(r) => r.remap_references(remapper),
TableDataOwned::ExportedType(r) => r.remap_references(remapper),
TableDataOwned::ManifestResource(r) => r.remap_references(remapper),
TableDataOwned::NestedClass(r) => r.remap_references(remapper),
TableDataOwned::GenericParam(r) => r.remap_references(remapper),
TableDataOwned::MethodSpec(r) => r.remap_references(remapper),
TableDataOwned::GenericParamConstraint(r) => r.remap_references(remapper),
TableDataOwned::Module(_)
| TableDataOwned::FieldPtr(_)
| TableDataOwned::Field(_)
| TableDataOwned::MethodPtr(_)
| TableDataOwned::ParamPtr(_)
| TableDataOwned::Param(_)
| TableDataOwned::EventPtr(_)
| TableDataOwned::Property(_)
| TableDataOwned::PropertyPtr(_)
| TableDataOwned::ModuleRef(_)
| TableDataOwned::TypeSpec(_)
| TableDataOwned::StandAloneSig(_)
| TableDataOwned::EncLog(_)
| TableDataOwned::EncMap(_)
| TableDataOwned::Assembly(_)
| TableDataOwned::AssemblyProcessor(_)
| TableDataOwned::AssemblyOS(_)
| TableDataOwned::AssemblyRefProcessor(_)
| TableDataOwned::AssemblyRefOS(_)
| TableDataOwned::File(_)
| TableDataOwned::Document(_)
| TableDataOwned::MethodDebugInformation(_)
| TableDataOwned::LocalScope(_)
| TableDataOwned::LocalVariable(_)
| TableDataOwned::LocalConstant(_)
| TableDataOwned::ImportScope(_)
| TableDataOwned::StateMachineMethod(_)
| TableDataOwned::CustomDebugInformation(_) => {
}
}
}
}
impl RidRemapper {
#[must_use]
pub fn needs_remapping(&self, table_id: TableId) -> bool {
if self.is_empty() {
return false;
}
table_id.references().iter().any(|t| self.has_deletions(*t))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metadata::tables::CodedIndexType;
#[test]
fn test_calculate_remapping_no_deletions() {
let deleted = HashSet::new();
let remap = RidRemapper::calculate_remapping(&deleted, 10);
assert!(remap.is_empty());
}
#[test]
fn test_calculate_remapping_single_deletion() {
let mut deleted = HashSet::new();
deleted.insert(3);
let remap = RidRemapper::calculate_remapping(&deleted, 10);
assert_eq!(remap.get(&1), None); assert_eq!(remap.get(&2), None); assert_eq!(remap.get(&3), Some(&3)); assert_eq!(remap.get(&4), Some(&3)); assert_eq!(remap.get(&5), Some(&4)); assert_eq!(remap.get(&10), Some(&9)); }
#[test]
fn test_calculate_remapping_multiple_deletions() {
let mut deleted = HashSet::new();
deleted.insert(3);
deleted.insert(7);
let remap = RidRemapper::calculate_remapping(&deleted, 10);
assert_eq!(remap.get(&1), None);
assert_eq!(remap.get(&2), None);
assert_eq!(remap.get(&4), Some(&3));
assert_eq!(remap.get(&5), Some(&4));
assert_eq!(remap.get(&6), Some(&5));
assert_eq!(remap.get(&8), Some(&6));
assert_eq!(remap.get(&9), Some(&7));
assert_eq!(remap.get(&10), Some(&8));
}
#[test]
fn test_calculate_remapping_consecutive_deletions() {
let mut deleted = HashSet::new();
deleted.insert(3);
deleted.insert(4);
deleted.insert(5);
let remap = RidRemapper::calculate_remapping(&deleted, 10);
assert_eq!(remap.get(&1), None);
assert_eq!(remap.get(&2), None);
assert_eq!(remap.get(&6), Some(&3));
assert_eq!(remap.get(&7), Some(&4));
assert_eq!(remap.get(&10), Some(&7));
}
#[test]
fn test_calculate_remapping_first_row_deleted() {
let mut deleted = HashSet::new();
deleted.insert(1);
let remap = RidRemapper::calculate_remapping(&deleted, 5);
assert_eq!(remap.get(&2), Some(&1));
assert_eq!(remap.get(&3), Some(&2));
assert_eq!(remap.get(&4), Some(&3));
assert_eq!(remap.get(&5), Some(&4));
}
#[test]
fn test_calculate_remapping_last_row_deleted() {
let mut deleted = HashSet::new();
deleted.insert(10);
let remap = RidRemapper::calculate_remapping(&deleted, 10);
assert_eq!(remap.len(), 2);
assert_eq!(remap.get(&10), Some(&10)); assert_eq!(remap.get(&11), Some(&10)); }
#[test]
fn test_remap_rid() {
let mut deleted = HashSet::new();
deleted.insert(3);
let mut remaps = HashMap::new();
remaps.insert(
TableId::MethodDef,
RidRemapper::calculate_remapping(&deleted, 10),
);
let mut deleted_rids = HashMap::new();
deleted_rids.insert(TableId::MethodDef, deleted);
let remapper = RidRemapper {
remaps,
tables_with_deletions: [TableId::MethodDef].into_iter().collect(),
deleted_rids,
};
assert_eq!(remapper.remap_rid(TableId::MethodDef, 0), 0);
assert_eq!(remapper.remap_rid(TableId::MethodDef, 1), 1);
assert_eq!(remapper.remap_rid(TableId::MethodDef, 2), 2);
assert_eq!(remapper.remap_rid(TableId::MethodDef, 4), 3);
assert_eq!(remapper.remap_rid(TableId::MethodDef, 5), 4);
assert_eq!(remapper.remap_rid(TableId::Field, 5), 5);
}
#[test]
fn test_remap_coded_index() {
let mut deleted = HashSet::new();
deleted.insert(3);
let mut remaps = HashMap::new();
remaps.insert(
TableId::MethodDef,
RidRemapper::calculate_remapping(&deleted, 10),
);
let mut deleted_rids = HashMap::new();
deleted_rids.insert(TableId::MethodDef, deleted);
let remapper = RidRemapper {
remaps,
tables_with_deletions: [TableId::MethodDef].into_iter().collect(),
deleted_rids,
};
let mut ci = CodedIndex::new(TableId::MethodDef, 5, CodedIndexType::HasCustomAttribute);
remapper.remap_coded_index(&mut ci);
assert_eq!(ci.row, 4);
assert_eq!(ci.token.row(), 4);
let mut ci2 = CodedIndex::new(TableId::TypeDef, 5, CodedIndexType::HasCustomAttribute);
remapper.remap_coded_index(&mut ci2);
assert_eq!(ci2.row, 5); }
#[test]
fn test_remap_coded_index_null() {
let remapper = RidRemapper::empty();
let mut ci = CodedIndex::new(TableId::MethodDef, 0, CodedIndexType::HasCustomAttribute);
remapper.remap_coded_index(&mut ci);
assert_eq!(ci.row, 0);
}
#[test]
fn test_typedef_remap_references() {
let mut deleted = HashSet::new();
deleted.insert(3);
let mut remaps = HashMap::new();
remaps.insert(
TableId::MethodDef,
RidRemapper::calculate_remapping(&deleted, 10),
);
let mut deleted_rids = HashMap::new();
deleted_rids.insert(TableId::MethodDef, deleted);
let remapper = RidRemapper {
remaps,
tables_with_deletions: [TableId::MethodDef].into_iter().collect(),
deleted_rids,
};
let mut typedef = TypeDefRaw {
rid: 1,
token: crate::metadata::token::Token::new(0x02000001),
offset: 0,
flags: 0,
type_name: 0,
type_namespace: 0,
extends: CodedIndex::null(CodedIndexType::TypeDefOrRef),
field_list: 1,
method_list: 5, };
typedef.remap_references(&remapper);
assert_eq!(typedef.method_list, 4);
}
#[test]
fn test_typedef_remap_excludes_deleted_rids() {
let mut deleted = HashSet::new();
deleted.insert(3);
let mut remaps = HashMap::new();
remaps.insert(
TableId::TypeDef,
RidRemapper::calculate_remapping(&deleted, 5),
);
let mut deleted_rids = HashMap::new();
deleted_rids.insert(TableId::TypeDef, deleted);
let remapper = RidRemapper {
remaps,
tables_with_deletions: [TableId::TypeDef].into_iter().collect(),
deleted_rids,
};
let sig_remap = remapper.typedef_remap().expect("should have remap");
assert!(
!sig_remap.contains_key(&3),
"deleted RID should not be in signature remap"
);
assert_eq!(sig_remap.get(&4), Some(&3), "row 4 should map to 3");
assert_eq!(sig_remap.get(&5), Some(&4), "row 5 should map to 4");
}
#[test]
fn test_typedef_remap_with_no_typedef_deletions() {
let mut deleted = HashSet::new();
deleted.insert(3);
let mut remaps = HashMap::new();
remaps.insert(
TableId::MethodDef, RidRemapper::calculate_remapping(&deleted, 10),
);
let mut deleted_rids = HashMap::new();
deleted_rids.insert(TableId::MethodDef, deleted);
let remapper = RidRemapper {
remaps,
tables_with_deletions: [TableId::MethodDef].into_iter().collect(),
deleted_rids,
};
assert!(remapper.typedef_remap().is_none());
}
}