Skip to content

Commit 7a14dfe

Browse files
committed
Implement fill_via_chunks without using zerocopy or unsafe.
Reduce the use of `zerocopy` as a step towards eliminating the dependency.
1 parent bfd1826 commit 7a14dfe

File tree

2 files changed

+34
-27
lines changed

2 files changed

+34
-27
lines changed

rand_core/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,3 @@ serde = ["dep:serde"] # enables serde for BlockRng wrapper
3232
[dependencies]
3333
serde = { version = "1", features = ["derive"], optional = true }
3434
getrandom = { version = "0.3.0", optional = true }
35-
zerocopy = { version = "0.8.0", default-features = false }

rand_core/src/impls.rs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
//! non-reproducible sources (e.g. `OsRng`) need not bother with it.
1919
2020
use crate::RngCore;
21-
use core::cmp::min;
22-
use zerocopy::{Immutable, IntoBytes};
2321

2422
/// Implement `next_u64` via `next_u32`, little-endian order.
2523
pub fn next_u64_via_u32<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
@@ -53,17 +51,22 @@ pub fn fill_bytes_via_next<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) {
5351
}
5452
}
5553

56-
trait Observable: IntoBytes + Immutable + Copy {
57-
fn to_le(self) -> Self;
54+
trait Observable: Copy {
55+
type Bytes: Sized + AsRef<[u8]>;
56+
fn to_le_bytes(self) -> Self::Bytes;
5857
}
5958
impl Observable for u32 {
60-
fn to_le(self) -> Self {
61-
self.to_le()
59+
type Bytes = [u8; 4];
60+
61+
fn to_le_bytes(self) -> Self::Bytes {
62+
Self::to_le_bytes(self)
6263
}
6364
}
6465
impl Observable for u64 {
65-
fn to_le(self) -> Self {
66-
self.to_le()
66+
type Bytes = [u8; 8];
67+
68+
fn to_le_bytes(self) -> Self::Bytes {
69+
Self::to_le_bytes(self)
6770
}
6871
}
6972

@@ -72,32 +75,35 @@ impl Observable for u64 {
7275
/// Returns `(n, byte_len)`. `src[..n]` is consumed (and possibly mutated),
7376
/// `dest[..byte_len]` is filled. `src[n..]` and `dest[byte_len..]` are left
7477
/// unaltered.
75-
fn fill_via_chunks<T: Observable>(src: &mut [T], dest: &mut [u8]) -> (usize, usize) {
78+
fn fill_via_chunks<T: Observable>(src: &[T], dest: &mut [u8]) -> (usize, usize) {
7679
let size = core::mem::size_of::<T>();
77-
let byte_len = min(core::mem::size_of_val(src), dest.len());
78-
let num_chunks = (byte_len + size - 1) / size;
79-
80-
// Byte-swap for portability of results. This must happen before copying
81-
// since the size of dest is not guaranteed to be a multiple of T or to be
82-
// sufficiently aligned.
83-
if cfg!(target_endian = "big") {
84-
for x in &mut src[..num_chunks] {
85-
*x = x.to_le();
86-
}
87-
}
8880

89-
dest[..byte_len].copy_from_slice(&<[T]>::as_bytes(&src[..num_chunks])[..byte_len]);
81+
// Always use little endian for portability of results.
9082

91-
(num_chunks, byte_len)
83+
let mut dest = dest.chunks_exact_mut(size);
84+
let mut src = src.iter();
85+
86+
let zipped = dest.by_ref().zip(src.by_ref());
87+
let num_chunks = zipped.len();
88+
zipped.for_each(|(dest, src)| dest.copy_from_slice(src.to_le_bytes().as_ref()));
89+
90+
let byte_len = num_chunks * size;
91+
if let (dest_tail @ [_, ..], Some(src)) = (dest.into_remainder(), src.next()) {
92+
let n = dest_tail.len();
93+
dest_tail.copy_from_slice(&src.to_le_bytes().as_ref()[..n]);
94+
(num_chunks + 1, byte_len + n)
95+
} else {
96+
(num_chunks, byte_len)
97+
}
9298
}
9399

94100
/// Implement `fill_bytes` by reading chunks from the output buffer of a block
95101
/// based RNG.
96102
///
97103
/// The return values are `(consumed_u32, filled_u8)`.
98104
///
99-
/// On big-endian systems, endianness of `src[..consumed_u32]` values is
100-
/// swapped. No other adjustments to `src` are made.
105+
/// `src` is not modified; it is taken as a `&mut` reference for backward
106+
/// compatibility with previous versions that did change it.
101107
///
102108
/// `filled_u8` is the number of filled bytes in `dest`, which may be less than
103109
/// the length of `dest`.
@@ -125,6 +131,7 @@ fn fill_via_chunks<T: Observable>(src: &mut [T], dest: &mut [u8]) -> (usize, usi
125131
/// }
126132
/// ```
127133
pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
134+
// TODO(SemVer): src: `&[u32]` as we don't mutate it.
128135
fill_via_chunks(src, dest)
129136
}
130137

@@ -133,8 +140,8 @@ pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
133140
///
134141
/// The return values are `(consumed_u64, filled_u8)`.
135142
///
136-
/// On big-endian systems, endianness of `src[..consumed_u64]` values is
137-
/// swapped. No other adjustments to `src` are made.
143+
/// `src` is not modified; it is taken as a `&mut` reference for backward
144+
/// compatibility with previous versions that did change it.
138145
///
139146
/// `filled_u8` is the number of filled bytes in `dest`, which may be less than
140147
/// the length of `dest`.
@@ -143,6 +150,7 @@ pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
143150
///
144151
/// See `fill_via_u32_chunks` for an example.
145152
pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) {
153+
// TODO(SemVer): src: `&[u64]` as we don't mutate it.
146154
fill_via_chunks(src, dest)
147155
}
148156

0 commit comments

Comments
 (0)