Skip to content

Commit 56d3282

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 56d3282

File tree

3 files changed

+105
-1
lines changed

3 files changed

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

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)