Skip to content

Commit

Permalink
bevy_ptr standalone crate (#4653)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
jakobhellermann committed May 4, 2022
1 parent 9d440fb commit 1e322d9
Show file tree
Hide file tree
Showing 18 changed files with 62 additions and 12 deletions.
1 change: 1 addition & 0 deletions crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ trace = []
default = ["bevy_reflect"]

[dependencies]
bevy_ptr = { path = "../bevy_ptr", version = "0.8.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", optional = true }
bevy_tasks = { path = "../bevy_tasks", version = "0.8.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use crate::{
archetype::{AddBundle, Archetype, ArchetypeId, Archetypes, ComponentStatus},
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{Entities, Entity, EntityLocation},
ptr::OwningPtr,
storage::{SparseSetIndex, SparseSets, Storages, Table},
};
use bevy_ecs_macros::all_tuples;
use bevy_ptr::OwningPtr;
use std::{any::TypeId, collections::HashMap};

/// An ordered collection of [`Component`]s.
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Types for declaring and storing [`Component`]s.
use crate::{
ptr::OwningPtr,
storage::{SparseSetIndex, Storages},
system::Resource,
};
pub use bevy_ecs_macros::Component;
use bevy_ptr::OwningPtr;
use std::{
alloc::Layout,
any::{Any, TypeId},
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ pub mod change_detection;
pub mod component;
pub mod entity;
pub mod event;
pub mod ptr;
pub mod query;
#[cfg(feature = "bevy_reflect")]
pub mod reflect;
Expand All @@ -15,6 +14,8 @@ pub mod storage;
pub mod system;
pub mod world;

pub use bevy_ptr as ptr;

/// Most commonly used re-exported types.
pub mod prelude {
#[doc(hidden)]
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/query/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use crate::{
change_detection::Ticks,
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
entity::Entity,
ptr::{ThinSlicePtr, UnsafeCellDeref},
query::{debug_checked_unreachable, Access, FilteredAccess},
storage::{ComponentSparseSet, Table, Tables},
world::{Mut, World},
};
use bevy_ecs_macros::all_tuples;
pub use bevy_ecs_macros::WorldQuery;
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use std::{cell::UnsafeCell, marker::PhantomData};

/// Types that can be queried from a [`World`].
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/query/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::{
archetype::{Archetype, ArchetypeComponentId},
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
entity::Entity,
ptr::{ThinSlicePtr, UnsafeCellDeref},
query::{
debug_checked_unreachable, Access, Fetch, FetchState, FilteredAccess, QueryFetch,
ROQueryFetch, WorldQuery, WorldQueryGats,
Expand All @@ -11,6 +10,7 @@ use crate::{
world::World,
};
use bevy_ecs_macros::all_tuples;
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use std::{cell::UnsafeCell, marker::PhantomData};

use super::ReadOnlyFetch;
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/storage/blob_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
ptr::NonNull,
};

use crate::ptr::{OwningPtr, Ptr, PtrMut};
use bevy_ptr::{OwningPtr, Ptr, PtrMut};

/// A flat, type-erased data storage type
///
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/storage/sparse_set.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{
component::{ComponentId, ComponentInfo, ComponentTicks},
entity::Entity,
ptr::{OwningPtr, Ptr},
storage::BlobVec,
};
use bevy_ptr::{OwningPtr, Ptr};
use std::{cell::UnsafeCell, marker::PhantomData};

#[derive(Debug)]
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/storage/table.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{
component::{ComponentId, ComponentInfo, ComponentTicks, Components},
entity::Entity,
ptr::{OwningPtr, Ptr, PtrMut},
storage::{BlobVec, SparseSet},
};
use bevy_ptr::{OwningPtr, Ptr, PtrMut};
use bevy_utils::HashMap;
use std::{
cell::UnsafeCell,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/system/system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::{
change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components},
entity::{Entities, Entity},
ptr::UnsafeCellDeref,
query::{
Access, FilteredAccess, FilteredAccessSet, QueryFetch, QueryState, ReadOnlyFetch,
WorldQuery,
Expand All @@ -15,6 +14,7 @@ use crate::{
};
pub use bevy_ecs_macros::SystemParam;
use bevy_ecs_macros::{all_tuples, impl_param_set};
use bevy_ptr::UnsafeCellDeref;
use std::{
fmt::Debug,
marker::PhantomData,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use crate::{
change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{Entities, Entity, EntityLocation},
ptr::{OwningPtr, Ptr, UnsafeCellDeref},
storage::{SparseSet, Storages},
world::{Mut, World},
};
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
use std::{any::TypeId, cell::UnsafeCell};

/// A read-only reference to a particular [`Entity`] and all of its components
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ use crate::{
change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{AllocAtWithoutReplacement, Entities, Entity},
ptr::{OwningPtr, UnsafeCellDeref},
query::{QueryState, WorldQuery},
storage::{Column, SparseSet, Storages},
system::Resource,
};
use bevy_ptr::{OwningPtr, UnsafeCellDeref};
use bevy_utils::tracing::debug;
use std::{
any::TypeId,
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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_log = { path = "../bevy_log", 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_scene = { path = "../bevy_scene", version = "0.8.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.8.0-dev" }
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_internal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ pub mod 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 {
// TODO: remove these renames once TypeRegistryArc is no longer required
//! Type reflection used for dynamically interacting with rust types.
Expand Down
11 changes: 11 additions & 0 deletions crates/bevy_ptr/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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]
7 changes: 7 additions & 0 deletions crates/bevy_ptr/README.md
Original file line number Diff line number Diff line change
@@ -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.
25 changes: 24 additions & 1 deletion crates/bevy_ecs/src/ptr.rs → crates/bevy_ptr/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
use std::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, ptr::NonNull};

/// Type-erased borrow of some unknown type chosen when constructing this type.
Expand Down Expand Up @@ -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;

/// # 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;

/// 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
where
T: Copy;
}

impl<'a, T> UnsafeCellDeref<'a, T> for &'a UnsafeCell<T> {
#[inline]
unsafe fn deref_mut(self) -> &'a mut T {
Expand Down
1 change: 1 addition & 0 deletions tools/publish.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# if crate A depends on crate B, B must come before A in this list
crates=(
bevy_utils
bevy_ptr
bevy_macro_utils
bevy_derive
bevy_math
Expand Down

0 comments on commit 1e322d9

Please sign in to comment.