bevy_ptr standalone crate (#4653)

# Objective

The pointer types introduced in #3001 are useful not just in `bevy_ecs`, but also in crates like `bevy_reflect` (#4475) or even outside of bevy.

## Solution

Extract `Ptr<'a>`, `PtrMut<'a>`, `OwnedPtr<'a>`, `ThinSlicePtr<'a, T>` and `UnsafeCellDeref` from `bevy_ecs::ptr` into `bevy_ptr`.

**Note:** `bevy_ecs` still reexports the `bevy_ptr` as `bevy_ecs::ptr` so that crates like `bevy_transform` can use the `Bundle` derive without needing to depend on `bevy_ptr` themselves.
This commit is contained in:
Jakob Hellermann 2022-05-04 19:16:10 +00:00
parent 9d440fbcb5
commit 1e322d9f76
18 changed files with 62 additions and 12 deletions

View file

@ -14,6 +14,7 @@ trace = []
default = ["bevy_reflect"] default = ["bevy_reflect"]
[dependencies] [dependencies]
bevy_ptr = { path = "../bevy_ptr", version = "0.8.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", optional = true } bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", optional = true }
bevy_tasks = { path = "../bevy_tasks", version = "0.8.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.8.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }

View file

@ -8,10 +8,10 @@ use crate::{
archetype::{AddBundle, Archetype, ArchetypeId, Archetypes, ComponentStatus}, archetype::{AddBundle, Archetype, ArchetypeId, Archetypes, ComponentStatus},
component::{Component, ComponentId, ComponentTicks, Components, StorageType}, component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{Entities, Entity, EntityLocation}, entity::{Entities, Entity, EntityLocation},
ptr::OwningPtr,
storage::{SparseSetIndex, SparseSets, Storages, Table}, storage::{SparseSetIndex, SparseSets, Storages, Table},
}; };
use bevy_ecs_macros::all_tuples; use bevy_ecs_macros::all_tuples;
use bevy_ptr::OwningPtr;
use std::{any::TypeId, collections::HashMap}; use std::{any::TypeId, collections::HashMap};
/// An ordered collection of [`Component`]s. /// An ordered collection of [`Component`]s.

View file

@ -1,11 +1,11 @@
//! Types for declaring and storing [`Component`]s. //! Types for declaring and storing [`Component`]s.
use crate::{ use crate::{
ptr::OwningPtr,
storage::{SparseSetIndex, Storages}, storage::{SparseSetIndex, Storages},
system::Resource, system::Resource,
}; };
pub use bevy_ecs_macros::Component; pub use bevy_ecs_macros::Component;
use bevy_ptr::OwningPtr;
use std::{ use std::{
alloc::Layout, alloc::Layout,
any::{Any, TypeId}, any::{Any, TypeId},

View file

@ -6,7 +6,6 @@ pub mod change_detection;
pub mod component; pub mod component;
pub mod entity; pub mod entity;
pub mod event; pub mod event;
pub mod ptr;
pub mod query; pub mod query;
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
pub mod reflect; pub mod reflect;
@ -15,6 +14,8 @@ pub mod storage;
pub mod system; pub mod system;
pub mod world; pub mod world;
pub use bevy_ptr as ptr;
/// Most commonly used re-exported types. /// Most commonly used re-exported types.
pub mod prelude { pub mod prelude {
#[doc(hidden)] #[doc(hidden)]

View file

@ -3,13 +3,13 @@ use crate::{
change_detection::Ticks, change_detection::Ticks,
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType}, component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
entity::Entity, entity::Entity,
ptr::{ThinSlicePtr, UnsafeCellDeref},
query::{debug_checked_unreachable, Access, FilteredAccess}, query::{debug_checked_unreachable, Access, FilteredAccess},
storage::{ComponentSparseSet, Table, Tables}, storage::{ComponentSparseSet, Table, Tables},
world::{Mut, World}, world::{Mut, World},
}; };
use bevy_ecs_macros::all_tuples; use bevy_ecs_macros::all_tuples;
pub use bevy_ecs_macros::WorldQuery; pub use bevy_ecs_macros::WorldQuery;
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use std::{cell::UnsafeCell, marker::PhantomData}; use std::{cell::UnsafeCell, marker::PhantomData};
/// Types that can be queried from a [`World`]. /// Types that can be queried from a [`World`].

View file

@ -2,7 +2,6 @@ use crate::{
archetype::{Archetype, ArchetypeComponentId}, archetype::{Archetype, ArchetypeComponentId},
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType}, component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
entity::Entity, entity::Entity,
ptr::{ThinSlicePtr, UnsafeCellDeref},
query::{ query::{
debug_checked_unreachable, Access, Fetch, FetchState, FilteredAccess, QueryFetch, debug_checked_unreachable, Access, Fetch, FetchState, FilteredAccess, QueryFetch,
ROQueryFetch, WorldQuery, WorldQueryGats, ROQueryFetch, WorldQuery, WorldQueryGats,
@ -11,6 +10,7 @@ use crate::{
world::World, world::World,
}; };
use bevy_ecs_macros::all_tuples; use bevy_ecs_macros::all_tuples;
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use std::{cell::UnsafeCell, marker::PhantomData}; use std::{cell::UnsafeCell, marker::PhantomData};
use super::ReadOnlyFetch; use super::ReadOnlyFetch;

View file

@ -4,7 +4,7 @@ use std::{
ptr::NonNull, ptr::NonNull,
}; };
use crate::ptr::{OwningPtr, Ptr, PtrMut}; use bevy_ptr::{OwningPtr, Ptr, PtrMut};
/// A flat, type-erased data storage type /// A flat, type-erased data storage type
/// ///

View file

@ -1,9 +1,9 @@
use crate::{ use crate::{
component::{ComponentId, ComponentInfo, ComponentTicks}, component::{ComponentId, ComponentInfo, ComponentTicks},
entity::Entity, entity::Entity,
ptr::{OwningPtr, Ptr},
storage::BlobVec, storage::BlobVec,
}; };
use bevy_ptr::{OwningPtr, Ptr};
use std::{cell::UnsafeCell, marker::PhantomData}; use std::{cell::UnsafeCell, marker::PhantomData};
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,9 +1,9 @@
use crate::{ use crate::{
component::{ComponentId, ComponentInfo, ComponentTicks, Components}, component::{ComponentId, ComponentInfo, ComponentTicks, Components},
entity::Entity, entity::Entity,
ptr::{OwningPtr, Ptr, PtrMut},
storage::{BlobVec, SparseSet}, storage::{BlobVec, SparseSet},
}; };
use bevy_ptr::{OwningPtr, Ptr, PtrMut};
use bevy_utils::HashMap; use bevy_utils::HashMap;
use std::{ use std::{
cell::UnsafeCell, cell::UnsafeCell,

View file

@ -5,7 +5,6 @@ use crate::{
change_detection::Ticks, change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components}, component::{Component, ComponentId, ComponentTicks, Components},
entity::{Entities, Entity}, entity::{Entities, Entity},
ptr::UnsafeCellDeref,
query::{ query::{
Access, FilteredAccess, FilteredAccessSet, QueryFetch, QueryState, ReadOnlyFetch, Access, FilteredAccess, FilteredAccessSet, QueryFetch, QueryState, ReadOnlyFetch,
WorldQuery, WorldQuery,
@ -15,6 +14,7 @@ use crate::{
}; };
pub use bevy_ecs_macros::SystemParam; pub use bevy_ecs_macros::SystemParam;
use bevy_ecs_macros::{all_tuples, impl_param_set}; use bevy_ecs_macros::{all_tuples, impl_param_set};
use bevy_ptr::UnsafeCellDeref;
use std::{ use std::{
fmt::Debug, fmt::Debug,
marker::PhantomData, marker::PhantomData,

View file

@ -4,10 +4,10 @@ use crate::{
change_detection::Ticks, change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components, StorageType}, component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{Entities, Entity, EntityLocation}, entity::{Entities, Entity, EntityLocation},
ptr::{OwningPtr, Ptr, UnsafeCellDeref},
storage::{SparseSet, Storages}, storage::{SparseSet, Storages},
world::{Mut, World}, world::{Mut, World},
}; };
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
use std::{any::TypeId, cell::UnsafeCell}; use std::{any::TypeId, cell::UnsafeCell};
/// A read-only reference to a particular [`Entity`] and all of its components /// A read-only reference to a particular [`Entity`] and all of its components

View file

@ -13,11 +13,11 @@ use crate::{
change_detection::Ticks, change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components, StorageType}, component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{AllocAtWithoutReplacement, Entities, Entity}, entity::{AllocAtWithoutReplacement, Entities, Entity},
ptr::{OwningPtr, UnsafeCellDeref},
query::{QueryState, WorldQuery}, query::{QueryState, WorldQuery},
storage::{Column, SparseSet, Storages}, storage::{Column, SparseSet, Storages},
system::Resource, system::Resource,
}; };
use bevy_ptr::{OwningPtr, UnsafeCellDeref};
use bevy_utils::tracing::debug; use bevy_utils::tracing::debug;
use std::{ use std::{
any::TypeId, any::TypeId,

View file

@ -75,6 +75,7 @@ bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.8.0-dev" }
bevy_input = { path = "../bevy_input", version = "0.8.0-dev" } bevy_input = { path = "../bevy_input", version = "0.8.0-dev" }
bevy_log = { path = "../bevy_log", version = "0.8.0-dev" } bevy_log = { path = "../bevy_log", version = "0.8.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.8.0-dev" } bevy_math = { path = "../bevy_math", version = "0.8.0-dev" }
bevy_ptr = { path = "../bevy_ptr", version = "0.8.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", features = ["bevy"] } bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", features = ["bevy"] }
bevy_scene = { path = "../bevy_scene", version = "0.8.0-dev" } bevy_scene = { path = "../bevy_scene", version = "0.8.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.8.0-dev" } bevy_transform = { path = "../bevy_transform", version = "0.8.0-dev" }

View file

@ -47,6 +47,11 @@ pub mod math {
pub use bevy_math::*; pub use bevy_math::*;
} }
pub mod ptr {
//! Utilities for working with untyped pointers in a more safe way.
pub use bevy_ptr::*;
}
pub mod reflect { pub mod reflect {
// TODO: remove these renames once TypeRegistryArc is no longer required // TODO: remove these renames once TypeRegistryArc is no longer required
//! Type reflection used for dynamically interacting with rust types. //! Type reflection used for dynamically interacting with rust types.

View file

@ -0,0 +1,11 @@
[package]
name = "bevy_ptr"
version = "0.8.0-dev"
edition = "2021"
description = "Utilities for working with untyped pointers in a more safe way"
homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[dependencies]

View file

@ -0,0 +1,7 @@
# `bevy_ptr`
The `bevy_ptr` crate provides low-level abstractions for working with pointers in a more safe way than using rust's raw pointers.
Rust has lifetimed and typed references (`&'a T`), unlifetimed and typed references (`*const T`), but no lifetimed but untyped references.
`bevy_ptr` adds them, called `Ptr<'a>`, `PtrMut<'a>` and `OwningPtr<'a>`.
These types are lifetime-checked so can never lead to problems like use-after-frees and must always point to valid data.

View file

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
use std::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ptr::NonNull}; use std::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
/// Type-erased borrow of some unknown type chosen when constructing this type. /// Type-erased borrow of some unknown type chosen when constructing this type.
@ -249,13 +250,35 @@ impl<'a, T> From<&'a [T]> for ThinSlicePtr<'a, T> {
} }
} }
pub(crate) trait UnsafeCellDeref<'a, T> { mod private {
use std::cell::UnsafeCell;
pub trait SealedUnsafeCell {}
impl<'a, T> SealedUnsafeCell for &'a UnsafeCell<T> {}
}
/// Extension trait for helper methods on [`UnsafeCell`]
pub trait UnsafeCellDeref<'a, T>: private::SealedUnsafeCell {
/// # Safety
/// - The returned value must be unique and not alias any mutable or immutable references to the contents of the [`UnsafeCell`].
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
unsafe fn deref_mut(self) -> &'a mut T; unsafe fn deref_mut(self) -> &'a mut T;
/// # Safety
/// - For the lifetime `'a` of the returned value you must not construct a mutable reference to the contents of the [`UnsafeCell`].
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
unsafe fn deref(self) -> &'a T; unsafe fn deref(self) -> &'a T;
/// Returns a copy of the contained value.
///
/// # Safety
/// - The [`UnsafeCell`] must not currently have a mutable reference to its content.
/// - At all times, you must avoid data races. If multiple threads have access to the same [`UnsafeCell`], then any writes must have a proper happens-before relation to all other accesses or use atomics ([`UnsafeCell`] docs for reference).
unsafe fn read(self) -> T unsafe fn read(self) -> T
where where
T: Copy; T: Copy;
} }
impl<'a, T> UnsafeCellDeref<'a, T> for &'a UnsafeCell<T> { impl<'a, T> UnsafeCellDeref<'a, T> for &'a UnsafeCell<T> {
#[inline] #[inline]
unsafe fn deref_mut(self) -> &'a mut T { unsafe fn deref_mut(self) -> &'a mut T {

View file

@ -1,6 +1,7 @@
# if crate A depends on crate B, B must come before A in this list # if crate A depends on crate B, B must come before A in this list
crates=( crates=(
bevy_utils bevy_utils
bevy_ptr
bevy_macro_utils bevy_macro_utils
bevy_derive bevy_derive
bevy_math bevy_math