diff --git a/build.rs b/build.rs index cd43fcae..aa75c501 100644 --- a/build.rs +++ b/build.rs @@ -8,6 +8,10 @@ fn main() { if minor_ver >= 40 { println!("cargo:rustc-cfg=getrandom_non_exhaustive"); } + + if minor_ver >= 51 { + println!("cargo:rustc-cfg=getrandom_const_generics"); + } } // Based on libc's implementation: diff --git a/src/lib.rs b/src/lib.rs index edecf959..96f0e016 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,7 +193,7 @@ #[macro_use] extern crate cfg_if; -use crate::util::{slice_as_uninit_mut, slice_assume_init_mut}; +use crate::util::{slice_as_uninit_mut, slice_assume_init_mut, uninit_as_bytes_mut}; use core::mem::MaybeUninit; mod error; @@ -364,6 +364,57 @@ impl Options { // SAFETY: `dest` has been fully initialized by `imp::getrandom_inner` Ok(unsafe { slice_assume_init_mut(dest) }) } + + /// Return an array of random bytes. + /// + /// Supports returning `u8` arrays and _arbitrary levels_ of nested byte + /// arrays. Requires Rust 1.51 or later (due to the use of const generics). + /// + /// # Examples + /// ``` + /// use getrandom::{Error, Options}; + /// fn tls_hello_random() -> Result<[u8; 32], Error> { + /// Options::DEFAULT.array() + /// } + /// # tls_hello_random().unwrap(); + /// ``` + /// + /// The nested array support can be used to safely and efficiently construct + /// random values of types other than byte arrays: + /// ``` + /// # use getrandom::{Error, Options}; + /// # fn u32_array_example() -> Result<(), Error> { + /// let random_u32s: [u32; 4] = Options::DEFAULT.array()?.map(u32::from_ne_bytes); + /// # Ok(()) + /// # } + /// # u32_array_example().unwrap(); + /// ``` + /// + /// Multiple levels of array nesting can be used to construct more + /// complicated types, though some type annotations are needed: + /// ``` + /// # #![feature(portable_simd)] + /// use std::simd::Simd; + /// # use getrandom::{Error, Options}; + /// # fn simd_array_example() -> Result<(), Error> { + /// let random_vectors: [Simd; 16] = Options::DEFAULT + /// .array()? + /// .map(|bytes: [_; 8]| bytes.map(u32::from_ne_bytes)) + /// .map(Simd::from); + /// # Ok(()) + /// # } + /// # simd_array_example().unwrap(); + /// ``` + #[cfg(getrandom_const_generics)] + #[inline] + pub fn array(self) -> Result<[T; N], Error> { + let mut uninit: MaybeUninit<[T; N]> = MaybeUninit::uninit(); + imp::getrandom_inner(uninit_as_bytes_mut(&mut uninit))?; + + // SAFETY: uninit was entirely initalized by imp::getrandom_inner, and + // any sequence of initialized bytes is valid for any ArrayElement type. + Ok(unsafe { uninit.assume_init() }) + } } // TODO(MSRV 1.62): Use #[derive(Default)] @@ -372,3 +423,24 @@ impl Default for Options { Self::DEFAULT } } + +/// A type supported by [Options::array] that can be initialized with random data. +/// +/// # Safety +/// +/// Any type which implements ArrayElementmust ensure that any sequence of bytes +/// is a valid representation for that type. For example, it is safe to have +/// `[u8; 6]` implement this trait, but not `bool`. +#[cfg(getrandom_const_generics)] +pub unsafe trait ArrayElement: private::Sealed {} + +#[cfg(getrandom_const_generics)] +mod private { + use super::ArrayElement; + pub trait Sealed {} + + impl Sealed for u8 {} + unsafe impl ArrayElement for u8 {} + impl Sealed for [A; N] {} + unsafe impl ArrayElement for [A; N] {} +} diff --git a/src/util.rs b/src/util.rs index 3162afad..37962a0b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -99,3 +99,11 @@ pub unsafe fn slice_as_uninit_mut(slice: &mut [T]) -> &mut [MaybeUninit] { // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. &mut *(slice as *mut [T] as *mut [MaybeUninit]) } + +/// Polyfill for `maybe_uninit_as_bytes` features's `MaybeUninit::as_bytes_mut`. +#[inline(always)] +pub fn uninit_as_bytes_mut(t: &mut MaybeUninit) -> &mut [MaybeUninit] { + use core::{mem::size_of, slice::from_raw_parts_mut}; + // SAFETY: MaybeUninit is always valid for any type (including padding). + unsafe { from_raw_parts_mut(t.as_mut_ptr() as *mut MaybeUninit, size_of::()) } +}