Skip to content

Commit

Permalink
Migrate CustomStruct to IndexMap (#102)
Browse files Browse the repository at this point in the history
* Flatten CustomStruct

* improve array_index support

* HashableIndexMap, derive Hash

---------

Co-authored-by: Scott Anderson <[email protected]>
  • Loading branch information
scottanderson and scottanderson authored Sep 30, 2024
1 parent 8f65c48 commit 8c075b4
Show file tree
Hide file tree
Showing 9 changed files with 573 additions and 612 deletions.
159 changes: 82 additions & 77 deletions src/properties/map_property.rs

Large diffs are not rendered by default.

79 changes: 60 additions & 19 deletions src/properties/struct_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ use std::{
};

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use indexmap::IndexMap;

use crate::properties::struct_types::LinearColor;
use crate::{
cursor_ext::{ReadExt, WriteExt},
error::{DeserializeError, Error, SerializeError},
properties::{name_property::NameProperty, struct_types::LinearColor},
scoped_stack_entry::ScopedStackEntry,
types::map::HashableIndexMap,
types::Guid,
};

Expand Down Expand Up @@ -78,7 +80,12 @@ pub enum StructPropertyValue {
/// An `IntPoint` value.
IntPoint(IntPoint),
/// A custom struct value.
CustomStruct(String, Vec<(String, Property)>),
CustomStruct {
/// Type name.
type_name: String,
/// Properties.
properties: HashableIndexMap<String, Vec<Property>>,
},
}

impl StructProperty {
Expand Down Expand Up @@ -211,20 +218,24 @@ impl StructProperty {
UInt32Property::read(cursor, false)?.value,
])),
_ => {
let mut properties = Vec::new();
let mut properties = IndexMap::new();
loop {
let key_name = cursor.read_string()?;
if key_name == "None" {
let property_name = cursor.read_string()?;
if property_name == "None" {
break;
}
let value_type = cursor.read_string()?;
let property_type = cursor.read_string()?;
let _property_stack_entry =
ScopedStackEntry::new(options.properties_stack, key_name.clone());
ScopedStackEntry::new(options.properties_stack, property_name.clone());

let property = Property::new(cursor, &value_type, true, options, None)?;
properties.push((key_name, property));
let property = Property::new(cursor, &property_type, true, options, None)?;
insert_property(&mut properties, property_name, property);
}
let properties = HashableIndexMap(properties);
StructPropertyValue::CustomStruct {
type_name,
properties,
}
StructPropertyValue::CustomStruct(type_name, properties)
}
};

Expand Down Expand Up @@ -252,12 +263,29 @@ impl StructProperty {
StructPropertyValue::Guid(_) => "Guid",
StructPropertyValue::LinearColor(_) => "LinearColor",
StructPropertyValue::IntPoint(_) => "IntPoint",
StructPropertyValue::CustomStruct(type_name, _) => type_name,
StructPropertyValue::CustomStruct { type_name, .. } => type_name,
};
Ok(property_name)
}
}

fn insert_property(map: &mut IndexMap<String, Vec<Property>>, key: String, property: Property) {
let entry = map.entry(key).or_default();
#[cfg(debug_assertions)]
{
let array_index = match property {
// TODO: Move array_index to the Property layer
Property::NameProperty(NameProperty { array_index, .. }) => array_index,
_ => 0,
};
let actual_array_index = entry.len() as u32;
// Ensure that the position in the array matches the array_index value,
// otherwise this conversion would cause data loss.
assert_eq!(actual_array_index, array_index);
}
entry.push(property);
}

