bevy_utils/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![expect(
3    unsafe_code,
4    reason = "Some utilities, such as futures and cells, require unsafe code."
5)]
6#![doc(
7    html_logo_url = "https://bevyengine.org/assets/icon.png",
8    html_favicon_url = "https://bevyengine.org/assets/icon.png"
9)]
10#![cfg_attr(not(feature = "std"), no_std)]
11
12//! General utilities for first-party [Bevy] engine crates.
13//!
14//! [Bevy]: https://bevyengine.org/
15
16#[cfg(feature = "alloc")]
17extern crate alloc;
18
19/// The utilities prelude.
20///
21/// This includes the most common types in this crate, re-exported for your convenience.
22pub mod prelude {
23    pub use crate::default;
24}
25
26pub mod futures;
27pub mod synccell;
28pub mod syncunsafecell;
29
30mod default;
31mod object_safe;
32pub use object_safe::assert_object_safe;
33mod once;
34#[cfg(feature = "std")]
35mod parallel_queue;
36mod time;
37
38pub use ahash::{AHasher, RandomState};
39pub use bevy_utils_proc_macros::*;
40pub use default::default;
41pub use hashbrown;
42#[cfg(feature = "std")]
43pub use parallel_queue::*;
44pub use time::*;
45pub use tracing;
46
47#[cfg(feature = "alloc")]
48use alloc::boxed::Box;
49
50use core::{
51    any::TypeId,
52    fmt::Debug,
53    hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
54    marker::PhantomData,
55    mem::ManuallyDrop,
56    ops::Deref,
57};
58use hashbrown::hash_map::RawEntryMut;
59
60#[cfg(not(target_arch = "wasm32"))]
61mod conditional_send {
62    /// Use [`ConditionalSend`] to mark an optional Send trait bound. Useful as on certain platforms (eg. Wasm),
63    /// futures aren't Send.
64    pub trait ConditionalSend: Send {}
65    impl<T: Send> ConditionalSend for T {}
66}
67
68#[cfg(target_arch = "wasm32")]
69#[expect(missing_docs, reason = "Not all docs are written yet (#3492).")]
70mod conditional_send {
71    pub trait ConditionalSend {}
72    impl<T> ConditionalSend for T {}
73}
74
75pub use conditional_send::*;
76
77/// Use [`ConditionalSendFuture`] for a future with an optional Send trait bound, as on certain platforms (eg. Wasm),
78/// futures aren't Send.
79pub trait ConditionalSendFuture: core::future::Future + ConditionalSend {}
80impl<T: core::future::Future + ConditionalSend> ConditionalSendFuture for T {}
81
82/// An owned and dynamically typed Future used when you can't statically type your result or need to add some indirection.
83#[cfg(feature = "alloc")]
84pub type BoxedFuture<'a, T> = core::pin::Pin<Box<dyn ConditionalSendFuture<Output = T> + 'a>>;
85
86/// A shortcut alias for [`hashbrown::hash_map::Entry`].
87pub type Entry<'a, K, V, S = BuildHasherDefault<AHasher>> = hashbrown::hash_map::Entry<'a, K, V, S>;
88
89/// A hasher builder that will create a fixed hasher.
90#[derive(Debug, Clone, Default)]
91pub struct FixedState;
92
93impl BuildHasher for FixedState {
94    type Hasher = AHasher;
95
96    #[inline]
97    fn build_hasher(&self) -> AHasher {
98        RandomState::with_seeds(
99            0b10010101111011100000010011000100,
100            0b00000011001001101011001001111000,
101            0b11001111011010110111100010110101,
102            0b00000100001111100011010011010101,
103        )
104        .build_hasher()
105    }
106}
107
108/// A [`HashMap`][hashbrown::HashMap] implementing aHash, a high
109/// speed keyed hashing algorithm intended for use in in-memory hashmaps.
110///
111/// aHash is designed for performance and is NOT cryptographically secure.
112///
113/// Within the same execution of the program iteration order of different
114/// `HashMap`s only depends on the order of insertions and deletions,
115/// but it will not be stable between multiple executions of the program.
116pub type HashMap<K, V> = hashbrown::HashMap<K, V, BuildHasherDefault<AHasher>>;
117
118/// A stable hash map implementing aHash, a high speed keyed hashing algorithm
119/// intended for use in in-memory hashmaps.
120///
121/// Unlike [`HashMap`] the iteration order stability extends between executions
122/// using the same Bevy version on the same device.
123///
124/// aHash is designed for performance and is NOT cryptographically secure.
125#[deprecated(
126    note = "Will be required to use the hash library of your choice. Alias for: hashbrown::HashMap<K, V, FixedState>"
127)]
128pub type StableHashMap<K, V> = hashbrown::HashMap<K, V, FixedState>;
129
130/// A [`HashSet`][hashbrown::HashSet] implementing aHash, a high
131/// speed keyed hashing algorithm intended for use in in-memory hashmaps.
132///
133/// aHash is designed for performance and is NOT cryptographically secure.
134///
135/// Within the same execution of the program iteration order of different
136/// `HashSet`s only depends on the order of insertions and deletions,
137/// but it will not be stable between multiple executions of the program.
138pub type HashSet<K> = hashbrown::HashSet<K, BuildHasherDefault<AHasher>>;
139
140/// A stable hash set implementing aHash, a high speed keyed hashing algorithm
141/// intended for use in in-memory hashmaps.
142///
143/// Unlike [`HashMap`] the iteration order stability extends between executions
144/// using the same Bevy version on the same device.
145///
146/// aHash is designed for performance and is NOT cryptographically secure.
147#[deprecated(
148    note = "Will be required to use the hash library of your choice. Alias for: hashbrown::HashSet<K, FixedState>"
149)]
150pub type StableHashSet<K> = hashbrown::HashSet<K, FixedState>;
151
152/// A pre-hashed value of a specific type. Pre-hashing enables memoization of hashes that are expensive to compute.
153///
154/// It also enables faster [`PartialEq`] comparisons by short circuiting on hash equality.
155/// See [`PassHash`] and [`PassHasher`] for a "pass through" [`BuildHasher`] and [`Hasher`] implementation
156/// designed to work with [`Hashed`]
157/// See [`PreHashMap`] for a hashmap pre-configured to use [`Hashed`] keys.
158pub struct Hashed<V, H = FixedState> {
159    hash: u64,
160    value: V,
161    marker: PhantomData<H>,
162}
163
164impl<V: Hash, H: BuildHasher + Default> Hashed<V, H> {
165    /// Pre-hashes the given value using the [`BuildHasher`] configured in the [`Hashed`] type.
166    pub fn new(value: V) -> Self {
167        Self {
168            hash: H::default().hash_one(&value),
169            value,
170            marker: PhantomData,
171        }
172    }
173
174    /// The pre-computed hash.
175    #[inline]
176    pub fn hash(&self) -> u64 {
177        self.hash
178    }
179}
180
181impl<V, H> Hash for Hashed<V, H> {
182    #[inline]
183    fn hash<R: Hasher>(&self, state: &mut R) {
184        state.write_u64(self.hash);
185    }
186}
187
188impl<V, H> Deref for Hashed<V, H> {
189    type Target = V;
190
191    #[inline]
192    fn deref(&self) -> &Self::Target {
193        &self.value
194    }
195}
196
197impl<V: PartialEq, H> PartialEq for Hashed<V, H> {
198    /// A fast impl of [`PartialEq`] that first checks that `other`'s pre-computed hash
199    /// matches this value's pre-computed hash.
200    #[inline]
201    fn eq(&self, other: &Self) -> bool {
202        self.hash == other.hash && self.value.eq(&other.value)
203    }
204}
205
206impl<V: Debug, H> Debug for Hashed<V, H> {
207    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208        f.debug_struct("Hashed")
209            .field("hash", &self.hash)
210            .field("value", &self.value)
211            .finish()
212    }
213}
214
215impl<V: Clone, H> Clone for Hashed<V, H> {
216    #[inline]
217    fn clone(&self) -> Self {
218        Self {
219            hash: self.hash,
220            value: self.value.clone(),
221            marker: PhantomData,
222        }
223    }
224}
225
226impl<V: Copy, H> Copy for Hashed<V, H> {}
227
228impl<V: Eq, H> Eq for Hashed<V, H> {}
229
230/// A [`BuildHasher`] that results in a [`PassHasher`].
231#[derive(Default, Clone)]
232pub struct PassHash;
233
234impl BuildHasher for PassHash {
235    type Hasher = PassHasher;
236
237    fn build_hasher(&self) -> Self::Hasher {
238        PassHasher::default()
239    }
240}
241
242/// A no-op hash that only works on `u64`s. Will panic if attempting to
243/// hash a type containing non-u64 fields.
244#[derive(Debug, Default)]
245pub struct PassHasher {
246    hash: u64,
247}
248
249impl Hasher for PassHasher {
250    #[inline]
251    fn finish(&self) -> u64 {
252        self.hash
253    }
254
255    fn write(&mut self, _bytes: &[u8]) {
256        panic!("can only hash u64 using PassHasher");
257    }
258
259    #[inline]
260    fn write_u64(&mut self, i: u64) {
261        self.hash = i;
262    }
263}
264
265/// A [`HashMap`] pre-configured to use [`Hashed`] keys and [`PassHash`] passthrough hashing.
266/// Iteration order only depends on the order of insertions and deletions.
267pub type PreHashMap<K, V> = hashbrown::HashMap<Hashed<K>, V, PassHash>;
268
269/// Extension methods intended to add functionality to [`PreHashMap`].
270pub trait PreHashMapExt<K, V> {
271    /// Tries to get or insert the value for the given `key` using the pre-computed hash first.
272    /// If the [`PreHashMap`] does not already contain the `key`, it will clone it and insert
273    /// the value returned by `func`.
274    fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &Hashed<K>, func: F) -> &mut V;
275}
276
277impl<K: Hash + Eq + PartialEq + Clone, V> PreHashMapExt<K, V> for PreHashMap<K, V> {
278    #[inline]
279    fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &Hashed<K>, func: F) -> &mut V {
280        let entry = self
281            .raw_entry_mut()
282            .from_key_hashed_nocheck(key.hash(), key);
283        match entry {
284            RawEntryMut::Occupied(entry) => entry.into_mut(),
285            RawEntryMut::Vacant(entry) => {
286                let (_, value) = entry.insert_hashed_nocheck(key.hash(), key.clone(), func());
287                value
288            }
289        }
290    }
291}
292
293/// A specialized hashmap type with Key of [`TypeId`]
294/// Iteration order only depends on the order of insertions and deletions.
295pub type TypeIdMap<V> = hashbrown::HashMap<TypeId, V, NoOpHash>;
296
297/// [`BuildHasher`] for types that already contain a high-quality hash.
298#[derive(Clone, Default)]
299pub struct NoOpHash;
300
301impl BuildHasher for NoOpHash {
302    type Hasher = NoOpHasher;
303
304    fn build_hasher(&self) -> Self::Hasher {
305        NoOpHasher(0)
306    }
307}
308
309#[doc(hidden)]
310pub struct NoOpHasher(u64);
311
312// This is for types that already contain a high-quality hash and want to skip
313// re-hashing that hash.
314impl Hasher for NoOpHasher {
315    fn finish(&self) -> u64 {
316        self.0
317    }
318
319    fn write(&mut self, bytes: &[u8]) {
320        // This should never be called by consumers. Prefer to call `write_u64` instead.
321        // Don't break applications (slower fallback, just check in test):
322        self.0 = bytes.iter().fold(self.0, |hash, b| {
323            hash.rotate_left(8).wrapping_add(*b as u64)
324        });
325    }
326
327    #[inline]
328    fn write_u64(&mut self, i: u64) {
329        self.0 = i;
330    }
331}
332
333/// A type which calls a function when dropped.
334/// This can be used to ensure that cleanup code is run even in case of a panic.
335///
336/// Note that this only works for panics that [unwind](https://doc.rust-lang.org/nomicon/unwinding.html)
337/// -- any code within `OnDrop` will be skipped if a panic does not unwind.
338/// In most cases, this will just work.
339///
340/// # Examples
341///
342/// ```
343/// # use bevy_utils::OnDrop;
344/// # fn test_panic(do_panic: bool, log: impl FnOnce(&str)) {
345/// // This will print a message when the variable `_catch` gets dropped,
346/// // even if a panic occurs before we reach the end of this scope.
347/// // This is similar to a `try ... catch` block in languages such as C++.
348/// let _catch = OnDrop::new(|| log("Oops, a panic occurred and this function didn't complete!"));
349///
350/// // Some code that may panic...
351/// // ...
352/// # if do_panic { panic!() }
353///
354/// // Make sure the message only gets printed if a panic occurs.
355/// // If we remove this line, then the message will be printed regardless of whether a panic occurs
356/// // -- similar to a `try ... finally` block.
357/// core::mem::forget(_catch);
358/// # }
359/// #
360/// # test_panic(false, |_| unreachable!());
361/// # let mut did_log = false;
362/// # std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
363/// #   test_panic(true, |_| did_log = true);
364/// # }));
365/// # assert!(did_log);
366/// ```
367pub struct OnDrop<F: FnOnce()> {
368    callback: ManuallyDrop<F>,
369}
370
371impl<F: FnOnce()> OnDrop<F> {
372    /// Returns an object that will invoke the specified callback when dropped.
373    pub fn new(callback: F) -> Self {
374        Self {
375            callback: ManuallyDrop::new(callback),
376        }
377    }
378}
379
380impl<F: FnOnce()> Drop for OnDrop<F> {
381    fn drop(&mut self) {
382        // SAFETY: We may move out of `self`, since this instance can never be observed after it's dropped.
383        let callback = unsafe { ManuallyDrop::take(&mut self.callback) };
384        callback();
385    }
386}
387
388/// Calls the [`tracing::info!`] macro on a value.
389pub fn info<T: Debug>(data: T) {
390    tracing::info!("{:?}", data);
391}
392
393/// Calls the [`tracing::debug!`] macro on a value.
394pub fn dbg<T: Debug>(data: T) {
395    tracing::debug!("{:?}", data);
396}
397
398/// Processes a [`Result`] by calling the [`tracing::warn!`] macro in case of an [`Err`] value.
399pub fn warn<E: Debug>(result: Result<(), E>) {
400    if let Err(warn) = result {
401        tracing::warn!("{:?}", warn);
402    }
403}
404
405/// Processes a [`Result`] by calling the [`tracing::error!`] macro in case of an [`Err`] value.
406pub fn error<E: Debug>(result: Result<(), E>) {
407    if let Err(error) = result {
408        tracing::error!("{:?}", error);
409    }
410}
411
412/// Like [`tracing::trace`], but conditional on cargo feature `detailed_trace`.
413#[macro_export]
414macro_rules! detailed_trace {
415    ($($tts:tt)*) => {
416        if cfg!(feature = "detailed_trace") {
417            $crate::tracing::trace!($($tts)*);
418        }
419    }
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425    use static_assertions::assert_impl_all;
426
427    // Check that the HashMaps are Clone if the key/values are Clone
428    assert_impl_all!(PreHashMap::<u64, usize>: Clone);
429
430    #[test]
431    fn fast_typeid_hash() {
432        struct Hasher;
433
434        impl core::hash::Hasher for Hasher {
435            fn finish(&self) -> u64 {
436                0
437            }
438            fn write(&mut self, _: &[u8]) {
439                panic!("Hashing of core::any::TypeId changed");
440            }
441            fn write_u64(&mut self, _: u64) {}
442        }
443
444        Hash::hash(&TypeId::of::<()>(), &mut Hasher);
445    }
446
447    #[cfg(feature = "alloc")]
448    #[test]
449    fn stable_hash_within_same_program_execution() {
450        use alloc::vec::Vec;
451
452        let mut map_1 = HashMap::new();
453        let mut map_2 = HashMap::new();
454        for i in 1..10 {
455            map_1.insert(i, i);
456            map_2.insert(i, i);
457        }
458        assert_eq!(
459            map_1.iter().collect::<Vec<_>>(),
460            map_2.iter().collect::<Vec<_>>()
461        );
462    }
463}