Merge pull request #325 from pitdicker/isaac_blockrng

Implement `BlockRngCore` ISAAC and ISAAC-64 
diff --git a/.travis.yml b/.travis.yml
index 92ff8ff..af57fd4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,18 +12,18 @@
       install:
       script:
         - cargo test --all --tests --no-default-features
-        - cargo test --features serde-1,log
+        - cargo test --features serde1,log
     - rust: stable
       os: osx
       install:
       script:
         - cargo test --all --tests --no-default-features
-        - cargo test --features serde-1,log
+        - cargo test --features serde1,log
     - rust: beta
       install:
       script:
         - cargo test --all --tests --no-default-features
-        - cargo test --tests --no-default-features --features=serde-1
+        - cargo test --tests --no-default-features --features=serde1
     - rust: nightly
       install:
       before_script:
@@ -31,7 +31,7 @@
       script:
         - cargo test --all --tests --no-default-features --features=alloc
         - cargo test --all --features=alloc
-        - cargo test --features serde-1,log,nightly
+        - cargo test --features serde1,log,nightly
         - cargo test --benches
         - cargo doc --no-deps --all --all-features
         - cargo --list | egrep "^\s*deadlinks$" -q || cargo install cargo-deadlinks
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 12afe54..6a9a0a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,7 +16,7 @@
 - Create a seperate `rand_core` crate. (#288)
 - Deprecate `rand_derive`. (#256)
 - Add `log` feature. Logging is now available in `JitterRng`, `OsRng`, `EntropyRng` and `ReseedingRng`. (#246)
-- Add `serde-1` feature for some PRNGs. (#189)
+- Add `serde1` feature for some PRNGs. (#189)
 - `stdweb` feature for `OsRng` support on WASM via stdweb. (#272, #336)
 
 ### `Rng` trait
@@ -55,7 +55,7 @@
 - Change `thread_rng` reseeding threshold to 32 MiB. (#277)
 - PRNGs no longer implement `Copy`. (#209)
 - `Debug` implementations no longer show internals. (#209)
-- Implement serialisation for `XorShiftRng`, `IsaacRng` and `Isaac64Rng` under the `serde-1` feature. (#189)
+- Implement serialization for `XorShiftRng`, `IsaacRng` and `Isaac64Rng` under the `serde1` feature. (#189)
 - Implement `BlockRngCore` for `ChaChaCore` and `Hc128Core`. (#281)
 - All PRNGs are now portable across big- and little-endian architectures. (#209)
 - `Isaac64Rng::next_u32` no longer throws away half the results. (#209)
diff --git a/Cargo.toml b/Cargo.toml
index 479c2ad..27b0aad 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,7 +23,7 @@
 std = ["rand_core/std", "alloc", "libc", "winapi", "cloudabi", "fuchsia-zircon"]
 alloc = ["rand_core/alloc"]  # enables Vec and Box support (without std)
 i128_support = [] # enables i128 and u128 support
-serde-1 = ["serde", "serde_derive"] # enables serialisation for PRNGs
+serde1 = ["serde", "serde_derive", "rand_core/serde1"] # enables serialization for PRNGs
 
 [workspace]
 members = ["rand_core"]
diff --git a/README.md b/README.md
index 6947494..1f75787 100644
--- a/README.md
+++ b/README.md
@@ -103,7 +103,7 @@
 -   `i128_support` enables support for generating `u128` and `i128` values
 -   `log` enables some logging via the `log` crate
 -   `nightly` enables all unstable features (`i128_support`)
--   `serde-1` enables serialisation for some types, via Serde version 1
+-   `serde1` enables serialization for some types, via Serde version 1
 -   `stdweb` enables support for `OsRng` on WASM via stdweb.
 -   `std` enabled by default; by setting "default-features = false" `no_std`
     mode is activated; this removes features depending on `std` functionality:
@@ -132,7 +132,7 @@
 cargo test --tests --no-default-features --features alloc
 
 # Test log and serde support
-cargo test --features serde-1,log
+cargo test --features serde1,log
 
 # Test 128-bit support (requires nightly)
 cargo test --all --features nightly
diff --git a/UPDATING.md b/UPDATING.md
index 2f3b0b3..8a694b5 100644
--- a/UPDATING.md
+++ b/UPDATING.md
@@ -27,7 +27,7 @@
 Several new Cargo feature flags have been added:
 
 -   `alloc`, used without `std`, allows use of `Box` and `Vec`
--   `serde-1` adds serialisation support to some PRNGs
+-   `serde1` adds serialization support to some PRNGs
 -   `log` adds logging in a few places (primarily to `OsRng` and `JitterRng`)
 
 ### `Rng` and friends (core traits)
diff --git a/appveyor.yml b/appveyor.yml
index 823f552..d9b613e 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -34,6 +34,6 @@
 test_script:
   - cargo test --benches
   - cargo test --all
-  - cargo test --features serde-1,log,nightly
+  - cargo test --features serde1,log,nightly
   - cargo test --all --tests --no-default-features --features=alloc
-  - cargo test --tests --no-default-features --features=serde-1
+  - cargo test --tests --no-default-features --features=serde1
diff --git a/rand_core/Cargo.toml b/rand_core/Cargo.toml
index 59742bc..e75d927 100644
--- a/rand_core/Cargo.toml
+++ b/rand_core/Cargo.toml
@@ -22,3 +22,8 @@
 # default = ["std"]
 std = ["alloc"]    # use std library; should be default but for above bug
 alloc = []  # enables Vec and Box support without std
+serde1 = ["serde", "serde_derive"] # enables serde for BlockRng wrapper
+
+[dependencies]
+serde = { version = "1", optional = true }
+serde_derive = { version = "1", optional = true }
diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs
index 393d998..645dc8f 100644
--- a/rand_core/src/impls.rs
+++ b/rand_core/src/impls.rs
@@ -27,6 +27,8 @@
 use core::mem::size_of;
 use {RngCore, BlockRngCore, CryptoRng, SeedableRng, Error};
 
+#[cfg(feature="serde1")] use serde::{Serialize, Deserialize};
+
 /// Implement `next_u64` via `next_u32`, little-endian order.
 pub fn next_u64_via_u32<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
     // Use LE; we explicitly generate one value before the next.
@@ -184,10 +186,14 @@
 /// [`RngCore`]: ../RngCore.t.html
 /// [`SeedableRng`]: ../SeedableRng.t.html
 #[derive(Clone)]
+#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
 pub struct BlockRng<R: BlockRngCore + ?Sized> {
-    pub results: R::Results,
-    pub index: usize,
-    pub core: R,
+    #[cfg_attr(feature="serde1", serde(bound(
+        serialize = "R::Results: Serialize",
+        deserialize = "R::Results: Deserialize<'de>")))]
+    results: R::Results,
+    index: usize,
+    core: R,
 }
 
 // Custom Debug implementation that does not expose the contents of `results`.
@@ -201,6 +207,35 @@
     }
 }
 
+impl<R: BlockRngCore> BlockRng<R> {
+    /// Create a new `BlockRng` from an existing RNG implementing
+    /// `BlockRngCore`. Results will be generated on first use.
+    pub fn new(core: R) -> BlockRng<R>{
+        let results_empty = R::Results::default();
+        BlockRng {
+            core,
+            index: results_empty.as_ref().len(),
+            results: results_empty,
+        }
+    }
+
+    /// Return a reference the wrapped `BlockRngCore`.
+    pub fn inner(&self) -> &R {
+        &self.core
+    }
+
+    /// Return a mutable reference the wrapped `BlockRngCore`.
+    pub fn inner_mut(&mut self) -> &mut R {
+        &mut self.core
+    }
+
+    // Reset the number of available results.
+    // This will force a new set of results to be generated on next use.
+    pub fn reset(&mut self) {
+        self.index = self.results.as_ref().len();
+    }
+}
+
 impl<R: BlockRngCore<Item=u32>> RngCore for BlockRng<R>
 where <R as BlockRngCore>::Results: AsRef<[u32]>
 {
@@ -317,21 +352,190 @@
     type Seed = R::Seed;
 
     fn from_seed(seed: Self::Seed) -> Self {
+        Self::new(R::from_seed(seed))
+    }
+
+    fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> {
+        Ok(Self::new(R::from_rng(rng)?))
+    }
+}
+
+
+
+/// Wrapper around PRNGs that implement [`BlockRngCore`] to keep a results
+/// buffer and offer the methods from [`RngCore`].
+///
+/// This is similar to [`BlockRng`], but specialized for algorithms that operate
+/// on `u64` values.
+///
+/// [`BlockRngCore`]: ../BlockRngCore.t.html
+/// [`RngCore`]: ../RngCore.t.html
+/// [`BlockRng`]: struct.BlockRng.html
+#[derive(Clone)]
+#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
+pub struct BlockRng64<R: BlockRngCore + ?Sized> {
+    #[cfg_attr(feature="serde1", serde(bound(
+        serialize = "R::Results: Serialize",
+        deserialize = "R::Results: Deserialize<'de>")))]
+    results: R::Results,
+    index: usize,
+    half_used: bool, // true if only half of the previous result is used
+    core: R,
+}
+
+// Custom Debug implementation that does not expose the contents of `results`.
+impl<R: BlockRngCore + fmt::Debug> fmt::Debug for BlockRng64<R> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt.debug_struct("BlockRng64")
+           .field("core", &self.core)
+           .field("result_len", &self.results.as_ref().len())
+           .field("index", &self.index)
+           .field("half_used", &self.half_used)
+           .finish()
+    }
+}
+
+impl<R: BlockRngCore> BlockRng64<R> {
+    /// Create a new `BlockRng` from an existing RNG implementing
+    /// `BlockRngCore`. Results will be generated on first use.
+    pub fn new(core: R) -> BlockRng64<R>{
         let results_empty = R::Results::default();
-        Self {
-            core: R::from_seed(seed),
-            index: results_empty.as_ref().len(), // generate on first use
+        BlockRng64 {
+            core,
+            index: results_empty.as_ref().len(),
+            half_used: false,
             results: results_empty,
         }
     }
 
+    /// Return a mutable reference the wrapped `BlockRngCore`.
+    pub fn inner(&mut self) -> &mut R {
+        &mut self.core
+    }
+
+    // Reset the number of available results.
+    // This will force a new set of results to be generated on next use.
+    pub fn reset(&mut self) {
+        self.index = self.results.as_ref().len();
+    }
+}
+
+impl<R: BlockRngCore<Item=u64>> RngCore for BlockRng64<R>
+where <R as BlockRngCore>::Results: AsRef<[u64]>
+{
+    #[inline(always)]
+    fn next_u32(&mut self) -> u32 {
+        let mut index = self.index * 2 - self.half_used as usize;
+        if index >= self.results.as_ref().len() * 2 {
+            self.core.generate(&mut self.results);
+            self.index = 0;
+            // `self.half_used` is by definition `false`
+            self.half_used = false;
+            index = 0;
+        }
+
+        self.half_used = !self.half_used;
+        self.index += self.half_used as usize;
+
+        // Index as if this is a u32 slice.
+        unsafe {
+            let results =
+                &*(self.results.as_ref() as *const [u64] as *const [u32]);
+            if cfg!(target_endian = "little") {
+                *results.get_unchecked(index)
+            } else {
+                *results.get_unchecked(index ^ 1)
+            }
+        }
+    }
+
+    #[inline(always)]
+    fn next_u64(&mut self) -> u64 {
+        if self.index >= self.results.as_ref().len() {
+            self.core.generate(&mut self.results);
+            self.index = 0;
+        }
+
+        let value = self.results.as_ref()[self.index];
+        self.index += 1;
+        self.half_used = false;
+        value
+    }
+
+    // As an optimization we try to write directly into the output buffer.
+    // This is only enabled for little-endian platforms where unaligned writes
+    // are known to be safe and fast.
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    fn fill_bytes(&mut self, dest: &mut [u8]) {
+        let mut filled = 0;
+        self.half_used = false;
+
+        // Continue filling from the current set of results
+        if self.index < self.results.as_ref().len() {
+            let (consumed_u64, filled_u8) =
+                fill_via_u64_chunks(&self.results.as_ref()[self.index..],
+                                    dest);
+
+            self.index += consumed_u64;
+            filled += filled_u8;
+        }
+
+        let len_remainder =
+            (dest.len() - filled) % (self.results.as_ref().len() * 8);
+        let end_direct = dest.len() - len_remainder;
+
+        while filled < end_direct {
+            let dest_u64: &mut R::Results = unsafe {
+                ::core::mem::transmute(dest[filled..].as_mut_ptr())
+            };
+            self.core.generate(dest_u64);
+            filled += self.results.as_ref().len() * 8;
+        }
+        self.index = self.results.as_ref().len();
+
+        if len_remainder > 0 {
+            self.core.generate(&mut self.results);
+            let (consumed_u64, _) =
+                fill_via_u64_chunks(&mut self.results.as_ref(),
+                                    &mut dest[filled..]);
+
+            self.index = consumed_u64;
+        }
+    }
+
+    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+    fn fill_bytes(&mut self, dest: &mut [u8]) {
+        let mut read_len = 0;
+        self.half_used = false;
+        while read_len < dest.len() {
+            if self.index as usize >= self.results.as_ref().len() {
+                self.core.generate(&mut self.results);
+                self.index = 0;
+            }
+
+            let (consumed_u64, filled_u8) =
+                fill_via_u64_chunks(&self.results.as_ref()[self.index as usize..],
+                                    &mut dest[read_len..]);
+
+            self.index += consumed_u64;
+            read_len += filled_u8;
+        }
+    }
+
+    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
+        Ok(self.fill_bytes(dest))
+    }
+}
+
+impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng64<R> {
+    type Seed = R::Seed;
+
+    fn from_seed(seed: Self::Seed) -> Self {
+        Self::new(R::from_seed(seed))
+    }
+
     fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> {
-        let results_empty = R::Results::default();
-        Ok(Self {
-            core: R::from_rng(rng)?,
-            index: results_empty.as_ref().len(), // generate on first use
-            results: results_empty,
-        })
+        Ok(Self::new(R::from_rng(rng)?))
     }
 }
 
diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs
index 5eb1e17..483bc4b 100644
--- a/rand_core/src/lib.rs
+++ b/rand_core/src/lib.rs
@@ -44,6 +44,8 @@
 
 #[cfg(feature="std")] extern crate core;
 #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc;
+#[cfg(feature="serde1")] extern crate serde;
+#[cfg(feature="serde1")] #[macro_use] extern crate serde_derive;
 
 
 use core::default::Default;
diff --git a/src/lib.rs b/src/lib.rs
index 76ef632..2bccf0d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -195,9 +195,9 @@
 #[cfg(feature="std")] extern crate std as core;
 #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc;
 
-#[cfg(test)] #[cfg(feature="serde-1")] extern crate bincode;
-#[cfg(feature="serde-1")] extern crate serde;
-#[cfg(feature="serde-1")] #[macro_use] extern crate serde_derive;
+#[cfg(test)] #[cfg(feature="serde1")] extern crate bincode;
+#[cfg(feature="serde1")] extern crate serde;
+#[cfg(feature="serde1")] #[macro_use] extern crate serde_derive;
 
 #[cfg(all(target_arch = "wasm32", feature = "stdweb"))]
 #[macro_use]
diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs
index cf3926b..7d1748c 100644
--- a/src/prng/chacha.rs
+++ b/src/prng/chacha.rs
@@ -156,8 +156,8 @@
     /// assert_eq!(rng1.next_u32(), rng2.next_u32());
     /// ```
     pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) {
-        self.0.core.set_counter(counter_low, counter_high);
-        self.0.index = STATE_WORDS; // force recomputation on next use
+        self.0.inner_mut().set_counter(counter_low, counter_high);
+        self.0.reset(); // force recomputation on next use
     }
 
     /// Sets the number of rounds to run the ChaCha core algorithm per block to
@@ -179,8 +179,8 @@
     /// assert_eq!(rng.next_u32(), 0x2fef003e);
     /// ```
     pub fn set_rounds(&mut self, rounds: usize) {
-        self.0.core.set_rounds(rounds);
-        self.0.index = STATE_WORDS; // force recomputation on next use
+        self.0.inner_mut().set_rounds(rounds);
+        self.0.reset(); // force recomputation on next use
     }
 }
 
diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs
index 6193277..5701650 100644
--- a/src/prng/isaac.rs
+++ b/src/prng/isaac.rs
@@ -12,8 +12,9 @@
 
 use core::{fmt, slice};
 use core::num::Wrapping as w;
-
-use rand_core::{RngCore, SeedableRng, Error, impls, le};
+use rand_core::{BlockRngCore, RngCore, SeedableRng, Error, le};
+use rand_core::impls::BlockRng;
+use prng::isaac_array::IsaacArray;
 
 #[allow(non_camel_case_types)]
 type w32 = w<u32>;
@@ -84,38 +85,40 @@
 /// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*](
 ///      https://eprint.iacr.org/2006/438)
 ///
-/// [`Hc128Rng`]: hc128/struct.Hc128Rng.html
-#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))]
-pub struct IsaacRng {
-    #[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))]
-    rsl: [u32; RAND_SIZE],
-    #[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))]
-    mem: [w32; RAND_SIZE],
-    a: w32,
-    b: w32,
-    c: w32,
-    index: u32,
-}
+/// [`Hc128Rng`]: ../hc128/struct.Hc128Rng.html
+#[derive(Clone, Debug)]
+#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
+pub struct IsaacRng(BlockRng<IsaacCore>);
 
-// Cannot be derived because [u32; 256] does not implement Clone
-// FIXME: remove once RFC 2000 gets implemented
-impl Clone for IsaacRng {
-    fn clone(&self) -> IsaacRng {
-        IsaacRng {
-            rsl: self.rsl,
-            mem: self.mem,
-            a: self.a,
-            b: self.b,
-            c: self.c,
-            index: self.index,
-        }
+impl RngCore for IsaacRng {
+    #[inline(always)]
+    fn next_u32(&mut self) -> u32 {
+        self.0.next_u32()
+    }
+
+    #[inline(always)]
+    fn next_u64(&mut self) -> u64 {
+        self.0.next_u64()
+    }
+
+    fn fill_bytes(&mut self, dest: &mut [u8]) {
+        self.0.fill_bytes(dest)
+    }
+
+    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
+        self.0.try_fill_bytes(dest)
     }
 }
 
-// Custom Debug implementation that does not expose the internal state
-impl fmt::Debug for IsaacRng {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "IsaacRng {{}}")
+impl SeedableRng for IsaacRng {
+    type Seed = <IsaacCore as SeedableRng>::Seed;
+
+    fn from_seed(seed: Self::Seed) -> Self {
+        IsaacRng(BlockRng::<IsaacCore>::from_seed(seed))
+    }
+
+    fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> {
+        BlockRng::<IsaacCore>::from_rng(rng).map(|rng| IsaacRng(rng))
     }
 }
 
@@ -125,27 +128,41 @@
     ///
     /// DEPRECATED. `IsaacRng::new_from_u64(0)` will produce identical results.
     #[deprecated(since="0.5.0", note="use the NewRng or SeedableRng trait")]
-    pub fn new_unseeded() -> IsaacRng {
+    pub fn new_unseeded() -> Self {
         Self::new_from_u64(0)
     }
 
-    /// Creates an ISAAC random number generator using an u64 as seed.
+    /// Create an ISAAC random number generator using an `u64` as seed.
     /// If `seed == 0` this will produce the same stream of random numbers as
     /// the reference implementation when used unseeded.
-    pub fn new_from_u64(seed: u64) -> IsaacRng {
-        let mut key = [w(0); RAND_SIZE];
-        key[0] = w(seed as u32);
-        key[1] = w((seed >> 32) as u32);
-        // Initialize with only one pass.
-        // A second pass does not improve the quality here, because all of
-        // the seed was already available in the first round.
-        // Not doing the second pass has the small advantage that if `seed == 0`
-        // this method produces exactly the same state as the reference
-        // implementation when used unseeded.
-        init(key, 1)
+    pub fn new_from_u64(seed: u64) -> Self {
+        IsaacRng(BlockRng::new(IsaacCore::new_from_u64(seed)))
     }
+}
 
-    /// Refills the output buffer (`self.rsl`)
+/// The core of `IsaacRng`, used with `BlockRng`.
+#[derive(Clone)]
+#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
+pub struct IsaacCore {
+    #[cfg_attr(feature="serde1",serde(with="super::isaac_array::isaac_array_serde"))]
+    mem: [w32; RAND_SIZE],
+    a: w32,
+    b: w32,
+    c: w32,
+}
+
+// Custom Debug implementation that does not expose the internal state
+impl fmt::Debug for IsaacCore {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "IsaacCore {{}}")
+    }
+}
+
+impl BlockRngCore for IsaacCore {
+    type Item = u32;
+    type Results = IsaacArray<Self::Item>;
+
+    /// Refills the output buffer (`results`)
     /// See also the pseudocode desciption of the algorithm at the top of this
     /// file.
     ///
@@ -160,10 +177,11 @@
     /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the
     ///   `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer
     ///   arithmetic.
-    /// - We fill `rsl` backwards. The reference implementation reads values
-    ///   from `rsl` in reverse. We read them in the normal direction, to make
-    ///   `fill_bytes` a memcopy. To maintain compatibility we fill in reverse.
-    fn isaac(&mut self) {
+    /// - We fill `results` backwards. The reference implementation reads values
+    ///   from `results` in reverse. We read them in the normal direction, to
+    ///   make `fill_bytes` a memcopy. To maintain compatibility we fill in
+    ///   reverse.
+    fn generate(&mut self, results: &mut IsaacArray<Self::Item>) {
         self.c += w(1);
         // abbreviations
         let mut a = self.a;
@@ -177,180 +195,146 @@
         }
 
         #[inline]
-        fn rngstep(ctx: &mut IsaacRng,
+        fn rngstep(mem: &mut [w32; RAND_SIZE],
+                   results: &mut [u32; RAND_SIZE],
                    mix: w32,
                    a: &mut w32,
                    b: &mut w32,
                    base: usize,
                    m: usize,
                    m2: usize) {
-            let x = ctx.mem[base + m];
-            *a = mix + ctx.mem[base + m2];
-            let y = *a + *b + ind(&ctx.mem, x, 2);
-            ctx.mem[base + m] = y;
-            *b = x + ind(&ctx.mem, y, 2 + RAND_SIZE_LEN);
-            ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0;
+            let x = mem[base + m];
+            *a = mix + mem[base + m2];
+            let y = *a + *b + ind(&mem, x, 2);
+            mem[base + m] = y;
+            *b = x + ind(&mem, y, 2 + RAND_SIZE_LEN);
+            results[RAND_SIZE - 1 - base - m] = (*b).0;
         }
 
         let mut m = 0;
         let mut m2 = MIDPOINT;
         for i in (0..MIDPOINT/4).map(|i| i * 4) {
-            rngstep(self, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2);
-            rngstep(self, a ^ (a >> 6 ),  &mut a, &mut b, i + 1, m, m2);
-            rngstep(self, a ^ (a << 2 ),  &mut a, &mut b, i + 2, m, m2);
-            rngstep(self, a ^ (a >> 16),  &mut a, &mut b, i + 3, m, m2);
+            rngstep(&mut self.mem, results, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2);
+            rngstep(&mut self.mem, results, a ^ (a >> 6 ),  &mut a, &mut b, i + 1, m, m2);
+            rngstep(&mut self.mem, results, a ^ (a << 2 ),  &mut a, &mut b, i + 2, m, m2);
+            rngstep(&mut self.mem, results, a ^ (a >> 16),  &mut a, &mut b, i + 3, m, m2);
         }
 
         m = MIDPOINT;
         m2 = 0;
         for i in (0..MIDPOINT/4).map(|i| i * 4) {
-            rngstep(self, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2);
-            rngstep(self, a ^ (a >> 6 ),  &mut a, &mut b, i + 1, m, m2);
-            rngstep(self, a ^ (a << 2 ),  &mut a, &mut b, i + 2, m, m2);
-            rngstep(self, a ^ (a >> 16),  &mut a, &mut b, i + 3, m, m2);
+            rngstep(&mut self.mem, results, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2);
+            rngstep(&mut self.mem, results, a ^ (a >> 6 ),  &mut a, &mut b, i + 1, m, m2);
+            rngstep(&mut self.mem, results, a ^ (a << 2 ),  &mut a, &mut b, i + 2, m, m2);
+            rngstep(&mut self.mem, results, a ^ (a >> 16),  &mut a, &mut b, i + 3, m, m2);
         }
 
         self.a = a;
         self.b = b;
-        self.index = 0;
     }
 }
 
-impl RngCore for IsaacRng {
+impl IsaacCore {
+    /// Create a new ISAAC random number generator.
+    ///
+    /// The author Bob Jenkins describes how to best initialize ISAAC here:
+    /// <https://rt.cpan.org/Public/Bug/Display.html?id=64324>
+    /// The answer is included here just in case:
+    ///
+    /// "No, you don't need a full 8192 bits of seed data. Normal key sizes will
+    /// do fine, and they should have their expected strength (eg a 40-bit key
+    /// will take as much time to brute force as 40-bit keys usually will). You
+    /// could fill the remainder with 0, but set the last array element to the
+    /// length of the key provided (to distinguish keys that differ only by
+    /// different amounts of 0 padding). You do still need to call randinit() to
+    /// make sure the initial state isn't uniform-looking."
+    /// "After publishing ISAAC, I wanted to limit the key to half the size of
+    /// r[], and repeat it twice. That would have made it hard to provide a key
+    /// that sets the whole internal state to anything convenient. But I'd
+    /// already published it."
+    ///
+    /// And his answer to the question "For my code, would repeating the key
+    /// over and over to fill 256 integers be a better solution than
+    /// zero-filling, or would they essentially be the same?":
+    /// "If the seed is under 32 bytes, they're essentially the same, otherwise
+    /// repeating the seed would be stronger. randinit() takes a chunk of 32
+    /// bytes, mixes it, and combines that with the next 32 bytes, et cetera.
+    /// Then loops over all the elements the same way a second time."
     #[inline]
-    fn next_u32(&mut self) -> u32 {
-        // Using a local variable for `index`, and checking the size avoids a
-        // bounds check later on.
-        let mut index = self.index as usize;
-        if index >= RAND_SIZE {
-            self.isaac();
-            index = 0;
+    fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> Self {
+        fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32,
+               e: &mut w32, f: &mut w32, g: &mut w32, h: &mut w32) {
+            *a ^= *b << 11; *d += *a; *b += *c;
+            *b ^= *c >> 2;  *e += *b; *c += *d;
+            *c ^= *d << 8;  *f += *c; *d += *e;
+            *d ^= *e >> 16; *g += *d; *e += *f;
+            *e ^= *f << 10; *h += *e; *f += *g;
+            *f ^= *g >> 4;  *a += *f; *g += *h;
+            *g ^= *h << 8;  *b += *g; *h += *a;
+            *h ^= *a >> 9;  *c += *h; *a += *b;
         }
 
-        let value = self.rsl[index];
-        self.index += 1;
-        value
-    }
+        // These numbers are the result of initializing a...h with the
+        // fractional part of the golden ratio in binary (0x9e3779b9)
+        // and applying mix() 4 times.
+        let mut a = w(0x1367df5a);
+        let mut b = w(0x95d90059);
+        let mut c = w(0xc3163e4b);
+        let mut d = w(0x0f421ad8);
+        let mut e = w(0xd92a4a78);
+        let mut f = w(0xa51a3c49);
+        let mut g = w(0xc4efea1b);
+        let mut h = w(0x30609119);
 
-    #[inline]
-    fn next_u64(&mut self) -> u64 {
-        impls::next_u64_via_u32(self)
-    }
-
-    fn fill_bytes(&mut self, dest: &mut [u8]) {
-        let mut read_len = 0;
-        while read_len < dest.len() {
-            if self.index as usize >= RAND_SIZE {
-                self.isaac();
+        // Normally this should do two passes, to make all of the seed effect
+        // all of `mem`
+        for _ in 0..rounds {
+            for i in (0..RAND_SIZE/8).map(|i| i * 8) {
+                a += mem[i  ]; b += mem[i+1];
+                c += mem[i+2]; d += mem[i+3];
+                e += mem[i+4]; f += mem[i+5];
+                g += mem[i+6]; h += mem[i+7];
+                mix(&mut a, &mut b, &mut c, &mut d,
+                    &mut e, &mut f, &mut g, &mut h);
+                mem[i  ] = a; mem[i+1] = b;
+                mem[i+2] = c; mem[i+3] = d;
+                mem[i+4] = e; mem[i+5] = f;
+                mem[i+6] = g; mem[i+7] = h;
             }
-
-            let (consumed_u32, filled_u8) =
-                impls::fill_via_u32_chunks(&self.rsl[(self.index as usize)..],
-                                           &mut dest[read_len..]);
-
-            self.index += consumed_u32 as u32;
-            read_len += filled_u8;
         }
+
+        Self { mem, a: w(0), b: w(0), c: w(0) }
     }
 
-    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
-        Ok(self.fill_bytes(dest))
+    /// Create an ISAAC random number generator using an `u64` as seed.
+    /// If `seed == 0` this will produce the same stream of random numbers as
+    /// the reference implementation when used unseeded.
+    fn new_from_u64(seed: u64) -> Self {
+        let mut key = [w(0); RAND_SIZE];
+        key[0] = w(seed as u32);
+        key[1] = w((seed >> 32) as u32);
+        // Initialize with only one pass.
+        // A second pass does not improve the quality here, because all of the
+        // seed was already available in the first round.
+        // Not doing the second pass has the small advantage that if
+        // `seed == 0` this method produces exactly the same state as the
+        // reference implementation when used unseeded.
+        Self::init(key, 1)
     }
 }
 
-/// Creates a new ISAAC random number generator.
-///
-/// The author Bob Jenkins describes how to best initialize ISAAC here:
-/// <https://rt.cpan.org/Public/Bug/Display.html?id=64324>
-/// The answer is included here just in case:
-///
-/// "No, you don't need a full 8192 bits of seed data. Normal key sizes will do
-/// fine, and they should have their expected strength (eg a 40-bit key will
-/// take as much time to brute force as 40-bit keys usually will). You could
-/// fill the remainder with 0, but set the last array element to the length of
-/// the key provided (to distinguish keys that differ only by different amounts
-/// of 0 padding). You do still need to call randinit() to make sure the initial
-/// state isn't uniform-looking."
-/// "After publishing ISAAC, I wanted to limit the key to half the size of r[],
-/// and repeat it twice. That would have made it hard to provide a key that sets
-/// the whole internal state to anything convenient. But I'd already published
-/// it."
-///
-/// And his answer to the question "For my code, would repeating the key over
-/// and over to fill 256 integers be a better solution than zero-filling, or
-/// would they essentially be the same?":
-/// "If the seed is under 32 bytes, they're essentially the same, otherwise
-/// repeating the seed would be stronger. randinit() takes a chunk of 32 bytes,
-/// mixes it, and combines that with the next 32 bytes, et cetera. Then loops
-/// over all the elements the same way a second time."
-#[inline]
-fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> IsaacRng {
-    // These numbers are the result of initializing a...h with the
-    // fractional part of the golden ratio in binary (0x9e3779b9)
-    // and applying mix() 4 times.
-    let mut a = w(0x1367df5a);
-    let mut b = w(0x95d90059);
-    let mut c = w(0xc3163e4b);
-    let mut d = w(0x0f421ad8);
-    let mut e = w(0xd92a4a78);
-    let mut f = w(0xa51a3c49);
-    let mut g = w(0xc4efea1b);
-    let mut h = w(0x30609119);
-
-    // Normally this should do two passes, to make all of the seed effect all
-    // of `mem`
-    for _ in 0..rounds {
-        for i in (0..RAND_SIZE/8).map(|i| i * 8) {
-            a += mem[i  ]; b += mem[i+1];
-            c += mem[i+2]; d += mem[i+3];
-            e += mem[i+4]; f += mem[i+5];
-            g += mem[i+6]; h += mem[i+7];
-            mix(&mut a, &mut b, &mut c, &mut d,
-                &mut e, &mut f, &mut g, &mut h);
-            mem[i  ] = a; mem[i+1] = b;
-            mem[i+2] = c; mem[i+3] = d;
-            mem[i+4] = e; mem[i+5] = f;
-            mem[i+6] = g; mem[i+7] = h;
-        }
-    }
-
-    let mut rng = IsaacRng {
-        rsl: [0; RAND_SIZE],
-        mem,
-        a: w(0),
-        b: w(0),
-        c: w(0),
-        index: 0,
-    };
-
-    // Prepare the first set of results
-    rng.isaac();
-    rng
-}
-
-fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32,
-       e: &mut w32, f: &mut w32, g: &mut w32, h: &mut w32) {
-    *a ^= *b << 11; *d += *a; *b += *c;
-    *b ^= *c >> 2;  *e += *b; *c += *d;
-    *c ^= *d << 8;  *f += *c; *d += *e;
-    *d ^= *e >> 16; *g += *d; *e += *f;
-    *e ^= *f << 10; *h += *e; *f += *g;
-    *f ^= *g >> 4;  *a += *f; *g += *h;
-    *g ^= *h << 8;  *b += *g; *h += *a;
-    *h ^= *a >> 9;  *c += *h; *a += *b;
-}
-
-impl SeedableRng for IsaacRng {
+impl SeedableRng for IsaacCore {
     type Seed = [u8; 32];
 
     fn from_seed(seed: Self::Seed) -> Self {
         let mut seed_u32 = [0u32; 8];
         le::read_u32_into(&seed, &mut seed_u32);
+        // Convert the seed to `Wrapping<u32>` and zero-extend to `RAND_SIZE`.
         let mut seed_extended = [w(0); RAND_SIZE];
         for (x, y) in seed_extended.iter_mut().zip(seed_u32.iter()) {
             *x = w(*y);
         }
-        init(seed_extended, 2)
+        Self::init(seed_extended, 2)
     }
 
     fn from_rng<R: RngCore>(mut rng: R) -> Result<Self, Error> {
@@ -367,7 +351,7 @@
             *i = w(i.0.to_le());
         }
 
-        Ok(init(seed, 2))
+        Ok(Self::init(seed, 2))
     }
 }
 
@@ -472,7 +456,7 @@
     }
 
     #[test]
-    #[cfg(all(feature="serde-1", feature="std"))]
+    #[cfg(all(feature="serde1", feature="std"))]
     fn test_isaac_serde() {
         use bincode;
         use std::io::{BufWriter, BufReader};
@@ -489,20 +473,8 @@
         let mut read = BufReader::new(&buf[..]);
         let mut deserialized: IsaacRng = bincode::deserialize_from(&mut read).expect("Could not deserialize");
 
-        assert_eq!(rng.index, deserialized.index);
-        /* Can't assert directly because of the array size */
-        for (orig,deser) in rng.rsl.iter().zip(deserialized.rsl.iter()) {
-            assert_eq!(orig, deser);
-        }
-        for (orig,deser) in rng.mem.iter().zip(deserialized.mem.iter()) {
-            assert_eq!(orig, deser);
-        }
-        assert_eq!(rng.a, deserialized.a);
-        assert_eq!(rng.b, deserialized.b);
-        assert_eq!(rng.c, deserialized.c);
-
-        for _ in 0..16 {
-            assert_eq!(rng.next_u64(), deserialized.next_u64());
+        for _ in 0..300 { // more than the 256 buffered results
+            assert_eq!(rng.next_u32(), deserialized.next_u32());
         }
     }
 }
diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs
index 65dedac..05468fc 100644
--- a/src/prng/isaac64.rs
+++ b/src/prng/isaac64.rs
@@ -12,8 +12,9 @@
 
 use core::{fmt, slice};
 use core::num::Wrapping as w;
-
-use rand_core::{RngCore, SeedableRng, Error, impls, le};
+use rand_core::{BlockRngCore, RngCore, SeedableRng, Error, le};
+use rand_core::impls::BlockRng64;
+use prng::isaac_array::IsaacArray;
 
 #[allow(non_camel_case_types)]
 type w64 = w<u64>;
@@ -74,40 +75,40 @@
 ///      http://burtleburtle.net/bob/rand/isaac.html)
 ///
 /// [`IsaacRng`]: ../isaac/struct.IsaacRng.html
-/// [`Hc128Rng`]: hc128/struct.Hc128Rng.html
-#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))]
-pub struct Isaac64Rng {
-    #[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))]
-    rsl: [u64; RAND_SIZE],
-    #[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))]
-    mem: [w64; RAND_SIZE],
-    a: w64,
-    b: w64,
-    c: w64,
-    index: u32,
-    half_used: bool, // true if only half of the previous result is used
-}
+/// [`Hc128Rng`]: ../hc128/struct.Hc128Rng.html
+#[derive(Clone, Debug)]
+#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
+pub struct Isaac64Rng(BlockRng64<Isaac64Core>);
 
-// Cannot be derived because [u64; 256] does not implement Clone
-// FIXME: remove once RFC 2000 gets implemented
-impl Clone for Isaac64Rng {
-    fn clone(&self) -> Isaac64Rng {
-        Isaac64Rng {
-            rsl: self.rsl,
-            mem: self.mem,
-            a: self.a,
-            b: self.b,
-            c: self.c,
-            index: self.index,
-            half_used: self.half_used,
-        }
+impl RngCore for Isaac64Rng {
+    #[inline(always)]
+    fn next_u32(&mut self) -> u32 {
+        self.0.next_u32()
+    }
+
+    #[inline(always)]
+    fn next_u64(&mut self) -> u64 {
+        self.0.next_u64()
+    }
+
+    fn fill_bytes(&mut self, dest: &mut [u8]) {
+        self.0.fill_bytes(dest)
+    }
+
+    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
+        self.0.try_fill_bytes(dest)
     }
 }
 
-// Custom Debug implementation that does not expose the internal state
-impl fmt::Debug for Isaac64Rng {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "Isaac64Rng {{}}")
+impl SeedableRng for Isaac64Rng {
+    type Seed = <Isaac64Core as SeedableRng>::Seed;
+
+    fn from_seed(seed: Self::Seed) -> Self {
+        Isaac64Rng(BlockRng64::<Isaac64Core>::from_seed(seed))
+    }
+
+    fn from_rng<S: RngCore>(rng: S) -> Result<Self, Error> {
+        BlockRng64::<Isaac64Core>::from_rng(rng).map(|rng| Isaac64Rng(rng))
     }
 }
 
@@ -117,26 +118,41 @@
     ///
     /// DEPRECATED. `Isaac64Rng::new_from_u64(0)` will produce identical results.
     #[deprecated(since="0.5.0", note="use the NewRng or SeedableRng trait")]
-    pub fn new_unseeded() -> Isaac64Rng {
+    pub fn new_unseeded() -> Self {
         Self::new_from_u64(0)
     }
 
-    /// Creates an ISAAC-64 random number generator using an u64 as seed.
+    /// Create an ISAAC-64 random number generator using an `u64` as seed.
     /// If `seed == 0` this will produce the same stream of random numbers as
     /// the reference implementation when used unseeded.
-    pub fn new_from_u64(seed: u64) -> Isaac64Rng {
-        let mut key = [w(0); RAND_SIZE];
-        key[0] = w(seed);
-        // Initialize with only one pass.
-        // A second pass does not improve the quality here, because all of
-        // the seed was already available in the first round.
-        // Not doing the second pass has the small advantage that if `seed == 0`
-        // this method produces exactly the same state as the reference
-        // implementation when used unseeded.
-        init(key, 1)
+    pub fn new_from_u64(seed: u64) -> Self {
+        Isaac64Rng(BlockRng64::new(Isaac64Core::new_from_u64(seed)))
     }
+}
 
-    /// Refills the output buffer (`self.rsl`)
+/// The core of `Isaac64Rng`, used with `BlockRng`.
+#[derive(Clone)]
+#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
+pub struct Isaac64Core {
+    #[cfg_attr(feature="serde1",serde(with="super::isaac_array::isaac_array_serde"))]
+    mem: [w64; RAND_SIZE],
+    a: w64,
+    b: w64,
+    c: w64,
+}
+
+// Custom Debug implementation that does not expose the internal state
+impl fmt::Debug for Isaac64Core {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Isaac64Core {{}}")
+    }
+}
+
+impl BlockRngCore for Isaac64Core {
+    type Item = u64;
+    type Results = IsaacArray<Self::Item>;
+
+    /// Refills the output buffer (`results`)
     /// See also the pseudocode desciption of the algorithm at the top of this
     /// file.
     ///
@@ -151,10 +167,11 @@
     /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the
     ///   `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer
     ///   arithmetic.
-    /// - We fill `rsl` backwards. The reference implementation reads values
-    ///   from `rsl` in reverse. We read them in the normal direction, to make
-    ///   `fill_bytes` a memcopy. To maintain compatibility we fill in reverse.
-    fn isaac64(&mut self) {
+    /// - We fill `results` backwards. The reference implementation reads values
+    ///   from `results` in reverse. We read them in the normal direction, to
+    ///   make `fill_bytes` a memcopy. To maintain compatibility we fill in
+    ///   reverse.
+    fn generate(&mut self, results: &mut IsaacArray<Self::Item>) {
         self.c += w(1);
         // abbreviations
         let mut a = self.a;
@@ -168,171 +185,120 @@
         }
 
         #[inline]
-        fn rngstep(ctx: &mut Isaac64Rng,
+        fn rngstep(mem: &mut [w64; RAND_SIZE],
+                   results: &mut [u64; RAND_SIZE],
                    mix: w64,
                    a: &mut w64,
                    b: &mut w64,
                    base: usize,
                    m: usize,
                    m2: usize) {
-            let x = ctx.mem[base + m];
-            *a = mix + ctx.mem[base + m2];
-            let y = *a + *b + ind(&ctx.mem, x, 3);
-            ctx.mem[base + m] = y;
-            *b = x + ind(&ctx.mem, y, 3 + RAND_SIZE_LEN);
-            ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0;
+            let x = mem[base + m];
+            *a = mix + mem[base + m2];
+            let y = *a + *b + ind(&mem, x, 3);
+            mem[base + m] = y;
+            *b = x + ind(&mem, y, 3 + RAND_SIZE_LEN);
+            results[RAND_SIZE - 1 - base - m] = (*b).0;
         }
 
         let mut m = 0;
         let mut m2 = MIDPOINT;
         for i in (0..MIDPOINT/4).map(|i| i * 4) {
-            rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2);
-            rngstep(self,   a ^ (a >> 5 ),  &mut a, &mut b, i + 1, m, m2);
-            rngstep(self,   a ^ (a << 12),  &mut a, &mut b, i + 2, m, m2);
-            rngstep(self,   a ^ (a >> 33),  &mut a, &mut b, i + 3, m, m2);
+            rngstep(&mut self.mem, results, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2);
+            rngstep(&mut self.mem, results,   a ^ (a >> 5 ),  &mut a, &mut b, i + 1, m, m2);
+            rngstep(&mut self.mem, results,   a ^ (a << 12),  &mut a, &mut b, i + 2, m, m2);
+            rngstep(&mut self.mem, results,   a ^ (a >> 33),  &mut a, &mut b, i + 3, m, m2);
         }
 
         m = MIDPOINT;
         m2 = 0;
         for i in (0..MIDPOINT/4).map(|i| i * 4) {
-            rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2);
-            rngstep(self,   a ^ (a >> 5 ),  &mut a, &mut b, i + 1, m, m2);
-            rngstep(self,   a ^ (a << 12),  &mut a, &mut b, i + 2, m, m2);
-            rngstep(self,   a ^ (a >> 33),  &mut a, &mut b, i + 3, m, m2);
+            rngstep(&mut self.mem, results, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2);
+            rngstep(&mut self.mem, results,   a ^ (a >> 5 ),  &mut a, &mut b, i + 1, m, m2);
+            rngstep(&mut self.mem, results,   a ^ (a << 12),  &mut a, &mut b, i + 2, m, m2);
+            rngstep(&mut self.mem, results,   a ^ (a >> 33),  &mut a, &mut b, i + 3, m, m2);
         }
 
         self.a = a;
         self.b = b;
-        self.index = 0;
-        self.half_used = false;
     }
 }
 
-impl RngCore for Isaac64Rng {
-    #[inline]
-    fn next_u32(&mut self) -> u32 {
-        // Using a local variable for `index`, and checking the size avoids a
-        // bounds check later on.
-        let mut index = self.index as usize * 2 - self.half_used as usize;
-        if index >= RAND_SIZE * 2 {
-            self.isaac64();
-            index = 0;
+impl Isaac64Core {
+    /// Create a new ISAAC-64 random number generator.
+    fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Self {
+        fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64,
+               e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) {
+            *a -= *e; *f ^= *h >> 9;  *h += *a;
+            *b -= *f; *g ^= *a << 9;  *a += *b;
+            *c -= *g; *h ^= *b >> 23; *b += *c;
+            *d -= *h; *a ^= *c << 15; *c += *d;
+            *e -= *a; *b ^= *d >> 14; *d += *e;
+            *f -= *b; *c ^= *e << 20; *e += *f;
+            *g -= *c; *d ^= *f >> 17; *f += *g;
+            *h -= *d; *e ^= *g << 14; *g += *h;
         }
 
-        self.half_used = !self.half_used;
-        self.index += self.half_used as u32;
+        // These numbers are the result of initializing a...h with the
+        // fractional part of the golden ratio in binary (0x9e3779b97f4a7c13)
+        // and applying mix() 4 times.
+        let mut a = w(0x647c4677a2884b7c);
+        let mut b = w(0xb9f8b322c73ac862);
+        let mut c = w(0x8c0ea5053d4712a0);
+        let mut d = w(0xb29b2e824a595524);
+        let mut e = w(0x82f053db8355e0ce);
+        let mut f = w(0x48fe4a0fa5a09315);
+        let mut g = w(0xae985bf2cbfc89ed);
+        let mut h = w(0x98f5704f6c44c0ab);
 
-        // Index as if this is a u32 slice.
-        let rsl = unsafe { &*(&mut self.rsl as *mut [u64; RAND_SIZE]
-                                            as *mut [u32; RAND_SIZE * 2]) };
-
-        if cfg!(target_endian = "little") {
-            rsl[index]
-        } else {
-            rsl[index ^ 1]
-        }
-    }
-
-    #[inline]
-    fn next_u64(&mut self) -> u64 {
-        let mut index = self.index as usize;
-        if index >= RAND_SIZE {
-            self.isaac64();
-            index = 0;
-        }
-
-        let value = self.rsl[index];
-        self.index += 1;
-        self.half_used = false;
-        value
-    }
-
-    fn fill_bytes(&mut self, dest: &mut [u8]) {
-        let mut read_len = 0;
-        while read_len < dest.len() {
-            if self.index as usize >= RAND_SIZE {
-                self.isaac64();
+        // Normally this should do two passes, to make all of the seed effect
+        // all of `mem`
+        for _ in 0..rounds {
+            for i in (0..RAND_SIZE/8).map(|i| i * 8) {
+                a += mem[i  ]; b += mem[i+1];
+                c += mem[i+2]; d += mem[i+3];
+                e += mem[i+4]; f += mem[i+5];
+                g += mem[i+6]; h += mem[i+7];
+                mix(&mut a, &mut b, &mut c, &mut d,
+                    &mut e, &mut f, &mut g, &mut h);
+                mem[i  ] = a; mem[i+1] = b;
+                mem[i+2] = c; mem[i+3] = d;
+                mem[i+4] = e; mem[i+5] = f;
+                mem[i+6] = g; mem[i+7] = h;
             }
-
-            let (consumed_u64, filled_u8) =
-                impls::fill_via_u64_chunks(&self.rsl[self.index as usize..],
-                                           &mut dest[read_len..]);
-
-            self.index += consumed_u64 as u32;
-            read_len += filled_u8;
         }
+
+        Self { mem, a: w(0), b: w(0), c: w(0) }
     }
 
-    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
-        Ok(self.fill_bytes(dest))
+    /// Create an ISAAC-64 random number generator using an `u64` as seed.
+    /// If `seed == 0` this will produce the same stream of random numbers as
+    /// the reference implementation when used unseeded.
+    pub fn new_from_u64(seed: u64) -> Self {
+        let mut key = [w(0); RAND_SIZE];
+        key[0] = w(seed);
+        // Initialize with only one pass.
+        // A second pass does not improve the quality here, because all of the
+        // seed was already available in the first round.
+        // Not doing the second pass has the small advantage that if
+        // `seed == 0` this method produces exactly the same state as the
+        // reference implementation when used unseeded.
+        Self::init(key, 1)
     }
 }
 
-/// Creates a new ISAAC-64 random number generator.
-fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng {
-    // These numbers are the result of initializing a...h with the
-    // fractional part of the golden ratio in binary (0x9e3779b97f4a7c13)
-    // and applying mix() 4 times.
-    let mut a = w(0x647c4677a2884b7c);
-    let mut b = w(0xb9f8b322c73ac862);
-    let mut c = w(0x8c0ea5053d4712a0);
-    let mut d = w(0xb29b2e824a595524);
-    let mut e = w(0x82f053db8355e0ce);
-    let mut f = w(0x48fe4a0fa5a09315);
-    let mut g = w(0xae985bf2cbfc89ed);
-    let mut h = w(0x98f5704f6c44c0ab);
-
-    // Normally this should do two passes, to make all of the seed effect all
-    // of `mem`
-    for _ in 0..rounds {
-        for i in (0..RAND_SIZE/8).map(|i| i * 8) {
-            a += mem[i  ]; b += mem[i+1];
-            c += mem[i+2]; d += mem[i+3];
-            e += mem[i+4]; f += mem[i+5];
-            g += mem[i+6]; h += mem[i+7];
-            mix(&mut a, &mut b, &mut c, &mut d,
-                &mut e, &mut f, &mut g, &mut h);
-            mem[i  ] = a; mem[i+1] = b;
-            mem[i+2] = c; mem[i+3] = d;
-            mem[i+4] = e; mem[i+5] = f;
-            mem[i+6] = g; mem[i+7] = h;
-        }
-    }
-
-    Isaac64Rng {
-        rsl: [0; RAND_SIZE],
-        mem,
-        a: w(0),
-        b: w(0),
-        c: w(0),
-        index: RAND_SIZE as u32, // generate on first use
-        half_used: false,
-    }
-}
-
-fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64,
-       e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) {
-    *a -= *e; *f ^= *h >> 9;  *h += *a;
-    *b -= *f; *g ^= *a << 9;  *a += *b;
-    *c -= *g; *h ^= *b >> 23; *b += *c;
-    *d -= *h; *a ^= *c << 15; *c += *d;
-    *e -= *a; *b ^= *d >> 14; *d += *e;
-    *f -= *b; *c ^= *e << 20; *e += *f;
-    *g -= *c; *d ^= *f >> 17; *f += *g;
-    *h -= *d; *e ^= *g << 14; *g += *h;
-}
-
-impl SeedableRng for Isaac64Rng {
+impl SeedableRng for Isaac64Core {
     type Seed = [u8; 32];
 
     fn from_seed(seed: Self::Seed) -> Self {
         let mut seed_u64 = [0u64; 4];
         le::read_u64_into(&seed, &mut seed_u64);
+        // Convert the seed to `Wrapping<u64>` and zero-extend to `RAND_SIZE`.
         let mut seed_extended = [w(0); RAND_SIZE];
         for (x, y) in seed_extended.iter_mut().zip(seed_u64.iter()) {
             *x = w(*y);
         }
-        init(seed_extended, 2)
+        Self::init(seed_extended, 2)
     }
 
     fn from_rng<R: RngCore>(mut rng: R) -> Result<Self, Error> {
@@ -341,7 +307,6 @@
         let mut seed = [w(0u64); RAND_SIZE];
         unsafe {
             let ptr = seed.as_mut_ptr() as *mut u8;
-
             let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8);
             rng.try_fill_bytes(slice)?;
         }
@@ -349,7 +314,7 @@
             *i = w(i.0.to_le());
         }
 
-        Ok(init(seed, 2))
+        Ok(Self::init(seed, 2))
     }
 }
 
@@ -483,7 +448,7 @@
     }
 
     #[test]
-    #[cfg(all(feature="serde-1", feature="std"))]
+    #[cfg(all(feature="serde1", feature="std"))]
     fn test_isaac64_serde() {
         use bincode;
         use std::io::{BufWriter, BufReader};
@@ -500,20 +465,7 @@
         let mut read = BufReader::new(&buf[..]);
         let mut deserialized: Isaac64Rng = bincode::deserialize_from(&mut read).expect("Could not deserialize");
 
-        assert_eq!(rng.index, deserialized.index);
-        assert_eq!(rng.half_used, deserialized.half_used);
-        /* Can't assert directly because of the array size */
-        for (orig,deser) in rng.rsl.iter().zip(deserialized.rsl.iter()) {
-            assert_eq!(orig, deser);
-        }
-        for (orig,deser) in rng.mem.iter().zip(deserialized.mem.iter()) {
-            assert_eq!(orig, deser);
-        }
-        assert_eq!(rng.a, deserialized.a);
-        assert_eq!(rng.b, deserialized.b);
-        assert_eq!(rng.c, deserialized.c);
-
-        for _ in 0..16 {
+        for _ in 0..300 { // more than the 256 buffered results
             assert_eq!(rng.next_u64(), deserialized.next_u64());
         }
     }
diff --git a/src/prng/isaac_array.rs b/src/prng/isaac_array.rs
new file mode 100644
index 0000000..327cfbf
--- /dev/null
+++ b/src/prng/isaac_array.rs
@@ -0,0 +1,130 @@
+// Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// https://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! ISAAC helper functions for 256-element arrays.
+
+// Terrible workaround because arrays with more than 32 elements do not
+// implement `AsRef`, `Default`, `Serialize`, `Deserialize`, or any other
+// traits for that matter.
+
+#[cfg(feature="serde1")] use serde::{Serialize, Deserialize};
+
+const RAND_SIZE_LEN: usize = 8;
+const RAND_SIZE: usize = 1 << RAND_SIZE_LEN;
+
+
+#[derive(Copy, Clone)]
+#[allow(missing_debug_implementations)]
+#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))]
+pub struct IsaacArray<T> {
+    #[cfg_attr(feature="serde1",serde(with="isaac_array_serde"))]
+    #[cfg_attr(feature="serde1", serde(bound(
+        serialize = "T: Serialize",
+        deserialize = "T: Deserialize<'de> + Copy + Default")))]
+    inner: [T; RAND_SIZE]
+}
+
+impl<T> ::core::convert::AsRef<[T]> for IsaacArray<T> {
+    #[inline(always)]
+    fn as_ref(&self) -> &[T] {
+        &self.inner[..]
+    }
+}
+
+impl<T> ::core::ops::Deref for IsaacArray<T> {
+    type Target = [T; RAND_SIZE];
+    #[inline(always)]
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
+impl<T> ::core::ops::DerefMut for IsaacArray<T> {
+    #[inline(always)]
+    fn deref_mut(&mut self) -> &mut [T; RAND_SIZE] {
+        &mut self.inner
+    }
+}
+
+impl<T> ::core::default::Default for IsaacArray<T> where T: Copy + Default {
+    fn default() -> IsaacArray<T> {
+        IsaacArray { inner: [T::default(); RAND_SIZE] }
+    }
+}
+
+
+#[cfg(feature="serde1")]
+pub(super) mod isaac_array_serde {
+    const RAND_SIZE_LEN: usize = 8;
+    const RAND_SIZE: usize = 1 << RAND_SIZE_LEN;
+
+    use serde::{Deserialize, Deserializer, Serialize, Serializer};
+    use serde::de::{Visitor,SeqAccess};
+    use serde::de;
+
+    use core::fmt;
+
+    pub fn serialize<T, S>(arr: &[T;RAND_SIZE], ser: S) -> Result<S::Ok, S::Error>
+    where
+        T: Serialize,
+        S: Serializer
+    {
+        use serde::ser::SerializeTuple;
+
+        let mut seq = ser.serialize_tuple(RAND_SIZE)?;
+
+        for e in arr.iter() {
+            seq.serialize_element(&e)?;
+        }
+
+        seq.end()
+    }
+
+    #[inline]
+    pub fn deserialize<'de, T, D>(de: D) -> Result<[T;RAND_SIZE], D::Error>
+    where
+        T: Deserialize<'de>+Default+Copy,
+        D: Deserializer<'de>,
+    {
+        use core::marker::PhantomData;
+        struct ArrayVisitor<T> {
+            _pd: PhantomData<T>,
+        };
+        impl<'de,T> Visitor<'de> for ArrayVisitor<T>
+        where
+            T: Deserialize<'de>+Default+Copy
+        {
+            type Value = [T; RAND_SIZE];
+
+            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+                formatter.write_str("Isaac state array")
+            }
+
+            #[inline]
+            fn visit_seq<A>(self, mut seq: A) -> Result<[T; RAND_SIZE], A::Error>
+            where
+                A: SeqAccess<'de>,
+            {
+                let mut out = [Default::default();RAND_SIZE];
+
+                for i in 0..RAND_SIZE {
+                    match seq.next_element()? {
+                        Some(val) => out[i] = val,
+                        None => return Err(de::Error::invalid_length(i, &self)),
+                    };
+                }
+
+                Ok(out)
+            }
+        }
+
+        de.deserialize_tuple(RAND_SIZE, ArrayVisitor{_pd: PhantomData})
+    }
+}
diff --git a/src/prng/isaac_serde.rs b/src/prng/isaac_serde.rs
deleted file mode 100644
index 459b928..0000000
--- a/src/prng/isaac_serde.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// https://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! ISAAC serde helper functions.
-
-pub(super) mod rand_size_serde {
-    const RAND_SIZE_LEN: usize = 8;
-    const RAND_SIZE: usize = 1 << RAND_SIZE_LEN;
-
-    use serde::{Deserialize, Deserializer, Serialize, Serializer};
-    use serde::de::{Visitor,SeqAccess};
-    use serde::de;
-
-    use core::fmt;
-
-    pub fn serialize<T, S>(arr: &[T;RAND_SIZE], ser: S) -> Result<S::Ok, S::Error> 
-    where
-        T: Serialize,
-        S: Serializer 
-    {
-        use serde::ser::SerializeTuple;
-
-        let mut seq = ser.serialize_tuple(RAND_SIZE)?;
-
-        for e in arr.iter() {
-            seq.serialize_element(&e)?;
-        }
-
-        seq.end()
-    }
-
-    #[inline]
-    pub fn deserialize<'de, T, D>(de: D) -> Result<[T;RAND_SIZE], D::Error>
-    where
-        T: Deserialize<'de>+Default+Copy,
-        D: Deserializer<'de>,
-    {
-        use core::marker::PhantomData;
-        struct ArrayVisitor<T> {
-            _pd: PhantomData<T>,
-        };
-        impl<'de,T> Visitor<'de> for ArrayVisitor<T>
-        where
-            T: Deserialize<'de>+Default+Copy
-        {
-            type Value = [T; RAND_SIZE];
-
-            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-                formatter.write_str("Isaac state array")
-            }
-
-            #[inline]
-            fn visit_seq<A>(self, mut seq: A) -> Result<[T; RAND_SIZE], A::Error>
-            where
-                A: SeqAccess<'de>,
-            {
-                let mut out = [Default::default();RAND_SIZE];
-
-                for i in 0..RAND_SIZE {
-                    match seq.next_element()? {
-                        Some(val) => out[i] = val,
-                        None => return Err(de::Error::invalid_length(i, &self)),
-                    };
-                }
-
-                Ok(out)
-            }
-        }
-
-        de.deserialize_tuple(RAND_SIZE, ArrayVisitor{_pd: PhantomData})
-    }
-}
diff --git a/src/prng/mod.rs b/src/prng/mod.rs
index edf8f5d..c4bd003 100644
--- a/src/prng/mod.rs
+++ b/src/prng/mod.rs
@@ -42,15 +42,14 @@
 
 pub mod chacha;
 pub mod hc128;
