rust: init: add Zeroable trait and init::zeroed function

Add the `Zeroable` trait which marks types that can be initialized by
writing `0x00` to every byte of the type. Also add the `init::zeroed`
function that creates an initializer for a `Zeroable` type that writes
`0x00` to every byte.

Signed-off-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Andreas Hindborg <a.hindborg@samsung.com>
Link: https://lore.kernel.org/r/20230408122429.1103522-12-y86-dev@protonmail.com
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
Benno Lossin 2023-04-08 12:26:12 +00:00 committed by Miguel Ojeda
parent 6841d45a30
commit 38cde0bd7b

View File

@ -195,8 +195,14 @@ use crate::{
};
use alloc::boxed::Box;
use core::{
alloc::AllocError, cell::Cell, convert::Infallible, marker::PhantomData, mem::MaybeUninit,
pin::Pin, ptr,
alloc::AllocError,
cell::Cell,
convert::Infallible,
marker::PhantomData,
mem::MaybeUninit,
num::*,
pin::Pin,
ptr::{self, NonNull},
};
#[doc(hidden)]
@ -1323,3 +1329,90 @@ pub unsafe trait PinnedDrop: __internal::HasPinData {
/// automatically.
fn drop(self: Pin<&mut Self>, only_call_from_drop: __internal::OnlyCallFromDrop);
}
/// Marker trait for types that can be initialized by writing just zeroes.
///
/// # Safety
///
/// The bit pattern consisting of only zeroes is a valid bit pattern for this type. In other words,
/// this is not UB:
///
/// ```rust,ignore
/// let val: Self = unsafe { core::mem::zeroed() };
/// ```
pub unsafe trait Zeroable {}
/// Create a new zeroed T.
///
/// The returned initializer will write `0x00` to every byte of the given `slot`.
#[inline]
pub fn zeroed<T: Zeroable>() -> impl Init<T> {
// SAFETY: Because `T: Zeroable`, all bytes zero is a valid bit pattern for `T`
// and because we write all zeroes, the memory is initialized.
unsafe {
init_from_closure(|slot: *mut T| {
slot.write_bytes(0, 1);
Ok(())
})
}
}
macro_rules! impl_zeroable {
($($({$($generics:tt)*})? $t:ty, )*) => {
$(unsafe impl$($($generics)*)? Zeroable for $t {})*
};
}
impl_zeroable! {
// SAFETY: All primitives that are allowed to be zero.
bool,
char,
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
f32, f64,
// SAFETY: These are ZSTs, there is nothing to zero.
{<T: ?Sized>} PhantomData<T>, core::marker::PhantomPinned, Infallible, (),
// SAFETY: Type is allowed to take any value, including all zeros.
{<T>} MaybeUninit<T>,
// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee).
Option<NonZeroU8>, Option<NonZeroU16>, Option<NonZeroU32>, Option<NonZeroU64>,
Option<NonZeroU128>, Option<NonZeroUsize>,
Option<NonZeroI8>, Option<NonZeroI16>, Option<NonZeroI32>, Option<NonZeroI64>,
Option<NonZeroI128>, Option<NonZeroIsize>,
// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee).
//
// In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant.
{<T: ?Sized>} Option<NonNull<T>>,
{<T: ?Sized>} Option<Box<T>>,
// SAFETY: `null` pointer is valid.
//
// We cannot use `T: ?Sized`, since the VTABLE pointer part of fat pointers is not allowed to be
// null.
//
// When `Pointee` gets stabilized, we could use
// `T: ?Sized where <T as Pointee>::Metadata: Zeroable`
{<T>} *mut T, {<T>} *const T,
// SAFETY: `null` pointer is valid and the metadata part of these fat pointers is allowed to be
// zero.
{<T>} *mut [T], {<T>} *const [T], *mut str, *const str,
// SAFETY: `T` is `Zeroable`.
{<const N: usize, T: Zeroable>} [T; N], {<T: Zeroable>} Wrapping<T>,
}
macro_rules! impl_tuple_zeroable {
($(,)?) => {};
($first:ident, $($t:ident),* $(,)?) => {
// SAFETY: All elements are zeroable and padding can be zero.
unsafe impl<$first: Zeroable, $($t: Zeroable),*> Zeroable for ($first, $($t),*) {}
impl_tuple_zeroable!($($t),* ,);
}
}
impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J);