Skip to content

Commit 07a9d4f

Browse files
committed
Add getrandom_array.
Implement `getrandom_array`. It requires Rust 1.51 and the user must enable the "array" feature explicitly.
1 parent 47a59dd commit 07a9d4f

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ js-sys = { version = "0.3", optional = true }
3030
wasm-bindgen-test = "0.3.18"
3131

3232
[features]
33+
# Feature to enable `getrandom_array`, which requires Rust 1.51 or later.
34+
array = []
3335
# Implement std-only traits for getrandom::Error
3436
std = []
3537
# Feature to enable fallback RDRAND-based implementation on x86/x86_64
@@ -49,5 +51,5 @@ rustc-dep-of-std = [
4951
test-in-browser = []
5052

5153
[package.metadata.docs.rs]
52-
features = ["std", "custom"]
54+
features = ["array", "custom", "std"]
5355
rustdoc-args = ["--cfg", "docsrs"]

src/array.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use crate::{getrandom_uninit, Error};
2+
use core::mem::MaybeUninit;
3+
4+
mod private {
5+
pub trait Sealed {}
6+
}
7+
8+
impl private::Sealed for u8 {}
9+
impl<S: private::Sealed, const N: usize> private::Sealed for [S; N] {}
10+
11+
/// A type supported by [`getrandom_array`](crate::getrandom_array).
12+
pub unsafe trait ArrayItem: private::Sealed {}
13+
14+
unsafe impl<B: private::Sealed> ArrayItem for B {}
15+
16+
/// Returns an array of bytes where all bytes of the value were
17+
/// initialized using `getrandom_uninit`. Feature: `array`.
18+
///
19+
/// Requires Rust 1.51.
20+
///
21+
/// This can construct random byte arrays, and arbitrary levels of nested byte
22+
/// arrays
23+
///
24+
/// ```
25+
/// # use getrandom::getrandom_array;
26+
/// fn tls_hello_random() -> Result<[u8; 32], getrandom::Error> {
27+
/// getrandom_array()
28+
/// }
29+
/// ```
30+
///
31+
/// The nested array support can be used to safely and efficiently construct
32+
/// random values of types other than byte arrays:
33+
/// ```
34+
/// # use getrandom::{getrandom_array, Error};
35+
/// # fn u32_array_example() -> Result<(), Error> {
36+
/// let random_u32s: [u32; 4] =
37+
/// getrandom_array()?.map(u32::from_ne_bytes);
38+
/// # Ok(())
39+
/// # }
40+
/// ```
41+
///
42+
/// Multiple levels of array nesting can be used to construct more complicated
43+
/// types, though some type annotations are needed:
44+
/// ```
45+
/// # // TODO: Use `std::simd::Simd` when the `portable_simd` feature is
46+
/// # // available; until then, here is a minimal polyfill for it that
47+
/// # // allows the examle to work in stable Rust.
48+
/// # struct Simd<T, const N: usize>([T; N]);
49+
/// # impl<T, const N: usize> From<[T; N]> for Simd<T, N> {
50+
/// # fn from(value: [T; N]) -> Self {
51+
/// # Self(value)
52+
/// # }
53+
/// # }
54+
/// # use getrandom::{getrandom_array, Error};
55+
/// # fn simd_array_example() -> Result<(), Error> {
56+
/// let random_vectors: [Simd<u32, 4>; 16] =
57+
/// getrandom_array()?
58+
/// .map(|bytes: [_; 4]| bytes.map(u32::from_ne_bytes))
59+
/// .map(Simd::from);
60+
/// # Ok(())
61+
/// # }
62+
/// ```
63+
///
64+
/// Arbitrary levels of nesting are supported.
65+
/// ```
66+
/// # use getrandom::{getrandom_array, Error};
67+
/// # fn more_nesting_example() -> Result<(), Error> {
68+
/// let more_nesting: [[[[[[u8; 1]; 2]; 3]; 4]; 5]; 6] = getrandom_array()?;
69+
/// # Ok(())
70+
/// }
71+
/// ```
72+
///
73+
/// The patterns above allows us to avoid implementing
74+
/// `ArrayItem` for an endless number of types.
75+
#[inline(always)]
76+
pub fn getrandom_array<I: ArrayItem, const N: usize>() -> Result<[I; N], Error> {
77+
// This is `inline(always)` because the code generated by the compiler is
78+
// terrible when it isn't inlined.
79+
//
80+
// This function only requires Rust 1.51 but `[T]::map()` used in the
81+
// doctests is stable only in Rust 1.55.
82+
83+
let mut uninit: MaybeUninit<[I; N]> = MaybeUninit::uninit();
84+
// TODO: `uninit.as_bytes_mut()` when that is available.
85+
{
86+
// SAFETY: MaybeUninit<u8> is always valid, even for padding bytes.
87+
// The compiler will ensure that `B` isn't too large.
88+
let as_bytes_mut = unsafe {
89+
core::slice::from_raw_parts_mut(
90+
uninit.as_mut_ptr() as *mut MaybeUninit<u8>,
91+
core::mem::size_of::<[I; N]>(),
92+
)
93+
};
94+
getrandom_uninit(as_bytes_mut)?;
95+
}
96+
// SAFETY: `dest` has been fully initialized by `getrandom_uninit`
97+
// since it returned `Ok`.
98+
Ok(unsafe { uninit.assume_init() })
99+
}

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,9 @@ pub fn getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error
327327
// since it returned `Ok`.
328328
Ok(unsafe { slice_assume_init_mut(dest) })
329329
}
330+
331+
#[cfg(feature = "array")]
332+
mod array;
333+
334+
#[cfg(feature = "array")]
335+
pub use array::*;

0 commit comments

Comments
 (0)