From bd489068c6537a6f87e19a682e15e27afbeafdaa Mon Sep 17 00:00:00 2001 From: Zachary Harrold Date: Thu, 19 Sep 2024 02:00:03 +1000 Subject: [PATCH] Allow `bevy_utils` in `no_std` Contexts (#15279) # Objective - Adjust `bevy_utils` to make it `no_std` compatible - Partially replaces #6581 - Contributes to #8161 - Contributes to #6370 ## Solution Added `alloc` and `std` features to `bevy_utils` (`std` is enabled by default), allowing the crate's use in `no_std` contexts. ## Testing - CI passed locally. - Used `bevy_utils` in a `no_std` crate as an experiment and compiled successfully. ## Migration Guide If you were importing `bevy_utils` and setting `default_features` to `false`, but relying on elements which are now gated behind the `std` or `alloc` features, include the relevant feature in your `Cargo.toml`. ## Notes - Bevy already includes a single `no_std` crate, `bevy_ptr`, so there is precedent for this change. - As `bevy_utils` is widely used across the rest of Bevy, further work to make Bevy `no_std` compatible would be blocked on this crate, if such work was to be undertaken. - Most of the changes in this PR are just the removal of an unnecessary call to `to_string()` within unit tests. --- crates/bevy_utils/Cargo.toml | 9 ++++-- crates/bevy_utils/src/futures.rs | 4 +-- crates/bevy_utils/src/lib.rs | 27 +++++++++++++----- crates/bevy_utils/src/parallel_queue.rs | 6 +++- crates/bevy_utils/src/short_names.rs | 37 ++++++++++--------------- crates/bevy_utils/src/synccell.rs | 2 +- 6 files changed, 50 insertions(+), 35 deletions(-) diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index 52f995961a..7f83ec17c9 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -9,11 +9,16 @@ license = "MIT OR Apache-2.0" keywords = ["bevy"] [features] +default = ["std"] +std = ["alloc", "tracing/std", "ahash/std"] +alloc = [] detailed_trace = [] [dependencies] -ahash = "0.8.7" -tracing = { version = "0.1", default-features = false, features = ["std"] } +ahash = { version = "0.8.7", default-features = false, features = [ + "runtime-rng", +] } +tracing = { version = "0.1", default-features = false } web-time = { version = "1.1" } hashbrown = { version = "0.14.2", features = ["serde"] } bevy_utils_proc_macros = { version = "0.15.0-dev", path = "macros" } diff --git a/crates/bevy_utils/src/futures.rs b/crates/bevy_utils/src/futures.rs index acf19797ea..6a4f9ff9cc 100644 --- a/crates/bevy_utils/src/futures.rs +++ b/crates/bevy_utils/src/futures.rs @@ -1,5 +1,5 @@ //! Utilities for working with [`Future`]s. -use std::{ +use core::{ future::Future, pin::Pin, task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, @@ -44,7 +44,7 @@ fn noop(_data: *const ()) {} const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); fn noop_raw_waker() -> RawWaker { - RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE) + RawWaker::new(core::ptr::null(), &NOOP_WAKER_VTABLE) } fn noop_waker() -> Waker { diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index 4c04b793df..d729dfa33d 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -4,12 +4,16 @@ html_logo_url = "https://bevyengine.org/assets/icon.png", html_favicon_url = "https://bevyengine.org/assets/icon.png" )] +#![cfg_attr(not(feature = "std"), no_std)] //! General utilities for first-party [Bevy] engine crates. //! //! [Bevy]: https://bevyengine.org/ //! +#[cfg(feature = "alloc")] +extern crate alloc; + /// The utilities prelude. /// /// This includes the most common types in this crate, re-exported for your convenience. @@ -18,7 +22,9 @@ pub mod prelude { } pub mod futures; +#[cfg(feature = "alloc")] mod short_names; +#[cfg(feature = "alloc")] pub use short_names::get_short_name; pub mod synccell; pub mod syncunsafecell; @@ -37,8 +43,10 @@ pub use parallel_queue::*; pub use tracing; pub use web_time::{Duration, Instant, SystemTime, SystemTimeError, TryFromFloatSecsError}; -use hashbrown::hash_map::RawEntryMut; -use std::{ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use core::{ any::TypeId, fmt::Debug, hash::{BuildHasher, BuildHasherDefault, Hash, Hasher}, @@ -46,6 +54,7 @@ use std::{ mem::ManuallyDrop, ops::Deref, }; +use hashbrown::hash_map::RawEntryMut; #[cfg(not(target_arch = "wasm32"))] mod conditional_send { @@ -66,11 +75,12 @@ pub use conditional_send::*; /// Use [`ConditionalSendFuture`] for a future with an optional Send trait bound, as on certain platforms (eg. Wasm), /// futures aren't Send. -pub trait ConditionalSendFuture: std::future::Future + ConditionalSend {} -impl ConditionalSendFuture for T {} +pub trait ConditionalSendFuture: core::future::Future + ConditionalSend {} +impl ConditionalSendFuture for T {} /// An owned and dynamically typed Future used when you can't statically type your result or need to add some indirection. -pub type BoxedFuture<'a, T> = std::pin::Pin + 'a>>; +#[cfg(feature = "alloc")] +pub type BoxedFuture<'a, T> = core::pin::Pin + 'a>>; /// A shortcut alias for [`hashbrown::hash_map::Entry`]. pub type Entry<'a, K, V, S = BuildHasherDefault> = hashbrown::hash_map::Entry<'a, K, V, S>; @@ -192,7 +202,7 @@ impl PartialEq for Hashed { } impl Debug for Hashed { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Hashed") .field("hash", &self.hash) .field("value", &self.value) @@ -417,7 +427,7 @@ mod tests { fn fast_typeid_hash() { struct Hasher; - impl std::hash::Hasher for Hasher { + impl core::hash::Hasher for Hasher { fn finish(&self) -> u64 { 0 } @@ -430,8 +440,11 @@ mod tests { Hash::hash(&TypeId::of::<()>(), &mut Hasher); } + #[cfg(feature = "alloc")] #[test] fn stable_hash_within_same_program_execution() { + use alloc::vec::Vec; + let mut map_1 = HashMap::new(); let mut map_2 = HashMap::new(); for i in 1..10 { diff --git a/crates/bevy_utils/src/parallel_queue.rs b/crates/bevy_utils/src/parallel_queue.rs index 61741da4f2..01377c7573 100644 --- a/crates/bevy_utils/src/parallel_queue.rs +++ b/crates/bevy_utils/src/parallel_queue.rs @@ -1,4 +1,7 @@ -use std::{cell::RefCell, ops::DerefMut}; +#[cfg(all(feature = "alloc", not(feature = "std")))] +use alloc::vec::Vec; + +use core::{cell::RefCell, ops::DerefMut}; use thread_local::ThreadLocal; /// A cohesive set of thread-local values of a given type. @@ -56,6 +59,7 @@ where } } +#[cfg(feature = "alloc")] impl Parallel> { /// Collect all enqueued items from all threads and appends them to the end of a /// single Vec. diff --git a/crates/bevy_utils/src/short_names.rs b/crates/bevy_utils/src/short_names.rs index 60a7b70d40..d78b9c5394 100644 --- a/crates/bevy_utils/src/short_names.rs +++ b/crates/bevy_utils/src/short_names.rs @@ -1,3 +1,5 @@ +use alloc::string::String; + /// Shortens a type name to remove all module paths. /// /// The short name of a type is its full name as returned by @@ -88,43 +90,37 @@ mod name_formatting_tests { fn path_separated() { assert_eq!( get_short_name("bevy_prelude::make_fun_game"), - "make_fun_game".to_string() + "make_fun_game" ); } #[test] fn tuple_type() { - assert_eq!( - get_short_name("(String, String)"), - "(String, String)".to_string() - ); + assert_eq!(get_short_name("(String, String)"), "(String, String)"); } #[test] fn array_type() { - assert_eq!(get_short_name("[i32; 3]"), "[i32; 3]".to_string()); + assert_eq!(get_short_name("[i32; 3]"), "[i32; 3]"); } #[test] fn trivial_generics() { - assert_eq!(get_short_name("a"), "a".to_string()); + assert_eq!(get_short_name("a"), "a"); } #[test] fn multiple_type_parameters() { - assert_eq!(get_short_name("a"), "a".to_string()); + assert_eq!(get_short_name("a"), "a"); } #[test] fn enums() { - assert_eq!(get_short_name("Option::None"), "Option::None".to_string()); - assert_eq!( - get_short_name("Option::Some(2)"), - "Option::Some(2)".to_string() - ); + assert_eq!(get_short_name("Option::None"), "Option::None"); + assert_eq!(get_short_name("Option::Some(2)"), "Option::Some(2)"); assert_eq!( get_short_name("bevy_render::RenderSet::Prepare"), - "RenderSet::Prepare".to_string() + "RenderSet::Prepare" ); } @@ -132,7 +128,7 @@ mod name_formatting_tests { fn generics() { assert_eq!( get_short_name("bevy_render::camera::camera::extract_cameras"), - "extract_cameras".to_string() + "extract_cameras" ); } @@ -140,7 +136,7 @@ mod name_formatting_tests { fn nested_generics() { assert_eq!( get_short_name("bevy::mad_science::do_mad_science, bavy::TypeSystemAbuse>"), - "do_mad_science, TypeSystemAbuse>".to_string() + "do_mad_science, TypeSystemAbuse>" ); } @@ -148,15 +144,12 @@ mod name_formatting_tests { fn sub_path_after_closing_bracket() { assert_eq!( get_short_name("bevy_asset::assets::Assets::asset_event_system"), - "Assets::asset_event_system".to_string() + "Assets::asset_event_system" ); assert_eq!( get_short_name("(String, String)::default"), - "(String, String)::default".to_string() - ); - assert_eq!( - get_short_name("[i32; 16]::default"), - "[i32; 16]::default".to_string() + "(String, String)::default" ); + assert_eq!(get_short_name("[i32; 16]::default"), "[i32; 16]::default"); } } diff --git a/crates/bevy_utils/src/synccell.rs b/crates/bevy_utils/src/synccell.rs index e99c97b7d3..628fb2756d 100644 --- a/crates/bevy_utils/src/synccell.rs +++ b/crates/bevy_utils/src/synccell.rs @@ -2,7 +2,7 @@ //! //! [`std::sync::Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html -use std::ptr; +use core::ptr; /// See [`Exclusive`](https://github.com/rust-lang/rust/issues/98407) for stdlib's upcoming implementation, /// which should replace this one entirely.