Skip to content

Commit

Permalink
fill_via_chunks: on BE, swap then copy
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed Sep 12, 2021
1 parent 9684ebf commit 45f33d1
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 31 deletions.
4 changes: 2 additions & 2 deletions rand_core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ where
self.generate_and_set(0);
}
let (consumed_u32, filled_u8) =
fill_via_u32_chunks(&self.results.as_ref()[self.index..], &mut dest[read_len..]);
fill_via_u32_chunks(&mut self.results.as_mut()[self.index..], &mut dest[read_len..]);

self.index += consumed_u32;
read_len += filled_u8;
Expand Down Expand Up @@ -399,7 +399,7 @@ where
}

let (consumed_u64, filled_u8) = fill_via_u64_chunks(
&self.results.as_ref()[self.index as usize..],
&mut self.results.as_mut()[self.index as usize..],
&mut dest[read_len..],
);

Expand Down
66 changes: 37 additions & 29 deletions rand_core/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,28 +58,21 @@ macro_rules! fill_via_chunks {
let chunk_size_u8 = min($src.len() * SIZE, $dst.len());
let chunk_size = (chunk_size_u8 + SIZE - 1) / SIZE;

if cfg!(target_endian = "little") {
// On LE we can do a simple copy, which is 25-50% faster:
unsafe {
core::ptr::copy_nonoverlapping(
$src.as_ptr() as *const u8,
$dst.as_mut_ptr(),
chunk_size_u8);
}
} else {
// This code is valid on all arches, but slower than the above:
let mut i = 0;
let mut iter = $dst[..chunk_size_u8].chunks_exact_mut(SIZE);
while let Some(chunk) = iter.next() {
chunk.copy_from_slice(&$src[i].to_le_bytes());
i += 1;
}
let chunk = iter.into_remainder();
if !chunk.is_empty() {
chunk.copy_from_slice(&$src[i].to_le_bytes()[..chunk.len()]);
// Byte-swap for portability of results:
if cfg!(target_endian = "big") {
for x in &mut $src[..chunk_size] {
*x = x.to_le();
}
}

// We do a simple copy, which is 25-50% faster:
unsafe {
core::ptr::copy_nonoverlapping(
$src.as_ptr() as *const u8,
$dst.as_mut_ptr(),
chunk_size_u8);
}

(chunk_size, chunk_size_u8)
}};
}
Expand All @@ -89,6 +82,9 @@ macro_rules! fill_via_chunks {
///
/// 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.
///
/// `filled_u8` is the number of filled bytes in `dest`, which may be less than
/// the length of `dest`.
/// `consumed_u32` is the number of words consumed from `src`, which is the same
Expand All @@ -114,21 +110,25 @@ macro_rules! fill_via_chunks {
/// }
/// }
/// ```
pub fn fill_via_u32_chunks(src: &[u32], dest: &mut [u8]) -> (usize, usize) {
pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
fill_via_chunks!(src, dest, u32)
}

/// Implement `fill_bytes` by reading chunks from the output buffer of a block
/// based RNG.
///
/// 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.
///
/// `filled_u8` is the number of filled bytes in `dest`, which may be less than
/// the length of `dest`.
/// `consumed_u64` is the number of words consumed from `src`, which is the same
/// as `filled_u8 / 8` rounded up.
///
/// See `fill_via_u32_chunks` for an example.
pub fn fill_via_u64_chunks(src: &[u64], dest: &mut [u8]) -> (usize, usize) {
pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) {
fill_via_chunks!(src, dest, u64)
}

Expand All @@ -152,33 +152,41 @@ mod test {

#[test]
fn test_fill_via_u32_chunks() {
let src = [1, 2, 3];
let src_orig = [1, 2, 3];

let mut src = src_orig;
let mut dst = [0u8; 11];
assert_eq!(fill_via_u32_chunks(&src, &mut dst), (3, 11));
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (3, 11));
assert_eq!(dst, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0]);

let mut src = src_orig;
let mut dst = [0u8; 13];
assert_eq!(fill_via_u32_chunks(&src, &mut dst), (3, 12));
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (3, 12));
assert_eq!(dst, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0]);

let mut src = src_orig;
let mut dst = [0u8; 5];
assert_eq!(fill_via_u32_chunks(&src, &mut dst), (2, 5));
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (2, 5));
assert_eq!(dst, [1, 0, 0, 0, 2]);
}

#[test]
fn test_fill_via_u64_chunks() {
let src = [1, 2];
let src_orig = [1, 2];

let mut src = src_orig;
let mut dst = [0u8; 11];
assert_eq!(fill_via_u64_chunks(&src, &mut dst), (2, 11));
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (2, 11));
assert_eq!(dst, [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0]);

let mut src = src_orig;
let mut dst = [0u8; 17];
assert_eq!(fill_via_u64_chunks(&src, &mut dst), (2, 16));
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (2, 16));
assert_eq!(dst, [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]);

let mut src = src_orig;
let mut dst = [0u8; 5];
assert_eq!(fill_via_u64_chunks(&src, &mut dst), (1, 5));
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (1, 5));
assert_eq!(dst, [1, 0, 0, 0, 0]);
}
}

0 comments on commit 45f33d1

Please sign in to comment.