-
Notifications
You must be signed in to change notification settings - Fork 190
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement `getrandom::array`. It requires Rust 1.51 and the user must enable the "array" feature explicitly.
- Loading branch information
1 parent
47a59dd
commit 56d3282
Showing
3 changed files
with
105 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() }) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters