use std::sync::Arc;
use crate::{
metadata::{
tables::{
MetadataTable, PropertyList, PropertyMap, PropertyMapEntry, PropertyMapEntryRc,
PropertyPtrMap, TableId, TableInfoRef, TableRow,
},
token::Token,
typesystem::TypeRegistry,
},
Result,
};
#[derive(Clone, Debug)]
pub struct PropertyMapRaw {
pub rid: u32,
pub token: Token,
pub offset: usize,
pub parent: u32,
pub property_list: u32,
}
impl PropertyMapRaw {
fn resolve_property_list(
&self,
properties: &PropertyMap,
property_ptr: &PropertyPtrMap,
map: &MetadataTable<PropertyMapRaw>,
) -> Result<PropertyList> {
if self.property_list == 0 || properties.is_empty() {
return Ok(Arc::new(boxcar::Vec::new()));
}
let next_row_id = self.rid + 1;
let start = self.property_list as usize;
let end = if next_row_id > map.row_count {
properties.len() + 1
} else {
match map.get(next_row_id) {
Some(next_row) => next_row.property_list as usize,
None => {
return Err(malformed_error!(
"Failed to resolve property_end from next row - {}",
next_row_id
))
}
}
};
if start > properties.len() || end > (properties.len() + 1) || end < start {
return Ok(Arc::new(boxcar::Vec::new()));
}
let property_list = Arc::new(boxcar::Vec::with_capacity(end - start));
for counter in start..end {
let actual_property_token = if property_ptr.is_empty() {
let token_value = counter | 0x1700_0000;
Token::new(
u32::try_from(token_value)
.map_err(|_| malformed_error!("Property counter overflow"))?,
)
} else {
let property_ptr_token_value =
u32::try_from(counter | 0x0E00_0000).map_err(|_| {
malformed_error!(
"PropertyPtr token value too large: {}",
counter | 0x0E00_0000
)
})?;
let property_ptr_token = Token::new(property_ptr_token_value);
match property_ptr.get(&property_ptr_token) {
Some(property_ptr_entry) => {
let actual_property_rid = property_ptr_entry.value().property;
let actual_property_token_value = u32::try_from(
actual_property_rid as usize | 0x1700_0000,
)
.map_err(|_| {
malformed_error!(
"Property token value too large: {}",
actual_property_rid as usize | 0x1700_0000
)
})?;
Token::new(actual_property_token_value)
}
None => {
return Err(malformed_error!(
"Failed to resolve PropertyPtr - {}",
counter | 0x0E00_0000
))
}
}
};
match properties.get(&actual_property_token) {
Some(property) => _ = property_list.push(property.value().clone()),
None => {
return Err(malformed_error!(
"Failed to resolve property - {}",
actual_property_token.value()
))
}
}
}
Ok(property_list)
}
pub fn to_owned(
&self,
types: &TypeRegistry,
properties: &PropertyMap,
property_ptr: &PropertyPtrMap,
map: &MetadataTable<PropertyMapRaw>,
) -> Result<PropertyMapEntryRc> {
let parent = match types.get(&Token::new(self.parent | 0x0200_0000)) {
Some(parent_type) => parent_type.into(),
None => {
return Err(malformed_error!(
"Failed to resolve parent type - {}",
self.parent | 0x0200_0000
))
}
};
Ok(Arc::new(PropertyMapEntry {
rid: self.rid,
token: self.token,
offset: self.offset,
parent,
properties: self.resolve_property_list(properties, property_ptr, map)?,
}))
}
pub fn apply(
&self,
types: &TypeRegistry,
properties: &PropertyMap,
property_ptr: &PropertyPtrMap,
map: &MetadataTable<PropertyMapRaw>,
) -> Result<()> {
let property_list = self.resolve_property_list(properties, property_ptr, map)?;
if property_list.is_empty() && (self.property_list != 0 && !properties.is_empty()) {
return Err(malformed_error!("Invalid property_list"));
}
match types.get(&Token::new(self.parent | 0x0200_0000)) {
Some(entry) => {
for (_, property) in property_list.iter() {
_ = entry.properties.push(property.clone());
}
Ok(())
}
None => Err(malformed_error!(
"Failed to resolve parent - {}",
self.parent | 0x0200_0000
)),
}
}
}
impl TableRow for PropertyMapRaw {
#[rustfmt::skip]
fn row_size(sizes: &TableInfoRef) -> u32 {
u32::from(
sizes.table_index_bytes(TableId::TypeDef) +
sizes.table_index_bytes(TableId::Property)
)
}
}