Skip to content

Commit 707c3e1

Browse files
committed
Fill isaac64 backwards, and use fill_via_u32_chunks
1 parent f92a347 commit 707c3e1

File tree

1 file changed

+35
-27
lines changed

1 file changed

+35
-27
lines changed

src/prng/isaac64.rs

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use core::iter::repeat;
1515
use core::num::Wrapping as w;
1616
use core::fmt;
1717

18+
use rand_core::impls;
19+
1820
use {Rng, SeedFromRng, SeedableRng, Error};
1921

2022
#[allow(non_camel_case_types)]
@@ -71,12 +73,12 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN;
7173
/// [1]: Bob Jenkins, [*ISAAC and RC4*]
7274
/// (http://burtleburtle.net/bob/rand/isaac.html)
7375
pub struct Isaac64Rng {
74-
rsl: [w64; RAND_SIZE],
76+
rsl: [u64; RAND_SIZE],
7577
mem: [w64; RAND_SIZE],
7678
a: w64,
7779
b: w64,
7880
c: w64,
79-
cnt: u32,
81+
index: u32,
8082
}
8183

8284
// Cannot be derived because [u64; 256] does not implement Clone
@@ -89,7 +91,7 @@ impl Clone for Isaac64Rng {
8991
a: self.a,
9092
b: self.b,
9193
c: self.c,
92-
cnt: self.cnt,
94+
index: self.index,
9395
}
9496
}
9597
}
@@ -132,20 +134,23 @@ impl Isaac64Rng {
132134
/// - We maintain one index `i` and add `m` or `m2` as base (m2 for the
133135
/// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer
134136
/// arithmetic.
137+
/// - We fill `rsl` backwards. The reference implementation reads values
138+
/// from `rsl` in reverse. We read them in the normal direction, to make
139+
/// `fill_bytes` a memcopy. To maintain compatibility we fill in reverse.
135140
fn isaac64(&mut self) {
136141
self.c += w(1);
137142
// abbreviations
138143
let mut a = self.a;
139144
let mut b = self.b + self.c;
140145
const MIDPOINT: usize = RAND_SIZE / 2;
141146

142-
#[inline(always)]
147+
#[inline]
143148
fn ind(mem:&[w64; RAND_SIZE], v: w64, amount: usize) -> w64 {
144149
let index = (v >> amount).0 as usize % RAND_SIZE;
145150
mem[index]
146151
}
147152

148-
#[inline(always)]
153+
#[inline]
149154
fn rngstep(ctx: &mut Isaac64Rng,
150155
mix: w64,
151156
a: &mut w64,
@@ -158,7 +163,7 @@ impl Isaac64Rng {
158163
let y = *a + *b + ind(&ctx.mem, x, 3);
159164
ctx.mem[base + m] = y;
160165
*b = x + ind(&ctx.mem, y, 3 + RAND_SIZE_LEN);
161-
ctx.rsl[base + m] = *b;
166+
ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0;
162167
}
163168

164169
let mut m = 0;
@@ -181,7 +186,7 @@ impl Isaac64Rng {
181186

182187
self.a = a;
183188
self.b = b;
184-
self.cnt = RAND_SIZE as u32;
189+
self.index = 0;
185190
}
186191
}
187192

@@ -193,33 +198,36 @@ impl Rng for Isaac64Rng {
193198

194199
#[inline]
195200
fn next_u64(&mut self) -> u64 {
196-
if self.cnt == 0 {
197-
// make some more numbers
201+
let mut index = self.index as usize;
202+
if index >= RAND_SIZE {
198203
self.isaac64();
204+
index = 0;
199205
}
200-
self.cnt -= 1;
201-
202-
// self.cnt is at most RAND_SIZE, but that is before the
203-
// subtraction above. We want to index without bounds
204-
// checking, but this could lead to incorrect code if someone
205-
// misrefactors, so we check, sometimes.
206-
//
207-
// (Changes here should be reflected in IsaacRng.next_u32.)
208-
debug_assert!((self.cnt as usize) < RAND_SIZE);
209-
210-
// (the % is cheaply telling the optimiser that we're always
211-
// in bounds, without unsafe. NB. this is a power of two, so
212-
// it optimises to a bitwise mask).
213-
self.rsl[self.cnt as usize % RAND_SIZE].0
206+
207+
let value = self.rsl[index];
208+
self.index += 1;
209+
value
214210
}
215211

216212
#[cfg(feature = "i128_support")]
217213
fn next_u128(&mut self) -> u128 {
218-
::rand_core::impls::next_u128_via_u64(self)
214+
impls::next_u128_via_u64(self)
219215
}
220216

221217
fn fill_bytes(&mut self, dest: &mut [u8]) {
222-
::rand_core::impls::fill_bytes_via_u64(self, dest);
218+
let mut read_len = 0;
219+
while read_len < dest.len() {
220+
if self.index as usize >= RAND_SIZE {
221+
self.isaac64();
222+
}
223+
224+
let (consumed_u64, filled_u8) =
225+
impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize)..],
226+
&mut dest[read_len..]);
227+
228+
self.index += consumed_u64 as u32;
229+
read_len += filled_u8;
230+
}
223231
}
224232

225233
fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
@@ -259,12 +267,12 @@ fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng {
259267
}
260268

261269
let mut rng = Isaac64Rng {
262-
rsl: [w(0); RAND_SIZE],
270+
rsl: [0; RAND_SIZE],
263271
mem: mem,
264272
a: w(0),
265273
b: w(0),
266274
c: w(0),
267-
cnt: 0,
275+
index: 0,
268276
};
269277

270278
// Prepare the first set of results

0 commit comments

Comments
 (0)