From cdc09e530a4e5176884f83d9ead7b0853ffbdcfa Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 20 Jan 2018 19:55:18 +0100 Subject: [PATCH] Make Fill_via_u*_chunks not modify src --- src/impls.rs | 52 +++++++++++++++++++++++++-------------------- src/prng/chacha.rs | 16 ++++++++++++-- src/prng/hc128.rs | 6 +++--- src/prng/isaac.rs | 2 +- src/prng/isaac64.rs | 2 +- 5 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/impls.rs b/src/impls.rs index 34c17786..ef9115a5 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -23,6 +23,7 @@ #![allow(unused)] use core::intrinsics::transmute; +use core::ptr::copy_nonoverlapping; use core::slice; use core::cmp::min; use core::mem::size_of; @@ -82,21 +83,34 @@ macro_rules! impl_uint_from_fill { } macro_rules! fill_via_chunks { - ($src:expr, $dest:expr, $N:expr) => ({ - let chunk_size_u8 = min($src.len() * $N, $dest.len()); - let chunk_size = (chunk_size_u8 + $N - 1) / $N; - - // Convert to little-endian: - for ref mut x in $src[0..chunk_size].iter_mut() { - **x = (*x).to_le(); + ($src:expr, $dst:expr, $size:expr) => ({ + let chunk_size_u8 = min($src.len() * $size, $dst.len()); + let chunk_size = (chunk_size_u8 + $size - 1) / $size; + if cfg!(target_endian="little") { + // Unsafe code copied from `byteorder::write_slice_native`. + unsafe { + copy_nonoverlapping( + $src.as_ptr() as *const u8, + $dst.as_mut_ptr(), + chunk_size_u8); + } + } else { + // Unsafe code copied from `byteorder::write_slice` and + // `byteorder::write_num_bytes`, with the addition to copy only + // `chunk.len()`. + // Byteorder assumes we want to copy only complete integers, while + // for us the destination may need only a part of the last integer. + for (&n, chunk) in $src.iter().zip($dst.chunks_mut($size)) { + unsafe { + // N.B. https://github.com/rust-lang/rust/issues/22776 + let bytes = transmute::<_, [u8; $size]>(n.to_le()); + copy_nonoverlapping((&bytes).as_ptr(), + chunk.as_mut_ptr(), + chunk.len()); + } + } } - let bytes = unsafe { slice::from_raw_parts($src.as_ptr() as *const u8, - $src.len() * $N) }; - - let dest_chunk = &mut $dest[0..chunk_size_u8]; - dest_chunk.copy_from_slice(&bytes[0..chunk_size_u8]); - (chunk_size, chunk_size_u8) }); } @@ -111,10 +125,6 @@ macro_rules! fill_via_chunks { /// `consumed_u32` is the number of words consumed from `src`, which is the same /// as `filled_u8 / 4` rounded up. /// -/// Note that on big-endian systems values in the output buffer `src` are -/// mutated. `src[0..consumed_u32]` get converted to little-endian before -/// copying. -/// /// # Example /// (from `IsaacRng`) /// @@ -135,7 +145,7 @@ macro_rules! fill_via_chunks { /// } /// } /// ``` -pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { +pub fn fill_via_u32_chunks(src: &[u32], dest: &mut [u8]) -> (usize, usize) { fill_via_chunks!(src, dest, 4) } @@ -148,12 +158,8 @@ pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { /// `consumed_u64` is the number of words consumed from `src`, which is the same /// as `filled_u8 / 8` rounded up. /// -/// Note that on big-endian systems values in the output buffer `src` are -/// mutated. `src[0..consumed_u64]` get converted to little-endian before -/// copying. -/// /// See `fill_via_u32_chunks` for an example. -pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) { +pub fn fill_via_u64_chunks(src: &[u64], dest: &mut [u8]) -> (usize, usize) { fill_via_chunks!(src, dest, 8) } diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 61d475fd..639f4e2e 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -208,8 +208,20 @@ impl Rng for ChaChaRng { } - fn fill_bytes(&mut self, bytes: &mut [u8]) { - impls::fill_bytes_via_u32(self, bytes) + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut read_len = 0; + while read_len < dest.len() { + if self.index >= self.buffer.len() { + self.update(); + } + + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&self.buffer[self.index..], + &mut dest[read_len..]); + + self.index += consumed_u32; + read_len += filled_u8; + } } } diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs index 6ac2f27e..a0905e47 100644 --- a/src/prng/hc128.rs +++ b/src/prng/hc128.rs @@ -342,7 +342,7 @@ impl Rng for Hc128Rng { // Continue filling from the current set of results if self.index < 16 { let (consumed_u32, filled_u8) = - impls::fill_via_u32_chunks(&mut self.results[self.index..], + impls::fill_via_u32_chunks(&self.results[self.index..], dest); self.index += consumed_u32; @@ -367,7 +367,7 @@ impl Rng for Hc128Rng { self.state.update(&mut self.results); let (consumed_u32, _) = - impls::fill_via_u32_chunks(&mut self.results, + impls::fill_via_u32_chunks(&self.results, &mut dest[filled..]); self.index = consumed_u32; @@ -384,7 +384,7 @@ impl Rng for Hc128Rng { } let (consumed_u32, filled_u8) = - impls::fill_via_u32_chunks(&mut self.results[self.index..], + impls::fill_via_u32_chunks(&self.results[self.index..], &mut dest[read_len..]); self.index += consumed_u32; diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 0d217009..4aa03d6c 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -242,7 +242,7 @@ impl Rng for IsaacRng { } let (consumed_u32, filled_u8) = - impls::fill_via_u32_chunks(&mut self.rsl[(self.index as usize)..], + impls::fill_via_u32_chunks(&self.rsl[(self.index as usize)..], &mut dest[read_len..]); self.index += consumed_u32 as u32; diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index f791c244..2ee3c3bb 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -245,7 +245,7 @@ impl Rng for Isaac64Rng { } let (consumed_u64, filled_u8) = - impls::fill_via_u64_chunks(&mut self.rsl[self.index as usize..], + impls::fill_via_u64_chunks(&self.rsl[self.index as usize..], &mut dest[read_len..]); self.index += consumed_u64 as u32;