
# Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
234 lines
7.9 KiB
Rust
234 lines
7.9 KiB
Rust
use core::num::NonZero;
|
|
|
|
use super::kinds::IdKind;
|
|
|
|
/// Mask for extracting the value portion of a 32-bit high segment. This
|
|
/// yields 31-bits of total value, as the final bit (the most significant)
|
|
/// is reserved as a flag bit. Can be negated to extract the flag bit.
|
|
pub(crate) const HIGH_MASK: u32 = 0x7FFF_FFFF;
|
|
|
|
/// Abstraction over masks needed to extract values/components of an [`super::Identifier`].
|
|
pub(crate) struct IdentifierMask;
|
|
|
|
impl IdentifierMask {
|
|
/// Returns the low component from a `u64` value
|
|
#[inline(always)]
|
|
pub(crate) const fn get_low(value: u64) -> u32 {
|
|
// This will truncate to the lowest 32 bits
|
|
value as u32
|
|
}
|
|
|
|
/// Returns the high component from a `u64` value
|
|
#[inline(always)]
|
|
pub(crate) const fn get_high(value: u64) -> u32 {
|
|
// This will discard the lowest 32 bits
|
|
(value >> u32::BITS) as u32
|
|
}
|
|
|
|
/// Pack a low and high `u32` values into a single `u64` value.
|
|
#[inline(always)]
|
|
pub(crate) const fn pack_into_u64(low: u32, high: u32) -> u64 {
|
|
((high as u64) << u32::BITS) | (low as u64)
|
|
}
|
|
|
|
/// Pack the [`IdKind`] bits into a high segment.
|
|
#[inline(always)]
|
|
pub(crate) const fn pack_kind_into_high(value: u32, kind: IdKind) -> u32 {
|
|
value | ((kind as u32) << 24)
|
|
}
|
|
|
|
/// Extract the value component from a high segment of an [`super::Identifier`].
|
|
#[inline(always)]
|
|
pub(crate) const fn extract_value_from_high(value: u32) -> u32 {
|
|
value & HIGH_MASK
|
|
}
|
|
|
|
/// Extract the ID kind component from a high segment of an [`super::Identifier`].
|
|
#[inline(always)]
|
|
pub(crate) const fn extract_kind_from_high(value: u32) -> IdKind {
|
|
// The negated HIGH_MASK will extract just the bit we need for kind.
|
|
let kind_mask = !HIGH_MASK;
|
|
let bit = value & kind_mask;
|
|
|
|
if bit == kind_mask {
|
|
IdKind::Placeholder
|
|
} else {
|
|
IdKind::Entity
|
|
}
|
|
}
|
|
|
|
/// Offsets a masked generation value by the specified amount, wrapping to 1 instead of 0.
|
|
/// Will never be greater than [`HIGH_MASK`] or less than `1`, and increments are masked to
|
|
/// never be greater than [`HIGH_MASK`].
|
|
#[inline(always)]
|
|
pub(crate) const fn inc_masked_high_by(lhs: NonZero<u32>, rhs: u32) -> NonZero<u32> {
|
|
let lo = (lhs.get() & HIGH_MASK).wrapping_add(rhs & HIGH_MASK);
|
|
// Checks high 32 bit for whether we have overflowed 31 bits.
|
|
let overflowed = lo >> 31;
|
|
|
|
// SAFETY:
|
|
// - Adding the overflow flag will offset overflows to start at 1 instead of 0
|
|
// - The sum of `0x7FFF_FFFF` + `u32::MAX` + 1 (overflow) == `0x7FFF_FFFF`
|
|
// - If the operation doesn't overflow at 31 bits, no offsetting takes place
|
|
unsafe { NonZero::<u32>::new_unchecked(lo.wrapping_add(overflowed) & HIGH_MASK) }
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn get_u64_parts() {
|
|
// Two distinct bit patterns per low/high component
|
|
let value: u64 = 0x7FFF_FFFF_0000_000C;
|
|
|
|
assert_eq!(IdentifierMask::get_low(value), 0x0000_000C);
|
|
assert_eq!(IdentifierMask::get_high(value), 0x7FFF_FFFF);
|
|
}
|
|
|
|
#[test]
|
|
fn extract_kind() {
|
|
// All bits are ones.
|
|
let high: u32 = 0xFFFF_FFFF;
|
|
|
|
assert_eq!(
|
|
IdentifierMask::extract_kind_from_high(high),
|
|
IdKind::Placeholder
|
|
);
|
|
|
|
// Second and second to last bits are ones.
|
|
let high: u32 = 0x4000_0002;
|
|
|
|
assert_eq!(IdentifierMask::extract_kind_from_high(high), IdKind::Entity);
|
|
}
|
|
|
|
#[test]
|
|
fn extract_high_value() {
|
|
// All bits are ones.
|
|
let high: u32 = 0xFFFF_FFFF;
|
|
|
|
// Excludes the most significant bit as that is a flag bit.
|
|
assert_eq!(IdentifierMask::extract_value_from_high(high), 0x7FFF_FFFF);
|
|
|
|
// Start bit and end bit are ones.
|
|
let high: u32 = 0x8000_0001;
|
|
|
|
assert_eq!(IdentifierMask::extract_value_from_high(high), 0x0000_0001);
|
|
|
|
// Classic bit pattern.
|
|
let high: u32 = 0xDEAD_BEEF;
|
|
|
|
assert_eq!(IdentifierMask::extract_value_from_high(high), 0x5EAD_BEEF);
|
|
}
|
|
|
|
#[test]
|
|
fn pack_kind_bits() {
|
|
// All bits are ones expect the most significant bit, which is zero
|
|
let high: u32 = 0x7FFF_FFFF;
|
|
|
|
assert_eq!(
|
|
IdentifierMask::pack_kind_into_high(high, IdKind::Placeholder),
|
|
0xFFFF_FFFF
|
|
);
|
|
|
|
// Arbitrary bit pattern
|
|
let high: u32 = 0x00FF_FF00;
|
|
|
|
assert_eq!(
|
|
IdentifierMask::pack_kind_into_high(high, IdKind::Entity),
|
|
// Remains unchanged as before
|
|
0x00FF_FF00
|
|
);
|
|
|
|
// Bit pattern that almost spells a word
|
|
let high: u32 = 0x40FF_EEEE;
|
|
|
|
assert_eq!(
|
|
IdentifierMask::pack_kind_into_high(high, IdKind::Placeholder),
|
|
0xC0FF_EEEE // Milk and no sugar, please.
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn pack_into_u64() {
|
|
let high: u32 = 0x7FFF_FFFF;
|
|
let low: u32 = 0x0000_00CC;
|
|
|
|
assert_eq!(
|
|
IdentifierMask::pack_into_u64(low, high),
|
|
0x7FFF_FFFF_0000_00CC
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn incrementing_masked_nonzero_high_is_safe() {
|
|
// Adding from lowest value with lowest to highest increment
|
|
// No result should ever be greater than 0x7FFF_FFFF or HIGH_MASK
|
|
assert_eq!(
|
|
NonZero::<u32>::MIN,
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MIN, 0)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::new(2).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MIN, 1)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::new(3).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MIN, 2)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::MIN,
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MIN, HIGH_MASK)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::MIN,
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MIN, u32::MAX)
|
|
);
|
|
// Adding from absolute highest value with lowest to highest increment
|
|
// No result should ever be greater than 0x7FFF_FFFF or HIGH_MASK
|
|
assert_eq!(
|
|
NonZero::<u32>::new(HIGH_MASK).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MAX, 0)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::MIN,
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MAX, 1)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::new(2).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MAX, 2)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::new(HIGH_MASK).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MAX, HIGH_MASK)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::new(HIGH_MASK).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::MAX, u32::MAX)
|
|
);
|
|
// Adding from actual highest value with lowest to highest increment
|
|
// No result should ever be greater than 0x7FFF_FFFF or HIGH_MASK
|
|
assert_eq!(
|
|
NonZero::<u32>::new(HIGH_MASK).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::new(HIGH_MASK).unwrap(), 0)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::MIN,
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::new(HIGH_MASK).unwrap(), 1)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::new(2).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::new(HIGH_MASK).unwrap(), 2)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::new(HIGH_MASK).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::new(HIGH_MASK).unwrap(), HIGH_MASK)
|
|
);
|
|
assert_eq!(
|
|
NonZero::<u32>::new(HIGH_MASK).unwrap(),
|
|
IdentifierMask::inc_masked_high_by(NonZero::<u32>::new(HIGH_MASK).unwrap(), u32::MAX)
|
|
);
|
|
}
|
|
}
|