diff --git a/rand_core/Cargo.toml b/rand_core/Cargo.toml index d1d9e66d7fa..5626ebf8fc9 100644 --- a/rand_core/Cargo.toml +++ b/rand_core/Cargo.toml @@ -32,4 +32,3 @@ serde = ["dep:serde"] # enables serde for BlockRng wrapper [dependencies] serde = { version = "1", features = ["derive"], optional = true } getrandom = { version = "0.3.0", optional = true } -zerocopy = { version = "0.8.0", default-features = false } diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 584a4c16f10..c2d8470ff8d 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -18,8 +18,6 @@ //! non-reproducible sources (e.g. `OsRng`) need not bother with it. use crate::RngCore; -use core::cmp::min; -use zerocopy::{Immutable, IntoBytes}; /// Implement `next_u64` via `next_u32`, little-endian order. pub fn next_u64_via_u32(rng: &mut R) -> u64 { @@ -53,17 +51,22 @@ pub fn fill_bytes_via_next(rng: &mut R, dest: &mut [u8]) { } } -trait Observable: IntoBytes + Immutable + Copy { - fn to_le(self) -> Self; +trait Observable: Copy { + type Bytes: Sized + AsRef<[u8]>; + fn to_le_bytes(self) -> Self::Bytes; } impl Observable for u32 { - fn to_le(self) -> Self { - self.to_le() + type Bytes = [u8; 4]; + + fn to_le_bytes(self) -> Self::Bytes { + Self::to_le_bytes(self) } } impl Observable for u64 { - fn to_le(self) -> Self { - self.to_le() + type Bytes = [u8; 8]; + + fn to_le_bytes(self) -> Self::Bytes { + Self::to_le_bytes(self) } } @@ -72,23 +75,26 @@ impl Observable for u64 { /// Returns `(n, byte_len)`. `src[..n]` is consumed (and possibly mutated), /// `dest[..byte_len]` is filled. `src[n..]` and `dest[byte_len..]` are left /// unaltered. -fn fill_via_chunks(src: &mut [T], dest: &mut [u8]) -> (usize, usize) { +fn fill_via_chunks(src: &[T], dest: &mut [u8]) -> (usize, usize) { let size = core::mem::size_of::(); - let byte_len = min(core::mem::size_of_val(src), dest.len()); - let num_chunks = (byte_len + size - 1) / size; - - // Byte-swap for portability of results. This must happen before copying - // since the size of dest is not guaranteed to be a multiple of T or to be - // sufficiently aligned. - if cfg!(target_endian = "big") { - for x in &mut src[..num_chunks] { - *x = x.to_le(); - } - } - dest[..byte_len].copy_from_slice(&<[T]>::as_bytes(&src[..num_chunks])[..byte_len]); + // Always use little endian for portability of results. - (num_chunks, byte_len) + let mut dest = dest.chunks_exact_mut(size); + let mut src = src.iter(); + + let zipped = dest.by_ref().zip(src.by_ref()); + let num_chunks = zipped.len(); + zipped.for_each(|(dest, src)| dest.copy_from_slice(src.to_le_bytes().as_ref())); + + let byte_len = num_chunks * size; + if let (dest_tail @ [_, ..], Some(src)) = (dest.into_remainder(), src.next()) { + let n = dest_tail.len(); + dest_tail.copy_from_slice(&src.to_le_bytes().as_ref()[..n]); + (num_chunks + 1, byte_len + n) + } else { + (num_chunks, byte_len) + } } /// Implement `fill_bytes` by reading chunks from the output buffer of a block @@ -96,8 +102,8 @@ fn fill_via_chunks(src: &mut [T], dest: &mut [u8]) -> (usize, usi /// /// The return values are `(consumed_u32, filled_u8)`. /// -/// On big-endian systems, endianness of `src[..consumed_u32]` values is -/// swapped. No other adjustments to `src` are made. +/// `src` is not modified; it is taken as a `&mut` reference for backward +/// compatibility with previous versions that did change it. /// /// `filled_u8` is the number of filled bytes in `dest`, which may be less than /// the length of `dest`. @@ -125,6 +131,7 @@ fn fill_via_chunks(src: &mut [T], dest: &mut [u8]) -> (usize, usi /// } /// ``` pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { + // TODO(SemVer): src: `&[u32]` as we don't mutate it. fill_via_chunks(src, dest) } @@ -133,8 +140,8 @@ pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { /// /// The return values are `(consumed_u64, filled_u8)`. /// -/// On big-endian systems, endianness of `src[..consumed_u64]` values is -/// swapped. No other adjustments to `src` are made. +/// `src` is not modified; it is taken as a `&mut` reference for backward +/// compatibility with previous versions that did change it. /// /// `filled_u8` is the number of filled bytes in `dest`, which may be less than /// the length of `dest`. @@ -143,6 +150,7 @@ pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { /// /// See `fill_via_u32_chunks` for an example. pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) { + // TODO(SemVer): src: `&[u64]` as we don't mutate it. fill_via_chunks(src, dest) }