From 8ab910e18f6e2ea4a9eba747d1882b3e098c57af Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Sat, 3 May 2025 22:11:58 -0700
Subject: [PATCH] wip: hash_str

---
 lib/rbx_loader/src/lib.rs                     | 14 ++---
 lib/rbx_loader/src/loader.rs                  | 11 ++--
 lib/rbx_loader/src/rbx.rs                     | 53 +++++++++----------
 lib/roblox_emulator/src/context.rs            | 47 ++++++++--------
 lib/roblox_emulator/src/lib.rs                |  1 -
 lib/roblox_emulator/src/runner/enum.rs        | 16 +++---
 .../src/runner/instance/instance.rs           | 41 +++++++-------
 lib/roblox_emulator/src/runner/runner.rs      |  6 +--
 lib/roblox_emulator/src/util.rs               |  3 --
 9 files changed, 90 insertions(+), 102 deletions(-)
 delete mode 100644 lib/roblox_emulator/src/util.rs

diff --git a/lib/rbx_loader/src/lib.rs b/lib/rbx_loader/src/lib.rs
index 723f481..993d453 100644
--- a/lib/rbx_loader/src/lib.rs
+++ b/lib/rbx_loader/src/lib.rs
@@ -21,19 +21,19 @@ pub mod data{
 	}
 }
 
-pub struct Model{
-	dom:WeakDom,
+pub struct Model<'a>{
+	dom:WeakDom<'a>,
 }
