Skip to content

Commit 270f6b3

Browse files
authored
Impl fill_via_chunks without using zerocopy or unsafe (#1607)
2 parents 7aa2637 + 0680e6d commit 270f6b3

File tree

4 files changed

+54
-44
lines changed

4 files changed

+54
-44
lines changed

rand_core/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [Unreleased]
8+
### Other
9+
- Remove `zerocopy` dependency (#1607)
10+
711
## [0.9.2] - 2025-02-22
812
### API changes
913
- Relax `Sized` bound on impls of `TryRngCore`, `TryCryptoRng` and `UnwrapMut` (#1593)

rand_core/Cargo.toml

-1
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/block.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
//! [`BlockRngCore`]: crate::block::BlockRngCore
5454
//! [`fill_bytes`]: RngCore::fill_bytes
5555
56-
use crate::impls::{fill_via_u32_chunks, fill_via_u64_chunks};
56+
use crate::impls::fill_via_chunks;
5757
use crate::{CryptoRng, RngCore, SeedableRng, TryRngCore};
5858
use core::fmt;
5959
#[cfg(feature = "serde")]
@@ -225,10 +225,8 @@ impl<R: BlockRngCore<Item = u32>> RngCore for BlockRng<R> {
225225
if self.index >= self.results.as_ref().len() {
226226
self.generate_and_set(0);
227227
}
228-
let (consumed_u32, filled_u8) = fill_via_u32_chunks(
229-
&mut self.results.as_mut()[self.index..],
230-
&mut dest[read_len..],
231-
);
228+
let (consumed_u32, filled_u8) =
229+
fill_via_chunks(&self.results.as_mut()[self.index..], &mut dest[read_len..]);
232230

233231
self.index += consumed_u32;
234232
read_len += filled_u8;
@@ -390,10 +388,8 @@ impl<R: BlockRngCore<Item = u64>> RngCore for BlockRng64<R> {
390388
self.index = 0;
391389
}
392390

393-
let (consumed_u64, filled_u8) = fill_via_u64_chunks(
394-
&mut self.results.as_mut()[self.index..],
395-
&mut dest[read_len..],
396-
);
391+
let (consumed_u64, filled_u8) =
392+
fill_via_chunks(&self.results.as_mut()[self.index..], &mut dest[read_len..]);
397393

398394
self.index += consumed_u64;
399395
read_len += filled_u8;

rand_core/src/impls.rs

+45-34
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,41 +51,52 @@ 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+
pub(crate) 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

7073
/// Fill dest from src
7174
///
72-
/// Returns `(n, byte_len)`. `src[..n]` is consumed (and possibly mutated),
75+
/// Returns `(n, byte_len)`. `src[..n]` is consumed,
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+
pub(crate) 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

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 Some(src) = src.next() {
92+
// We have consumed all full chunks of dest, but not src.
93+
let dest = dest.into_remainder();
94+
let n = dest.len();
95+
if n > 0 {
96+
dest.copy_from_slice(&src.to_le_bytes().as_ref()[..n]);
97+
return (num_chunks + 1, byte_len + n);
98+
}
99+
}
91100
(num_chunks, byte_len)
92101
}
93102

@@ -96,8 +105,8 @@ fn fill_via_chunks<T: Observable>(src: &mut [T], dest: &mut [u8]) -> (usize, usi
96105
///
97106
/// The return values are `(consumed_u32, filled_u8)`.
98107
///
99-
/// On big-endian systems, endianness of `src[..consumed_u32]` values is
100-
/// swapped. No other adjustments to `src` are made.
108+
/// `src` is not modified; it is taken as a `&mut` reference for backward
109+
/// compatibility with previous versions that did change it.
101110
///
102111
/// `filled_u8` is the number of filled bytes in `dest`, which may be less than
103112
/// the length of `dest`.
@@ -124,6 +133,7 @@ fn fill_via_chunks<T: Observable>(src: &mut [T], dest: &mut [u8]) -> (usize, usi
124133
/// }
125134
/// }
126135
/// ```
136+
#[deprecated(since = "0.9.3", note = "use BlockRng instead")]
127137
pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
128138
fill_via_chunks(src, dest)
129139
}
@@ -133,15 +143,16 @@ pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
133143
///
134144
/// The return values are `(consumed_u64, filled_u8)`.
135145
///
136-
/// On big-endian systems, endianness of `src[..consumed_u64]` values is
137-
/// swapped. No other adjustments to `src` are made.
146+
/// `src` is not modified; it is taken as a `&mut` reference for backward
147+
/// compatibility with previous versions that did change it.
138148
///
139149
/// `filled_u8` is the number of filled bytes in `dest`, which may be less than
140150
/// the length of `dest`.
141151
/// `consumed_u64` is the number of words consumed from `src`, which is the same
142152
/// as `filled_u8 / 8` rounded up.
143153
///
144154
/// See `fill_via_u32_chunks` for an example.
155+
#[deprecated(since = "0.9.3", note = "use BlockRng64 instead")]
145156
pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) {
146157
fill_via_chunks(src, dest)
147158
}
@@ -166,41 +177,41 @@ mod test {
166177

167178
#[test]
168179
fn test_fill_via_u32_chunks() {
169-
let src_orig = [1, 2, 3];
180+
let src_orig = [1u32, 2, 3];
170181

171182
let mut src = src_orig;
172183
let mut dst = [0u8; 11];
173-
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (3, 11));
184+
assert_eq!(fill_via_chunks(&mut src, &mut dst), (3, 11));
174185
assert_eq!(dst, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0]);
175186

176187
let mut src = src_orig;
177188
let mut dst = [0u8; 13];
178-
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (3, 12));
189+
assert_eq!(fill_via_chunks(&mut src, &mut dst), (3, 12));
179190
assert_eq!(dst, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0]);
180191

181192
let mut src = src_orig;
182193
let mut dst = [0u8; 5];
183-
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (2, 5));
194+
assert_eq!(fill_via_chunks(&mut src, &mut dst), (2, 5));
184195
assert_eq!(dst, [1, 0, 0, 0, 2]);
185196
}
186197

187198
#[test]
188199
fn test_fill_via_u64_chunks() {
189-
let src_orig = [1, 2];
200+
let src_orig = [1u64, 2];
190201

191202
let mut src = src_orig;
192203
let mut dst = [0u8; 11];
193-
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (2, 11));
204+
assert_eq!(fill_via_chunks(&mut src, &mut dst), (2, 11));
194205
assert_eq!(dst, [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0]);
195206

196207
let mut src = src_orig;
197208
let mut dst = [0u8; 17];
198-
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (2, 16));
209+
assert_eq!(fill_via_chunks(&mut src, &mut dst), (2, 16));
199210
assert_eq!(dst, [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]);
200211

201212
let mut src = src_orig;
202213
let mut dst = [0u8; 5];
203-
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (1, 5));
214+
assert_eq!(fill_via_chunks(&mut src, &mut dst), (1, 5));
204215
assert_eq!(dst, [1, 0, 0, 0, 0]);
205216
}
206217
}

0 commit comments

Comments
 (0)