Skip to content

Commit

Permalink
Add Options::array method for creating random arrays
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Richey <[email protected]>
  • Loading branch information
josephlr committed Mar 25, 2023
1 parent 6d7cb5b commit a08af6e
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
4 changes: 4 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
74 changes: 73 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<u32, 8>; 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<T: ArrayElement, const N: usize>(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)]
Expand All @@ -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<A: ArrayElement, const N: usize> Sealed for [A; N] {}
unsafe impl<A: ArrayElement, const N: usize> ArrayElement for [A; N] {}
}
8 changes: 8 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,11 @@ pub unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
// SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
&mut *(slice as *mut [T] as *mut [MaybeUninit<T>])
}

/// Polyfill for `maybe_uninit_as_bytes` features's `MaybeUninit::as_bytes_mut`.
#[inline(always)]
pub fn uninit_as_bytes_mut<T>(t: &mut MaybeUninit<T>) -> &mut [MaybeUninit<u8>] {
use core::{mem::size_of, slice::from_raw_parts_mut};
// SAFETY: MaybeUninit<u8> is always valid for any type (including padding).
unsafe { from_raw_parts_mut(t.as_mut_ptr() as *mut MaybeUninit<u8>, size_of::<T>()) }
}

0 comments on commit a08af6e

Please sign in to comment.