-mod isaac;
-mod isaac64;
+pub mod isaac;
+pub mod isaac64;
 mod xorshift;
 
-#[cfg(feature="serde-1")]
-mod isaac_serde;
+mod isaac_array;
 
-#[doc(inline)] pub use self::chacha::ChaChaRng;
-#[doc(inline)] pub use self::hc128::Hc128Rng;
+pub use self::chacha::ChaChaRng;
+pub use self::hc128::Hc128Rng;
 pub use self::isaac::IsaacRng;
 pub use self::isaac64::Isaac64Rng;
 pub use self::xorshift::XorShiftRng;
diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs
index 181fbf2..9f7a3c8 100644
--- a/src/prng/xorshift.rs
+++ b/src/prng/xorshift.rs
@@ -25,7 +25,7 @@
 /// RNGs"](https://www.jstatsoft.org/v08/i14/paper). *Journal of
 /// Statistical Software*. Vol. 8 (Issue 14).
 #[derive(Clone)]
-#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))]
+#[cfg_attr(feature="serde1", derive(Serialize,Deserialize))]
 pub struct XorShiftRng {
     x: w<u32>,
     y: w<u32>,
@@ -195,7 +195,7 @@
         }
     }
 
-    #[cfg(all(feature="serde-1", feature="std"))]
+    #[cfg(all(feature="serde1", feature="std"))]
     #[test]
     fn test_xorshift_serde() {
         use bincode;
diff --git a/src/reseeding.rs b/src/reseeding.rs
index af8bcba..263a6dc 100644
--- a/src/reseeding.rs
+++ b/src/reseeding.rs
@@ -68,28 +68,13 @@
     /// * `rng`: the random number generator to use.
     /// * `threshold`: the number of generated bytes after which to reseed the RNG.
     /// * `reseeder`: the RNG to use for reseeding.
-    pub fn new(rng: R, threshold: u64, reseeder: Rsdr)
-        -> ReseedingRng<R, Rsdr>
-    {
-        assert!(threshold <= ::core::i64::MAX as u64);
-        let results_empty = R::Results::default();
-        ReseedingRng(
-            BlockRng {
-                core: ReseedingCore {
-                    inner: rng,
-                    reseeder,
-                    threshold: threshold as i64,
-                    bytes_until_reseed: threshold as i64,
-                },
-                index: results_empty.as_ref().len(), // generate on first use
-                results: results_empty,
-            }
-        )
+    pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self {
+        ReseedingRng(BlockRng::new(ReseedingCore::new(rng, threshold, reseeder)))
     }
 
     /// Reseed the internal PRNG.
     pub fn reseed(&mut self) -> Result<(), Error> {
-        self.0.core.reseed()
+        self.0.inner_mut().reseed()
     }
 }
 
@@ -154,6 +139,23 @@
 where R: BlockRngCore + SeedableRng,
       Rsdr: RngCore
 {
+    /// Create a new `ReseedingCore` with the given parameters.
+    ///
+    /// # Arguments
+    ///
+    /// * `rng`: the random number generator to use.
+    /// * `threshold`: the number of generated bytes after which to reseed the RNG.
+    /// * `reseeder`: the RNG to use for reseeding.
+    pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self {
+        assert!(threshold <= ::core::i64::MAX as u64);
+        ReseedingCore {
+            inner: rng,
+            reseeder,
+            threshold: threshold as i64,
+            bytes_until_reseed: threshold as i64,
+        }
+    }
+
     /// Reseed the internal PRNG.
     fn reseed(&mut self) -> Result<(), Error> {
         R::from_rng(&mut self.reseeder).map(|result| {
diff --git a/utils/ci/script.sh b/utils/ci/script.sh
index 26d694b..a34dc5f 100644
--- a/utils/ci/script.sh
+++ b/utils/ci/script.sh
@@ -6,18 +6,18 @@
     if [ ! -z $DISABLE_TESTS ]; then
         cross build --all --no-default-features --target $TARGET --release
         if [ -z $DISABLE_STD ]; then
-            cross build --features log,serde-1 --target $TARGET
+            cross build --features log,serde1 --target $TARGET
         fi
         return
     fi
 
     if [ ! -z $NIGHTLY ]; then
         cross test --all --tests --no-default-features --features alloc --target $TARGET
-        cross test --features serde-1,log,nightly --target $TARGET
+        cross test --features serde1,log,nightly --target $TARGET
         cross test --all --benches --target $TARGET
     else
         cross test --all --tests --no-default-features --target $TARGET
-        cross test --features serde-1,log --target $TARGET
+        cross test --features serde1,log --target $TARGET
     fi
 }