Skip to content
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

Add getrandom::array() #293

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: 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"]
99 changes: 99 additions & 0 deletions src/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use crate::{getrandom_uninit, Error};
use core::mem::MaybeUninit;

mod private {
pub trait Sealed {}
}

/// A type supported by [`getrandom::array`](crate::getrandom::array).
#[cfg_attr(docsrs, doc(cfg(feature = "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] {}
#[cfg_attr(docsrs, doc(cfg(feature = "array")))]
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.
#[cfg_attr(docsrs, doc(cfg(feature = "array")))]
#[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::*;