|
| 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 | +} |
0 commit comments