impl PropertyTrait for StructProperty {
impl_write!(
StructProperty,
Expand Down Expand Up @@ -376,11 +404,16 @@ impl PropertyTrait for StructProperty {
cursor.write_guid(guid)?;
Ok(16)
}
StructPropertyValue::CustomStruct(_, properties) => {
StructPropertyValue::CustomStruct {
properties: HashableIndexMap(properties),
..
} => {
let mut len = 0;
for (key, value) in properties {
len += cursor.write_string(key)?;
len += value.write(cursor, true, options)?;
for (key, values) in properties {
for value in values {
len += cursor.write_string(key)?;
len += value.write(cursor, true, options)?;
}
}
len += cursor.write_string("None")?;
Ok(len)
Expand Down Expand Up @@ -465,18 +498,26 @@ impl StructPropertyValue {

/// Retrieves the enum value as a `CustomStruct`.
#[inline]
pub fn get_custom_struct(&self) -> Option<(&String, &Vec<(String, Property)>)> {
pub fn get_custom_struct(&self) -> Option<(&String, &IndexMap<String, Vec<Property>>)> {
match self {
Self::CustomStruct(type_name, properties) => Some((type_name, properties)),
Self::CustomStruct {
type_name,
properties: HashableIndexMap(properties),
} => Some((type_name, properties)),
_ => None,
}
}

/// Retrieves the mutable enum value as a `CustomStruct`.
#[inline]
pub fn get_custom_struct_mut(&mut self) -> Option<(&mut String, &mut Vec<(String, Property)>)> {
pub fn get_custom_struct_mut(
&mut self,
) -> Option<(&mut String, &mut IndexMap<String, Vec<Property>>)> {
match self {
Self::CustomStruct(type_name, properties) => Some((type_name, properties)),
Self::CustomStruct {
type_name,
properties: HashableIndexMap(properties),
} => Some((type_name, properties)),
_ => None,
}
}
Expand Down
144 changes: 6 additions & 138 deletions src/properties/text_property.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::hash::{Hash, Hasher};
use std::hash::Hash;
use std::{
fmt::Debug,
io::{Cursor, Read, Seek, Write},
Expand All @@ -13,6 +13,7 @@ use unreal_helpers::{UnrealReadExt, UnrealWriteExt};
use crate::custom_version::FEditorObjectVersion;
use crate::properties::int_property::UInt64Property;
use crate::properties::struct_types::DateTime;
use crate::types::map::HashableIndexMap;
use crate::{
cursor_ext::{ReadExt, WriteExt},
error::Error,
Expand Down Expand Up @@ -172,7 +173,7 @@ pub enum TextHistoryType {
}

/// FText history
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", serde_with::skip_serializing_none)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "history"))]
Expand All @@ -198,7 +199,7 @@ pub enum FTextHistory {
/// Source format
source_format: Box<FText>,
/// Arguments
arguments: IndexMap<String, FormatArgumentValue>,
arguments: HashableIndexMap<String, FormatArgumentValue>,
},
/// Ordered format text history
OrderedFormat {
Expand Down Expand Up @@ -341,6 +342,7 @@ impl FTextHistory {
arguments.insert(key, value);
}

let arguments = HashableIndexMap(arguments);
FTextHistory::NamedFormat {
source_format,
arguments,
Expand Down Expand Up @@ -546,7 +548,7 @@ impl FTextHistory {

FTextHistory::NamedFormat {
source_format,
arguments,
arguments: HashableIndexMap(arguments),
} => {
let mut len = 1;
cursor.write_enum(TextHistoryType::NamedFormat)?;
Expand Down Expand Up @@ -708,140 +710,6 @@ impl FTextHistory {
}
}

impl Hash for FTextHistory {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
FTextHistory::Empty {} => {
state.write_u8(0);
}
FTextHistory::None {
culture_invariant_string,
} => {
state.write_u8(0);
culture_invariant_string.hash(state);
}
FTextHistory::Base {
namespace,
key,
source_string,
} => {
state.write_u8(1);
namespace.hash(state);
key.hash(state);
source_string.hash(state);
}
FTextHistory::NamedFormat {
source_format,
arguments,
} => {
state.write_u8(2);
source_format.hash(state);
for (key, value) in arguments {
key.hash(state);
value.hash(state);
}
}
FTextHistory::OrderedFormat {
source_format,
arguments,
} => {
state.write_u8(3);
source_format.hash(state);
arguments.hash(state);
}
FTextHistory::ArgumentFormat {
source_format,
arguments,
} => {
state.write_u8(4);
source_format.hash(state);
arguments.hash(state);
}
FTextHistory::AsNumber {
source_value,
format_options,
target_culture,
} => {
state.write_u8(5);
source_value.hash(state);
format_options.hash(state);
target_culture.hash(state);
}
FTextHistory::AsPercent {
source_value,
format_options,
target_culture,
} => {
state.write_u8(6);
source_value.hash(state);
format_options.hash(state);
target_culture.hash(state);
}
FTextHistory::AsCurrency {
currency_code,
source_value,
format_options,
target_culture,
} => {
state.write_u8(7);
currency_code.hash(state);
source_value.hash(state);
format_options.hash(state);
target_culture.hash(state);
}
FTextHistory::AsDate {
date_time,
date_style,
target_culture,
} => {
state.write_u8(8);
date_time.hash(state);
date_style.hash(state);
target_culture.hash(state);
}
FTextHistory::AsTime {
source_date_time,
time_style,
time_zone,
target_culture,
} => {
state.write_u8(9);
source_date_time.hash(state);
time_style.hash(state);
time_zone.hash(state);
target_culture.hash(state);
}
FTextHistory::AsDateTime {
source_date_time,
date_style,
time_style,
time_zone,
target_culture,
} => {
state.write_u8(10);
source_date_time.hash(state);
date_style.hash(state);
time_style.hash(state);
time_zone.hash(state);
target_culture.hash(state);
}
FTextHistory::Transform {
source_text,
transform_type,
} => {
state.write_u8(11);
source_text.hash(state);
transform_type.hash(state);
}
FTextHistory::StringTableEntry { table_id, key } => {
state.write_u8(12);
table_id.hash(state);
key.hash(state);
}
}
}
}

/// Format argument type
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[repr(i8)]
Expand Down
Loading

0 comments on commit 8c075b4

Please sign in to comment.