Skip to content

Commit

Permalink
Add getrandom::array.
Browse files Browse the repository at this point in the history
Implement `getrandom::array`. It requires Rust 1.51 and the user must
enable the "array" feature explicitly.
  • Loading branch information
briansmith committed Oct 21, 2022
1 parent 47a59dd commit 56d3282
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 1 deletion.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ js-sys = { version = "0.3", optional = true }
wasm-bindgen-test = "0.3.18"

[features]
# Feature to enable the `array()` function, which requires Rust 1.51 or later.
array = []
# Implement std-only traits for getrandom::Error
std = []
# Feature to enable fallback RDRAND-based implementation on x86/x86_64
Expand All @@ -49,5 +51,5 @@ rustc-dep-of-std = [
test-in-browser = []

[package.metadata.docs.rs]
features = ["std", "custom"]
features = ["array", "custom", "std"]
rustdoc-args = ["--cfg", "docsrs"]
96 changes: 96 additions & 0 deletions src/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::{getrandom_uninit, Error};
use core::mem::MaybeUninit;

mod private {
pub trait Sealed {}
}

/// A type supported by [`getrandom::array`](crate::getrandom::array).
pub unsafe trait ArrayItem: private::Sealed {}

impl private::Sealed for u8 {}
unsafe impl ArrayItem for u8 {}

impl<I: private::Sealed, const N: usize> private::Sealed for [I; N] {}
unsafe impl<I: ArrayItem, const N: usize> ArrayItem for [I; N] {}

/// Returns an array of bytes where all bytes of the value were
/// initialized using `getrandom_uninit`. Feature: `array`.
///
/// Requires Rust 1.51.
///
/// This can construct random byte arrays, and arbitrary levels of nested byte
/// arrays
///
/// ```
/// fn tls_hello_random() -> Result<[u8; 32], getrandom::Error> {
/// getrandom::array()
/// }
/// ```
///
/// The nested array support can be used to safely and efficiently construct
/// random values of types other than byte arrays:
/// ```
/// # fn u32_array_example() -> Result<(), getrandom::Error> {
/// let random_u32s: [u32; 4] =
/// getrandom::array()?.map(u32::from_ne_bytes);
/// # Ok(())
/// # }
/// ```
///
/// Multiple levels of array nesting can be used to construct more complicated
/// types, though some type annotations are needed:
/// ```
/// # // TODO: Use `std::simd::Simd` when the `portable_simd` feature is
/// # // available; until then, here is a minimal polyfill for it that
/// # // allows the examle to work in stable Rust.
/// # struct Simd<T, const N: usize>([T; N]);
/// # impl<T, const N: usize> From<[T; N]> for Simd<T, N> {
/// # fn from(value: [T; N]) -> Self {
/// # Self(value)
/// # }
/// # }
/// # fn simd_array_example() -> Result<(), getrandom::Error> {
/// let random_vectors: [Simd<u32, 4>; 16] =
/// getrandom::array()?
/// .map(|bytes: [_; 4]| bytes.map(u32::from_ne_bytes))
/// .map(Simd::from);
/// # Ok(())
/// # }
/// ```
///
/// Arbitrary levels of nesting are supported.
/// ```
/// # fn many_nesting_levels_example() -> Result<(), getrandom::Error> {
/// let many_nesting_levels: [[[[[[u8; 1]; 2]; 3]; 4]; 5]; 6] = getrandom::array()?;
/// # Ok(())
/// }
/// ```
///
/// The patterns above allows us to avoid implementing
/// `ArrayItem` for an endless number of types.
#[inline(always)]
pub fn array<I: ArrayItem, const N: usize>() -> Result<[I; N], Error> {
// This is `inline(always)` because the code generated by the compiler is
// terrible when it isn't inlined.
//
// This function only requires Rust 1.51 but `[T]::map()` used in the
// doctests is stable only in Rust 1.55.

let mut uninit: MaybeUninit<[I; N]> = MaybeUninit::uninit();
// TODO: `uninit.as_bytes_mut()` when that is available.
{
// SAFETY: MaybeUninit<u8> is always valid, even for padding bytes.
// The compiler will ensure that `B` isn't too large.
let as_bytes_mut = unsafe {
core::slice::from_raw_parts_mut(
uninit.as_mut_ptr() as *mut MaybeUninit<u8>,
core::mem::size_of::<[I; N]>(),
)
};
getrandom_uninit(as_bytes_mut)?;
}
// SAFETY: `dest` has been fully initialized by `getrandom_uninit`
// since it returned `Ok`.
Ok(unsafe { uninit.assume_init() })
}
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,9 @@ pub fn getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error
// since it returned `Ok`.
Ok(unsafe { slice_assume_init_mut(dest) })
}

#[cfg(feature = "array")]
mod array;

#[cfg(feature = "array")]
pub use array::*;

0 comments on commit 56d3282

Please sign in to comment.