|
19 | 19 | //! to/from byte sequences, and since its purpose is reproducibility,
|
20 | 20 | //! non-reproducible sources (e.g. `OsRng`) need not bother with it.
|
21 | 21 |
|
| 22 | +use core::convert::AsRef; |
22 | 23 | use core::intrinsics::transmute;
|
23 | 24 | use core::ptr::copy_nonoverlapping;
|
24 |
| -use core::slice; |
| 25 | +use core::{fmt, slice}; |
25 | 26 | use core::cmp::min;
|
26 | 27 | use core::mem::size_of;
|
27 |
| -use RngCore; |
| 28 | +use {RngCore, BlockRngCore, CryptoRng, SeedableRng, Error}; |
28 | 29 |
|
29 | 30 | /// Implement `next_u64` via `next_u32`, little-endian order.
|
30 | 31 | pub fn next_u64_via_u32<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
|
@@ -164,4 +165,174 @@ pub fn next_u64_via_fill<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
|
164 | 165 | impl_uint_from_fill!(rng, u64, 8)
|
165 | 166 | }
|
166 | 167 |
|
| 168 | +/// Wrapper around PRNGs that implement [`BlockRngCore`] to keep a results |
| 169 | +/// buffer and offer the methods from [`RngCore`]. |
| 170 | +/// |
| 171 | +/// `BlockRng` has heavily optimized implementations of the [`RngCore`] methods |
| 172 | +/// reading values from the results buffer, as well as |
| 173 | +/// calling `BlockRngCore::generate` directly on the output array when |
| 174 | +/// `fill_bytes` / `try_fill_bytes` is called on a large array. These methods |
| 175 | +/// also handle the bookkeeping of when to generate a new batch of values. |
| 176 | +/// No generated values are ever thown away. |
| 177 | +/// |
| 178 | +/// Currently `BlockRng` only implements `RngCore` for buffers which are slices |
| 179 | +/// of `u32` elements; this may be extended to other types in the future. |
| 180 | +/// |
| 181 | +/// For easy initialization `BlockRng` also implements [`SeedableRng`]. |
| 182 | +/// |
| 183 | +/// [`BlockRngCore`]: ../BlockRngCore.t.html |
| 184 | +/// [`RngCore`]: ../RngCore.t.html |
| 185 | +/// [`SeedableRng`]: ../SeedableRng.t.html |
| 186 | +#[derive(Clone)] |
| 187 | +pub struct BlockRng<R: BlockRngCore + ?Sized> { |
| 188 | + pub results: R::Results, |
| 189 | + pub index: usize, |
| 190 | + pub core: R, |
| 191 | +} |
| 192 | + |
| 193 | +// Custom Debug implementation that does not expose the contents of `results`. |
| 194 | +impl<R: BlockRngCore + fmt::Debug> fmt::Debug for BlockRng<R> { |
| 195 | + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| 196 | + fmt.debug_struct("BlockRng") |
| 197 | + .field("core", &self.core) |
| 198 | + .field("result_len", &self.results.as_ref().len()) |
| 199 | + .field("index", &self.index) |
| 200 | + .finish() |
| 201 | + } |
| 202 | +} |
| 203 | + |
| 204 | +impl<R: BlockRngCore<Item=u32>> RngCore for BlockRng<R> |
| 205 | +where <R as BlockRngCore>::Results: AsRef<[u32]> |
| 206 | +{ |
| 207 | + #[inline(always)] |
| 208 | + fn next_u32(&mut self) -> u32 { |
| 209 | + if self.index >= self.results.as_ref().len() { |
| 210 | + self.core.generate(&mut self.results); |
| 211 | + self.index = 0; |
| 212 | + } |
| 213 | + |
| 214 | + let value = self.results.as_ref()[self.index]; |
| 215 | + self.index += 1; |
| 216 | + value |
| 217 | + } |
| 218 | + |
| 219 | + #[inline(always)] |
| 220 | + fn next_u64(&mut self) -> u64 { |
| 221 | + let read_u64 = |results: &[u32], index| { |
| 222 | + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { |
| 223 | + // requires little-endian CPU supporting unaligned reads: |
| 224 | + unsafe { *(&results[index] as *const u32 as *const u64) } |
| 225 | + } else { |
| 226 | + let x = results[index] as u64; |
| 227 | + let y = results[index + 1] as u64; |
| 228 | + (y << 32) | x |
| 229 | + } |
| 230 | + }; |
| 231 | + |
| 232 | + let len = self.results.as_ref().len(); |
| 233 | + |
| 234 | + let index = self.index; |
| 235 | + if index < len-1 { |
| 236 | + self.index += 2; |
| 237 | + // Read an u64 from the current index |
| 238 | + read_u64(self.results.as_ref(), index) |
| 239 | + } else if index >= len { |
| 240 | + self.core.generate(&mut self.results); |
| 241 | + self.index = 2; |
| 242 | + read_u64(self.results.as_ref(), 0) |
| 243 | + } else { |
| 244 | + let x = self.results.as_ref()[len-1] as u64; |
| 245 | + self.core.generate(&mut self.results); |
| 246 | + self.index = 1; |
| 247 | + let y = self.results.as_ref()[0] as u64; |
| 248 | + (y << 32) | x |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | + // As an optimization we try to write directly into the output buffer. |
| 253 | + // This is only enabled for little-endian platforms where unaligned writes |
| 254 | + // are known to be safe and fast. |
| 255 | + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 256 | + fn fill_bytes(&mut self, dest: &mut [u8]) { |
| 257 | + let mut filled = 0; |
| 258 | + |
| 259 | + // Continue filling from the current set of results |
| 260 | + if self.index < self.results.as_ref().len() { |
| 261 | + let (consumed_u32, filled_u8) = |
| 262 | + fill_via_u32_chunks(&self.results.as_ref()[self.index..], |
| 263 | + dest); |
| 264 | + |
| 265 | + self.index += consumed_u32; |
| 266 | + filled += filled_u8; |
| 267 | + } |
| 268 | + |
| 269 | + let len_remainder = |
| 270 | + (dest.len() - filled) % (self.results.as_ref().len() * 4); |
| 271 | + let end_direct = dest.len() - len_remainder; |
| 272 | + |
| 273 | + while filled < end_direct { |
| 274 | + let dest_u32: &mut R::Results = unsafe { |
| 275 | + ::core::mem::transmute(dest[filled..].as_mut_ptr()) |
| 276 | + }; |
| 277 | + self.core.generate(dest_u32); |
| 278 | + filled += self.results.as_ref().len() * 4; |
| 279 | + } |
| 280 | + self.index = self.results.as_ref().len(); |
| 281 | + |
| 282 | + if len_remainder > 0 { |
| 283 | + self.core.generate(&mut self.results); |
| 284 | + let (consumed_u32, _) = |
| 285 | + fill_via_u32_chunks(&mut self.results.as_ref(), |
| 286 | + &mut dest[filled..]); |
| 287 | + |
| 288 | + self.index = consumed_u32; |
| 289 | + } |
| 290 | + } |
| 291 | + |
| 292 | + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] |
| 293 | + fn fill_bytes(&mut self, dest: &mut [u8]) { |
| 294 | + let mut read_len = 0; |
| 295 | + while read_len < dest.len() { |
| 296 | + if self.index >= self.results.as_ref().len() { |
| 297 | + self.core.generate(&mut self.results); |
| 298 | + self.index = 0; |
| 299 | + } |
| 300 | + let (consumed_u32, filled_u8) = |
| 301 | + fill_via_u32_chunks(&self.results.as_ref()[self.index..], |
| 302 | + &mut dest[read_len..]); |
| 303 | + |
| 304 | + self.index += consumed_u32; |
| 305 | + read_len += filled_u8; |
| 306 | + } |
| 307 | + } |
| 308 | + |
| 309 | + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { |
| 310 | + Ok(self.fill_bytes(dest)) |
| 311 | + } |
| 312 | +} |
| 313 | + |
| 314 | +impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng<R> { |
| 315 | + type Seed = R::Seed; |
| 316 | + |
| 317 | + fn from_seed(seed: Self::Seed) -> Self { |
| 318 | + let results_empty = R::Results::default(); |
| 319 | + Self { |
| 320 | + core: R::from_seed(seed), |
| 321 | + index: results_empty.as_ref().len(), // generate on first use |
| 322 | + results: results_empty, |
| 323 | + } |
| 324 | + } |
| 325 | + |
| 326 | + fn from_rng<RNG: RngCore>(rng: &mut RNG) -> Result<Self, Error> { |
| 327 | + let results_empty = R::Results::default(); |
| 328 | + Ok(Self { |
| 329 | + core: R::from_rng(rng)?, |
| 330 | + index: results_empty.as_ref().len(), // generate on first use |
| 331 | + results: results_empty, |
| 332 | + }) |
| 333 | + } |
| 334 | +} |
| 335 | + |
| 336 | +impl<R: BlockRngCore + CryptoRng> CryptoRng for BlockRng<R> {} |
| 337 | + |
167 | 338 | // TODO: implement tests for the above
|
0 commit comments