-impl Model{
-	fn new(dom:WeakDom)->Self{
+impl<'a> Model<'a>{
+	fn new(dom:WeakDom<'a>)->Self{
 		Self{dom}
 	}
 	pub fn to_snf(&self,failure_mode:LoadFailureMode)->Result<strafesnet_common::map::CompleteMap,LoadError>{
 		to_snf(self,failure_mode)
 	}
 }
-impl AsRef<WeakDom> for Model{
-	fn as_ref(&self)->&WeakDom{
+impl<'a> AsRef<WeakDom<'a>> for Model<'a>{
+	fn as_ref(&self)->&WeakDom<'a>{
 		&self.dom
 	}
 }
@@ -95,7 +95,7 @@ pub fn read<R:Read>(input:R)->Result<Model,ReadError>{
 	let mut buf=std::io::BufReader::new(input);
 	let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
 	match peek.get(0..8){
-		Some(b"<roblox!")=>rbx_binary::from_reader(buf).map(Model::new).map_err(ReadError::RbxBinary),
+		Some(b"<roblox!")=>rbx_binary::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxBinary),
 		Some(b"<roblox ")=>rbx_xml::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxXml),
 		_=>Err(ReadError::UnknownFileFormat),
 	}
diff --git a/lib/rbx_loader/src/loader.rs b/lib/rbx_loader/src/loader.rs
index 9096c46..f04d633 100644
--- a/lib/rbx_loader/src/loader.rs
+++ b/lib/rbx_loader/src/loader.rs
@@ -6,10 +6,7 @@ use strafesnet_deferred_loader::{loader::Loader,texture::Texture};
 use crate::data::RobloxMeshBytes;
 use crate::rbx::RobloxPartDescription;
 
-// disallow non-static lifetimes
-fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
-	rbx_dom_weak::ustr(s)
-}
+use rbx_dom_weak::hstr;
 
 fn read_entire_file(path:impl AsRef<std::path::Path>)->Result<Vec<u8>,std::io::Error>{
 	let mut file=std::fs::File::open(path)?;
@@ -168,7 +165,7 @@ impl Loader for MeshLoader{
 					let RobloxAssetId(asset_id)=index.content.parse()?;
 					let file_name=format!("unions/{}",asset_id);
 					let data=read_entire_file(file_name)?;
-					let dom=rbx_binary::from_reader(std::io::Cursor::new(data))?;
+					let dom=rbx_binary::from_reader_default(data.as_slice())?;
 					let &[referent]=dom.root().children()else{
 						return Err(MeshError::OneChildPolicy);
 					};
@@ -176,12 +173,12 @@ impl Loader for MeshLoader{
 						return Err(MeshError::MissingInstance);
 					};
 					if physics_data.is_empty(){
-						if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(&static_ustr("PhysicsData")){
+						if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(hstr!("PhysicsData")){
 							physics_data=data.as_ref();
 						}
 					}
 					if mesh_data.is_empty(){
-						if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(&static_ustr("MeshData")){
+						if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(hstr!("MeshData")){
 							mesh_data=data.as_ref();
 						}
 					}
diff --git a/lib/rbx_loader/src/rbx.rs b/lib/rbx_loader/src/rbx.rs
index cb4953d..4209e53 100644
--- a/lib/rbx_loader/src/rbx.rs
+++ b/lib/rbx_loader/src/rbx.rs
@@ -13,16 +13,13 @@ use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,Mes
 use strafesnet_deferred_loader::mesh::Meshes;
 use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
 
-// disallow non-static lifetimes
-fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
-	rbx_dom_weak::ustr(s)
-}
+use rbx_dom_weak::{hstr,HashStr};
 
 fn recursive_collect_superclass(
 	objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>,
 	dom:&rbx_dom_weak::WeakDom,
 	instance:&rbx_dom_weak::Instance,
-	superclass:&str
+	superclass:&HashStr,
 ){
 	let instance=instance;
 	let db=rbx_reflection_database::get();
@@ -31,7 +28,7 @@ fn recursive_collect_superclass(
 	};
 	objects.extend(
 		dom.descendants_of(instance.referent()).filter_map(|instance|{
-			let class=db.classes.get(instance.class.as_str())?;
+			let class=db.classes.get(instance.class)?;
 			db.has_superclass(class,superclass).then(||instance.referent())
 		})
 	);
@@ -410,7 +407,7 @@ fn get_texture_description<'a>(
 	//use the biggest one and cut it down later...
 	let mut part_texture_description=RobloxPartDescription::default();
 	temp_objects.clear();
-	recursive_collect_superclass(temp_objects,&dom,object,"Decal");
+	recursive_collect_superclass(temp_objects,&dom,object,hstr!("Decal"));
 	for &mut decal_ref in temp_objects{
 		let Some(decal)=dom.get_by_ref(decal_ref) else{
 			println!("Decal get_by_ref failed");
@@ -422,10 +419,10 @@ fn get_texture_description<'a>(
 			Some(rbx_dom_weak::types::Variant::Color3(decal_color3)),
 			Some(rbx_dom_weak::types::Variant::Float32(decal_transparency)),
 		)=(
-			decal.properties.get(&static_ustr("TextureContent")),
-			decal.properties.get(&static_ustr("Face")),
-			decal.properties.get(&static_ustr("Color3")),
-			decal.properties.get(&static_ustr("Transparency")),
+			decal.properties.get(hstr!("TextureContent")),
+			decal.properties.get(hstr!("Face")),
+			decal.properties.get(hstr!("Color3")),
+			decal.properties.get(hstr!("Transparency")),
 		)else{
 			println!("Decal is missing a required property");
 			continue;
@@ -447,10 +444,10 @@ fn get_texture_description<'a>(
 					Some(&rbx_dom_weak::types::Variant::Float32(studs_per_tile_u)),
 					Some(&rbx_dom_weak::types::Variant::Float32(studs_per_tile_v)),
 				) = (
-					decal.properties.get(&static_ustr("OffsetStudsU")),
-					decal.properties.get(&static_ustr("OffsetStudsV")),
-					decal.properties.get(&static_ustr("StudsPerTileU")),
-					decal.properties.get(&static_ustr("StudsPerTileV")),
+					decal.properties.get(hstr!("OffsetStudsU")),
+					decal.properties.get(hstr!("OffsetStudsV")),
+					decal.properties.get(hstr!("StudsPerTileU")),
+					decal.properties.get(hstr!("StudsPerTileV")),
 				)
 			{
 				let (size_u,size_v)=match cube_face{
@@ -537,7 +534,7 @@ pub fn convert<'a>(
 
 	let mut object_refs=Vec::new();
 	let mut temp_objects=Vec::new();
-	recursive_collect_superclass(&mut object_refs, &dom, dom.root(),"BasePart");
+	recursive_collect_superclass(&mut object_refs, &dom, dom.root(),hstr!("BasePart"));
 	for object_ref in object_refs {
 		if let Some(object)=dom.get_by_ref(object_ref){
 			if let (
@@ -548,12 +545,12 @@ pub fn convert<'a>(
 					Some(rbx_dom_weak::types::Variant::Color3uint8(color3)),
 					Some(rbx_dom_weak::types::Variant::Bool(can_collide)),
 				) = (
-					object.properties.get(&static_ustr("CFrame")),
-					object.properties.get(&static_ustr("Size")),
-					object.properties.get(&static_ustr("Velocity")),
-					object.properties.get(&static_ustr("Transparency")),
-					object.properties.get(&static_ustr("Color")),
-					object.properties.get(&static_ustr("CanCollide")),
+					object.properties.get(hstr!("CFrame")),
+					object.properties.get(hstr!("Size")),
+					object.properties.get(hstr!("Velocity")),
+					object.properties.get(hstr!("Transparency")),
+					object.properties.get(hstr!("Color")),
+					object.properties.get(hstr!("CanCollide")),
 				)
 			{
 				let model_transform=planar64_affine3_from_roblox(cf,size);
@@ -572,7 +569,7 @@ pub fn convert<'a>(
 
 				//TODO: also detect "CylinderMesh" etc here
 				let shape=match object.class.as_str(){
-					"Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get(&static_ustr("Shape")){
+					"Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get(hstr!("Shape")){
 						Shape::Primitive(shape.to_u32().try_into().expect("Funky roblox PartType"))
 					}else{
 						panic!("Part has no Shape!");
@@ -646,9 +643,9 @@ pub fn convert<'a>(
 						Some(rbx_dom_weak::types::Variant::Content(texture_content)),
 					)=(
 						// mesh must exist
-						object.properties.get(&static_ustr("MeshContent")),
+						object.properties.get(hstr!("MeshContent")),
 						// texture is allowed to be none
-						object.properties.get(&static_ustr("TextureContent")),
+						object.properties.get(hstr!("TextureContent")),
 					){
 						let mesh_asset_id=get_content_url(mesh_content).expect("MeshPart Mesh is not a Uri");
 						let texture_asset_id=get_content_url(texture_content);
@@ -663,13 +660,13 @@ pub fn convert<'a>(
 						let mut content="";
 						let mut mesh_data:&[u8]=&[];
 						let mut physics_data:&[u8]=&[];
-						if let Some(rbx_dom_weak::types::Variant::ContentId(asset_id))=object.properties.get(&static_ustr("AssetId")){
+						if let Some(rbx_dom_weak::types::Variant::ContentId(asset_id))=object.properties.get(hstr!("AssetId")){
 							content=asset_id.as_ref();
 						}
-						if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&static_ustr("MeshData")){
+						if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(hstr!("MeshData")){
 							mesh_data=data.as_ref();
 						}
-						if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&static_ustr("PhysicsData")){
+						if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(hstr!("PhysicsData")){
 							physics_data=data.as_ref();
 						}
 						let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size);
diff --git a/lib/roblox_emulator/src/context.rs b/lib/roblox_emulator/src/context.rs
index f2d4a1b..a38eca1 100644
--- a/lib/roblox_emulator/src/context.rs
+++ b/lib/roblox_emulator/src/context.rs
@@ -1,5 +1,4 @@
-use crate::util::static_ustr;
-use rbx_dom_weak::{types::Ref,InstanceBuilder,WeakDom};
+use rbx_dom_weak::{hstr,types::Ref,InstanceBuilder,WeakDom,UnhashedStr};
 
 #[derive(Debug)]
 pub enum ServicesError{
@@ -28,36 +27,36 @@ impl Services{
 	}
 }
 
-pub type LuaAppData=&'static mut WeakDom;
-pub struct Context{
-	pub(crate)dom:WeakDom,
+pub type LuaAppData=&'static mut WeakDom<'static>;
+pub struct Context<'a>{
+	pub(crate)dom:WeakDom<'a>,
 	pub(crate)services:Services,
 }
 
-impl Context{
-	pub fn from_place(dom:WeakDom)->Result<Context,ServicesError>{
+impl<'a> Context<'a>{
+	pub fn from_place(dom:WeakDom<'a>)->Result<Context<'a>,ServicesError>{
 		let services=Services::find_services(&dom)?;
 		Ok(Self{dom,services})
 	}
-	pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance){
-		let script=InstanceBuilder::new("Script")
-			.with_property("Source",rbx_types::Variant::String(source));
+	pub fn script_singleton(source:String)->(Context<'a>,crate::runner::instance::Instance){
+		let script=InstanceBuilder::new(hstr!("Script"))
+			.with_property(hstr!("Source"),rbx_types::Variant::String(source));
 		let script_ref=script.referent();
 		let dom=WeakDom::new(
-			InstanceBuilder::new("DataModel")
+			InstanceBuilder::new(hstr!("DataModel"))
 			.with_child(script)
 		);
 		let context=Self::from_model(dom);
 		(context,crate::runner::instance::Instance::new_unchecked(script_ref))
 	}
 	/// Creates an iterator over all items of a particular class.
-	pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{
+	pub fn superclass_iter<'b>(&'b self,superclass:&'b str)->impl Iterator<Item=Ref>+'b{
 		let db=rbx_reflection_database::get();
-		let Some(superclass)=db.classes.get(superclass)else{
+		let Some(superclass)=db.classes.get(UnhashedStr::from_ref(superclass))else{
 			panic!("Invalid class");
 		};
 		self.dom.descendants().filter_map(|instance|{
-			let class=db.classes.get(instance.class.as_str())?;
+			let class=db.classes.get(instance.class)?;
 			db.has_superclass(class,superclass).then(||instance.referent())
 		})
 	}
@@ -65,7 +64,7 @@ impl Context{
 		self.superclass_iter("Script")
 			.filter_map(|script_ref|{
 				let script=self.dom.get_by_ref(script_ref)?;
-				if let None|Some(rbx_dom_weak::types::Variant::Bool(false))=script.properties.get(&static_ustr("Disabled")){
+				if let None|Some(rbx_dom_weak::types::Variant::Bool(false))=script.properties.get(hstr!("Disabled")){
 					return Some(crate::runner::instance::Instance::new_unchecked(script_ref));
 				}
 				None
@@ -73,17 +72,17 @@ impl Context{
 			.collect()
 	}
 
-	pub fn from_model(mut dom:WeakDom)->Context{
+	pub fn from_model(mut dom:WeakDom<'a>)->Context<'a>{
 		//snapshot root instances
 		let children=dom.root().children().to_owned();
 
 		//insert services
 		let game=dom.root_ref();
-		let terrain_bldr=InstanceBuilder::new("Terrain");
+		let terrain_bldr=InstanceBuilder::new(hstr!("Terrain"));
 		let workspace=dom.insert(game,
-			InstanceBuilder::new("Workspace")
+			InstanceBuilder::new(hstr!("Workspace"))
 				//Set Workspace.Terrain property equal to Terrain
-				.with_property("Terrain",terrain_bldr.referent())
+				.with_property(hstr!("Terrain"),terrain_bldr.referent())
 				.with_child(terrain_bldr)
 		);
 
@@ -96,18 +95,18 @@ impl Context{
 			//Lowercase and upper case workspace property!
 			let game=dom.root_mut();
 			// TODO: DELETE THIS!
-			game.properties.insert(static_ustr("workspace"),rbx_types::Variant::Ref(workspace));
-			game.properties.insert(static_ustr("Workspace"),rbx_types::Variant::Ref(workspace));
+			game.properties.insert(hstr!("workspace"),rbx_types::Variant::Ref(workspace));
+			game.properties.insert(hstr!("Workspace"),rbx_types::Variant::Ref(workspace));
 		}
-		dom.insert(game,InstanceBuilder::new("Lighting"));
+		dom.insert(game,InstanceBuilder::new(hstr!("Lighting")));
 
 		let services=Services{game,workspace};
 		Self{dom,services}
 	}
 }
 
-impl AsRef<WeakDom> for Context{
-	fn as_ref(&self)->&WeakDom{
+impl<'a> AsRef<WeakDom<'a>> for Context<'a>{
+	fn as_ref(&self)->&WeakDom<'a>{
 		&self.dom
 	}
 }
diff --git a/lib/roblox_emulator/src/lib.rs b/lib/roblox_emulator/src/lib.rs
index 0a825a4..362280a 100644
--- a/lib/roblox_emulator/src/lib.rs
+++ b/lib/roblox_emulator/src/lib.rs
@@ -1,4 +1,3 @@
-mod util;
 pub mod runner;
 pub mod context;
 #[cfg(feature="run-service")]
diff --git a/lib/roblox_emulator/src/runner/enum.rs b/lib/roblox_emulator/src/runner/enum.rs
index d3c3dc3..e018279 100644
--- a/lib/roblox_emulator/src/runner/enum.rs
+++ b/lib/roblox_emulator/src/runner/enum.rs
@@ -1,11 +1,13 @@
+use rbx_dom_weak::{HashStr, UnhashedStr};
+
 #[derive(Clone,Copy)]
 pub struct EnumItem<'a>{
-	name:Option<&'a str>,
+	name:Option<&'a HashStr>,
 	value:u32,
 }
 impl<'a> EnumItem<'a>{
-	fn known_name((name,&value):(&'a std::borrow::Cow<'a,str>,&u32))->Self{
-		Self{name:Some(name.as_ref()),value}
+	fn known_name((name,&value):(&&'a HashStr,&u32))->Self{
+		Self{name:Some(name),value}
 	}
 }
 impl<'a> From<rbx_types::Enum> for EnumItem<'a>{
@@ -38,7 +40,7 @@ pub struct Enums;
 impl Enums{
 	pub fn get(&self,index:&str)->Option<EnumItems<'static>>{
 		let db=rbx_reflection_database::get();
-		db.enums.get(index).map(|ed|EnumItems{ed})
+		db.enums.get(UnhashedStr::from_ref(index)).map(|ed|EnumItems{ed})
 	}
 }
 #[derive(Clone,Copy)]
@@ -51,7 +53,7 @@ impl<'a> EnumItems<'a>{
 		self.ed.items.iter().find(|&(_,&v)|v==value).map(EnumItem::known_name)
 	}
 	pub fn from_name(&self,name:&str)->Option<EnumItem<'a>>{
-		self.ed.items.get_key_value(name).map(EnumItem::known_name)
+		self.ed.items.get_key_value(UnhashedStr::from_ref(name)).map(EnumItem::known_name)
 	}
 	pub fn from_enum(&self,enum_item:EnumItem)->Option<EnumItem<'a>>{
 		match enum_item.name{
@@ -105,7 +107,7 @@ impl mlua::UserData for EnumItems<'static>{
 		});
 		methods.add_meta_function(mlua::MetaMethod::Index,|_,(this,val):(EnumItems,mlua::String)|{
 			let index=&*val.to_str()?;
-			Ok(this.ed.items.get_key_value(index).map(EnumItem::known_name))
+			Ok(this.ed.items.get_key_value(UnhashedStr::from_ref(index)).map(EnumItem::known_name))
 		});
 	}
 }
@@ -124,7 +126,7 @@ type_from_lua_userdata!(Enums);
 
 impl mlua::UserData for EnumItem<'_>{
 	fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
-		fields.add_field_method_get("Name",|_,this|Ok(this.name));
+		fields.add_field_method_get("Name",|_,this|Ok(this.name.map(|s|s.as_str())));
 		fields.add_field_method_get("Value",|_,this|Ok(this.value));
 	}
 	fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
diff --git a/lib/roblox_emulator/src/runner/instance/instance.rs b/lib/roblox_emulator/src/runner/instance/instance.rs
index 1804691..0ccd089 100644
--- a/lib/roblox_emulator/src/runner/instance/instance.rs
+++ b/lib/roblox_emulator/src/runner/instance/instance.rs
@@ -2,9 +2,8 @@ use std::collections::{hash_map::Entry,HashMap};
 
 use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
 use rbx_types::Ref;
-use rbx_dom_weak::{Ustr,InstanceBuilder,WeakDom};
+use rbx_dom_weak::{hstr,HashStr,UnhashedStr,InstanceBuilder,WeakDom};
 
-use crate::util::static_ustr;
 use crate::runner::vector3::Vector3;
 use crate::runner::number::Number;
 
@@ -38,7 +37,7 @@ pub fn dom_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result<T>
 
 pub fn class_is_a(class:&str,superclass:&str)->bool{
 	let db=rbx_reflection_database::get();
-	let (Some(class),Some(superclass))=(db.classes.get(class),db.classes.get(superclass))else{
+	let (Some(class),Some(superclass))=(db.classes.get(UnhashedStr::from_ref(class)),db.classes.get(UnhashedStr::from_ref(superclass)))else{
 		return false;
 	};
 	db.has_superclass(class,superclass)
@@ -57,7 +56,7 @@ fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->S
 pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),mlua::Error>{
 	dom_mut(lua,|dom|{
 		let instance=script.get(dom)?;
-		let source=match instance.properties.get(&static_ustr("Source")){
+		let source=match instance.properties.get(hstr!("Source")){
 			Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(),
 			_=>Err(mlua::Error::external("Missing script.Source"))?,
 		};
@@ -65,32 +64,32 @@ pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),m
 	})
 }
 
-pub fn find_first_child<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{
+pub fn find_first_child<'a>(dom:&'a rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,name:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
 	instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name)
 }
-pub fn find_first_descendant<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{
+pub fn find_first_descendant<'a>(dom:&'a rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,name:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
 	dom.descendants_of(instance.referent()).find(|&inst|inst.name==name)
 }
 
-pub fn find_first_child_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{
+pub fn find_first_child_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,class:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
 	instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.class==class)
 }
-pub fn find_first_descendant_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{
+pub fn find_first_descendant_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,class:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
 	dom.descendants_of(instance.referent()).find(|&inst|inst.class==class)
 }
 
-pub fn find_first_child_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str)->Option<&'a rbx_dom_weak::Instance>{
+pub fn find_first_child_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,superclass:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
 	let db=rbx_reflection_database::get();
-	let superclass_descriptor=db.classes.get(superclass)?;
+	let superclass_descriptor=db.classes.get(UnhashedStr::from_ref(superclass))?;
 	instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|{
-		db.classes.get(inst.class.as_str()).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
+		db.classes.get(inst.class).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
 	})
 }
-pub fn find_first_descendant_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str)->Option<&'a rbx_dom_weak::Instance>{
+pub fn find_first_descendant_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,superclass:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
 	let db=rbx_reflection_database::get();
-	let superclass_descriptor=db.classes.get(superclass)?;
+	let superclass_descriptor=db.classes.get(UnhashedStr::from_ref(superclass))?;
 	dom.descendants_of(instance.referent()).find(|inst|{
-		db.classes.get(inst.class.as_str()).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
+		db.classes.get(inst.class).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
 	})
 }
 
@@ -105,10 +104,10 @@ impl Instance{
 	pub fn new(referent:Ref)->Option<Self>{
 		referent.is_some().then_some(Self{referent})
 	}
-	pub fn get<'a>(&self,dom:&'a WeakDom)->mlua::Result<&'a rbx_dom_weak::Instance>{
+	pub fn get<'a>(&self,dom:&'a WeakDom<'a>)->mlua::Result<&'a rbx_dom_weak::Instance<'a>>{
 		dom.get_by_ref(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
 	}
-	pub fn get_mut<'a>(&self,dom:&'a mut WeakDom)->mlua::Result<&'a mut rbx_dom_weak::Instance>{
+	pub fn get_mut<'a,'b>(&self,dom:&'a mut WeakDom<'b>)->mlua::Result<&'a mut rbx_dom_weak::Instance<'b>>{
 		dom.get_by_ref_mut(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
 	}
 }
@@ -150,7 +149,7 @@ impl mlua::UserData for Instance{
 		fields.add_field_method_get("ClassName",|lua,this|{
 			dom_mut(lua,|dom|{
 				let instance=this.get(dom)?;
-				Ok(instance.class.to_owned())
+				Ok(instance.class.as_str().to_owned())
 			})
 		});
 	}
@@ -283,7 +282,7 @@ impl mlua::UserData for Instance{
 				let instance=this.get(dom)?;
 				//println!("__index t={} i={index:?}",instance.name);
 				let db=rbx_reflection_database::get();
-				let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
+				let class=db.classes.get(instance.class).ok_or_else(||mlua::Error::runtime("Class missing"))?;
 				// Find existing property
 				// Interestingly, ustr can know ahead of time if
 				// a property does not exist in any runtime instance
@@ -342,7 +341,7 @@ impl mlua::UserData for Instance{
 			dom_mut(lua,|dom|{
 				let instance=this.get_mut(dom)?;
 				let db=rbx_reflection_database::get();
-				let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
+				let class=db.classes.get(instance.class).ok_or_else(||mlua::Error::runtime("Class missing"))?;
 				let property=db.superclasses_iter(class).find_map(|cls|
 					cls.properties.get(index_str)
 				).ok_or_else(||
@@ -590,7 +589,7 @@ static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
 };
 
 fn find_virtual_property(
-	properties:&rbx_dom_weak::UstrMap<rbx_types::Variant>,
+	properties:&rbx_dom_weak::HashStrMap<rbx_types::Variant>,
 	class:&rbx_reflection::ClassDescriptor,
 	index:&str,
 )->Option<rbx_types::Variant>{
@@ -641,7 +640,7 @@ static LAZY_USER_DATA:LUD=phf::phf_map!{
 fn get_or_create_userdata(instance:&mut rbx_dom_weak::Instance,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::AnyUserData>>{
 	use std::collections::hash_map::Entry;
 	let db=rbx_reflection_database::get();
-	let Some(class)=db.classes.get(instance.class.as_str())else{
+	let Some(class)=db.classes.get(instance.class)else{
 		return Ok(None)
 	};
 	if let Some((&static_str,create_userdata))=db.superclasses_iter(class).find_map(|superclass|
diff --git a/lib/roblox_emulator/src/runner/runner.rs b/lib/roblox_emulator/src/runner/runner.rs
index 7c3eb98..255c58e 100644
--- a/lib/roblox_emulator/src/runner/runner.rs
+++ b/lib/roblox_emulator/src/runner/runner.rs
@@ -1,5 +1,4 @@
 use crate::context::Context;
-use crate::util::static_ustr;
 #[cfg(feature="run-service")]
 use crate::scheduler::scheduler_mut;
 
@@ -59,7 +58,7 @@ impl Runner{
 		init(&runner.lua).map_err(Error::RustLua)?;
 		Ok(runner)
 	}
-	pub fn runnable_context<'a>(self,context:&'a mut Context)->Result<Runnable<'a>,Error>{
+	pub fn runnable_context<'a>(self,context:&'a mut Context<'a>)->Result<Runnable<'a>,Error>{
 		{
 			let globals=self.lua.globals();
 			globals.set("game",super::instance::Instance::new_unchecked(context.services.game)).map_err(Error::RustLua)?;
@@ -68,8 +67,7 @@ impl Runner{
 		// SAFETY: This is not a &'static mut WeakDom,
 		// but as long as Runnable<'a> holds the lifetime of &'a mut Context
 		// it is a valid unique reference.
-		let ptr=&mut context.dom as *mut rbx_dom_weak::WeakDom;
-		self.lua.set_app_data::<crate::context::LuaAppData>(unsafe{&mut*ptr});
+		self.lua.set_app_data::<crate::context::LuaAppData>(unsafe{core::mem::transmute(&mut context.dom)});
 		#[cfg(feature="run-service")]
 		self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
 		Ok(Runnable{
diff --git a/lib/roblox_emulator/src/util.rs b/lib/roblox_emulator/src/util.rs
deleted file mode 100644
index 92ac42d..0000000
--- a/lib/roblox_emulator/src/util.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
-	rbx_dom_weak::ustr(s)
-}