Skip to content

Add Options::array method for creating random arrays #352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>()) }
}