Skip to main content

baracuda_types/
zero_bits.rs

1//! [`ValidAsZeroBits`] — safety marker for zero-initializable types.
2
3use crate::{BFloat16, Complex32, Complex64, Half};
4
5/// Marker trait for types whose all-zero bit pattern is a valid value.
6///
7/// Crates that allocate GPU memory often use `cudaMemset(0)` / `cuMemsetD8`
8/// to initialize buffers to zero and then treat the allocation as a
9/// populated `[T]`. That's only sound for types where all-zero bytes are a
10/// valid `T` — e.g. integer primitives and IEEE-754 floating-point types
11/// (where `0x00000000` is the representation of `+0.0`).
12///
13/// Implementing `ValidAsZeroBits` is a promise that:
14///
15/// 1. Reading a zero-initialized `T` is not undefined behavior.
16/// 2. The zero-initialized value is semantically sensible (`0` / `0.0` /
17///    `(0, 0, ..., 0)` — *not* a niche-optimized enum where zero means
18///    something else).
19///
20/// # Safety
21///
22/// This trait is `unsafe` to implement: a wrong impl will lead to UB the
23/// first time downstream code zero-initializes a buffer and reads it back.
24/// Common counter-examples include:
25///
26/// - `&T`, `&mut T` (null references are UB).
27/// - `NonZero*` from `std::num`.
28/// - Enums with a non-zero discriminant for their zero-value variant.
29/// - `Box<T>`, `Arc<T>`, `String`, `Vec<T>` (heap-owned data).
30///
31/// # Supplied impls
32///
33/// - Every unsigned integer (`u8`..`u128`, `usize`).
34/// - Every signed integer (`i8`..`i128`, `isize`).
35/// - `f32`, `f64` (zero = `+0.0`).
36/// - [`Half`], [`BFloat16`], [`Complex32`], [`Complex64`].
37/// - `()` — the unit type.
38/// - `bool` — zero = `false`.
39/// - Tuples of arity 1–12 where every element is `ValidAsZeroBits`.
40/// - Fixed-size arrays `[T; N]` where `T: ValidAsZeroBits`.
41///
42/// # Example
43///
44/// ```
45/// use baracuda_types::ValidAsZeroBits;
46///
47/// fn zeroed_vec<T: ValidAsZeroBits>(n: usize) -> Vec<T> {
48///     // Safe because T: ValidAsZeroBits guarantees all-zero bytes are a valid T.
49///     let mut v = Vec::with_capacity(n);
50///     unsafe {
51///         core::ptr::write_bytes(v.as_mut_ptr(), 0, n);
52///         v.set_len(n);
53///     }
54///     v
55/// }
56///
57/// let xs: Vec<f32> = zeroed_vec(4);
58/// assert_eq!(xs, [0.0, 0.0, 0.0, 0.0]);
59/// ```
60pub unsafe trait ValidAsZeroBits: Copy + 'static {}
61
62// ---- Primitives ---------------------------------------------------------
63
64macro_rules! impl_zero_bits {
65    ($($t:ty),* $(,)?) => {
66        $(unsafe impl ValidAsZeroBits for $t {})*
67    };
68}
69
70impl_zero_bits!(
71    u8, u16, u32, u64, u128, usize,
72    i8, i16, i32, i64, i128, isize,
73    f32, f64,
74    bool, (),
75    Half, BFloat16, Complex32, Complex64,
76);
77
78// ---- Fixed-size arrays --------------------------------------------------
79
80unsafe impl<T: ValidAsZeroBits, const N: usize> ValidAsZeroBits for [T; N] {}
81
82// ---- Tuples -------------------------------------------------------------
83//
84// Up to arity 12, matching the `DeviceRepr` / `KernelArg` coverage.
85
86macro_rules! impl_zero_bits_tuple {
87    ($($t:ident),+) => {
88        unsafe impl<$($t: ValidAsZeroBits),+> ValidAsZeroBits for ($($t,)+) {}
89    };
90}
91
92impl_zero_bits_tuple!(A);
93impl_zero_bits_tuple!(A, B);
94impl_zero_bits_tuple!(A, B, C);
95impl_zero_bits_tuple!(A, B, C, D);
96impl_zero_bits_tuple!(A, B, C, D, E);
97impl_zero_bits_tuple!(A, B, C, D, E, F);
98impl_zero_bits_tuple!(A, B, C, D, E, F, G);
99impl_zero_bits_tuple!(A, B, C, D, E, F, G, H);
100impl_zero_bits_tuple!(A, B, C, D, E, F, G, H, I);
101impl_zero_bits_tuple!(A, B, C, D, E, F, G, H, I, J);
102impl_zero_bits_tuple!(A, B, C, D, E, F, G, H, I, J, K);
103impl_zero_bits_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);