Skip to content

Commit a08af6e

Browse files
committed
Add Options::array method for creating random arrays
Signed-off-by: Joe Richey <[email protected]>
1 parent 6d7cb5b commit a08af6e

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

build.rs

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ fn main() {
88
if minor_ver >= 40 {
99
println!("cargo:rustc-cfg=getrandom_non_exhaustive");
1010
}
11+
12+
if minor_ver >= 51 {
13+
println!("cargo:rustc-cfg=getrandom_const_generics");
14+
}
1115
}
1216

1317
// Based on libc's implementation:

src/lib.rs

+73-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@
193193
#[macro_use]
194194
extern crate cfg_if;
195195

196-
use crate::util::{slice_as_uninit_mut, slice_assume_init_mut};
196+
use crate::util::{slice_as_uninit_mut, slice_assume_init_mut, uninit_as_bytes_mut};
197197
use core::mem::MaybeUninit;
198198

199199
mod error;
@@ -364,6 +364,57 @@ impl Options {
364364
// SAFETY: `dest` has been fully initialized by `imp::getrandom_inner`
365365
Ok(unsafe { slice_assume_init_mut(dest) })
366366
}
367+
368+
/// Return an array of random bytes.
369+
///
370+
/// Supports returning `u8` arrays and _arbitrary levels_ of nested byte
371+
/// arrays. Requires Rust 1.51 or later (due to the use of const generics).
372+
///
373+
/// # Examples
374+
/// ```
375+
/// use getrandom::{Error, Options};
376+
/// fn tls_hello_random() -> Result<[u8; 32], Error> {
377+
/// Options::DEFAULT.array()
378+
/// }
379+
/// # tls_hello_random().unwrap();
380+
/// ```
381+
///
382+
/// The nested array support can be used to safely and efficiently construct
383+
/// random values of types other than byte arrays:
384+
/// ```
385+
/// # use getrandom::{Error, Options};
386+
/// # fn u32_array_example() -> Result<(), Error> {
387+
/// let random_u32s: [u32; 4] = Options::DEFAULT.array()?.map(u32::from_ne_bytes);
388+
/// # Ok(())
389+
/// # }
390+
/// # u32_array_example().unwrap();
391+
/// ```
392+
///
393+
/// Multiple levels of array nesting can be used to construct more
394+
/// complicated types, though some type annotations are needed:
395+
/// ```
396+
/// # #![feature(portable_simd)]
397+
/// use std::simd::Simd;
398+
/// # use getrandom::{Error, Options};
399+
/// # fn simd_array_example() -> Result<(), Error> {
400+
/// let random_vectors: [Simd<u32, 8>; 16] = Options::DEFAULT
401+
/// .array()?
402+
/// .map(|bytes: [_; 8]| bytes.map(u32::from_ne_bytes))
403+
/// .map(Simd::from);
404+
/// # Ok(())
405+
/// # }
406+
/// # simd_array_example().unwrap();
407+
/// ```
408+
#[cfg(getrandom_const_generics)]
409+
#[inline]
410+
pub fn array<T: ArrayElement, const N: usize>(self) -> Result<[T; N], Error> {
411+
let mut uninit: MaybeUninit<[T; N]> = MaybeUninit::uninit();
412+
imp::getrandom_inner(uninit_as_bytes_mut(&mut uninit))?;
413+
414+
// SAFETY: uninit was entirely initalized by imp::getrandom_inner, and
415+
// any sequence of initialized bytes is valid for any ArrayElement type.
416+
Ok(unsafe { uninit.assume_init() })
417+
}
367418
}
368419

369420
// TODO(MSRV 1.62): Use #[derive(Default)]
@@ -372,3 +423,24 @@ impl Default for Options {
372423
Self::DEFAULT
373424
}
374425
}
426+
427+
/// A type supported by [Options::array] that can be initialized with random data.
428+
///
429+
/// # Safety
430+
///
431+
/// Any type which implements ArrayElementmust ensure that any sequence of bytes
432+
/// is a valid representation for that type. For example, it is safe to have
433+
/// `[u8; 6]` implement this trait, but not `bool`.
434+
#[cfg(getrandom_const_generics)]
435+
pub unsafe trait ArrayElement: private::Sealed {}
436+
437+
#[cfg(getrandom_const_generics)]
438+
mod private {
439+
use super::ArrayElement;
440+
pub trait Sealed {}
441+
442+
impl Sealed for u8 {}
443+
unsafe impl ArrayElement for u8 {}
444+
impl<A: ArrayElement, const N: usize> Sealed for [A; N] {}
445+
unsafe impl<A: ArrayElement, const N: usize> ArrayElement for [A; N] {}
446+
}

src/util.rs

+8
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,11 @@ pub unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
9999
// SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
100100
&mut *(slice as *mut [T] as *mut [MaybeUninit<T>])
101101
}
102+
103+
/// Polyfill for `maybe_uninit_as_bytes` features's `MaybeUninit::as_bytes_mut`.
104+
#[inline(always)]
105+
pub fn uninit_as_bytes_mut<T>(t: &mut MaybeUninit<T>) -> &mut [MaybeUninit<u8>] {
106+
use core::{mem::size_of, slice::from_raw_parts_mut};
107+
// SAFETY: MaybeUninit<u8> is always valid for any type (including padding).
108+
unsafe { from_raw_parts_mut(t.as_mut_ptr() as *mut MaybeUninit<u8>, size_of::<T>()) }
109+
}

0 commit comments

Comments
 (0)