diff --git a/Cargo.lock b/Cargo.lock index ef1216a023..eb2eb380c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1221,6 +1221,7 @@ name = "bones_schema" version = "0.3.0" dependencies = [ "append-only-vec", + "bevy_ptr", "bones_schema_macros", "bones_utils", "erased-serde", @@ -1262,7 +1263,6 @@ dependencies = [ name = "bones_utils" version = "0.3.0" dependencies = [ - "bevy_ptr", "bones_utils_macros", "branches", "futures-lite", diff --git a/demos/features/assets/menu.lua b/demos/features/assets/menu.lua index bf9c382a81..6a94d1e838 100644 --- a/demos/features/assets/menu.lua +++ b/demos/features/assets/menu.lua @@ -4,6 +4,6 @@ local menuData = world.resources:get(schema("MenuData")) menuData.frame = menuData.frame + 1 if menuData.frame % 30 == 0 then - info("Menu frame: ", menuData.frame) + info(menuData) end diff --git a/demos/scripting/assets/update.lua b/demos/scripting/assets/update.lua index a5c4b28a03..4b97d1a8b4 100644 --- a/demos/scripting/assets/update.lua +++ b/demos/scripting/assets/update.lua @@ -1,2 +1,3 @@ -local data = world.resources:get(s"AssetServer") -info(data) +local data = world.resources:get(s"DemoData") +data = s"DemoData":create() +info(data.age) diff --git a/framework_crates/bones_asset/src/asset.rs b/framework_crates/bones_asset/src/asset.rs index 0faa0241b5..ce622364ee 100644 --- a/framework_crates/bones_asset/src/asset.rs +++ b/framework_crates/bones_asset/src/asset.rs @@ -328,7 +328,7 @@ pub trait AssetLoader: Sync + Send + 'static { pub struct SchemaMetaAssetLoader( pub fn( ctx: &mut MetaAssetLoadCtx, - ptr: SchemaRefMut<'_, '_>, + ptr: SchemaRefMut<'_>, deserialzer: &mut dyn erased_serde::Deserializer, ) -> anyhow::Result<()>, ); @@ -338,7 +338,7 @@ impl SchemaMetaAssetLoader { pub fn load<'a, 'de, D>( &self, ctx: &mut MetaAssetLoadCtx, - ptr: SchemaRefMut<'a, 'a>, + ptr: SchemaRefMut<'a>, deserializer: D, ) -> Result<(), erased_serde::Error> where diff --git a/framework_crates/bones_asset/src/lib.rs b/framework_crates/bones_asset/src/lib.rs index 5325dc7cd2..698b96cc0c 100644 --- a/framework_crates/bones_asset/src/lib.rs +++ b/framework_crates/bones_asset/src/lib.rs @@ -72,7 +72,7 @@ impl From> for Maybe { fn maybe_loader( ctx: &mut MetaAssetLoadCtx, - ptr: SchemaRefMut<'_, '_>, + ptr: SchemaRefMut<'_>, deserialzer: &mut dyn erased_serde::Deserializer, ) -> anyhow::Result<()> { deserialzer.deserialize_option(MaybeVisitor { ctx, ptr })?; @@ -82,7 +82,7 @@ fn maybe_loader( struct MaybeVisitor<'a, 'srv> { ctx: &'a mut MetaAssetLoadCtx<'srv>, - ptr: SchemaRefMut<'a, 'a>, + ptr: SchemaRefMut<'a>, } impl<'a, 'srv, 'de> serde::de::Visitor<'de> for MaybeVisitor<'a, 'srv> { @@ -112,7 +112,7 @@ impl<'a, 'srv, 'de> serde::de::Visitor<'de> for MaybeVisitor<'a, 'srv> { // Write the enum discriminant for the `Set` variant // SOUND: we know the discriminant due to the `#[repr(C, u8)]` annotation. unsafe { - self.ptr.as_ptr().write(1); + self.ptr.as_ptr().cast::().write(1); } // Get the pointer to the enum value diff --git a/framework_crates/bones_asset/src/server.rs b/framework_crates/bones_asset/src/server.rs index d1fb18b8d2..6e8950f09d 100644 --- a/framework_crates/bones_asset/src/server.rs +++ b/framework_crates/bones_asset/src/server.rs @@ -34,8 +34,6 @@ pub struct AssetServer { pub inner: Arc, /// The [`AssetIo`] implementation used to load assets. pub io: Arc, - /// List of assets that have been changed and that we are waiting to re-load. - pub pending_asset_changes: Vec, } /// The inner state of the asset server. @@ -67,7 +65,6 @@ impl Default for AssetServer { Self { inner: default(), io: Arc::new(DummyIo::new([])), - pending_asset_changes: default(), } } } @@ -160,7 +157,6 @@ impl AssetServer { ..default() }), io: Arc::new(io), - pending_asset_changes: default(), } } @@ -217,11 +213,12 @@ impl AssetServer { &mut self, mut handle_change: F, ) { + let mut pending_asset_changes = Vec::new(); while let Ok(changed) = self.asset_change_recv.try_recv() { match changed { ChangedAsset::Loc(loc) => { let handle = self.load_asset_forced(loc.as_ref()); - self.pending_asset_changes.push(handle); + pending_asset_changes.push(handle); } ChangedAsset::Handle(handle) => { let entry = self @@ -233,13 +230,13 @@ impl AssetServer { let loc = entry.key().to_owned(); drop(entry); self.load_asset_forced(loc.as_ref()); - self.pending_asset_changes.push(handle); + pending_asset_changes.push(handle); } } } if self.load_progress.is_finished() { - for handle in std::mem::take(&mut self.pending_asset_changes) { + for handle in std::mem::take(&mut pending_asset_changes) { handle_change(self, handle) } } @@ -719,13 +716,16 @@ impl AssetServer { } /// Borrow a [`LoadedAsset`] associated to the given handle. - pub fn get_untyped(&self, handle: UntypedHandle) -> Option> { + pub fn get_asset_untyped(&self, handle: UntypedHandle) -> Option> { let cid = self.store.asset_ids.get(&handle)?; self.store.assets.get(&cid) } /// Borrow a [`LoadedAsset`] associated to the given handle. - pub fn get_untyped_mut(&self, handle: UntypedHandle) -> Option> { + pub fn get_asset_untyped_mut( + &self, + handle: UntypedHandle, + ) -> Option> { let cid = self.store.asset_ids.get(&handle)?; self.store.assets.get_mut(&cid) } @@ -745,6 +745,11 @@ impl AssetServer { self.get(self.core().root.typed()) } + /// Get the core asset pack's root asset as a type-erased [`SchemaBox`]. + pub fn untyped_root(&self) -> MappedMapRef { + self.get_untyped(self.core().root) + } + /// Read the loaded asset packs. pub fn packs(&self) -> &DashMap { &self.store.packs @@ -762,7 +767,29 @@ impl AssetServer { } /// Borrow a loaded asset. + /// + /// # Panics + /// + /// Panics if the asset is not loaded. + #[track_caller] + pub fn get_untyped(&self, handle: UntypedHandle) -> MappedMapRef { + self.try_get_untyped(handle).unwrap() + } + + /// Borrow a loaded asset. + /// + /// # Panics + /// + /// Panics if the asset is not loaded. #[track_caller] + pub fn get_untyped_mut( + &self, + handle: UntypedHandle, + ) -> MappedMapRefMut { + self.try_get_untyped_mut(handle).unwrap() + } + + /// Borrow a loaded asset. pub fn try_get( &self, handle: Handle, @@ -782,6 +809,29 @@ impl AssetServer { })) } + /// Borrow a loaded asset. + pub fn try_get_untyped( + &self, + handle: UntypedHandle, + ) -> Option> { + let cid = self.store.asset_ids.get(&handle)?; + Some(MapRef::map(self.store.assets.get(&cid).unwrap(), |x| { + &x.data + })) + } + + /// Borrow a loaded asset. + pub fn try_get_untyped_mut( + &self, + handle: UntypedHandle, + ) -> Option> { + let cid = self.store.asset_ids.get_mut(&handle)?; + Some(MapRefMut::map( + self.store.assets.get_mut(&cid).unwrap(), + |x| &mut x.data, + )) + } + /// Mutably borrow a loaded asset. /// /// # Panics @@ -868,14 +918,14 @@ mod metadata { } /// The load context for a [`SchemaRefMut`]. - pub struct SchemaPtrLoadCtx<'a, 'srv, 'ptr, 'prnt> { + pub struct SchemaPtrLoadCtx<'a, 'srv, 'ptr> { /// The metadata asset load context. pub ctx: &'a mut MetaAssetLoadCtx<'srv>, /// The pointer to load. - pub ptr: SchemaRefMut<'ptr, 'prnt>, + pub ptr: SchemaRefMut<'ptr>, } - impl<'a, 'srv, 'ptr, 'prnt, 'de> DeserializeSeed<'de> for SchemaPtrLoadCtx<'a, 'srv, 'ptr, 'prnt> { + impl<'a, 'srv, 'ptr, 'de> DeserializeSeed<'de> for SchemaPtrLoadCtx<'a, 'srv, 'ptr> { type Value = (); fn deserialize(mut self, deserializer: D) -> Result @@ -947,15 +997,11 @@ mod metadata { ptr: self.ptr, ctx: self.ctx, })?, - SchemaKind::Box(_) => { - // SOUND: schema asserts pointer is a SchemaBox. - let b = unsafe { self.ptr.deref_mut::() }; - SchemaPtrLoadCtx { - ctx: self.ctx, - ptr: b.as_mut(), - } - .deserialize(deserializer)? + SchemaKind::Box(_) => SchemaPtrLoadCtx { + ctx: self.ctx, + ptr: self.ptr.into_box().unwrap(), } + .deserialize(deserializer)?, SchemaKind::Primitive(p) => { match p { Primitive::Bool => *self.ptr.cast_mut() = bool::deserialize(deserializer)?, @@ -988,12 +1034,12 @@ mod metadata { } } - struct StructVisitor<'a, 'srv, 'ptr, 'prnt> { + struct StructVisitor<'a, 'srv, 'ptr> { ctx: &'a mut MetaAssetLoadCtx<'srv>, - ptr: SchemaRefMut<'ptr, 'prnt>, + ptr: SchemaRefMut<'ptr>, } - impl<'a, 'srv, 'ptr, 'prnt, 'de> Visitor<'de> for StructVisitor<'a, 'srv, 'ptr, 'prnt> { + impl<'a, 'srv, 'ptr, 'de> Visitor<'de> for StructVisitor<'a, 'srv, 'ptr> { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -1015,11 +1061,11 @@ mod metadata { let field_count = self.ptr.schema().kind.as_struct().unwrap().fields.len(); for i in 0..field_count { - let field = self.ptr.get_field(i).unwrap(); + let field = self.ptr.access_mut().field(i).unwrap(); if seq .next_element_seed(SchemaPtrLoadCtx { ctx: self.ctx, - ptr: field, + ptr: field.into_schema_ref_mut(), })? .is_none() { @@ -1035,11 +1081,11 @@ mod metadata { A: serde::de::MapAccess<'de>, { while let Some(key) = map.next_key::()? { - match self.ptr.get_field(&key) { + match self.ptr.access_mut().field(&key) { Ok(field) => { map.next_value_seed(SchemaPtrLoadCtx { ctx: self.ctx, - ptr: field, + ptr: field.into_schema_ref_mut(), })?; } Err(_) => { @@ -1069,12 +1115,12 @@ mod metadata { } } - struct VecVisitor<'a, 'srv, 'ptr, 'prnt> { + struct VecVisitor<'a, 'srv, 'ptr> { ctx: &'a mut MetaAssetLoadCtx<'srv>, - ptr: SchemaRefMut<'ptr, 'prnt>, + ptr: SchemaRefMut<'ptr>, } - impl<'a, 'srv, 'ptr, 'prnt, 'de> Visitor<'de> for VecVisitor<'a, 'srv, 'ptr, 'prnt> { + impl<'a, 'srv, 'ptr, 'de> Visitor<'de> for VecVisitor<'a, 'srv, 'ptr> { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -1112,12 +1158,12 @@ mod metadata { } } - struct MapVisitor<'a, 'srv, 'ptr, 'prnt> { + struct MapVisitor<'a, 'srv, 'ptr> { ctx: &'a mut MetaAssetLoadCtx<'srv>, - ptr: SchemaRefMut<'ptr, 'prnt>, + ptr: SchemaRefMut<'ptr>, } - impl<'a, 'srv, 'ptr, 'prnt, 'de> Visitor<'de> for MapVisitor<'a, 'srv, 'ptr, 'prnt> { + impl<'a, 'srv, 'ptr, 'de> Visitor<'de> for MapVisitor<'a, 'srv, 'ptr> { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -1159,12 +1205,12 @@ mod metadata { } } - struct EnumVisitor<'a, 'srv, 'ptr, 'prnt> { + struct EnumVisitor<'a, 'srv, 'ptr> { ctx: &'a mut MetaAssetLoadCtx<'srv>, - ptr: SchemaRefMut<'ptr, 'prnt>, + ptr: SchemaRefMut<'ptr>, } - impl<'a, 'srv, 'ptr, 'prnt, 'de> Visitor<'de> for EnumVisitor<'a, 'srv, 'ptr, 'prnt> { + impl<'a, 'srv, 'ptr, 'de> Visitor<'de> for EnumVisitor<'a, 'srv, 'ptr> { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -1227,12 +1273,12 @@ mod metadata { } } - struct EnumPtrLoadCtx<'ptr, 'prnt> { - ptr: SchemaRefMut<'ptr, 'prnt>, + struct EnumPtrLoadCtx<'ptr> { + ptr: SchemaRefMut<'ptr>, } - impl<'ptr, 'prnt, 'de> DeserializeSeed<'de> for EnumPtrLoadCtx<'ptr, 'prnt> { - type Value = SchemaRefMut<'ptr, 'prnt>; + impl<'ptr, 'de> DeserializeSeed<'de> for EnumPtrLoadCtx<'ptr> { + type Value = SchemaRefMut<'ptr>; fn deserialize(self, deserializer: D) -> Result where @@ -1261,7 +1307,7 @@ mod metadata { // Write the enum variant // SOUND: the schema asserts that the write to the enum discriminant is valid match enum_info.tag_type { - EnumTagType::U8 => unsafe { self.ptr.as_ptr().write(var_idx as u8) }, + EnumTagType::U8 => unsafe { self.ptr.as_ptr().cast::().write(var_idx as u8) }, EnumTagType::U16 => unsafe { self.ptr.as_ptr().cast::().write(var_idx as u16) }, diff --git a/framework_crates/bones_bevy_renderer/src/lib.rs b/framework_crates/bones_bevy_renderer/src/lib.rs index bcc53b9f66..dfe424bc52 100644 --- a/framework_crates/bones_bevy_renderer/src/lib.rs +++ b/framework_crates/bones_bevy_renderer/src/lib.rs @@ -803,7 +803,7 @@ fn step_bones_game(world: &mut World) { asset_server.handle_asset_changes(|asset_server, handle| { let mut bones_egui_textures = game.shared_resource_mut::().unwrap(); - let mut asset = asset_server.get_untyped_mut(handle).unwrap(); + let mut asset = asset_server.get_asset_untyped_mut(handle).unwrap(); // TODO: hot reload changed fonts. diff --git a/framework_crates/bones_ecs/src/components.rs b/framework_crates/bones_ecs/src/components.rs index 75e482d8de..cf54dfe139 100644 --- a/framework_crates/bones_ecs/src/components.rs +++ b/framework_crates/bones_ecs/src/components.rs @@ -12,7 +12,10 @@ pub use iterator::*; pub use typed::*; pub use untyped::*; -type AtomicComponentStore = Arc>>; +/// An atomic component store. +pub type AtomicComponentStore = Arc>>; +/// An untyped atomic component store. +pub type UntypedAtomicComponentStore = Arc>; /// A collection of [`ComponentStore`]. /// @@ -20,7 +23,7 @@ type AtomicComponentStore = Arc>>; /// initialized for that world. #[derive(Default)] pub struct ComponentStores { - pub(crate) components: HashMap>>, + pub(crate) components: HashMap, } /// An error returned when trying to access an uninitialized component. diff --git a/framework_crates/bones_ecs/src/components/iterator.rs b/framework_crates/bones_ecs/src/components/iterator.rs index 2731b8b150..c13a01e98e 100644 --- a/framework_crates/bones_ecs/src/components/iterator.rs +++ b/framework_crates/bones_ecs/src/components/iterator.rs @@ -9,7 +9,7 @@ pub type ComponentBitsetIterator<'a, T> = /// Mutable iterator over components matching a given bitset pub type ComponentBitsetIteratorMut<'a, T> = std::iter::Map< UntypedComponentBitsetIteratorMut<'a>, - for<'b> fn(SchemaRefMut<'b, 'b>) -> &'b mut T, + for<'b> fn(SchemaRefMut<'b>) -> &'b mut T, >; /// Iterates over components using a provided bitset. Each time the bitset has a 1 in index i, the @@ -34,10 +34,7 @@ impl<'a> Iterator for UntypedComponentBitsetIterator<'a> { // SAFE: Here we are just getting a pointer, not doing anything unsafe with it. Some(unsafe { SchemaRef::from_ptr_schema( - self.components - .storage - .unchecked_idx(self.current_id) - .as_ptr(), + self.components.storage.unchecked_idx(self.current_id), self.components.schema, ) }) @@ -58,7 +55,7 @@ pub struct UntypedComponentBitsetIteratorMut<'a> { } impl<'a> Iterator for UntypedComponentBitsetIteratorMut<'a> { - type Item = SchemaRefMut<'a, 'a>; + type Item = SchemaRefMut<'a>; fn next(&mut self) -> Option { let max_id = self.components.max_id; while !(self.bitset.bit_test(self.current_id) @@ -72,10 +69,7 @@ impl<'a> Iterator for UntypedComponentBitsetIteratorMut<'a> { // valid for the new lifetime. Some(unsafe { SchemaRefMut::from_ptr_schema( - self.components - .storage - .unchecked_idx_mut(self.current_id) - .as_ptr(), + self.components.storage.unchecked_idx(self.current_id), self.components.schema, ) }) diff --git a/framework_crates/bones_ecs/src/components/typed.rs b/framework_crates/bones_ecs/src/components/typed.rs index 8b56535e40..75e8bdf3ab 100644 --- a/framework_crates/bones_ecs/src/components/typed.rs +++ b/framework_crates/bones_ecs/src/components/typed.rs @@ -88,7 +88,9 @@ impl ComponentStore { std::array::from_fn(move |i| { // SOUND: we know that the schema matches. - result[i].take().map(|x| unsafe { x.deref_mut() }) + result[i] + .take() + .map(|x| unsafe { x.cast_into_mut_unchecked() }) }) } @@ -105,7 +107,9 @@ impl ComponentStore { #[inline] pub fn iter(&self) -> impl Iterator { // SOUND: we know the schema matches. - self.untyped.iter().map(|x| unsafe { x.deref() }) + self.untyped + .iter() + .map(|x| unsafe { x.cast_into_unchecked() }) } /// Iterates mutably over all components of this type. @@ -113,7 +117,9 @@ impl ComponentStore { #[inline] pub fn iter_mut(&mut self) -> impl Iterator { // SOUND: we know the schema matches. - self.untyped.iter_mut().map(|x| unsafe { x.deref_mut() }) + self.untyped + .iter_mut() + .map(|x| unsafe { x.cast_into_mut_unchecked() }) } /// Iterates immutably over the components of this type where `bitset` @@ -123,7 +129,7 @@ impl ComponentStore { pub fn iter_with_bitset(&self, bitset: Rc) -> ComponentBitsetIterator { // SOUND: we know the schema matches. fn map(r: SchemaRef) -> &T { - unsafe { r.deref() } + unsafe { r.cast_into_unchecked() } } self.untyped.iter_with_bitset(bitset).map(map) } @@ -134,8 +140,8 @@ impl ComponentStore { #[inline] pub fn iter_mut_with_bitset(&mut self, bitset: Rc) -> ComponentBitsetIteratorMut { // SOUND: we know the schema matches. - fn map<'a, T>(r: SchemaRefMut<'a, 'a>) -> &'a mut T { - unsafe { r.deref_mut() } + fn map(r: SchemaRefMut<'_>) -> &mut T { + unsafe { r.cast_into_mut_unchecked() } } self.untyped.iter_mut_with_bitset(bitset).map(map) diff --git a/framework_crates/bones_ecs/src/components/untyped.rs b/framework_crates/bones_ecs/src/components/untyped.rs index 65e1965866..130a2ca4fe 100644 --- a/framework_crates/bones_ecs/src/components/untyped.rs +++ b/framework_crates/bones_ecs/src/components/untyped.rs @@ -2,6 +2,7 @@ use crate::prelude::*; use bones_schema::alloc::ResizableAlloc; use std::{ + ffi::c_void, mem::MaybeUninit, ptr::{self}, rc::Rc, @@ -23,7 +24,7 @@ unsafe impl Send for UntypedComponentStore {} impl Clone for UntypedComponentStore { fn clone(&self) -> Self { let size = self.schema.layout().size(); - let mut new_storage = self.storage.clone(); + let new_storage = self.storage.clone(); for i in 0..self.max_id { if self.bitset.bit_test(i) { @@ -33,12 +34,9 @@ impl Clone for UntypedComponentStore { // - And our previous pointer is a valid pointer to component data // - And our new pointer is a writable pointer with the same layout unsafe { - let prev_ptr = self.storage.ptr().byte_add(i * size); - let new_ptr = new_storage.ptr_mut().byte_add(i * size); - (self.schema.clone_fn.expect("Cannot clone component"))( - prev_ptr.as_ptr(), - new_ptr.as_ptr(), - ); + let prev_ptr = self.storage.as_ptr().add(i * size); + let new_ptr = new_storage.as_ptr().add(i * size); + (self.schema.clone_fn.expect("Cannot clone component"))(prev_ptr, new_ptr); } } } @@ -62,8 +60,8 @@ impl Drop for UntypedComponentStore { // // And our pointer is valid. unsafe { - let ptr = self.storage.unchecked_idx_mut(i); - drop_fn(ptr.as_ptr()); + let ptr = self.storage.unchecked_idx(i); + drop_fn(ptr); } } } @@ -120,12 +118,12 @@ impl UntypedComponentStore { pub fn try_insert_box( &mut self, entity: Entity, - mut data: SchemaBox, + data: SchemaBox, ) -> Result, SchemaMismatchError> { if self.schema != data.schema() { Err(SchemaMismatchError) } else { - let ptr = data.as_mut().as_ptr(); + let ptr = data.as_ptr(); // SOUND: we validated schema matches let already_had_component = unsafe { self.insert_raw(entity, ptr) }; if already_had_component { @@ -159,7 +157,7 @@ impl UntypedComponentStore { if self.schema != T::schema() { Err(SchemaMismatchError) } else { - let ptr = &mut data as *mut T as *mut u8; + let ptr = &mut data as *mut T as *mut c_void; // SOUND: we validated schema matches let already_had_component = unsafe { self.insert_raw(entity, ptr) }; if already_had_component { @@ -180,16 +178,16 @@ impl UntypedComponentStore { /// # Safety /// - The data must be a pointer to memory with the same schema. /// - If `false` is returned you must ensure the `data` pointer is not used after pushing. - pub unsafe fn insert_raw(&mut self, entity: Entity, data: *mut u8) -> bool { + pub unsafe fn insert_raw(&mut self, entity: Entity, data: *mut c_void) -> bool { let index = entity.index() as usize; let size = self.schema.layout().size(); // If the component already exists on the entity if self.bitset.bit_test(entity.index() as usize) { - let ptr = self.storage.unchecked_idx_mut(index); + let ptr = self.storage.unchecked_idx(index); // Swap the data with the data already there - ptr::swap_nonoverlapping(ptr.as_ptr(), data, size); + ptr::swap_nonoverlapping(ptr, data, size); // There was already a component of this type true @@ -207,8 +205,7 @@ impl UntypedComponentStore { // Copy the data from the data pointer into our storage self.storage - .unchecked_idx_mut(index) - .as_ptr() + .unchecked_idx(index) .copy_from_nonoverlapping(data, size); // There was not already a component of this type @@ -264,7 +261,7 @@ impl UntypedComponentStore { // SOUND: we ensure that there is allocated storge for entities that have their bit set. let ptr = unsafe { self.storage.unchecked_idx(idx) }; // SOUND: we know that the pointer has our schema. - Some(unsafe { SchemaRef::from_ptr_schema(ptr.as_ptr(), self.schema) }) + Some(unsafe { SchemaRef::from_ptr_schema(ptr, self.schema) }) } else { None } @@ -293,18 +290,18 @@ impl UntypedComponentStore { /// Get a [`SchemaRefMut`] to the component for the given [`Entity`] #[inline] - pub fn get_ref_mut<'a>(&mut self, entity: Entity) -> Option> { + pub fn get_ref_mut<'a>(&mut self, entity: Entity) -> Option> { let idx = entity.index() as usize; self.get_idx_mut(idx) } - fn get_idx_mut<'a>(&mut self, idx: usize) -> Option> { + fn get_idx_mut<'a>(&mut self, idx: usize) -> Option> { if self.bitset.bit_test(idx) { // SOUND: we ensure that there is allocated storage for entities that have their bit // set. - let ptr = unsafe { self.storage.unchecked_idx_mut(idx) }; + let ptr = unsafe { self.storage.unchecked_idx(idx) }; // SOUND: we know that the pointer has our schema. - Some(unsafe { SchemaRefMut::from_ptr_schema(ptr.as_ptr(), self.schema) }) + Some(unsafe { SchemaRefMut::from_ptr_schema(ptr, self.schema) }) } else { None } @@ -350,7 +347,7 @@ impl UntypedComponentStore { let refs = std::array::from_fn(|i| { let r = refs[i].take(); // SOUND: we've validated the schema matches. - r.map(|r| unsafe { r.deref_mut() }) + r.map(|r| unsafe { r.cast_into_mut_unchecked() }) }); Ok(refs) @@ -390,8 +387,8 @@ impl UntypedComponentStore { // The new lifetime is sound because we validate that all of these borrows don't // overlap and their lifetimes are that of the &mut self borrow. unsafe { - let ptr = self.storage.unchecked_idx_mut(index); - Some(SchemaRefMut::from_ptr_schema(ptr.as_ptr(), self.schema)) + let ptr = self.storage.unchecked_idx(index); + Some(SchemaRefMut::from_ptr_schema(ptr, self.schema)) } } else { None @@ -420,7 +417,7 @@ impl UntypedComponentStore { } else if self.bitset.contains(entity) { let mut data = MaybeUninit::::uninit(); // SOUND: the data doesn't overlap the storage. - unsafe { self.remove_raw(entity, Some(data.as_mut_ptr() as *mut u8)) }; + unsafe { self.remove_raw(entity, Some(data.as_mut_ptr() as *mut c_void)) }; // SOUND: we've initialized the data. Ok(Some(unsafe { data.assume_init() })) @@ -435,9 +432,9 @@ impl UntypedComponentStore { pub fn remove_box(&mut self, entity: Entity) -> Option { if self.bitset.contains(entity) { // SOUND: we will immediately initialize the schema box with data matching the schema. - let mut b = unsafe { SchemaBox::uninitialized(self.schema) }; + let b = unsafe { SchemaBox::uninitialized(self.schema) }; // SOUND: the box data doesn't overlap the storage. - unsafe { self.remove_raw(entity, Some(b.as_mut().as_ptr())) }; + unsafe { self.remove_raw(entity, Some(b.as_ptr())) }; Some(b) } else { // SOUND: we don't use the out pointer. @@ -453,14 +450,14 @@ impl UntypedComponentStore { /// # Safety /// /// If set, the `out` pointer, must not overlap the internal component storage. - pub unsafe fn remove_raw(&mut self, entity: Entity, out: Option<*mut u8>) -> bool { + pub unsafe fn remove_raw(&mut self, entity: Entity, out: Option<*mut c_void>) -> bool { let index = entity.index() as usize; let size = self.schema.layout().size(); if self.bitset.bit_test(index) { self.bitset.bit_reset(index); - let ptr = self.storage.unchecked_idx_mut(index).as_ptr(); + let ptr = self.storage.unchecked_idx(index); if let Some(out) = out { // SAFE: user asserts `out` is non-overlapping @@ -578,7 +575,7 @@ pub struct UntypedComponentStoreIterMut<'a> { idx: usize, } impl<'a> Iterator for UntypedComponentStoreIterMut<'a> { - type Item = SchemaRefMut<'a, 'a>; + type Item = SchemaRefMut<'a>; fn next(&mut self) -> Option { loop { if self.idx < self.store.max_id { diff --git a/framework_crates/bones_ecs/src/resources.rs b/framework_crates/bones_ecs/src/resources.rs index 80dc4fa14b..a96ed9135c 100644 --- a/framework_crates/bones_ecs/src/resources.rs +++ b/framework_crates/bones_ecs/src/resources.rs @@ -51,7 +51,7 @@ impl UntypedAtomicResource { pub fn borrow(&self) -> AtomicSchemaRef { let (reference, borrow) = Ref::into_split(self.cell.borrow()); // SOUND: we keep the borrow along with the reference so that the pointer remains valid. - let schema_ref = NoClone(unsafe { reference.as_ref() }.as_ref()); + let schema_ref = unsafe { reference.as_ref() }.as_ref(); AtomicSchemaRef { schema_ref, borrow } } @@ -77,35 +77,33 @@ impl UntypedAtomicResource { } } -/// Wrapper type that prevents cloning or copying the inner type. -#[derive(Deref, DerefMut)] -pub struct NoClone(T); - /// An atomic borrow of a [`SchemaRef`]. -#[derive(Deref)] pub struct AtomicSchemaRef<'a> { - /// This is wrappwed in a [`NoClone`] because of the limitations of the deref trait. - /// We would prefer to have deref return a [`SchemaRef`] with an appropriate lifetime, - /// that indicates it borrows from the [`AtomicSchemaRef`], but since deref must return - /// a normal reference, instead we return a reference to the `SchemaRef` wrapped inside - /// a [`NoClone`] to prevent copying the [`SchemaRef`] out of dereference with a lifetime - /// that outlives the atomicborrow. - #[deref] - schema_ref: NoClone>, + schema_ref: SchemaRef<'a>, borrow: AtomicBorrow<'a>, } impl<'a> AtomicSchemaRef<'a> { + /// Get a [`SchemaRef`] that points to the inner value. + /// + /// > **Note:** Ideally this method would be unnecessary, but it is impossible to properly + /// implement [`Deref`][std::ops::Deref] because deref must return a reference and we actually + /// need to return a [`SchemaRef`] with a shortened lifetime, binding it to this + /// [`AtomicSchemaRef`] borrow. + pub fn as_ref(&self) -> SchemaRef<'_> { + self.schema_ref + } + /// # Safety /// You must know that T represents the data in the [`SchemaRef`]. pub unsafe fn deref(self) -> Ref<'a, T> { - Ref::with_borrow(self.schema_ref.deref(), self.borrow) + Ref::with_borrow(self.schema_ref.cast_into_unchecked(), self.borrow) } /// Convert into typed [`Ref`]. This panics if the schema doesn't match. #[track_caller] pub fn typed(self) -> Ref<'a, T> { - assert_eq!(T::schema(), self.schema(), "Schema mismatch"); + assert_eq!(T::schema(), self.schema_ref.schema(), "Schema mismatch"); // SOUND: we've checked for matching schema. unsafe { self.deref() } } @@ -115,7 +113,7 @@ impl<'a> AtomicSchemaRef<'a> { #[derive(Deref, DerefMut)] pub struct AtomicSchemaRefMut<'a> { #[deref] - schema_ref: SchemaRefMut<'a, 'a>, + schema_ref: SchemaRefMut<'a>, borrow: AtomicBorrowMut<'a>, } @@ -123,7 +121,7 @@ impl<'a> AtomicSchemaRefMut<'a> { /// # Safety /// You must know that T represents the data in the [`SchemaRefMut`]. pub unsafe fn deref_mut(self) -> RefMut<'a, T> { - RefMut::with_borrow(self.schema_ref.deref_mut(), self.borrow) + RefMut::with_borrow(self.schema_ref.cast_into_mut_unchecked(), self.borrow) } /// Convert into typed [`RefMut`]. This panics if the schema doesn't match. @@ -425,6 +423,6 @@ mod test { // Validate that data1 is unchanged let data1_cell = store.get_cell(data1_schema.id()).unwrap(); let data1_borrow = data1_cell.borrow(); - assert_eq!(data1_borrow.cast::(), &0); + assert_eq!(data1_borrow.as_ref().cast::(), &0); } } diff --git a/framework_crates/bones_schema/Cargo.toml b/framework_crates/bones_schema/Cargo.toml index 202251f24e..8efefe3d77 100644 --- a/framework_crates/bones_schema/Cargo.toml +++ b/framework_crates/bones_schema/Cargo.toml @@ -30,6 +30,7 @@ glam = { version = "0.24", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } erased-serde = { version = "0.3.31", optional = true } humantime = { version = "2.1", optional = true } +bevy_ptr = "0.11.3" [[test]] name = "tests" diff --git a/framework_crates/bones_schema/src/alloc/map.rs b/framework_crates/bones_schema/src/alloc/map.rs index 4c0f06878c..d923e3cd6b 100644 --- a/framework_crates/bones_schema/src/alloc/map.rs +++ b/framework_crates/bones_schema/src/alloc/map.rs @@ -160,7 +160,7 @@ impl SchemaMap { // SOUND: we've veriried that they key schema matches the map's let value = unsafe { self.get_ref_unchecked(SchemaRef::new(key)) } // SOUND: we've verified that the value schema maches the map's - .map(|x| unsafe { x.deref() }); + .map(|x| unsafe { x.cast_into_unchecked() }); Ok(value) } @@ -290,7 +290,7 @@ impl SchemaMap { // SOUND: we've checked that the key schema matches. let value = unsafe { self.get_ref_unchecked_mut(SchemaRef::new(key)) } // SOUND: we've checked that the value schema matches. - .map(|x| unsafe { x.deref_mut() }); + .map(|x| unsafe { x.cast_into_mut_unchecked() }); Ok(value) } } @@ -428,7 +428,7 @@ type SchemaMapIter<'iter> = std::iter::Map< >; type SchemaMapIterMut<'iter> = std::iter::Map< hash_map::IterMut<'iter, SchemaBox, SchemaBox>, - for<'a> fn((&'a SchemaBox, &'a mut SchemaBox)) -> (SchemaRef<'a>, SchemaRefMut<'a, 'a>), + for<'a> fn((&'a SchemaBox, &'a mut SchemaBox)) -> (SchemaRef<'a>, SchemaRefMut<'a>), >; impl SchemaMap { /// Iterate over entries in the map. @@ -447,7 +447,7 @@ impl SchemaMap { pub fn iter_mut(&mut self) -> SchemaMapIterMut { fn map_fn<'a>( (key, value): (&'a SchemaBox, &'a mut SchemaBox), - ) -> (SchemaRef<'a>, SchemaRefMut<'a, 'a>) { + ) -> (SchemaRef<'a>, SchemaRefMut<'a>) { (key.as_ref(), value.as_mut()) } self.map.iter_mut().map(map_fn) @@ -487,7 +487,7 @@ impl SchemaMap { &mut self, ) -> std::iter::Map< hash_map::ValuesMut, - for<'a> fn(&'a mut SchemaBox) -> SchemaRefMut<'a, 'a>, + for<'a> fn(&'a mut SchemaBox) -> SchemaRefMut<'a>, > { fn map_fn(key: &mut SchemaBox) -> SchemaRefMut { key.as_mut() @@ -503,7 +503,7 @@ impl<'a> IntoIterator for &'a SchemaMap { } } impl<'a> IntoIterator for &'a mut SchemaMap { - type Item = (SchemaRef<'a>, SchemaRefMut<'a, 'a>); + type Item = (SchemaRef<'a>, SchemaRefMut<'a>); type IntoIter = SchemaMapIterMut<'a>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() @@ -602,7 +602,7 @@ impl SMap { unsafe { self.map .get_ref_unchecked(SchemaRef::new(key)) - .map(|x| x.deref()) + .map(|x| x.cast_into_unchecked()) } } @@ -612,7 +612,7 @@ impl SMap { unsafe { self.map .get_ref_unchecked_mut(SchemaRef::new(key)) - .map(|x| x.deref_mut()) + .map(|x| x.cast_into_mut_unchecked()) } } @@ -646,7 +646,12 @@ impl SMap { pub fn iter(&self) -> SMapIter { fn map_fn<'a, K, V>((key, value): (&'a SchemaBox, &'a SchemaBox)) -> (&K, &V) { // SOUND: SMap ensures K and V schemas always match. - unsafe { (key.as_ref().deref(), value.as_ref().deref()) } + unsafe { + ( + key.as_ref().cast_into_unchecked(), + value.as_ref().cast_into_unchecked(), + ) + } } self.map.map.iter().map(map_fn) } @@ -658,7 +663,12 @@ impl SMap { (key, value): (&'a SchemaBox, &'a mut SchemaBox), ) -> (&'a K, &'a mut V) { // SOUND: SMap ensures K and V schemas always match. - unsafe { (key.as_ref().deref(), value.as_mut().deref_mut()) } + unsafe { + ( + key.as_ref().cast_into_unchecked(), + value.as_mut().cast_into_mut_unchecked(), + ) + } } self.map.map.iter_mut().map(map_fn) } @@ -671,7 +681,7 @@ impl SMap { { fn map_fn(key: &SchemaBox) -> &K { // SOUND: SMap ensures key schema always match - unsafe { key.as_ref().deref() } + unsafe { key.as_ref().cast_into_unchecked() } } self.map.map.keys().map(map_fn) } @@ -684,7 +694,7 @@ impl SMap { { fn map_fn(value: &SchemaBox) -> &V { // SOUND: SMap ensures value schema always matches. - unsafe { value.as_ref().deref() } + unsafe { value.as_ref().cast_into_unchecked() } } self.map.map.values().map(map_fn) } @@ -699,7 +709,7 @@ impl SMap { > { fn map_fn(value: &mut SchemaBox) -> &mut V { // SOUND: SMap ensures value schema always matches - unsafe { value.as_mut().deref_mut() } + unsafe { value.as_mut().cast_into_mut_unchecked() } } self.map.map.values_mut().map(map_fn) } diff --git a/framework_crates/bones_schema/src/alloc/resizable.rs b/framework_crates/bones_schema/src/alloc/resizable.rs index 305459c985..b7d7cc1f53 100644 --- a/framework_crates/bones_schema/src/alloc/resizable.rs +++ b/framework_crates/bones_schema/src/alloc/resizable.rs @@ -1,10 +1,9 @@ use std::{ alloc::{self, handle_alloc_error, Layout, LayoutError}, + ffi::c_void, ptr::NonNull, }; -use bones_utils::prelude::*; - use super::layout::*; /// A low-level memory allocation utility for creating a resizable buffer of elements of a specific @@ -17,7 +16,7 @@ use super::layout::*; pub struct ResizableAlloc { /// The pointer to the allocation. May be dangling for a capacity of zero or for a zero-sized /// layout. - ptr: NonNull, + ptr: NonNull, /// The layout of the items stored layout: Layout, /// The layout of the items stored, with it's size padded to its alignment. @@ -105,7 +104,7 @@ impl ResizableAlloc { let old_alloc_layout = self.layout.repeat(old_capacity)?.0; // Deallocate the old memory - unsafe { alloc::dealloc(self.ptr.as_ptr(), old_alloc_layout) } + unsafe { alloc::dealloc(self.ptr.as_ptr() as *mut u8, old_alloc_layout) } } // Update our pointer to be dangling. @@ -118,14 +117,18 @@ impl ResizableAlloc { let old_alloc_layout = self.layout.repeat(old_capacity).unwrap().0; let new_alloc_layout = self.layout.repeat(new_capacity).unwrap().0; self.ptr = NonNull::new(unsafe { - alloc::realloc(self.ptr.as_ptr(), old_alloc_layout, new_alloc_layout.size()) + alloc::realloc( + self.ptr.as_ptr() as *mut u8, + old_alloc_layout, + new_alloc_layout.size(), + ) as *mut c_void }) .unwrap_or_else(|| handle_alloc_error(new_alloc_layout)); // If we need to allocate new memory } else { let alloc_layout = self.layout.repeat(new_capacity).unwrap().0; - self.ptr = NonNull::new(unsafe { alloc::alloc(alloc_layout) }) + self.ptr = NonNull::new(unsafe { alloc::alloc(alloc_layout) } as *mut c_void) .unwrap_or_else(|| handle_alloc_error(alloc_layout)); } } @@ -145,16 +148,9 @@ impl ResizableAlloc { self.cap } - /// Get a mutable pointer to the allocation - #[inline] - pub fn ptr_mut(&mut self) -> PtrMut<'_> { - unsafe { PtrMut::new(self.ptr) } - } - - /// Get a read-only pointer to the allocation - #[inline] - pub fn ptr(&self) -> Ptr<'_> { - unsafe { Ptr::new(self.ptr) } + /// Get a raw pointer to the allocation. + pub fn as_ptr(&self) -> *mut c_void { + self.ptr.as_ptr() } /// Iterate over the allocation. @@ -174,32 +170,16 @@ impl ResizableAlloc { } /// Get a pointer to the item with the given index without performing any bounds checks. - /// - /// # Safety - /// - /// This does no checks that the index is within bounds. - #[inline] - pub unsafe fn unchecked_idx_mut(&mut self, idx: usize) -> PtrMut<'_> { - PtrMut::new(NonNull::new_unchecked( - self.ptr.as_ptr().add(self.padded.size() * idx), - )) - } - - /// Get a pointer to the item with the given index without performing any bounds checks. - /// /// # Safety - /// - /// This does no checks that the index is within bounds. + /// This does no checks that the index is within bounds or that the returne dpointer is unaliased. #[inline] - pub unsafe fn unchecked_idx(&self, idx: usize) -> Ptr<'_> { - Ptr::new(NonNull::new_unchecked( - self.ptr.as_ptr().add(self.padded.size() * idx), - )) + pub unsafe fn unchecked_idx(&self, idx: usize) -> *mut c_void { + self.ptr.as_ptr().add(self.padded.size() * idx) } /// Helper to create a dangling pointer that is properly aligned for our layout. #[inline] - fn dangling(layout: &Layout) -> NonNull { + fn dangling(layout: &Layout) -> NonNull { // SOUND: the layout ensures a non-zero alignment. unsafe { NonNull::new_unchecked(sptr::invalid_mut(layout.align())) } } @@ -208,7 +188,12 @@ impl ResizableAlloc { impl Drop for ResizableAlloc { fn drop(&mut self) { if self.cap > 0 && self.layout.size() > 0 { - unsafe { alloc::dealloc(self.ptr.as_ptr(), self.layout.repeat(self.cap).unwrap().0) } + unsafe { + alloc::dealloc( + self.ptr.as_ptr() as *mut u8, + self.layout.repeat(self.cap).unwrap().0, + ) + } } } } @@ -219,7 +204,7 @@ pub struct ResizableAllocIter<'a> { idx: usize, } impl<'a> Iterator for ResizableAllocIter<'a> { - type Item = Ptr<'a>; + type Item = *const c_void; fn next(&mut self) -> Option { if self.idx < self.alloc.cap { @@ -239,13 +224,13 @@ pub struct ResizableAllocIterMut<'a> { idx: usize, } impl<'a> Iterator for ResizableAllocIterMut<'a> { - type Item = PtrMut<'a>; + type Item = *mut c_void; fn next(&mut self) -> Option { if self.idx < self.alloc.cap { // SOUND: we've checked that it is within bounds, and we know that the pointer will be // valid for the new lifetime. - let r = unsafe { self.alloc.unchecked_idx_mut(self.idx).transmute_lifetime() }; + let r = unsafe { self.alloc.unchecked_idx(self.idx) }; self.idx += 1; Some(r) } else { @@ -299,17 +284,13 @@ mod test { // We write some data. for i in 0..3 { unsafe { - a.ptr_mut() - .as_ptr() - .cast::() - .add(i) - .write((i as _, i as _)); + a.as_ptr().cast::().add(i).write((i as _, i as _)); } } unsafe { - assert_eq!((0, 0), (a.ptr_mut().as_ptr() as *mut Ty).read()); - assert_eq!((1, 1), (a.ptr_mut().as_ptr() as *mut Ty).add(1).read()); - assert_eq!((2, 2), (a.ptr_mut().as_ptr() as *mut Ty).add(2).read()); + assert_eq!((0, 0), (a.as_ptr() as *mut Ty).read()); + assert_eq!((1, 1), (a.as_ptr() as *mut Ty).add(1).read()); + assert_eq!((2, 2), (a.as_ptr() as *mut Ty).add(2).read()); } // We can grow the allocation by resizing @@ -317,21 +298,21 @@ mod test { // And write to the new data unsafe { - a.ptr_mut().as_ptr().cast::().add(3).write((3, 3)); + a.as_ptr().cast::().add(3).write((3, 3)); // The previous values will be there - assert_eq!((0, 0), (a.ptr_mut().as_ptr() as *mut Ty).read()); - assert_eq!((1, 1), (a.ptr_mut().as_ptr() as *mut Ty).add(1).read()); - assert_eq!((2, 2), (a.ptr_mut().as_ptr() as *mut Ty).add(2).read()); + assert_eq!((0, 0), (a.as_ptr() as *mut Ty).read()); + assert_eq!((1, 1), (a.as_ptr() as *mut Ty).add(1).read()); + assert_eq!((2, 2), (a.as_ptr() as *mut Ty).add(2).read()); // As well as the new one - assert_eq!((3, 3), (a.ptr_mut().as_ptr() as *mut Ty).add(3).read()); + assert_eq!((3, 3), (a.as_ptr() as *mut Ty).add(3).read()); } // We can shrink the allocation, too, which will delete the items at the end without dropping them, keeping the // items at the beginning. a.resize(1).unwrap(); unsafe { - assert_eq!((0, 0), (a.ptr_mut().as_ptr() as *mut Ty).read()); + assert_eq!((0, 0), (a.as_ptr() as *mut Ty).read()); } // And we can delete all the items by resizing to zero ( again, this doesn't drop item, just @@ -339,6 +320,6 @@ mod test { a.resize(0).unwrap(); // Now the pointer will be dangling, but aligned to our layout - assert_eq!(a.ptr_mut().as_ptr() as usize, layout.align()); + assert_eq!(a.as_ptr() as usize, layout.align()); } } diff --git a/framework_crates/bones_schema/src/alloc/vec.rs b/framework_crates/bones_schema/src/alloc/vec.rs index c848d76636..fdad026e5f 100644 --- a/framework_crates/bones_schema/src/alloc/vec.rs +++ b/framework_crates/bones_schema/src/alloc/vec.rs @@ -1,5 +1,6 @@ use std::{ any::{type_name, TypeId}, + ffi::c_void, fmt::Debug, iter::Iterator, marker::PhantomData, @@ -61,7 +62,7 @@ impl SchemaVec { /// # Safety /// - The item must be a pointer to data with the same schema. /// - You must ensure the `item` pointer is not used after pusing. - unsafe fn push_raw(&mut self, item: *mut u8) { + unsafe fn push_raw(&mut self, item: *mut c_void) { // Make room for more elements if necessary if self.len == self.buffer.capacity() { self.grow(); @@ -70,8 +71,7 @@ impl SchemaVec { // Copy the item into the vec unsafe { self.buffer - .unchecked_idx_mut(self.len) - .as_ptr() + .unchecked_idx(self.len) .copy_from_nonoverlapping(item, self.buffer.layout().size()); } @@ -90,7 +90,7 @@ impl SchemaVec { } unsafe { - self.push_raw(&mut item as *mut T as *mut u8); + self.push_raw(&mut item as *mut T as *mut c_void); std::mem::forget(item); } @@ -156,7 +156,7 @@ impl SchemaVec { let ret = unsafe { self.raw_pop() }.map(|ptr| { let mut data = MaybeUninit::::uninit(); unsafe { - (data.as_mut_ptr() as *mut u8) + (data.as_mut_ptr() as *mut c_void) .copy_from_nonoverlapping(ptr, self.buffer.layout().size()); data.assume_init() } @@ -168,7 +168,7 @@ impl SchemaVec { /// # Safety /// The pointer may only be used immediately after calling raw_pop to read the data out of the /// popped item. Any further mutations to the vector may make the pointer invalid. - unsafe fn raw_pop(&mut self) -> Option<*mut u8> { + unsafe fn raw_pop(&mut self) -> Option<*mut c_void> { if self.len == 0 { None } else { @@ -176,7 +176,7 @@ impl SchemaVec { self.len -= 1; // Return the pointer to the item that is being popped off. - Some(unsafe { self.buffer.unchecked_idx_mut(self.len).as_ptr() }) + Some(unsafe { self.buffer.unchecked_idx(self.len) }) } } @@ -211,8 +211,7 @@ impl SchemaVec { None } else { let ptr = unsafe { self.buffer.unchecked_idx(idx) }; - - unsafe { Some(SchemaRef::from_ptr_schema(ptr.as_ptr(), self.schema)) } + unsafe { Some(SchemaRef::from_ptr_schema(ptr, self.schema)) } } } @@ -240,13 +239,12 @@ impl SchemaVec { } /// Get an item with the given index. - pub fn get_ref_mut(&mut self, idx: usize) -> Option> { + pub fn get_ref_mut(&mut self, idx: usize) -> Option> { if idx >= self.len { None } else { let ptr = unsafe { self.buffer.unchecked_idx(idx) }; - - unsafe { Some(SchemaRefMut::from_ptr_schema(ptr.as_ptr(), self.schema)) } + unsafe { Some(SchemaRefMut::from_ptr_schema(ptr, self.schema)) } } } @@ -317,7 +315,7 @@ impl SchemaVec { }; let mut hasher = FxHasher::default(); for item_ptr in self.buffer.iter() { - let item_hash = unsafe { (hash_fn)(item_ptr.as_ptr()) }; + let item_hash = unsafe { (hash_fn)(item_ptr) }; item_hash.hash(&mut hasher); } hasher.finish() @@ -326,7 +324,7 @@ impl SchemaVec { /// Raw version of the [`hash()`][Self::hash] function. Not meant for normal use. /// # Safety /// Pointer must be a valid pointer to a [`SchemaVec`]. - pub unsafe extern "C-unwind" fn raw_hash(ptr: *const u8) -> u64 { + pub unsafe extern "C-unwind" fn raw_hash(ptr: *const c_void) -> u64 { let this = unsafe { &*(ptr as *const Self) }; this.hash() } @@ -334,7 +332,7 @@ impl SchemaVec { /// Raw version of the [`eq()`][PartialEq::eq] function. Not meant for normal use. /// # Safety /// Pointers must be valid pointers to [`SchemaVec`]s. - pub unsafe extern "C-unwind" fn raw_eq(a: *const u8, b: *const u8) -> bool { + pub unsafe extern "C-unwind" fn raw_eq(a: *const c_void, b: *const c_void) -> bool { let a = &*(a as *const Self); let b = &*(b as *const Self); a.eq(b) @@ -359,7 +357,7 @@ impl<'a> IntoIterator for &'a SchemaVec { } } impl<'a> IntoIterator for &'a mut SchemaVec { - type Item = SchemaRefMut<'a, 'a>; + type Item = SchemaRefMut<'a>; type IntoIter = SchemaVecIterMut<'a>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() @@ -389,7 +387,7 @@ pub struct SchemaVecIterMut<'a> { idx: usize, } impl<'a> Iterator for SchemaVecIterMut<'a> { - type Item = SchemaRefMut<'a, 'a>; + type Item = SchemaRefMut<'a>; fn next(&mut self) -> Option { let item = self @@ -418,8 +416,8 @@ impl PartialEq for SchemaVec { for i in 0..self.len { unsafe { - let a = self.buffer.unchecked_idx(i).as_ptr(); - let b = self.buffer.unchecked_idx(i).as_ptr(); + let a = self.buffer.unchecked_idx(i); + let b = self.buffer.unchecked_idx(i); if !(eq_fn)(a, b) { return false; } @@ -442,8 +440,8 @@ impl Clone for SchemaVec { // SOUND: we've check that the index is within bounds, and the schema asserts the // validity of the clone function. unsafe { - let item = self.buffer.unchecked_idx(i).as_ptr() as *const u8; - (clone_fn)(item, buffer_clone.unchecked_idx_mut(i).as_ptr()); + let item = self.buffer.unchecked_idx(i); + (clone_fn)(item, buffer_clone.unchecked_idx(i)); } } @@ -499,7 +497,7 @@ impl SVec { pub fn push(&mut self, mut item: T) { // SOUND: We know that the schema matches, and we forget the item after pushing. unsafe { - self.vec.push_raw(&mut item as *mut T as *mut u8); + self.vec.push_raw(&mut item as *mut T as *mut c_void); } std::mem::forget(item); } @@ -519,13 +517,17 @@ impl SVec { /// Get an item from the vec. pub fn get(&self, idx: usize) -> Option<&T> { // SOUND: We know that the pointer is to a type T - self.vec.get_ref(idx).map(|x| unsafe { x.deref() }) + self.vec + .get_ref(idx) + .map(|x| unsafe { x.cast_into_unchecked() }) } /// Get an item from the vec. pub fn get_mut(&mut self, idx: usize) -> Option<&mut T> { // SOUND: We know that the pointer is to a type T - self.vec.get_ref_mut(idx).map(|x| unsafe { x.deref_mut() }) + self.vec + .get_ref_mut(idx) + .map(|x| unsafe { x.cast_into_mut_unchecked() }) } /// Iterate over references to the items in the vec. @@ -597,18 +599,14 @@ impl std::ops::Deref for SVec { fn deref(&self) -> &Self::Target { // SOUND: we know that the schema matches T, and the internal buffer of a SchemaVec stores // the types contiguously in memory. - unsafe { - std::slice::from_raw_parts(self.vec.buffer.ptr().as_ptr() as *const T, self.len()) - } + unsafe { std::slice::from_raw_parts(self.vec.buffer.as_ptr() as *const T, self.len()) } } } impl std::ops::DerefMut for SVec { fn deref_mut(&mut self) -> &mut Self::Target { // SOUND: we know that the schema matches T, and the internal buffer of a SchemaVec stores // the types contiguously in memory. - unsafe { - std::slice::from_raw_parts_mut(self.vec.buffer.ptr().as_ptr() as *mut T, self.len()) - } + unsafe { std::slice::from_raw_parts_mut(self.vec.buffer.as_ptr() as *mut T, self.len()) } } } diff --git a/framework_crates/bones_schema/src/ptr.rs b/framework_crates/bones_schema/src/ptr.rs index fc1b8eacdd..d96cc94b14 100644 --- a/framework_crates/bones_schema/src/ptr.rs +++ b/framework_crates/bones_schema/src/ptr.rs @@ -3,6 +3,7 @@ use std::{ alloc::handle_alloc_error, any::{type_name, TypeId}, + ffi::c_void, hash::Hash, iter::{Filter, Map}, marker::PhantomData, @@ -22,11 +23,26 @@ use bones_utils::{parking_lot::RwLock, prelude::*}; /// type. #[derive(Clone, Copy)] pub struct SchemaRef<'pointer> { - ptr: Ptr<'pointer>, + ptr: NonNull, schema: &'static Schema, + _phantom: PhantomData<&'pointer ()>, } impl<'pointer> SchemaRef<'pointer> { + /// Unsafely cast this pointer to a specifc Rust type. + /// # Safety + /// All of the safety requirements of [`NonNull::as_ref()`] must be met. + pub unsafe fn cast_unchecked(&self) -> &T { + self.ptr.cast::().as_ref() + } + + /// Unsafely cast this pointer to a specifc Rust type. + /// # Safety + /// All of the safety requirements of [`NonNull::as_ref()`] must be met. + pub unsafe fn cast_into_unchecked(self) -> &'pointer T { + self.ptr.cast::().as_ref() + } + /// Cast this pointer to a reference to a type with a matching [`Schema`]. /// /// # Panics @@ -45,7 +61,7 @@ impl<'pointer> SchemaRef<'pointer> { pub fn try_cast(&self) -> Result<&'pointer T, SchemaMismatchError> { if self.schema.represents(T::schema()) { // SOUND: the schemas have the same memory representation. - Ok(unsafe { self.ptr.deref() }) + Ok(unsafe { self.cast_into_unchecked() }) } else { Err(SchemaMismatchError) } @@ -55,8 +71,10 @@ impl<'pointer> SchemaRef<'pointer> { pub fn new(v: &'pointer T) -> SchemaRef<'pointer> { let schema = T::schema(); SchemaRef { - ptr: v.into(), + /// SOUND: The &T passed in cannot be null. + ptr: unsafe { NonNull::new_unchecked(v as *const T as *mut c_void) }, schema, + _phantom: PhantomData, } } @@ -71,119 +89,19 @@ impl<'pointer> SchemaRef<'pointer> { /// - The lifetime `'a` must be constrained such that this [`PtrMut`] will stay valid and /// nothing else can read or mutate the pointee while this [`PtrMut`] is live. #[track_caller] - pub unsafe fn from_ptr_schema(ptr: *const u8, schema: &'static Schema) -> Self { + pub unsafe fn from_ptr_schema(ptr: *const c_void, schema: &'static Schema) -> Self { Self { - // SOUND: casting the `*const u8` to a `*mut u8` is dangerous but sound in this case - // because we are passing the `NonNull` to a read-only `Ptr`. Unfortunately there's not - // a read-only `NonNull` type to do that to instead. Also, the user verifies that the - // pointer is non-null. - ptr: unsafe { Ptr::new(NonNull::new_unchecked(ptr as *mut u8)) }, + ptr: NonNull::new_unchecked(ptr as *mut c_void), schema, - } - } - - /// Get a pointer to a field. - /// - /// # Panics - /// - /// Panics if the field doesn't exist in the schema. - #[track_caller] - pub fn field<'a, I: Into>>(&self, idx: I) -> SchemaRef<'pointer> { - self.get_field(idx).unwrap() - } - - /// Get a nested field from the box. - /// - /// # Panics - /// - /// Panics if the field doesn't exist in the schema. - #[track_caller] - pub fn field_path<'a, I: IntoIterator>>( - self, - path: I, - ) -> SchemaRef<'pointer> { - self.get_field_path(path).unwrap() - } - - /// Get a nested field from the box. - /// - /// # Errors - /// - /// Errors if the field doesn't exist in the schema. - pub fn get_field_path<'a, I: IntoIterator>>( - self, - path: I, - ) -> Result, SchemaFieldNotFoundError<'a>> { - let mut schemaref = self; - for item in path { - schemaref = schemaref.get_field(item)?; - } - Ok(schemaref) - } - - /// Get a pointer to a field. - /// - /// # Errors - /// - /// Errors if the field doesn't exist in the schema. - pub fn get_field<'a, I: Into>>( - &self, - idx: I, - ) -> Result, SchemaFieldNotFoundError<'a>> { - let idx = idx.into(); - let not_found = Err(SchemaFieldNotFoundError { idx }); - match &self.schema.kind { - SchemaKind::Struct(s) => { - let field_offsets = self.schema.field_offsets(); - let Some((idx, offset)) = - field_offsets - .iter() - .enumerate() - .find_map(|(i, (name, offset))| { - let matches = match idx { - FieldIdx::Idx(n) => n == i, - FieldIdx::Name(n) => name.as_deref() == Some(n), - }; - if matches { - Some((i, *offset)) - } else { - None - } - }) - else { - return not_found; - }; - let field = &s.fields[idx]; - - Ok(SchemaRef { - // SOUND: the schema certifies the soundness of the offset for the given field. - ptr: unsafe { self.ptr.byte_add(offset) }, - schema: field.schema, - }) - } - SchemaKind::Box(_) => { - // SOUND: schema asserts that type is box - let the_box = unsafe { self.ptr.deref::() }; - the_box.get_field(idx) - } - SchemaKind::Vec(_) - | SchemaKind::Primitive(_) - | SchemaKind::Map { .. } - | SchemaKind::Enum(_) => not_found, + _phantom: PhantomData, } } /// Get the pointer. - pub fn as_ptr(&self) -> *const u8 { + pub fn as_ptr(&self) -> *const c_void { self.ptr.as_ptr() } - /// # Safety - /// Assert that the pointer is valid for type T, and that the lifetime is valid. - pub unsafe fn deref(&self) -> &'pointer T { - self.ptr.deref() - } - /// Get the [`Schema`] for the pointer. pub fn schema(&self) -> &'static Schema { self.schema @@ -200,33 +118,91 @@ impl<'pointer> SchemaRef<'pointer> { pub fn as_map(&self) -> Option<&'pointer SchemaMap> { matches!(self.schema.kind, SchemaKind::Map { .. }) // SOUND: Schema asserts this is a schema map - .then_some(unsafe { self.ptr.deref::() }) + .then_some(unsafe { self.cast_into_unchecked::() }) } /// Borrow the schema ref as a [`SchemaVec`] if it is one. pub fn as_vec(&self) -> Option<&'pointer SchemaVec> { matches!(self.schema.kind, SchemaKind::Vec(_)) // SOUND: Schema asserts this is a schema map - .then_some(unsafe { self.ptr.deref::() }) + .then_some(unsafe { self.cast_into_unchecked::() }) } /// Borrow the schema ref as a [`SchemaBox`] if it is one. pub fn as_box(&self) -> Option> { matches!(self.schema.kind, SchemaKind::Vec(_)) // SOUND: Schema asserts this is a schema box - .then_some(unsafe { self.ptr.deref::().as_ref() }) - } - - /// Get a helper to access the inner data at runtime. - pub fn access(&self) -> SchemaRefAccess<'pointer> { - (*self).into() + .then_some(unsafe { self.cast_into_unchecked::().as_ref() }) } /// Debug format the value stored in the schema box. /// /// This is used in the display and debug implementations. pub fn debug_format_value(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self.access() { + f.write_fmt(format_args!("{:?}", self.access_borrowed())) + } + + /// Get a helper to access the inner data. + pub fn access(self) -> SchemaRefAccess<'pointer> { + SchemaRefAccess::new(self) + } + + /// Get a helper to access the inner without consuming this reference. + fn access_borrowed(&self) -> SchemaRefAccess { + SchemaRefAccess::new_borrowed(self) + } + + /// Get the reference to a field. + /// # Panics + /// Panics if the field does not exist. + #[track_caller] + pub fn field<'a, I: Into>>(&self, field_idx: I) -> SchemaRef { + self.get_field(field_idx).unwrap() + } + + /// Get the reference to a field. + pub fn get_field<'a, I: Into>>(&self, field_idx: I) -> Option { + Some(self.access().field(field_idx).ok()?.into_schema_ref()) + } +} + +struct SchemaRefValueDebug<'a>(SchemaRef<'a>); +impl std::fmt::Debug for SchemaRefValueDebug<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.debug_format_value(f) + } +} + +impl std::fmt::Debug for SchemaRef<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("SchemaRef<'_>") + .field(&SchemaRefValueDebug(*self)) + .finish() + } +} +impl std::fmt::Display for SchemaRef<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ::fmt(&SchemaRefValueDebug(*self), f) + } +} + +/// Helper for accessing the inner data of a schema ref at runtime. +pub enum SchemaRefAccess<'a> { + /// Access a struct. + Struct(StructRefAccess<'a>), + /// Access a vec. + Vec(SchemaVecAccess<'a>), + /// Access an enum. + Enum(EnumRefAccess<'a>), + /// Access a map. + Map(SchemaMapAccess<'a>), + /// Access a struct. + Primitive(PrimitiveRef<'a>), +} + +impl std::fmt::Debug for SchemaRefAccess<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { SchemaRefAccess::Struct(s) => { let is_tuple = s.fields().any(|x| x.name.is_none()); if is_tuple { @@ -276,7 +252,7 @@ impl<'pointer> SchemaRef<'pointer> { PrimitiveRef::F32(n) => f.write_fmt(format_args!("{n}")), PrimitiveRef::F64(n) => f.write_fmt(format_args!("{n}")), PrimitiveRef::String(s) => f.write_fmt(format_args!("{s:?}")), - PrimitiveRef::Opaque { size, align } => f + PrimitiveRef::Opaque { size, align, .. } => f .debug_struct("Opaque") .field("size", &size) .field("align", &align) @@ -286,55 +262,121 @@ impl<'pointer> SchemaRef<'pointer> { } } -struct SchemaRefValueDebug<'a>(SchemaRef<'a>); -impl std::fmt::Debug for SchemaRefValueDebug<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.debug_format_value(f) +impl<'ptr> SchemaRefAccess<'ptr> { + /// Create a new [`SchemaRefAccess`] for the given [`SchemaRef`]. + /// + /// This will create a new independent [`SchemaRefAccess`] that may be used even after + /// the original [`SchemaRef`] is dropped ( but not beyond the safe lifetime of the + /// original schema ref ). + pub fn new(value: SchemaRef) -> SchemaRefAccess { + match &value.schema.kind { + SchemaKind::Struct(_) => SchemaRefAccess::Struct(StructRefAccess(value)), + SchemaKind::Vec(_) => SchemaRefAccess::Vec(SchemaVecAccess { + vec: value.as_vec().unwrap(), + orig_ref: value, + }), + SchemaKind::Enum(_) => SchemaRefAccess::Enum(EnumRefAccess(value)), + SchemaKind::Map { .. } => SchemaRefAccess::Map(SchemaMapAccess { + map: value.as_map().unwrap(), + orig_ref: value, + }), + SchemaKind::Box(_) => value.as_box().unwrap().access(), + SchemaKind::Primitive(_) => SchemaRefAccess::Primitive(value.into()), + } } -} -impl std::fmt::Debug for SchemaRef<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("SchemaRef<'_>") - .field(&SchemaRefValueDebug(*self)) - .finish() + /// Create a new [`SchemaRefAccess`] for the given [`SchemaRef`] that borrows the original + /// [`SchemaRef`]. + /// + /// This is subtly different from [`SchemaRefAccess::new()`] because it requires that it hold + /// a borrow to the original schema ref it was created from. This is specifically useful becuse + /// it lets you create a [`SchemaRefAccess`] from a refeence to a schema ref, which is required + /// when accessing a schema ref that is behind an atomic resource borrow, for example. + pub fn new_borrowed<'borrow>(value: &'borrow SchemaRef<'_>) -> SchemaRefAccess<'borrow> { + match &value.schema.kind { + SchemaKind::Struct(_) => SchemaRefAccess::Struct(StructRefAccess(*value)), + SchemaKind::Vec(_) => SchemaRefAccess::Vec(SchemaVecAccess { + vec: value.as_vec().unwrap(), + orig_ref: *value, + }), + SchemaKind::Enum(_) => SchemaRefAccess::Enum(EnumRefAccess(*value)), + SchemaKind::Map { .. } => SchemaRefAccess::Map(SchemaMapAccess { + map: value.as_map().unwrap(), + orig_ref: *value, + }), + SchemaKind::Box(_) => value.as_box().unwrap().access(), + SchemaKind::Primitive(_) => SchemaRefAccess::Primitive((*value).into()), + } + } + + /// Get field with the given index. + pub fn field<'a, I: Into>>(self, field_idx: I) -> Result { + let field_idx = field_idx.into(); + match self { + SchemaRefAccess::Struct(s) => s.field(field_idx).map_err(SchemaRefAccess::Struct), + other @ (SchemaRefAccess::Vec(_) + | SchemaRefAccess::Enum(_) + | SchemaRefAccess::Map(_) + | SchemaRefAccess::Primitive(_)) => Err(other), + } + } + + /// Get the field pointed to by the given path. + pub fn field_path<'a, I: IntoIterator>>(self, path: I) -> Option { + let mut current_field = self; + for field_idx in path { + current_field = current_field.field(field_idx).ok()?; + } + Some(current_field) + } + + /// Borrow this [`SchemaRefMutAccess`] as a [`SchemaRefAccess`]. + pub fn into_schema_ref(self) -> SchemaRef<'ptr> { + match self { + SchemaRefAccess::Struct(s) => s.0, + SchemaRefAccess::Vec(v) => v.into_schema_ref(), + SchemaRefAccess::Enum(e) => e.0, + SchemaRefAccess::Map(m) => m.into_schema_ref(), + SchemaRefAccess::Primitive(p) => p.into_schema_ref(), + } } } -impl std::fmt::Display for SchemaRef<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ::fmt(&SchemaRefValueDebug(*self), f) + +/// Access helper for a [`SchemaVec`]. +#[derive(Deref, DerefMut)] +pub struct SchemaVecAccess<'a> { + /// The schema vec borrow. + #[deref] + vec: &'a SchemaVec, + orig_ref: SchemaRef<'a>, +} + +impl<'a> SchemaVecAccess<'a> { + /// Convert back to a [`SchemaRefMut`] + pub fn into_schema_ref(self) -> SchemaRef<'a> { + self.orig_ref } } -/// Helper for accessing the inner data of a schema ref at runtime. -pub enum SchemaRefAccess<'a> { - /// Access a struct. - Struct(StructRefAccess<'a>), - /// Access a vec. - Vec(&'a SchemaVec), - /// Access an enum. - Enum(EnumRefAccess<'a>), - /// Access a map. - Map(&'a SchemaMap), - /// Access a struct. - Primitive(PrimitiveRef<'a>), +/// Access helper for a [`SchemaMap`]. +#[derive(Deref, DerefMut)] +pub struct SchemaMapAccess<'a> { + /// The schema map borrow. + #[deref] + map: &'a SchemaMap, + orig_ref: SchemaRef<'a>, } -impl<'a> From> for SchemaRefAccess<'a> { - fn from(value: SchemaRef<'a>) -> Self { - match &value.schema.kind { - SchemaKind::Struct(_) => SchemaRefAccess::Struct(StructRefAccess(value)), - SchemaKind::Vec(_) => SchemaRefAccess::Vec(value.as_vec().unwrap()), - SchemaKind::Enum(_) => SchemaRefAccess::Enum(EnumRefAccess(value)), - SchemaKind::Map { .. } => SchemaRefAccess::Map(value.as_map().unwrap()), - SchemaKind::Box(_) => value.as_box().unwrap().access(), - SchemaKind::Primitive(_) => SchemaRefAccess::Primitive(value.into()), - } +impl<'a> SchemaMapAccess<'a> { + /// Convert back to a [`SchemaRefMut`] + pub fn into_schema_ref(self) -> SchemaRef<'a> { + self.orig_ref } } /// Helper for accessing the inner data of a schema ref at runtime. -pub struct StructRefAccess<'a>(pub SchemaRef<'a>); +#[derive(Clone, Copy)] +pub struct StructRefAccess<'a>(SchemaRef<'a>); impl<'a> StructRefAccess<'a> { /// Get the struct's schema. @@ -342,13 +384,66 @@ impl<'a> StructRefAccess<'a> { self.0.schema } - /// Iterate over fields in the struct. - pub fn fields(&self) -> StructRefFieldIter<'a> { + /// Get the [`StructSchemaInfo`] for this struct. + pub fn info(&self) -> &'static StructSchemaInfo { + self.0.schema.kind.as_struct().unwrap() + } + + /// Interate over the fields on the struct. + pub fn fields(&self) -> StructRefFieldIter { StructRefFieldIter { ptr: self.0, field_idx: 0, } } + + /// Access a field, if it exists. + pub fn field<'i, I: Into>>( + self, + field_idx: I, + ) -> Result, Self> { + let field_idx = field_idx.into(); + let field_idx = match field_idx { + FieldIdx::Name(name) => { + if let Some(idx) = self + .info() + .fields + .iter() + .position(|x| x.name.as_ref().map(|x| x.as_ref()) == Some(name)) + { + idx + } else { + return Err(self); + } + } + FieldIdx::Idx(idx) => idx, + }; + let field_schema = self + .0 + .schema + .kind + .as_struct() + .unwrap() + .fields + .get(field_idx) + .unwrap() + .schema; + let (_, field_offset) = self.0.schema.field_offsets().get(field_idx).unwrap(); + + Ok(unsafe { + SchemaRef { + ptr: NonNull::new_unchecked(self.0.as_ptr().add(*field_offset) as *mut c_void), + schema: field_schema, + _phantom: PhantomData, + } + .access() + }) + } + + /// Convert to a [`SchemaRef`]. + pub fn as_schema_ref(&self) -> SchemaRef<'a> { + self.0 + } } /// Iterator for [`StructRefAccess::fields()`]. @@ -370,7 +465,12 @@ impl<'a> Iterator for StructRefFieldIter<'a> { fn next(&mut self) -> Option { let (name, _) = self.ptr.schema.field_offsets().get(self.field_idx)?; - let ptr = self.ptr.field(self.field_idx); + let ptr = self + .ptr + .access() + .field(self.field_idx) + .unwrap() + .into_schema_ref(); self.field_idx += 1; Some(StructRefFieldIterField { name: name.as_ref().map(|x| x.as_str()), @@ -396,6 +496,16 @@ impl<'a> EnumRefAccess<'a> { info } + /// Get the [`VariantInfo`] for the current variant. + pub fn variant_info(&self) -> &'static VariantInfo { + &self.info().variants[self.variant_idx() as usize] + } + + /// Get the [`StructSchemaInfo`] for the current variant. + pub fn variant_struct_info(&self) -> &'static StructSchemaInfo { + self.variant_info().schema.kind.as_struct().unwrap() + } + /// Get the currently-selected variant index. pub fn variant_idx(&self) -> u32 { let info = self.info(); @@ -421,8 +531,9 @@ impl<'a> EnumRefAccess<'a> { let schema = variant_info.schema; let value_offset = self.0.schema.field_offsets()[0].1; StructRefAccess(SchemaRef { - ptr: unsafe { self.0.ptr.byte_add(value_offset) }, + ptr: unsafe { NonNull::new_unchecked(self.0.ptr.as_ptr().add(value_offset)) }, schema, + _phantom: PhantomData, }) } } @@ -463,9 +574,33 @@ pub enum PrimitiveRef<'a> { size: usize, /// The align of the opaque type. align: usize, + /// The schema ref. + schema_ref: SchemaRef<'a>, }, } +impl<'ptr> PrimitiveRef<'ptr> { + fn into_schema_ref(self) -> SchemaRef<'ptr> { + match self { + PrimitiveRef::Bool(b) => SchemaRef::new(b), + PrimitiveRef::U8(n) => SchemaRef::new(n), + PrimitiveRef::U16(n) => SchemaRef::new(n), + PrimitiveRef::U32(n) => SchemaRef::new(n), + PrimitiveRef::U64(n) => SchemaRef::new(n), + PrimitiveRef::U128(n) => SchemaRef::new(n), + PrimitiveRef::I8(n) => SchemaRef::new(n), + PrimitiveRef::I16(n) => SchemaRef::new(n), + PrimitiveRef::I32(n) => SchemaRef::new(n), + PrimitiveRef::I64(n) => SchemaRef::new(n), + PrimitiveRef::I128(n) => SchemaRef::new(n), + PrimitiveRef::F32(n) => SchemaRef::new(n), + PrimitiveRef::F64(n) => SchemaRef::new(n), + PrimitiveRef::String(s) => SchemaRef::new(s), + PrimitiveRef::Opaque { schema_ref, .. } => schema_ref, + } + } +} + impl<'a> From> for PrimitiveRef<'a> { fn from(value: SchemaRef<'a>) -> Self { match &value.schema.kind { @@ -487,6 +622,7 @@ impl<'a> From> for PrimitiveRef<'a> { Primitive::Opaque { size, align } => PrimitiveRef::Opaque { size: *size, align: *align, + schema_ref: value, }, }, _ => panic!("Schema mismatch"), @@ -496,31 +632,13 @@ impl<'a> From> for PrimitiveRef<'a> { /// An untyped mutable reference that knows the [`Schema`] of the pointee and that can be cast to a matching /// type. -// TODO: Re-evaluate whether or not it is necessary to have two lifetimes for `SchemaRefMut`. -// I believe the current implementation is sound, and the extra liftime is not a huge annoyance, but -// it would be good to simplify if possible. See the comment below on `parent_lifetime` for a -// description of the purpose of the second lifetime. We need to maintain the effect of the -// lifetime, but we might be able to do that by using the '`pointer` lifetime to represent the -// `'parent` lifetime when necessary. **Note:** This is a little more advanced rust than "normal". -// This is not a beginner issue. -pub struct SchemaRefMut<'pointer, 'parent> { - ptr: PtrMut<'pointer>, +pub struct SchemaRefMut<'pointer> { + ptr: NonNull, schema: &'static Schema, - /// This `'parent` lifetime is used to lock the borrow to the parent [`SchemaRefMut`] if this - /// was created by borrowing the field of another [`SchemaRefMut`]. - /// - /// A top-level [`SchemaRefMut`] that doesn't borrow from another one will have the 'parent - /// lifetime equal to the 'pointer lifetime. - /// - /// This allows us to prevent borrowing the [`SchemaRefMut`], while one of the children - /// [`SchemaRefMut`]s are potentially writing to the fields. - /// - /// In other words, this represents that the child [`SchemaRefMut`] may borrow mutably from it's - /// parent schema walker. - parent_lifetime: PhantomData<&'parent mut ()>, + _phantom: PhantomData<&'pointer mut ()>, } -impl<'pointer, 'parent> std::fmt::Debug for SchemaRefMut<'pointer, 'parent> { +impl<'pointer> std::fmt::Debug for SchemaRefMut<'pointer> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("SchemaRefMut") // .field("ptr", &self.ptr) @@ -530,7 +648,21 @@ impl<'pointer, 'parent> std::fmt::Debug for SchemaRefMut<'pointer, 'parent> { } } -impl<'pointer, 'parent> SchemaRefMut<'pointer, 'parent> { +impl<'pointer> SchemaRefMut<'pointer> { + /// Cast this pointer to a mutable reference. + /// # Safety + /// You must uphold all safety requirements of [`NonNull::as_mut()`]. + pub unsafe fn cast_mut_unchecked(&mut self) -> &mut T { + self.ptr.cast::().as_mut() + } + + /// Cast this pointer to a mutable reference. + /// # Safety + /// You must uphold all safety requirements of [`NonNull::as_mut()`]. + pub unsafe fn cast_into_mut_unchecked(self) -> &'pointer mut T { + self.ptr.cast::().as_mut() + } + /// Cast this pointer to a reference to a type with a matching [`Schema`]. /// /// # Panics @@ -547,16 +679,8 @@ impl<'pointer, 'parent> SchemaRefMut<'pointer, 'parent> { /// Errors if the schema of the pointer does not match that of the type you are casting to. pub fn try_cast_mut(&mut self) -> Result<&mut T, SchemaMismatchError> { if self.schema.represents(T::schema()) { - // SOUND: here we clone our mutable pointer and dereference it. This is dangerous, but - // sound in this case because we don't use our pointer at the same time as it, and we - // make sure that we lock ourselves with a mutable borrow until the user drops the - // reference that we gave them. - unsafe { - let copied_ptr: PtrMut<'_, Aligned> = - PtrMut::new(NonNull::new_unchecked(self.ptr.as_ptr())); - - Ok(copied_ptr.deref_mut()) - } + // SOUND: this pointer has the same memory representation as T. + unsafe { Ok(self.ptr.cast::().as_mut()) } } else { Err(SchemaMismatchError) } @@ -583,20 +707,21 @@ impl<'pointer, 'parent> SchemaRefMut<'pointer, 'parent> { /// Errors if the schema of the pointer does not match that of the type you are casting to. pub fn try_cast_into_mut(self) -> Result<&'pointer mut T, SchemaMismatchError> { if self.schema.represents(T::schema()) { - // We've checked that the pointer represents T - Ok(unsafe { self.ptr.deref_mut() }) + // SOUND: We've checked that the pointer represents T + Ok(unsafe { self.ptr.cast::().as_mut() }) } else { Err(SchemaMismatchError) } } /// Create a new [`SchemaRefMut`] from a reference to a type that implements [`HasSchema`]. - pub fn new(v: &'pointer mut T) -> SchemaRefMut<'pointer, 'static> { + pub fn new(v: &'pointer mut T) -> SchemaRefMut<'pointer> { let schema = T::schema(); SchemaRefMut { - ptr: v.into(), + // SOUND: the &mut T reference cannot be null. + ptr: unsafe { NonNull::new_unchecked(v as *mut T as *mut c_void) }, schema, - parent_lifetime: PhantomData, + _phantom: PhantomData, } } @@ -608,228 +733,602 @@ impl<'pointer, 'parent> SchemaRefMut<'pointer, 'parent> { /// - If the `A` type parameter is [`Aligned`] then `inner` must be sufficiently aligned for the /// pointee type. /// - `ptr` must have correct provenance to allow read and writes of the pointee type. - /// - The lifetime `'a` must be constrained such that this [`PtrMut`] will stay valid and + /// - The lifetime `'pointer` must be constrained such that this [`PtrMut`] will stay valid and /// nothing else can read or mutate the pointee while this [`PtrMut`] is live. pub unsafe fn from_ptr_schema( - ptr: *mut u8, + ptr: *mut c_void, schema: &'static Schema, - ) -> SchemaRefMut<'pointer, 'parent> { + ) -> SchemaRefMut<'pointer> { Self { - ptr: PtrMut::new(NonNull::new_unchecked(ptr)), + ptr: NonNull::new_unchecked(ptr), schema, - parent_lifetime: PhantomData, + _phantom: PhantomData, } } - /// Get a pointer to a field. - /// + /// Borrow the schema ref as a [`SchemaMap`] if it is one. + pub fn into_map(self) -> Result<&'pointer mut SchemaMap, Self> { + matches!(self.schema.kind, SchemaKind::Map { .. }) + // SOUND: Schema asserts this is a schema map + .then_some(unsafe { &mut *(self.ptr.as_ptr() as *mut SchemaMap) }) + .ok_or(self) + } + + /// Borrow the schema ref as a [`SchemaVec`] if it is one. + pub fn into_vec(self) -> Result<&'pointer mut SchemaVec, Self> { + matches!(self.schema.kind, SchemaKind::Vec(_)) + // SOUND: Schema asserts this is a schema map + .then_some(unsafe { &mut *(self.ptr.as_ptr() as *mut SchemaVec) }) + .ok_or(self) + } + + /// Borrow the schema ref as a [`SchemaBox`] if it is one. + pub fn into_box(self) -> Result, Self> { + matches!(self.schema.kind, SchemaKind::Vec(_)) + // SOUND: Schema asserts this is a schema box + .then_some(unsafe { (*(self.ptr.as_ptr() as *mut SchemaBox)).as_mut() }) + .ok_or(self) + } + + /// Convert into an accessor for the inner data. + pub fn into_access_mut(self) -> SchemaRefMutAccess<'pointer> { + SchemaRefMutAccess::new(self) + } + + /// Get a mutable access helper to this reference. + pub fn access_mut(&mut self) -> SchemaRefMutAccess<'_> { + SchemaRefMutAccess::new_borrowed(self) + } + + /// Get the raw pointer + pub fn as_ptr(&self) -> *mut c_void { + self.ptr.as_ptr() + } + + /// Get the [`Schema`] for the pointer. + pub fn schema(&self) -> &'static Schema { + self.schema + } + + /// Get the hash of this schema box, if supported. + pub fn hash(&self) -> Option { + self.schema + .hash_fn + .map(|hash_fn| unsafe { (hash_fn)(self.ptr.as_ptr()) }) + } + + /// Borrow this [`SchemaRefMut`] as a [`SchemaRef`]. + pub fn as_ref(&self) -> SchemaRef<'_> { + SchemaRef { + ptr: self.ptr, + schema: self.schema, + _phantom: PhantomData, + } + } + + /// Convert a borrowed [`SchemaRefMut`] to an owned [`SchemaRefMut`] with a lifetime matching + /// That of the borrow. + pub fn reborrow(&mut self) -> SchemaRefMut<'_> { + SchemaRefMut { + ptr: self.ptr, + schema: self.schema, + _phantom: PhantomData, + } + } + + /// Get the reference to a field. /// # Panics - /// - /// Panics if the field doesn't exist in the schema. + /// Panics if the field does not exist. #[track_caller] - pub fn field<'this, 'b, I: Into>>( - &'this mut self, - idx: I, - ) -> SchemaRefMut<'pointer, 'this> { - self.get_field(idx).unwrap() + pub fn field<'a, I: Into>>(&mut self, field_idx: I) -> SchemaRefMut { + self.get_field(field_idx).unwrap() } - /// Get a nested field from the box. - /// + /// Get the reference to a field. + pub fn get_field<'a, I: Into>>(&mut self, field_idx: I) -> Option { + Some( + self.access_mut() + .field(field_idx) + .ok()? + .into_schema_ref_mut(), + ) + } + + /// Get the reference to a field. /// # Panics - /// - /// Panics if the field doesn't exist in the schema. + /// Panics if the field does not exist. #[track_caller] - pub fn into_field_path<'a, I: IntoIterator>>( - self, - path: I, - ) -> SchemaRefMut<'pointer, 'parent> { - self.try_into_field_path(path).unwrap() + pub fn into_field<'a, I: Into>>(self, field_idx: I) -> SchemaRefMut<'pointer> { + self.try_into_field(field_idx).unwrap() } - /// Get a nested field from the box. - /// - /// # Errors - /// - /// Errors if the field doesn't exist in the schema. - pub fn try_into_field_path<'a, I: IntoIterator>>( + /// Get the reference to a field. + pub fn try_into_field<'a, I: Into>>( self, - path: I, - ) -> Result, Self> { - let mut schemaref = self; - for item in path { - schemaref = schemaref.try_into_field(item)?; - } - Ok(schemaref) + field_idx: I, + ) -> Result, Self> { + self.into_access_mut() + .field(field_idx) + .map(|x| x.into_schema_ref_mut()) + .map_err(|access| access.into_schema_ref_mut()) } +} - /// Get a nested field from the box. - /// - /// # Panics - /// - /// Panics if the field doesn't exist in the schema. - #[track_caller] - pub fn get_field_path<'this, 'a, I: IntoIterator>>( - &'this mut self, - path: I, - ) -> SchemaRefMut<'pointer, 'this> { - self.try_get_field_path(path).unwrap() +/// Access a schema +pub enum SchemaRefMutAccess<'a> { + /// Access a struct. + Struct(StructRefMutAccess<'a>), + /// Access a vec. + Vec(SchemaVecMutAccess<'a>), + /// Access an enum. + Enum(EnumRefMutAccess<'a>), + /// Access a map. + Map(SchemaMapMutAccess<'a>), + /// Access a struct. + Primitive(PrimitiveRefMut<'a>), +} + +/// Mutable [`SchemaVec`] access helper. +#[derive(Deref, DerefMut)] +pub struct SchemaVecMutAccess<'a> { + /// The schema vec borrow. + #[deref] + vec: &'a mut SchemaVec, + /// The original pointer and schema to allow us to convert back to a [`SchemaRefMut`] + /// WARNING: This pointer aliases with the `vec: &'a SchemaVec` reference and mut not be used + /// until the borrow to the schema vec is dropped. + orig_ptr: *mut c_void, + orig_schema: &'static Schema, +} + +impl<'a> SchemaVecMutAccess<'a> { + /// Convert back to a [`SchemaRefMut`] + pub fn as_mut(self) -> SchemaRefMut<'a> { + // SOUND: we are taking ownership of self and dropping the reference that aliases, + // so that we can return a valid [`SchemaRefMut`]. + unsafe { SchemaRefMut::from_ptr_schema(self.orig_ptr, self.orig_schema) } } +} - /// Get a nested field from the box. - /// - /// # Errors +/// Mutable [`SchemaMap`] access helper. +#[derive(Deref, DerefMut)] +pub struct SchemaMapMutAccess<'a> { + /// The schema map borrow. + #[deref] + map: &'a mut SchemaMap, + /// The original pointer and schema to allow us to convert back to a [`SchemaRefMut`] + /// WARNING: This pointer aliases with the `vec: &'a SchemaVec` reference and mut not be used + /// until the borrow to the schema vec is dropped. + orig_ptr: *mut c_void, + orig_schema: &'static Schema, +} + +impl<'a> SchemaMapMutAccess<'a> { + /// Convert back to a [`SchemaRefMut`] + pub fn into_schema_ref_mut(self) -> SchemaRefMut<'a> { + // SOUND: we are taking ownership of self and dropping the reference that aliases, + // so that we can return a valid [`SchemaRefMut`]. + unsafe { SchemaRefMut::from_ptr_schema(self.orig_ptr, self.orig_schema) } + } +} + +impl std::fmt::Debug for SchemaRefMutAccess<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_ref().fmt(f) + } +} + +impl<'pointer> SchemaRefMutAccess<'pointer> { + /// Create a new [`SchemaRefAccess`] for the given [`SchemaRef`]. /// - /// Errors if the field doesn't exist in the schema. - pub fn try_get_field_path<'this, 'a, I: IntoIterator>>( - &'this mut self, - path: I, - ) -> Result, SchemaFieldNotFoundError<'a>> { - let mut schemaref = Self { - // SOUND: we are cloning our mutable reference here, but we are returning only one - // of them, and it contains the 'this lifetime that indicates a borrow of this one, - // preventing both from being used at the same time. - ptr: unsafe { PtrMut::new(NonNull::new_unchecked(self.ptr.as_ptr())) }, - schema: self.schema, - parent_lifetime: PhantomData, - }; - for item in path { - schemaref = schemaref - .try_into_field(item) - .map_err(|_| SchemaFieldNotFoundError { idx: item })?; + /// This will create a new independent [`SchemaRefAccess`] that may be used even after + /// the original [`SchemaRef`] is dropped ( but not beyond the safe lifetime of the + /// original schema ref ). + pub fn new(value: SchemaRefMut) -> SchemaRefMutAccess { + match &value.schema.kind { + SchemaKind::Struct(_) => SchemaRefMutAccess::Struct(StructRefMutAccess(value)), + SchemaKind::Vec(_) => SchemaRefMutAccess::Vec(SchemaVecMutAccess { + orig_ptr: value.as_ptr(), + orig_schema: value.schema, + vec: value.into_vec().unwrap(), + }), + SchemaKind::Enum(_) => SchemaRefMutAccess::Enum(EnumRefMutAccess(value)), + SchemaKind::Map { .. } => SchemaRefMutAccess::Map(SchemaMapMutAccess { + orig_ptr: value.as_ptr(), + orig_schema: value.schema, + map: value.into_map().unwrap(), + }), + SchemaKind::Box(_) => value.into_box().unwrap().into_access_mut(), + SchemaKind::Primitive(_) => SchemaRefMutAccess::Primitive(value.into()), } - Ok(schemaref) } - /// Get a pointer to a field. - /// - /// # Errors + /// Create a new [`SchemaRefAccess`] for the given [`SchemaRef`] that borrows the original + /// [`SchemaRef`]. /// - /// Errors if the field doesn't exist in the schema. - pub fn get_field<'this, 'idx, I: Into>>( - &'this mut self, - idx: I, - ) -> Result, SchemaFieldNotFoundError<'idx>> { - let idx = idx.into(); - let not_found = Err(SchemaFieldNotFoundError { idx }); - match &self.schema.kind { - SchemaKind::Struct(s) => { - let field_offsets = self.schema.field_offsets(); - let Some((idx, offset)) = - field_offsets - .iter() - .enumerate() - .find_map(|(i, (name, offset))| { - let matches = match idx { - FieldIdx::Idx(n) => n == i, - FieldIdx::Name(n) => name.as_deref() == Some(n), - }; - if matches { - Some((i, *offset)) - } else { - None - } - }) - else { - return not_found; - }; - let field = &s.fields[idx]; - - // SOUND: here we clone our mutable pointer, and then offset it according to the - // field. This is dangerous, but sound because we make sure that this - // `get_field` method returns a `SchemaWalkerMut` with a virtual mutable borrow - // to this one. - // - // This means that Rust will not let anybody use this `SchemaWalkerMut`, until - // the other one is dropped. That means all we have to do is not use our - // `self.ptr` while the `offset_ptr` exists. - // - // Additionally, the `new_unchecked` is sound because our pointer cannot be null - // because it comes out of a `PtrMut`. - let offset_ptr = unsafe { - PtrMut::new(NonNull::new_unchecked(self.ptr.as_ptr())).byte_add(offset) - }; - - Ok(SchemaRefMut { - ptr: offset_ptr, - schema: field.schema, - parent_lifetime: PhantomData, - }) + /// This is subtly different from [`SchemaRefAccess::new()`] because it requires that it hold + /// a borrow to the original schema ref it was created from. This is specifically useful becuse + /// it lets you create a [`SchemaRefAccess`] from a refeence to a schema ref, which is required + /// when accessing a schema ref that is behind an atomic resource borrow, for example. + pub fn new_borrowed<'borrow>( + value: &'borrow mut SchemaRefMut<'_>, + ) -> SchemaRefMutAccess<'borrow> { + match &value.schema.kind { + SchemaKind::Struct(_) => { + SchemaRefMutAccess::Struct(StructRefMutAccess(value.reborrow())) } - SchemaKind::Box(_) => { - // SOUND: schema asserts that type is box - let the_box = unsafe { &mut *(self.ptr.as_ptr() as *mut SchemaBox) }; - the_box.get_field_mut(idx) + SchemaKind::Vec(_) => SchemaRefMutAccess::Vec(SchemaVecMutAccess { + orig_ptr: value.as_ptr(), + orig_schema: value.schema, + vec: value.reborrow().into_vec().unwrap(), + }), + SchemaKind::Enum(_) => SchemaRefMutAccess::Enum(EnumRefMutAccess(value.reborrow())), + SchemaKind::Map { .. } => SchemaRefMutAccess::Map(SchemaMapMutAccess { + orig_ptr: value.as_ptr(), + orig_schema: value.schema, + map: value.reborrow().into_map().unwrap(), + }), + SchemaKind::Box(_) => value.reborrow().into_box().unwrap().into_access_mut(), + SchemaKind::Primitive(_) => SchemaRefMutAccess::Primitive(value.reborrow().into()), + } + } + + /// Convert this to a [`SchemaRefMut`]. + pub fn into_schema_ref_mut(self) -> SchemaRefMut<'pointer> { + match self { + SchemaRefMutAccess::Struct(s) => s.0, + SchemaRefMutAccess::Vec(v) => v.as_mut(), + SchemaRefMutAccess::Enum(e) => e.0, + SchemaRefMutAccess::Map(m) => m.into_schema_ref_mut(), + SchemaRefMutAccess::Primitive(p) => p.into_schema_ref_mut(), + } + } + + /// Get field with the given index. + pub fn field<'a, I: Into>>(self, field_idx: I) -> Result { + let field_idx = field_idx.into(); + match self { + SchemaRefMutAccess::Struct(s) => { + s.into_field(field_idx).map_err(SchemaRefMutAccess::Struct) } - SchemaKind::Map { .. } - | SchemaKind::Vec(_) - | SchemaKind::Primitive(_) - | SchemaKind::Enum(_) => not_found, + other @ (SchemaRefMutAccess::Vec(_) + | SchemaRefMutAccess::Enum(_) + | SchemaRefMutAccess::Map(_) + | SchemaRefMutAccess::Primitive(_)) => Err(other), } } - /// Convert this ref into a ref to one of it's fields. - /// - /// This is useful because it consumes self and avoids keeping a reference to it's parent - /// [`SchemaRefMut`]. - /// # Panics - /// Panics if the field does not exist. - #[inline] - #[track_caller] - pub fn into_field<'idx, I: Into>>( - self, - idx: I, - ) -> SchemaRefMut<'pointer, 'parent> { - self.try_into_field(idx).unwrap() + /// Get the field pointed to by the given path. + pub fn field_path<'a, I: IntoIterator>>(self, path: I) -> Option { + let mut current_field = self; + for field_idx in path { + current_field = current_field.field(field_idx).ok()?; + } + Some(current_field) } - /// Convert this ref into a ref to one of it's fields. - /// - /// This is useful because it consumes self and avoids keeping a reference to it's parent - /// [`SchemaRefMut`]. - /// # Errors - /// Errors if the field does not exist. - pub fn try_into_field<'idx, I: Into>>( - mut self, - idx: I, - ) -> Result, Self> { - match self.get_field(idx) { - Ok(r) => Ok(SchemaRefMut { - ptr: r.ptr, - schema: r.schema, - parent_lifetime: PhantomData, + /// Borrow this [`SchemaRefMutAccess`] as a [`SchemaRefAccess`]. + pub fn as_ref(&self) -> SchemaRefAccess { + match self { + SchemaRefMutAccess::Struct(s) => SchemaRefAccess::Struct(StructRefAccess(s.0.as_ref())), + SchemaRefMutAccess::Vec(v) => SchemaRefAccess::Vec(SchemaVecAccess { + vec: &*v.vec, + // SOUND: We hold an exclusive borrow which we are allowed to downgrade to a read-only reference. + orig_ref: unsafe { + SchemaRef::from_ptr_schema( + (&*v.vec) as *const SchemaVec as *const c_void, + v.orig_schema, + ) + }, + }), + SchemaRefMutAccess::Enum(e) => SchemaRefAccess::Enum(EnumRefAccess(e.0.as_ref())), + SchemaRefMutAccess::Map(m) => SchemaRefAccess::Map(SchemaMapAccess { + map: &*m.map, + // SOUND: We hold an exclusive borrow which we are allowed to downgrade to a read-only reference. + orig_ref: unsafe { + SchemaRef::from_ptr_schema( + (&*m.map) as *const SchemaMap as *const c_void, + m.orig_schema, + ) + }, }), - Err(_) => Err(self), + SchemaRefMutAccess::Primitive(p) => SchemaRefAccess::Primitive(p.as_ref()), } } +} - /// Get the raw pointer - pub fn as_ptr(&self) -> *mut u8 { - self.ptr.as_ptr() +/// Helper for accessing the inner data of a schema ref at runtime. +pub struct StructRefMutAccess<'a>(pub SchemaRefMut<'a>); + +impl<'a> StructRefMutAccess<'a> { + /// Get the struct's schema. + pub fn schema(&self) -> &'static Schema { + self.0.schema } - /// # Safety - /// You assert that the pointer points to a valid instance of T with the given lifetime. - pub unsafe fn deref_mut(self) -> &'pointer mut T { - self.ptr.deref_mut() + /// Get the [`StructSchemaInfo`] for this struct. + pub fn info(&self) -> &'static StructSchemaInfo { + self.0.schema.kind.as_struct().unwrap() } - /// Get the [`Schema`] for the pointer. + /// Access a field, if it exists. + pub fn into_field<'i, I: Into>>( + self, + field_idx: I, + ) -> Result, Self> { + let field_idx = field_idx.into(); + let field_idx = match field_idx { + FieldIdx::Name(name) => { + if let Some(idx) = self + .info() + .fields + .iter() + .position(|x| x.name.as_ref().map(|x| x.as_ref()) == Some(name)) + { + idx + } else { + return Err(self); + } + } + FieldIdx::Idx(idx) => idx, + }; + let field_schema = self + .0 + .schema + .kind + .as_struct() + .unwrap() + .fields + .get(field_idx) + .unwrap() + .schema; + let (_, field_offset) = self.0.schema.field_offsets().get(field_idx).unwrap(); + + Ok(unsafe { + SchemaRefMut { + ptr: NonNull::new_unchecked(self.0.as_ptr().add(*field_offset)), + schema: field_schema, + _phantom: PhantomData, + } + .into_access_mut() + }) + } + + /// Iterate over fields in the struct. + pub fn fields(&mut self) -> StructRefMutFieldIter<'_> { + StructRefMutFieldIter { + ptr: self.0.reborrow(), + field_idx: 0, + } + } + + /// Consume to create an iterator over fields in the struct. + pub fn into_fields(self) -> StructRefMutFieldIter<'a> { + StructRefMutFieldIter { + ptr: self.0, + field_idx: 0, + } + } +} + +/// Iterator for [`StructRefAccess::fields()`]. +pub struct StructRefMutFieldIter<'a> { + ptr: SchemaRefMut<'a>, + field_idx: usize, +} + +/// A field returned by [`StructRefFieldIter`]. +pub struct StructRefMutFieldIterField<'a> { + /// The name of the field, if set. + pub name: Option<&'static str>, + /// The field's value. + pub value: SchemaRefMut<'a>, +} + +impl<'a> Iterator for StructRefMutFieldIter<'a> { + type Item = StructRefMutFieldIterField<'a>; + + fn next(&mut self) -> Option { + let field_schema = self + .ptr + .schema + .kind + .as_struct() + .unwrap() + .fields + .get(self.field_idx)? + .schema; + let (name, field_offset) = self.ptr.schema.field_offsets().get(self.field_idx)?; + self.field_idx += 1; + + Some(StructRefMutFieldIterField { + name: name.as_ref().map(|x| x.as_str()), + // SOUND: Return a new SchemaRefMut with the 'a lifetime. This is sound because we + // don't return mutliple `SchemaRefMut`s to the same data. + value: unsafe { + SchemaRefMut { + ptr: NonNull::new_unchecked(self.ptr.as_ptr().add(*field_offset)), + schema: field_schema, + _phantom: PhantomData, + } + }, + }) + } +} + +/// Helper for accessing the inner data of a schema ref at runtime. +pub struct EnumRefMutAccess<'a>(pub SchemaRefMut<'a>); + +impl<'a> EnumRefMutAccess<'a> { + /// Get the enum's schema. pub fn schema(&self) -> &'static Schema { - self.schema + self.0.schema } - /// Get the hash of this schema box, if supported. - pub fn hash(&self) -> Option { - self.schema - .hash_fn - .map(|hash_fn| unsafe { (hash_fn)(self.ptr.as_ptr()) }) + /// Get the enum schema info. + pub fn info(&self) -> &'static EnumSchemaInfo { + let SchemaKind::Enum(info) = &self.0.schema.kind else { + panic!("Not an enum"); + }; + info + } + + /// Get the currently-selected variant index. + pub fn variant_idx(&self) -> u32 { + let info = self.info(); + match info.tag_type { + EnumTagType::U8 => unsafe { self.0.as_ptr().cast::().read() as u32 }, + EnumTagType::U16 => unsafe { self.0.as_ptr().cast::().read() as u32 }, + EnumTagType::U32 => unsafe { self.0.as_ptr().cast::().read() }, + } + } + + /// Get the name of the currently selected variant. + pub fn variant_name(&self) -> &'static str { + let info = self.info(); + let idx = self.variant_idx(); + info.variants[idx as usize].name.as_ref() + } + + /// Get a reference to the enum's currently selected value. + pub fn value(&self) -> StructRefMutAccess<'a> { + let info = self.info(); + let variant_idx = self.variant_idx(); + let variant_info = &info.variants[variant_idx as usize]; + let schema = variant_info.schema; + let value_offset = self.0.schema.field_offsets()[0].1; + StructRefMutAccess(SchemaRefMut { + ptr: unsafe { NonNull::new_unchecked(self.0.ptr.as_ptr().add(value_offset)) }, + schema, + _phantom: PhantomData, + }) + } +} + +/// Helper for accessing the inner data of a schema ref at runtime. +pub enum PrimitiveRefMut<'a> { + /// A [`bool`] + Bool(&'a mut bool), + /// A [`u8`] + U8(&'a mut u8), + /// A [`u16`] + U16(&'a mut u16), + /// A [`u32`] + U32(&'a mut u32), + /// A [`u64`] + U64(&'a mut u64), + /// A [`u128`] + U128(&'a mut u128), + /// An [`i8`] + I8(&'a mut i8), + /// An [`i16`] + I16(&'a mut i16), + /// An [`i32`] + I32(&'a mut i32), + /// An [`i64`] + I64(&'a mut i64), + /// An [`i128`] + I128(&'a mut i128), + /// An [`f23`] + F32(&'a mut f32), + /// An [`f64`] + F64(&'a mut f64), + /// A [`String`] + String(&'a mut String), + /// An opaque type + Opaque { + /// The size of the opaque type. + size: usize, + /// The align of the opaque type. + align: usize, + /// The schema ref. + schema_ref: SchemaRefMut<'a>, + }, +} + +impl<'ptr> PrimitiveRefMut<'ptr> { + /// Convert to an immutable [`PrimitiveRef`]. + pub fn as_ref(&self) -> PrimitiveRef { + match self { + PrimitiveRefMut::Bool(b) => PrimitiveRef::Bool(b), + PrimitiveRefMut::U8(n) => PrimitiveRef::U8(n), + PrimitiveRefMut::U16(n) => PrimitiveRef::U16(n), + PrimitiveRefMut::U32(n) => PrimitiveRef::U32(n), + PrimitiveRefMut::U64(n) => PrimitiveRef::U64(n), + PrimitiveRefMut::U128(n) => PrimitiveRef::U128(n), + PrimitiveRefMut::I8(n) => PrimitiveRef::I8(n), + PrimitiveRefMut::I16(n) => PrimitiveRef::I16(n), + PrimitiveRefMut::I32(n) => PrimitiveRef::I32(n), + PrimitiveRefMut::I64(n) => PrimitiveRef::I64(n), + PrimitiveRefMut::I128(n) => PrimitiveRef::I128(n), + PrimitiveRefMut::F32(n) => PrimitiveRef::F32(n), + PrimitiveRefMut::F64(n) => PrimitiveRef::F64(n), + PrimitiveRefMut::String(n) => PrimitiveRef::String(n), + PrimitiveRefMut::Opaque { + size, + align, + schema_ref, + } => PrimitiveRef::Opaque { + size: *size, + align: *align, + schema_ref: schema_ref.as_ref(), + }, + } + } + + fn into_schema_ref_mut(self) -> SchemaRefMut<'ptr> { + match self { + PrimitiveRefMut::Bool(b) => SchemaRefMut::new(b), + PrimitiveRefMut::U8(n) => SchemaRefMut::new(n), + PrimitiveRefMut::U16(n) => SchemaRefMut::new(n), + PrimitiveRefMut::U32(n) => SchemaRefMut::new(n), + PrimitiveRefMut::U64(n) => SchemaRefMut::new(n), + PrimitiveRefMut::U128(n) => SchemaRefMut::new(n), + PrimitiveRefMut::I8(n) => SchemaRefMut::new(n), + PrimitiveRefMut::I16(n) => SchemaRefMut::new(n), + PrimitiveRefMut::I32(n) => SchemaRefMut::new(n), + PrimitiveRefMut::I64(n) => SchemaRefMut::new(n), + PrimitiveRefMut::I128(n) => SchemaRefMut::new(n), + PrimitiveRefMut::F32(n) => SchemaRefMut::new(n), + PrimitiveRefMut::F64(n) => SchemaRefMut::new(n), + PrimitiveRefMut::String(s) => SchemaRefMut::new(s), + PrimitiveRefMut::Opaque { schema_ref, .. } => schema_ref, + } + } +} + +impl<'a> From> for PrimitiveRefMut<'a> { + fn from(value: SchemaRefMut<'a>) -> Self { + match &value.schema.kind { + SchemaKind::Primitive(p) => match p { + Primitive::Bool => PrimitiveRefMut::Bool(value.cast_into_mut()), + Primitive::U8 => PrimitiveRefMut::U8(value.cast_into_mut()), + Primitive::U16 => PrimitiveRefMut::U16(value.cast_into_mut()), + Primitive::U32 => PrimitiveRefMut::U32(value.cast_into_mut()), + Primitive::U64 => PrimitiveRefMut::U64(value.cast_into_mut()), + Primitive::U128 => PrimitiveRefMut::U128(value.cast_into_mut()), + Primitive::I8 => PrimitiveRefMut::I8(value.cast_into_mut()), + Primitive::I16 => PrimitiveRefMut::I16(value.cast_into_mut()), + Primitive::I32 => PrimitiveRefMut::I32(value.cast_into_mut()), + Primitive::I64 => PrimitiveRefMut::I64(value.cast_into_mut()), + Primitive::I128 => PrimitiveRefMut::I128(value.cast_into_mut()), + Primitive::F32 => PrimitiveRefMut::F32(value.cast_into_mut()), + Primitive::F64 => PrimitiveRefMut::F64(value.cast_into_mut()), + Primitive::String => PrimitiveRefMut::String(value.cast_into_mut()), + Primitive::Opaque { size, align } => PrimitiveRefMut::Opaque { + size: *size, + align: *align, + schema_ref: value, + }, + }, + _ => panic!("Schema mismatch"), + } } } /// A owning, type-erased [`Box`]-like container for types with a [`Schema`]. pub struct SchemaBox { - ptr: OwningPtr<'static>, + ptr: NonNull, schema: &'static Schema, } unsafe impl Sync for SchemaBox {} @@ -883,14 +1382,14 @@ impl Clone for SchemaBox { let layout = self.schema.layout(); let new_ptr = if layout.size() == 0 { - NonNull::::dangling().as_ptr() + NonNull::::dangling().as_ptr() } else { // SOUND: Non-zero size for layout - unsafe { std::alloc::alloc(layout) } + unsafe { std::alloc::alloc(layout) as *mut c_void } }; let new_ptr = unsafe { - (clone_fn)(self.ptr.as_ref().as_ptr(), new_ptr); - OwningPtr::new(NonNull::new(new_ptr).unwrap_or_else(|| handle_alloc_error(layout))) + (clone_fn)(self.ptr.as_ptr(), new_ptr); + NonNull::new(new_ptr).unwrap_or_else(|| handle_alloc_error(layout)) }; Self { ptr: new_ptr, @@ -900,6 +1399,11 @@ impl Clone for SchemaBox { } impl SchemaBox { + /// Get a raw pointer to the box data. + pub fn as_ptr(&self) -> *mut c_void { + self.ptr.as_ptr() + } + /// Cast this box to it's inner type and return it. /// # Panics /// Panics if the schema of the box does not match that of the type you are casting to. @@ -930,7 +1434,7 @@ impl SchemaBox { // Copy the data from the box into the stack. // SOUND: We've validated that the box has the same schema as T unsafe { - (ret.as_mut_ptr() as *mut u8) + (ret.as_mut_ptr() as *mut c_void) .copy_from_nonoverlapping(self.ptr.as_ptr(), self.schema.layout().size()); } @@ -955,7 +1459,7 @@ impl SchemaBox { pub fn try_cast_ref(&self) -> Result<&T, SchemaMismatchError> { if self.schema.represents(T::schema()) { // SOUND: the schemas have the same memory representation. - unsafe { Ok(self.ptr.as_ref().deref()) } + unsafe { Ok(self.ptr.cast::().as_ref()) } } else { Err(SchemaMismatchError) } @@ -975,7 +1479,7 @@ impl SchemaBox { pub fn try_cast_mut(&mut self) -> Result<&mut T, SchemaMismatchError> { if self.schema.represents(T::schema()) { // SOUND: the schemas have the same memory representation. - unsafe { Ok(self.ptr.as_mut().deref_mut()) } + unsafe { Ok(self.ptr.cast::().as_mut()) } } else { Err(SchemaMismatchError) } @@ -984,17 +1488,18 @@ impl SchemaBox { /// Borrow this box as a [`SchemaRef`]. pub fn as_ref(&self) -> SchemaRef<'_> { SchemaRef { - ptr: self.ptr.as_ref(), + ptr: self.ptr, schema: self.schema, + _phantom: PhantomData, } } /// Borrow this box as a [`SchemaRefMut`]. - pub fn as_mut(&mut self) -> SchemaRefMut<'_, '_> { + pub fn as_mut(&mut self) -> SchemaRefMut<'_> { SchemaRefMut { - ptr: self.ptr.as_mut(), + ptr: self.ptr, schema: self.schema, - parent_lifetime: PhantomData, + _phantom: PhantomData, } } @@ -1020,15 +1525,13 @@ impl SchemaBox { let layout = schema.layout(); let ptr = if layout.size() == 0 { - NonNull::::dangling().as_ptr() + NonNull::::dangling().as_ptr() } else { // SOUND: Non-zero size for layout - unsafe { std::alloc::alloc(layout) } + std::alloc::alloc(layout) as *mut c_void }; // SOUND: The pointer is allocated for the layout matching the schema. - let ptr = unsafe { - OwningPtr::new(NonNull::new(ptr).unwrap_or_else(|| handle_alloc_error(layout))) - }; + let ptr = NonNull::new(ptr).unwrap_or_else(|| handle_alloc_error(layout)); Self { ptr, schema } } @@ -1087,7 +1590,7 @@ impl SchemaBox { /// # Safety /// /// - You must insure that the pointer is valid for the given schema. - pub unsafe fn from_raw_parts(ptr: OwningPtr<'static>, schema: &'static Schema) -> Self { + pub unsafe fn from_raw_parts(ptr: NonNull, schema: &'static Schema) -> Self { Self { ptr, schema } } @@ -1114,44 +1617,10 @@ impl SchemaBox { self.try_hash().expect("Schema doesn't implement hash") } - /// Get a ref to the field with the given name/index, and panic if it doesn't exist. - #[inline] - #[track_caller] - pub fn field<'idx, I: Into>>(&self, idx: I) -> SchemaRef { - self.get_field(idx).unwrap() - } - - /// Get a reference to the field with the given name/index, if it exists. - pub fn get_field<'idx, 'ptr, I: Into>>( - &'ptr self, - idx: I, - ) -> Result, SchemaFieldNotFoundError<'idx>> { - self.as_ref().get_field(idx) - } - - /// Get a ref to the field with the given name/index, and panic if it doesn't exist. - #[inline] - #[track_caller] - pub fn field_mut<'idx, I: Into>>(&mut self, idx: I) -> SchemaRefMut { - self.get_field_mut(idx).unwrap() - } - - /// Get a mutable reference to the field with the given name/index, if it exists. - pub fn get_field_mut<'idx, 'ptr, I: Into>>( - &'ptr mut self, - idx: I, - ) -> Result, SchemaFieldNotFoundError<'idx>> { - let idx = idx.into(); - match self.as_mut().try_into_field(idx) { - Ok(r) => Ok(r), - Err(_) => Err(SchemaFieldNotFoundError { idx }), - } - } - /// Deallocate the memory in the box. unsafe fn dealloc(&mut self) { if self.schema.layout().size() > 0 { - std::alloc::dealloc(self.ptr.as_ptr(), self.schema.layout()) + std::alloc::dealloc(self.ptr.as_ptr() as *mut u8, self.schema.layout()) } } @@ -1159,7 +1628,7 @@ impl SchemaBox { unsafe fn drop_inner(&mut self) { if let Some(drop_fn) = self.schema.drop_fn { // Drop the type - (drop_fn)(self.ptr.as_mut().as_ptr()); + (drop_fn)(self.ptr.as_ptr()); } } } @@ -1309,12 +1778,13 @@ impl std::ops::Deref for SBox { fn deref(&self) -> &Self::Target { // SOUND: `SBox`s always contain their type `T`. - unsafe { self.b.ptr.as_ref().deref() } + unsafe { self.b.ptr.cast::().as_ref() } } } impl std::ops::DerefMut for SBox { fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { self.b.ptr.as_mut().deref_mut() } + // SOUND: `SBox`s always contain their type `T`. + unsafe { self.b.ptr.cast::().as_mut() } } } diff --git a/framework_crates/bones_schema/src/raw_fns.rs b/framework_crates/bones_schema/src/raw_fns.rs index 178b37fb21..ed8aeb5647 100644 --- a/framework_crates/bones_schema/src/raw_fns.rs +++ b/framework_crates/bones_schema/src/raw_fns.rs @@ -1,7 +1,10 @@ //! Traits implementing raw function calls for cloning, dropping, and creating default values for //! Rust types. -use std::hash::{Hash, Hasher}; +use std::{ + ffi::c_void, + hash::{Hash, Hasher}, +}; use bones_utils::fxhash::FxHasher; @@ -15,10 +18,10 @@ pub trait RawClone { /// The `dst` pointer must be aligned, writable, and have the same layout that this function is /// assocated to, and the `src` pointer must be readable and point to a valid instance of the /// type that this function is associated with. - unsafe extern "C-unwind" fn raw_clone(src: *const u8, dst: *mut u8); + unsafe extern "C-unwind" fn raw_clone(src: *const c_void, dst: *mut c_void); } impl RawClone for T { - unsafe extern "C-unwind" fn raw_clone(src: *const u8, dst: *mut u8) { + unsafe extern "C-unwind" fn raw_clone(src: *const c_void, dst: *mut c_void) { let t = &*(src as *const T); let t = t.clone(); (dst as *mut T).write(t) @@ -34,10 +37,10 @@ pub trait RawDrop { /// /// The pointer must be aligned, writable, and have the same layout that this function is /// assocated to. - unsafe extern "C-unwind" fn raw_drop(ptr: *mut u8); + unsafe extern "C-unwind" fn raw_drop(ptr: *mut c_void); } impl RawDrop for T { - unsafe extern "C-unwind" fn raw_drop(ptr: *mut u8) { + unsafe extern "C-unwind" fn raw_drop(ptr: *mut c_void) { if std::mem::needs_drop::() { (ptr as *mut T).drop_in_place() } @@ -53,10 +56,10 @@ pub trait RawHash { /// /// The pointer must be aligned, readable, and be a pointer to the type that this Hash function /// was created for. - unsafe extern "C-unwind" fn raw_hash(ptr: *const u8) -> u64; + unsafe extern "C-unwind" fn raw_hash(ptr: *const c_void) -> u64; } impl RawHash for T { - unsafe extern "C-unwind" fn raw_hash(ptr: *const u8) -> u64 { + unsafe extern "C-unwind" fn raw_hash(ptr: *const c_void) -> u64 { let this = unsafe { &*(ptr as *const Self) }; let mut hasher = FxHasher::default(); this.hash(&mut hasher); @@ -73,10 +76,10 @@ pub trait RawEq { /// /// The pointer must be aligned, readable, and be a pointer to the type that this Hash function /// was created for. - unsafe extern "C-unwind" fn raw_eq(a: *const u8, b: *const u8) -> bool; + unsafe extern "C-unwind" fn raw_eq(a: *const c_void, b: *const c_void) -> bool; } impl RawEq for T { - unsafe extern "C-unwind" fn raw_eq(a: *const u8, b: *const u8) -> bool { + unsafe extern "C-unwind" fn raw_eq(a: *const c_void, b: *const c_void) -> bool { let a = unsafe { &*(a as *const Self) }; let b = unsafe { &*(b as *const Self) }; a.eq(b) @@ -92,10 +95,10 @@ pub trait RawDefault { /// /// The pointer must be aligned, writable, and have the same layout that this function is /// assocated to. - unsafe extern "C-unwind" fn raw_default(dst: *mut u8); + unsafe extern "C-unwind" fn raw_default(dst: *mut c_void); } impl RawDefault for T { - unsafe extern "C-unwind" fn raw_default(dst: *mut u8) { + unsafe extern "C-unwind" fn raw_default(dst: *mut c_void) { let d = T::default(); (dst as *mut T).write(d) } diff --git a/framework_crates/bones_schema/src/schema.rs b/framework_crates/bones_schema/src/schema.rs index 8f3edda640..035eda1be1 100644 --- a/framework_crates/bones_schema/src/schema.rs +++ b/framework_crates/bones_schema/src/schema.rs @@ -1,6 +1,6 @@ //! [`Schema`], [`HasSchema`], [`SchemaData`], and related types. -use std::{alloc::Layout, any::TypeId, borrow::Cow}; +use std::{alloc::Layout, any::TypeId, borrow::Cow, ffi::c_void}; use crate::{alloc::TypeDatas, prelude::*}; @@ -177,20 +177,20 @@ pub struct SchemaData { pub type_id: Option, /// The function pointer that may be used to clone data with this schema. #[cfg_attr(feature = "serde", serde(skip))] - pub clone_fn: Option, + pub clone_fn: Option, /// The function pointer that may be used to drop data with this schema. #[cfg_attr(feature = "serde", serde(skip))] - pub drop_fn: Option, + pub drop_fn: Option, /// The function pointer that may be used to write a default value to a pointer. #[cfg_attr(feature = "serde", serde(skip))] - pub default_fn: Option, + pub default_fn: Option, /// The function pointer that may be used to hash the value. #[cfg_attr(feature = "serde", serde(skip))] - pub hash_fn: Option u64>, + pub hash_fn: Option u64>, /// The function pointer that may be used to compare two values for equality. Note that this is /// total equality, not partial equality. #[cfg_attr(feature = "serde", serde(skip))] - pub eq_fn: Option bool>, + pub eq_fn: Option bool>, } /// A schema describes the data layout of a type, to enable dynamic access to the type's data diff --git a/framework_crates/bones_schema/src/ser_de.rs b/framework_crates/bones_schema/src/ser_de.rs index ba4aef311e..dab50450ae 100644 --- a/framework_crates/bones_schema/src/ser_de.rs +++ b/framework_crates/bones_schema/src/ser_de.rs @@ -34,134 +34,104 @@ mod serializer_deserializer { return serializer.serialize_str(u); } - match &self.0.schema().kind { - SchemaKind::Struct(s) => { - if s.fields.len() == 1 && s.fields[0].name.is_none() { - // Serialize just the inner field - // SOUND: it is safe to cast a struct with one field to it's inner type. - SchemaSerializer(unsafe { - SchemaRef::from_ptr_schema(self.0.as_ptr(), s.fields[0].schema) - }) - .serialize(serializer) + return match self.0.access() { + SchemaRefAccess::Struct(s) => { + if s.fields().count() == 1 && s.fields().nth(0).unwrap().name.is_none() { + SchemaSerializer(s.fields().nth(0).unwrap().value).serialize(serializer) } else { - let named = s.fields.first().map(|x| x.name.is_some()).unwrap_or(false); + let named = s.fields().nth(0).map(|x| x.name.is_some()).unwrap_or(false); if named { let mut ser_struct = serializer - .serialize_struct(&self.0.schema().name, s.fields.len())?; - for (i, field) in s.fields.iter().enumerate() { + .serialize_struct(&self.0.schema().name, s.fields().count())?; + for field in s.fields() { ser_struct.serialize_field( field.name.as_ref().unwrap(), - &SchemaSerializer(self.0.field(i)), + &SchemaSerializer(field.value), )?; } ser_struct.end() } else { - let mut seq = serializer.serialize_seq(Some(s.fields.len()))?; - for i in 0..s.fields.len() { - seq.serialize_element(&SchemaSerializer(self.0.field(i)))?; + let mut seq = serializer.serialize_seq(Some(s.fields().count()))?; + for field in s.fields() { + seq.serialize_element(&SchemaSerializer(field.value))?; } seq.end() } } } - SchemaKind::Vec(_) => { - // SOUND: schema asserts this is a schema vec. - let v = unsafe { self.0.deref::() }; + SchemaRefAccess::Vec(v) => { let mut seq = serializer.serialize_seq(Some(v.len()))?; - for item in v { + for item in v.iter() { seq.serialize_element(&SchemaSerializer(item))?; } seq.end() } - SchemaKind::Enum(e) => { - let variant_idx = match e.tag_type { - EnumTagType::U8 => unsafe { self.0.as_ptr().cast::().read() as u32 }, - EnumTagType::U16 => unsafe { self.0.as_ptr().cast::().read() as u32 }, - EnumTagType::U32 => unsafe { self.0.as_ptr().cast::().read() }, - }; - - let variant_info = &e.variants[variant_idx as usize]; - let struct_info = variant_info.schema.kind.as_struct().unwrap(); + SchemaRefAccess::Map(m) => { + let mut map = serializer.serialize_map(Some(m.len()))?; + for (key, value) in m.iter() { + map.serialize_entry(&SchemaSerializer(key), &SchemaSerializer(value))?; + } + map.end() + } + SchemaRefAccess::Enum(e) => { + let variant_idx = e.variant_idx(); + let variant_info = e.variant_info(); + let access = e.value(); + let field_count = access.fields().count(); - if struct_info.fields.is_empty() { + if field_count == 0 { serializer.serialize_unit_variant( &self.0.schema().name, variant_idx, &variant_info.name, ) + } else if field_count == 1 && access.fields().nth(0).unwrap().name.is_none() { + serializer.serialize_newtype_variant( + &self.0.schema().name, + variant_idx, + &variant_info.name, + &SchemaSerializer(access.as_schema_ref()), + ) } else { - let value_offset = self.0.schema().field_offsets()[0].1; - // SOUND: we are returning a reference to the enum variants value - // offset by the size of it's discriminant, which is valid. - let value_ref = unsafe { - SchemaRef::from_ptr_schema( - self.0.as_ptr().add(value_offset), - variant_info.schema, - ) - }; - - if struct_info.fields.len() == 1 && struct_info.fields[0].name.is_none() { - serializer.serialize_newtype_variant( - &self.0.schema().name, - variant_idx, - &variant_info.name, - &SchemaSerializer(value_ref), - ) - } else { - let mut ser_struct = serializer.serialize_struct_variant( - &self.0.schema().name, - variant_idx, - &variant_info.name, - struct_info.fields.len(), - )?; - - for (i, field) in struct_info.fields.iter().enumerate() { - ser_struct.serialize_field( - field.name.as_ref().unwrap(), - &SchemaSerializer(value_ref.field(i)), - )?; - } + let mut ser_struct = serializer.serialize_struct_variant( + &self.0.schema().name, + variant_idx, + &variant_info.name, + field_count, + )?; - ser_struct.end() + for field in access.fields() { + ser_struct.serialize_field( + field.name.as_ref().unwrap(), + &SchemaSerializer(field.value), + )?; } + + ser_struct.end() } } - SchemaKind::Map { .. } => { - // SOUND: schema asserts this is a schema vec. - let m = unsafe { self.0.deref::() }; - let mut map = serializer.serialize_map(Some(m.len()))?; - for (key, value) in m { - map.serialize_entry(&SchemaSerializer(key), &SchemaSerializer(value))?; - } - map.end() - } - SchemaKind::Box(_) => { - // SOUND: schema asserts this is a schema box. - let b = unsafe { self.0.deref::() }; - SchemaSerializer(b.as_ref()).serialize(serializer) - } - SchemaKind::Primitive(p) => match p { - Primitive::Bool => serializer.serialize_bool(*self.0.cast::()), - Primitive::U8 => serializer.serialize_u8(*self.0.cast::()), - Primitive::U16 => serializer.serialize_u16(*self.0.cast::()), - Primitive::U32 => serializer.serialize_u32(*self.0.cast::()), - Primitive::U64 => serializer.serialize_u64(*self.0.cast::()), - Primitive::U128 => serializer.serialize_u128(*self.0.cast::()), - Primitive::I8 => serializer.serialize_i8(*self.0.cast::()), - Primitive::I16 => serializer.serialize_i16(*self.0.cast::()), - Primitive::I32 => serializer.serialize_i32(*self.0.cast::()), - Primitive::I64 => serializer.serialize_i64(*self.0.cast::()), - Primitive::I128 => serializer.serialize_i128(*self.0.cast::()), - Primitive::F32 => serializer.serialize_f32(*self.0.cast::()), - Primitive::F64 => serializer.serialize_f64(*self.0.cast::()), - Primitive::String => serializer.serialize_str(self.0.cast::()), - Primitive::Opaque { .. } => { + SchemaRefAccess::Primitive(p) => match p { + PrimitiveRef::Bool(b) => serializer.serialize_bool(*b), + PrimitiveRef::U8(n) => serializer.serialize_u8(*n), + PrimitiveRef::U16(n) => serializer.serialize_u16(*n), + PrimitiveRef::U32(n) => serializer.serialize_u32(*n), + PrimitiveRef::U64(n) => serializer.serialize_u64(*n), + PrimitiveRef::U128(n) => serializer.serialize_u128(*n), + PrimitiveRef::I8(n) => serializer.serialize_i8(*n), + PrimitiveRef::I16(n) => serializer.serialize_i16(*n), + PrimitiveRef::I32(n) => serializer.serialize_i32(*n), + PrimitiveRef::I64(n) => serializer.serialize_i64(*n), + PrimitiveRef::I128(n) => serializer.serialize_i128(*n), + PrimitiveRef::F32(n) => serializer.serialize_f32(*n), + PrimitiveRef::F64(n) => serializer.serialize_f64(*n), + PrimitiveRef::String(n) => serializer.serialize_str(n), + PrimitiveRef::Opaque { .. } => { use serde::ser::Error; Err(S::Error::custom("Cannot serialize opaque types")) } }, - } + }; } } @@ -188,7 +158,7 @@ mod serializer_deserializer { } } - impl<'a, 'b, 'de> DeserializeSeed<'de> for SchemaRefMut<'a, 'b> { + impl<'a, 'de> DeserializeSeed<'de> for SchemaRefMut<'a> { type Value = (); fn deserialize(mut self, deserializer: D) -> Result @@ -215,12 +185,7 @@ mod serializer_deserializer { SchemaKind::Vec(_) => deserializer.deserialize_seq(VecVisitor(self))?, SchemaKind::Map { .. } => deserializer.deserialize_map(MapVisitor(self))?, SchemaKind::Enum(_) => deserializer.deserialize_any(EnumVisitor(self))?, - SchemaKind::Box(_) => { - // SOUND: schema asserts this is a `SchemaBox` - let b = unsafe { self.deref_mut::() }; - - b.as_mut().deserialize(deserializer)? - } + SchemaKind::Box(_) => self.into_box().unwrap().deserialize(deserializer)?, SchemaKind::Primitive(p) => { match p { Primitive::Bool => *self.cast_mut() = bool::deserialize(deserializer)?, @@ -251,8 +216,8 @@ mod serializer_deserializer { } } - struct StructVisitor<'a, 'b>(SchemaRefMut<'a, 'b>); - impl<'a, 'b, 'de> Visitor<'de> for StructVisitor<'a, 'b> { + struct StructVisitor<'a>(SchemaRefMut<'a>); + impl<'a, 'de> Visitor<'de> for StructVisitor<'a> { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!( @@ -269,7 +234,7 @@ mod serializer_deserializer { let field_count = self.0.schema().kind.as_struct().unwrap().fields.len(); for i in 0..field_count { - let field = self.0.get_field(i).unwrap(); + let field = self.0.access_mut().field(i).unwrap().into_schema_ref_mut(); if seq.next_element_seed(field)?.is_none() { break; } @@ -283,7 +248,12 @@ mod serializer_deserializer { A: serde::de::MapAccess<'de>, { while let Some(key) = map.next_key::()? { - match self.0.get_field(&key) { + match self + .0 + .access_mut() + .field(&key) + .map(|x| x.into_schema_ref_mut()) + { Ok(field) => { map.next_value_seed(field)?; } @@ -314,8 +284,8 @@ mod serializer_deserializer { } } - struct VecVisitor<'a, 'b>(SchemaRefMut<'a, 'b>); - impl<'a, 'b, 'de> Visitor<'de> for VecVisitor<'a, 'b> { + struct VecVisitor<'a>(SchemaRefMut<'a>); + impl<'a, 'de> Visitor<'de> for VecVisitor<'a> { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!( @@ -344,8 +314,8 @@ mod serializer_deserializer { Ok(()) } } - struct MapVisitor<'a, 'b>(SchemaRefMut<'a, 'b>); - impl<'a, 'b, 'de> Visitor<'de> for MapVisitor<'a, 'b> { + struct MapVisitor<'a>(SchemaRefMut<'a>); + impl<'a, 'de> Visitor<'de> for MapVisitor<'a> { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!( @@ -381,8 +351,8 @@ mod serializer_deserializer { Ok(()) } } - struct EnumVisitor<'a, 'b>(SchemaRefMut<'a, 'b>); - impl<'a, 'b, 'de> Visitor<'de> for EnumVisitor<'a, 'b> { + struct EnumVisitor<'a>(SchemaRefMut<'a>); + impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> { type Value = (); fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!( @@ -438,9 +408,9 @@ mod serializer_deserializer { } } - struct EnumLoad<'a, 'b>(SchemaRefMut<'a, 'b>); - impl<'a, 'b, 'de> DeserializeSeed<'de> for EnumLoad<'a, 'b> { - type Value = SchemaRefMut<'a, 'b>; + struct EnumLoad<'a>(SchemaRefMut<'a>); + impl<'a, 'de> DeserializeSeed<'de> for EnumLoad<'a> { + type Value = SchemaRefMut<'a>; fn deserialize(self, deserializer: D) -> Result where @@ -469,7 +439,7 @@ mod serializer_deserializer { // Write the enum variant // SOUND: the schema asserts that the write to the enum discriminant is valid match enum_info.tag_type { - EnumTagType::U8 => unsafe { self.0.as_ptr().write(var_idx as u8) }, + EnumTagType::U8 => unsafe { self.0.as_ptr().cast::().write(var_idx as u8) }, EnumTagType::U16 => unsafe { self.0.as_ptr().cast::().write(var_idx as u16) }, EnumTagType::U32 => unsafe { self.0.as_ptr().cast::().write(var_idx as u32) }, } @@ -557,7 +527,7 @@ impl<'de> serde::de::Visitor<'de> for StructSchemaVisitor { pub struct SchemaDeserialize { /// The function that may be used to deserialize the type. pub deserialize_fn: for<'a, 'de> fn( - SchemaRefMut<'a, 'a>, + SchemaRefMut<'a>, deserializer: &'a mut dyn Deserializer<'de>, ) -> Result<(), erased_serde::Error>, } @@ -592,7 +562,7 @@ impl SchemaDeserialize { /// `reference`. pub fn deserialize<'a, 'de, D>( &self, - reference: SchemaRefMut<'a, 'a>, + reference: SchemaRefMut<'a>, deserializer: D, ) -> Result<(), D::Error> where diff --git a/framework_crates/bones_schema/src/std_impls.rs b/framework_crates/bones_schema/src/std_impls.rs index b50ecc5812..5408713a0a 100644 --- a/framework_crates/bones_schema/src/std_impls.rs +++ b/framework_crates/bones_schema/src/std_impls.rs @@ -4,6 +4,8 @@ use bones_utils::{fxhash::FxHasher, Ustr}; #[cfg(feature = "serde")] use serde::{de::Error, Deserialize}; +use std::ffi::c_void; + use crate::{alloc::TypeDatas, prelude::*, raw_fns::*}; use std::{alloc::Layout, any::TypeId, hash::Hasher, sync::OnceLock, time::Duration}; @@ -312,10 +314,10 @@ mod impl_glam { macro_rules! custom_fns_impl_bvec { ($ty:ident) => { impl CustomRawFns for glam::$ty { - unsafe extern "C-unwind" fn raw_hash(ptr: *const u8) -> u64 { + unsafe extern "C-unwind" fn raw_hash(ptr: *const c_void) -> u64 { ::raw_hash(ptr) } - unsafe extern "C-unwind" fn raw_eq(a: *const u8, b: *const u8) -> bool { + unsafe extern "C-unwind" fn raw_eq(a: *const c_void, b: *const c_void) -> bool { ::raw_eq(a, b) } } @@ -328,23 +330,23 @@ mod impl_glam { macro_rules! custom_fns_impl_glam { ($t:ty, $prim:ident, $($field:ident),+) => { impl CustomRawFns for $t { - unsafe extern "C-unwind" fn raw_hash(ptr: *const u8) -> u64 { + unsafe extern "C-unwind" fn raw_hash(ptr: *const c_void) -> u64 { let this = unsafe { &*(ptr as *const Self) }; let mut hasher = FxHasher::default(); $( - hasher.write_u64($prim::raw_hash(&this.$field as *const $prim as *const u8)); + hasher.write_u64($prim::raw_hash(&this.$field as *const $prim as *const c_void)); )+ hasher.finish() } - unsafe extern "C-unwind" fn raw_eq(a: *const u8, b: *const u8) -> bool { + unsafe extern "C-unwind" fn raw_eq(a: *const c_void, b: *const c_void) -> bool { let a = unsafe { &*(a as *const Self) }; let b = unsafe { &*(b as *const Self) }; $( $prim::raw_eq( - &a.$field as *const $prim as *const u8, - &b.$field as *const $prim as *const u8, + &a.$field as *const $prim as *const c_void, + &b.$field as *const $prim as *const c_void, ) )&&+ } @@ -368,14 +370,14 @@ mod impl_glam { /// Trait for types that require specific implementations of eq and hash fns, for use in this module only. trait CustomRawFns { - unsafe extern "C-unwind" fn raw_hash(ptr: *const u8) -> u64; - unsafe extern "C-unwind" fn raw_eq(a: *const u8, b: *const u8) -> bool; + unsafe extern "C-unwind" fn raw_hash(ptr: *const c_void) -> u64; + unsafe extern "C-unwind" fn raw_eq(a: *const c_void, b: *const c_void) -> bool; } macro_rules! custom_fns_impl_float { ($ty:ident) => { impl CustomRawFns for $ty { - unsafe extern "C-unwind" fn raw_hash(ptr: *const u8) -> u64 { + unsafe extern "C-unwind" fn raw_hash(ptr: *const c_void) -> u64 { let this = unsafe { &*(ptr as *const Self) }; let mut hasher = FxHasher::default(); @@ -391,7 +393,7 @@ macro_rules! custom_fns_impl_float { hasher.finish() } - unsafe extern "C-unwind" fn raw_eq(a: *const u8, b: *const u8) -> bool { + unsafe extern "C-unwind" fn raw_eq(a: *const c_void, b: *const c_void) -> bool { let a = unsafe { &*(a as *const Self) }; let b = unsafe { &*(b as *const Self) }; if a.is_nan() && a.is_nan() { diff --git a/framework_crates/bones_scripting/src/lua.rs b/framework_crates/bones_scripting/src/lua.rs index c752c4ecde..98440c8796 100644 --- a/framework_crates/bones_scripting/src/lua.rs +++ b/framework_crates/bones_scripting/src/lua.rs @@ -1,6 +1,7 @@ use crate::prelude::*; use append_only_vec::AppendOnlyVec; use bevy_tasks::{ComputeTaskPool, TaskPool, ThreadExecutor}; +use bones_asset::dashmap::mapref::one::{MappedRef, MappedRefMut}; use bones_lib::ecs::utils::*; use parking_lot::Mutex; use piccolo::{ @@ -8,7 +9,7 @@ use piccolo::{ StaticTable, Table, Thread, ThreadMode, Value, }; use send_wrapper::SendWrapper; -use std::sync::Arc; +use std::{rc::Rc, sync::Arc}; #[macro_use] mod freeze; @@ -285,14 +286,143 @@ impl LuaData { } } -/// Schema [type data][TypeDatas] that may be used to create a custom lua metatable for this type -/// when it is accessed in Lua scripts -#[derive(HasSchema, Clone, Copy, Debug)] -#[schema(no_default)] -struct SchemaLuaMetatable(pub fn(Context) -> StaticTable); +/// A reference to an ECS-compatible value. +#[derive(Clone)] +pub struct EcsRef { + /// The kind of reference. + pub data: EcsRefData, + /// The path to the desired field. + pub path: Ustr, +} + +/// The kind of value reference for [`EcsRef`]. +#[derive(Clone)] +pub enum EcsRefData { + /// A resource ref. + Resource(UntypedAtomicResource), + /// A component ref. + Component(ComponentRef), + /// An asset ref. + Asset(AssetRef), + /// A free-standing ref, not stored in the ECS. + Free(Rc>), +} + +pub enum EcsRefBorrowKind<'a> { + Resource(AtomicSchemaRef<'a>), + Component(ComponentBorrow<'a>), + Free(Ref<'a, SchemaBox>), + Asset(Option>), +} + +impl EcsRefBorrowKind<'_> { + /// Will return none if the value does not exist, such as an unloaded asset or a component + /// that is not set for a given entity. + pub fn access(&self) -> Option { + match self { + EcsRefBorrowKind::Resource(r) => Some(r.as_ref().access()), + EcsRefBorrowKind::Component(c) => c.borrow.get_ref(c.entity).map(|x| x.access()), + EcsRefBorrowKind::Free(f) => Some(f.as_ref().access()), + EcsRefBorrowKind::Asset(a) => a.as_ref().map(|x| x.as_ref().access()), + } + } +} + +pub struct ComponentBorrow<'a> { + pub borrow: Ref<'a, UntypedComponentStore>, + pub entity: Entity, +} + +pub struct ComponentBorrowMut<'a> { + pub borrow: RefMut<'a, UntypedComponentStore>, + pub entity: Entity, +} + +pub enum EcsRefBorrowMutKind<'a> { + Resource(AtomicSchemaRefMut<'a>), + Component(ComponentBorrowMut<'a>), + Free(RefMut<'a, SchemaBox>), + Asset(Option>), +} + +impl EcsRefBorrowMutKind<'_> { + pub fn access_mut(&mut self) -> Option { + match self { + EcsRefBorrowMutKind::Resource(r) => Some(r.access_mut()), + EcsRefBorrowMutKind::Component(c) => { + c.borrow.get_ref_mut(c.entity).map(|x| x.into_access_mut()) + } + EcsRefBorrowMutKind::Free(f) => Some(f.as_mut().into_access_mut()), + EcsRefBorrowMutKind::Asset(a) => a.as_mut().map(|x| x.as_mut().into_access_mut()), + } + } +} + +impl EcsRefData { + pub fn borrow(&self) -> EcsRefBorrowKind { + match self { + EcsRefData::Resource(resource) => { + let b = resource.borrow(); + EcsRefBorrowKind::Resource(b) + } + EcsRefData::Component(componentref) => { + let b = componentref.store.borrow(); + EcsRefBorrowKind::Component(ComponentBorrow { + borrow: b, + entity: componentref.entity, + }) + } + EcsRefData::Asset(assetref) => { + let b = assetref.server.try_get_untyped(assetref.handle); + EcsRefBorrowKind::Asset(b) + } + EcsRefData::Free(rc) => { + let b = rc.borrow(); + EcsRefBorrowKind::Free(b) + } + } + } + + /// Mutably borrow the ref. + pub fn borrow_mut(&self) -> EcsRefBorrowMutKind { + match self { + EcsRefData::Resource(resource) => { + let b = resource.borrow_mut(); + EcsRefBorrowMutKind::Resource(b) + } + EcsRefData::Component(componentref) => { + let b = componentref.store.borrow_mut(); + EcsRefBorrowMutKind::Component(ComponentBorrowMut { + borrow: b, + entity: componentref.entity, + }) + } + EcsRefData::Asset(assetref) => { + let b = assetref.server.try_get_untyped_mut(assetref.handle); + EcsRefBorrowMutKind::Asset(b) + } + EcsRefData::Free(rc) => { + let b = rc.borrow_mut(); + EcsRefBorrowMutKind::Free(b) + } + } + } +} + +/// A resource ref. +#[derive(Clone)] +pub struct ComponentRef { + /// The component store. + pub store: UntypedAtomicComponentStore, + /// The entity to get the component data for. + pub entity: Entity, +} -/// A reference to a resource -struct LuaRef { - cell: UntypedAtomicResource, - path: Ustr, +/// An asset ref. +#[derive(Clone)] +pub struct AssetRef { + /// The asset server handle. + pub server: AssetServer, + /// The kind of asset we are referencing. + pub handle: UntypedHandle, } diff --git a/framework_crates/bones_scripting/src/lua/bindings.rs b/framework_crates/bones_scripting/src/lua/bindings.rs index 79ec0c42d0..d8dbda4786 100644 --- a/framework_crates/bones_scripting/src/lua/bindings.rs +++ b/framework_crates/bones_scripting/src/lua/bindings.rs @@ -9,10 +9,10 @@ use super::*; /// Registers lua binding typedatas for bones_framework types. pub fn register_lua_typedata() { - ::schema() - .type_data - .insert(SchemaLuaMetatable(assetserver_metatable)) - .unwrap(); + // ::schema() + // .type_data + // .insert(SchemaLuaMetatable(assetserver_metatable)) + // .unwrap(); } pub fn no_newindex(ctx: Context) -> StaticCallback { @@ -24,53 +24,30 @@ pub fn no_newindex(ctx: Context) -> StaticCallback { ) } -pub fn assetserver_metatable(ctx: Context) -> StaticTable { - let metatable = Table::new(&ctx); - let static_metatable = ctx.state.registry.stash(&ctx, metatable); - metatable - .set( - ctx, - "__tostring", - AnyCallback::from_fn(&ctx, move |ctx, _fuel, stack| { - let value = stack.pop_front(); - if let Value::UserData(data) = value { - let server = data.downcast_static::()?; - let server = server.cell.borrow(); - let _server = server.try_cast::()?; - stack.push_front(piccolo::String::from_static(&ctx, "AssetServer").into()); - }; - Ok(CallbackReturn::Return) - }), - ) - .unwrap(); - - static_metatable -} - pub fn luaref_metatable(ctx: Context) -> StaticTable { let metatable = Table::new(&ctx); let static_metatable = ctx.state.registry.stash(&ctx, metatable); - let static_metatable_ = static_metatable.clone(); + metatable .set( ctx, "__tostring", AnyCallback::from_fn(&ctx, move |ctx, _fuel, stack| { - let value = stack.pop_front(); - if let Value::UserData(data) = value { - if let Ok(res) = data.downcast_static::() { - let data = res.cell.borrow().get_field_path(FieldPath(res.path))?; - stack - .push_front(piccolo::String::from_slice(&ctx, data.to_string()).into()); - } else { - stack.push_front( - piccolo::String::from_slice(&ctx, &format!("{value}")).into(), - ); - } - } else { - stack.push_front(piccolo::String::from_slice(&ctx, &format!("{value}")).into()); + let this = stack.pop_front(); + let Value::UserData(this) = this else { + return Err(anyhow::format_err!("Invalid type").into()); }; + let this = this.downcast_static::()?; + let b = this.data.borrow(); + if let Some(value) = b.access() { + if let Some(value) = value.field_path(FieldPath(this.path)) { + stack.push_front(Value::String(piccolo::String::from_slice( + &ctx, + format!("{value:?}"), + ))); + } + } Ok(CallbackReturn::Return) }), ) @@ -82,65 +59,56 @@ pub fn luaref_metatable(ctx: Context) -> StaticTable { AnyCallback::from_fn(&ctx, move |ctx, _fuel, stack| { let this = stack.pop_front(); let key = stack.pop_front(); - let type_err = "Invalid type for `self` in schemabox metatable."; let Value::UserData(this) = this else { - return Err(anyhow::format_err!(type_err).into()); - }; - - let schemaref; - let mut path; - let cell; - if let Ok(res) = this.downcast_static::() { - cell = res.cell.clone(); - schemaref = res.cell.borrow(); - path = res.path; - } else { - return Err(anyhow::format_err!(type_err).into()); + return Err(anyhow::format_err!( + "Invalid type for `self` in schemabox metatable." + ) + .into()); }; - - if let Value::String(key) = key { - path = ustr(&format!("{path}.{}", key.to_str()?)); - } else if let Value::Integer(i) = key { - path = ustr(&format!("{path}.{i}")); - } else { - return Err(anyhow::format_err!("Invalid index: {key}").into()); - } - let schemaref = schemaref.get_field_path(FieldPath(path))?; - - match &schemaref.schema().kind { - SchemaKind::Struct(_) - | SchemaKind::Primitive(Primitive::Opaque { .. }) - | SchemaKind::Vec(_) - | SchemaKind::Enum(_) - | SchemaKind::Map { .. } - | SchemaKind::Box(_) => { - let new_ref = AnyUserData::new_static(&ctx, LuaRef { cell, path }); - new_ref.set_metatable( - &ctx, - Some(ctx.state.registry.fetch(&static_metatable_)), - ); - stack.push_front(new_ref.into()); + let this = this.downcast_static::()?; + let b = this.data.borrow(); + let newpath = ustr(&format!("{}.{key}", this.path)); + + if let Some(field) = b.access().and_then(|x| x.field_path(FieldPath(newpath))) { + match field { + SchemaRefAccess::Primitive(p) + if !matches!(p, PrimitiveRef::Opaque { .. }) => + { + match p { + PrimitiveRef::Bool(b) => stack.push_front(Value::Boolean(*b)), + PrimitiveRef::U8(n) => stack.push_front(Value::Integer(*n as i64)), + PrimitiveRef::U16(n) => stack.push_front(Value::Integer(*n as i64)), + PrimitiveRef::U32(n) => stack.push_front(Value::Integer(*n as i64)), + PrimitiveRef::U64(n) => stack.push_front(Value::Integer(*n as i64)), + PrimitiveRef::U128(n) => { + stack.push_front(Value::Integer(*n as i64)) + } + PrimitiveRef::I8(n) => stack.push_front(Value::Integer(*n as i64)), + PrimitiveRef::I16(n) => stack.push_front(Value::Integer(*n as i64)), + PrimitiveRef::I32(n) => stack.push_front(Value::Integer(*n as i64)), + PrimitiveRef::I64(n) => stack.push_front(Value::Integer(*n)), + PrimitiveRef::I128(n) => { + stack.push_front(Value::Integer(*n as i64)) + } + PrimitiveRef::F32(n) => stack.push_front(Value::Number(*n as f64)), + PrimitiveRef::F64(n) => stack.push_front(Value::Number(*n)), + PrimitiveRef::String(s) => stack.push_front(Value::String( + piccolo::String::from_slice(&ctx, s), + )), + PrimitiveRef::Opaque { .. } => unreachable!(), + } + } + _ => { + let mut newref = this.clone(); + newref.path = newpath; + let data = AnyUserData::new_static(&ctx, newref); + data.set_metatable( + &ctx, + Some(ctx.state.registry.fetch(&static_metatable_)), + ); + stack.push_front(data.into()); + } } - SchemaKind::Primitive(prim) => stack.push_front(match prim { - Primitive::Bool => Value::Boolean(*schemaref.cast::()), - Primitive::U8 => Value::Integer(*schemaref.cast::() as i64), - Primitive::U16 => Value::Integer(*schemaref.cast::() as i64), - Primitive::U32 => Value::Integer(*schemaref.cast::() as i64), - Primitive::U64 => Value::Integer(*schemaref.cast::() as i64), - Primitive::U128 => Value::Integer(*schemaref.cast::() as i64), - Primitive::I8 => Value::Integer(*schemaref.cast::() as i64), - Primitive::I16 => Value::Integer(*schemaref.cast::() as i64), - Primitive::I32 => Value::Integer(*schemaref.cast::() as i64), - Primitive::I64 => Value::Integer(*schemaref.cast::()), - Primitive::I128 => Value::Integer(*schemaref.cast::() as i64), - Primitive::F32 => Value::Number(*schemaref.cast::() as f64), - Primitive::F64 => Value::Number(*schemaref.cast::()), - Primitive::String => Value::String(piccolo::String::from_slice( - &ctx, - schemaref.cast::().clone(), - )), - Primitive::Opaque { .. } => unreachable!(), - }), } Ok(CallbackReturn::Return) @@ -151,101 +119,117 @@ pub fn luaref_metatable(ctx: Context) -> StaticTable { .set( ctx, "__newindex", - AnyCallback::from_fn(&ctx, move |_ctx, _fuel, stack| { + AnyCallback::from_fn(&ctx, move |ctx, _fuel, stack| { let this = stack.pop_front(); let key = stack.pop_front(); let newvalue = stack.pop_front(); - let type_err = "Invalid type for `self` in schemabox metatable."; let Value::UserData(this) = this else { - return Err(anyhow::format_err!(type_err).into()); + return Err(anyhow::format_err!( + "Invalid type for `self` in schemabox metatable." + ) + .into()); }; + let this = this.downcast_static::()?; + let mut borrow = this.data.borrow_mut(); + let Some(access) = borrow.access_mut() else { + return Err(anyhow::format_err!("Value not found").into()); + }; + let newpath = ustr(&format!("{}.{key}", this.path)); - let mut schemaref; - let mut path; - if let Ok(res) = this.downcast_static::() { - schemaref = res.cell.borrow_mut(); - path = res.path; - } else { - return Err(anyhow::format_err!(type_err).into()); + let field_idx = match key { + Value::Integer(i) => FieldIdx::Idx(i as usize), + Value::String(s) => FieldIdx::Name(match s.to_str() { + Ok(s) => s, + Err(_) => return Err(anyhow::format_err!("Non UTF-8 string").into()), + }), + _ => return Err(anyhow::format_err!("Invalid index: {key}").into()), }; - if let Value::String(key) = key { - path = ustr(&format!("{path}.{}", key.to_str()?)); - } else if let Value::Integer(i) = key { - path = ustr(&format!("{path}.{i}")); - } else { - return Err(anyhow::format_err!("Invalid index: {key}").into()); - } - let mut schemaref = - schemaref.try_get_field_path(FieldPath(path)).map_err(|_| { - piccolo::Error::from(anyhow::format_err!("Attribute doesn't exist: {path}")) - })?; - - match &schemaref.schema().kind { - SchemaKind::Struct(_) | SchemaKind::Primitive(Primitive::Opaque { .. }) => { - return Err(anyhow::format_err!("Cannot assign to structs directly").into()); - } - SchemaKind::Vec(_) => todo!(), - SchemaKind::Enum(_) => todo!(), - SchemaKind::Map { .. } => todo!(), - SchemaKind::Box(_) => todo!(), - SchemaKind::Primitive(prim) => match (prim, newvalue) { - (Primitive::Bool, Value::Boolean(value)) => { - *schemaref.cast_mut::() = value; - } - (Primitive::U8, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as u8; - } - (Primitive::U16, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as u16; - } - (Primitive::U32, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as u32; - } - (Primitive::U64, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as u64; - } - (Primitive::U128, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as u128; - } - (Primitive::I8, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as i8; - } - (Primitive::I16, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as i16; - } - (Primitive::I32, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as i32; - } - (Primitive::I64, Value::Integer(value)) => { - *schemaref.cast_mut::() = value; - } - (Primitive::I128, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as i128; - } - (Primitive::F32, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as f32; - } - (Primitive::F64, Value::Integer(value)) => { - *schemaref.cast_mut::() = value as f32; - } - (Primitive::F32, Value::Number(value)) => { - *schemaref.cast_mut::() = value as f32; - } - (Primitive::F64, Value::Number(value)) => { - *schemaref.cast_mut::() = value; - } - (Primitive::String, Value::String(string)) => { - *schemaref.cast_mut::() = string.to_str()?.to_owned(); - } - (Primitive::Opaque { .. }, _) => unreachable!(), - _ => { - return Err(anyhow::format_err!( - "Type mismatch, expected `{prim:?}` found {newvalue:?}" - ) - .into()) + match access { + SchemaRefMutAccess::Struct(s) => { + match s + .into_field(field_idx) + .map_err(|_| anyhow::format_err!("Field not found: {field_idx}"))? + { + SchemaRefMutAccess::Struct(s) => { + if s.into_field(field_idx).is_ok() { + let newecsref = EcsRef { + data: this.data.clone(), + path: newpath, + }; + let newecsref = AnyUserData::new_static(&ctx, newecsref); + newecsref.set_metatable( + &ctx, + Some(ctx.state.registry.fetch(&static_metatable)), + ); + stack.push_front(newecsref.into()); + } + } + SchemaRefMutAccess::Vec(_) + | SchemaRefMutAccess::Enum(_) + | SchemaRefMutAccess::Map(_) => { + todo!("Implement vec, enum, and map assigment") + } + SchemaRefMutAccess::Primitive(p) => match (p, newvalue) { + (PrimitiveRefMut::Bool(b), Value::Boolean(newb)) => *b = newb, + (PrimitiveRefMut::U8(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::U16(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::U32(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::U64(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::U128(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::I8(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::I16(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::I32(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::I64(n), Value::Integer(newi)) => *n = newi, + (PrimitiveRefMut::I128(n), Value::Integer(newi)) => { + *n = newi.try_into().unwrap() + } + (PrimitiveRefMut::F32(n), Value::Number(newf)) => *n = newf as f32, + (PrimitiveRefMut::F64(n), Value::Number(newf)) => *n = newf, + (PrimitiveRefMut::String(s), Value::String(news)) => { + if let Ok(news) = news.to_str() { + s.clear(); + s.push_str(news); + } else { + return Err(anyhow::format_err!( + "Non UTF-8 string assignment." + ) + .into()); + } + } + (PrimitiveRefMut::Opaque { .. }, Value::UserData(_)) => { + todo!("Opaque type assignment") + } + _ => return Err(anyhow::format_err!("Invalid type").into()), + }, } - }, + } + SchemaRefMutAccess::Vec(_) + | SchemaRefMutAccess::Enum(_) + | SchemaRefMutAccess::Map(_) => { + todo!("Implement vec, enum, and map assigment.") + } + SchemaRefMutAccess::Primitive(_) => { + return Err( + anyhow::format_err!("Cannot assign to field of primitive.").into() + ) + } } Ok(CallbackReturn::Return) @@ -288,24 +272,18 @@ pub fn resources_metatable(ctx: Context) -> StaticTable { ); }; let schema = schema.downcast_static::<&Schema>()?; - let metatable = if let Some(t) = schema.type_data.get::() { - ctx.state.registry.fetch(&ctx.luadata().table(ctx, t.0)) - } else { - ctx.state.registry.fetch(&luaref_metatable) - }; world.with(|world| { let cell = world.resources.untyped().get_cell(schema.id()); - if let Some(cell) = cell { let data = AnyUserData::new_static( &ctx, - LuaRef { - cell, - path: ustr(""), + EcsRef { + data: EcsRefData::Resource(cell), + path: default(), }, ); - data.set_metatable(&ctx, Some(metatable)); + data.set_metatable(&ctx, Some(ctx.state.registry.fetch(&luaref_metatable))); stack.push_front(data.into()); } }); @@ -426,7 +404,7 @@ pub fn world_metatable(ctx: Context) -> StaticTable { pub fn schema_metatable(ctx: Context) -> StaticTable { let metatable = Table::new(&ctx); let luadata = ctx.luadata(); - let luaref_metatable = luadata.table(ctx, luaref_metatable); + let ecsref_metatable = luadata.table(ctx, luaref_metatable); metatable .set( @@ -455,19 +433,15 @@ pub fn schema_metatable(ctx: Context) -> StaticTable { return Err(type_err.into()); }; let this = this.downcast_static::<&Schema>()?; - let metatable = if let Some(t) = this.type_data.get::() { - ctx.state.registry.fetch(&ctx.luadata().table(ctx, t.0)) - } else { - ctx.state.registry.fetch(&luaref_metatable) - }; + let data = AnyUserData::new_static( &ctx, - LuaRef { - cell: UntypedAtomicResource::new(SchemaBox::default(this)), + EcsRef { + data: EcsRefData::Free(Rc::new(AtomicCell::new(SchemaBox::default(this)))), path: default(), }, ); - data.set_metatable(&ctx, Some(metatable)); + data.set_metatable(&ctx, Some(ctx.state.registry.fetch(&ecsref_metatable))); stack.push_front(data.into()); Ok(CallbackReturn::Return) diff --git a/framework_crates/bones_utils/Cargo.toml b/framework_crates/bones_utils/Cargo.toml index b2a3a0cfc8..b51cee58b9 100644 --- a/framework_crates/bones_utils/Cargo.toml +++ b/framework_crates/bones_utils/Cargo.toml @@ -19,7 +19,6 @@ smallvec = "1.11" fxhash = "0.2" hashbrown = { version = "0.14" } ulid = "1.0" -bevy_ptr = "0.11" parking_lot = "0.12" serde = { version = "1.0", optional = true } maybe-owned = "0.3" diff --git a/framework_crates/bones_utils/src/lib.rs b/framework_crates/bones_utils/src/lib.rs index 404bdba4df..6f6ced8874 100644 --- a/framework_crates/bones_utils/src/lib.rs +++ b/framework_crates/bones_utils/src/lib.rs @@ -10,17 +10,13 @@ mod collections; mod default; mod labeled_id; mod names; -mod ptr; mod random; mod ulid; /// Helper to export the same types in the crate root and in the prelude. macro_rules! pub_use { () => { - pub use crate::{ - collections::*, default::*, labeled_id::*, names::*, ptr::*, random::*, ulid::*, - }; - pub use bevy_ptr::*; + pub use crate::{collections::*, default::*, labeled_id::*, names::*, random::*, ulid::*}; pub use bones_utils_macros::*; pub use branches::{likely, unlikely}; pub use futures_lite as futures; diff --git a/framework_crates/bones_utils/src/ptr.rs b/framework_crates/bones_utils/src/ptr.rs deleted file mode 100644 index 4877feabec..0000000000 --- a/framework_crates/bones_utils/src/ptr.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::ptr::NonNull; - -use bevy_ptr::{Ptr, PtrMut}; - -/// Extension trait with utils for [`PtrMut`]. -pub trait PtrMutExt { - /// Unsafely alter the lifetime of this [`PtrMut`]. - /// # Safety - /// You must ensure that the data referenced by this pointer will be valid for the new lifetime. - unsafe fn transmute_lifetime<'new>(self) -> PtrMut<'new>; -} -impl<'a> PtrMutExt for PtrMut<'a> { - unsafe fn transmute_lifetime<'new>(self) -> PtrMut<'new> { - PtrMut::new(NonNull::new_unchecked(self.as_ptr())) - } -} - -/// Extension trait with utils for [`Ptr`]. -pub trait PtrExt { - /// Unsafely alter the lifetime of this [`Ptr`]. - /// # Safety - /// You must ensure that the data referenced by this pointer will be valid for the new lifetime. - unsafe fn transmute_lifetime<'new>(self) -> Ptr<'new>; -} -impl<'a> PtrExt for Ptr<'a> { - unsafe fn transmute_lifetime<'new>(self) -> Ptr<'new> { - Ptr::new(NonNull::new_unchecked(self.as_ptr())) - } -}