Skip to content

Commit 77770a4

Browse files
authored
Block buffer internals (#36)
1 parent 8d1d857 commit 77770a4

File tree

3 files changed

+110
-37
lines changed

3 files changed

+110
-37
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Remove `BlockRng64` ([#34])
2121
- Remove impl of `RngCore` for `BlockRng`, making the latter more generic ([#34])
2222
- Add trait `le::Word` ([#34])
23+
- Add fn `BlockRng::reconstruct` and fn `BlockRng::remaining_results` ([#36])
2324

2425
### Other
2526
- Changed repository from [rust-random/rand] to [rust-random/core].
@@ -36,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3637
[#28]: https://github.com/rust-random/rand-core/pull/28
3738
[#34]: https://github.com/rust-random/rand-core/pull/34
3839
[#35]: https://github.com/rust-random/rand-core/pull/35
40+
[#36]: https://github.com/rust-random/rand-core/pull/36
3941

4042
[rust-random/rand]: https://github.com/rust-random/rand
4143
[rust-random/core]: https://github.com/rust-random/core

src/block.rs

Lines changed: 88 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,19 @@ pub trait CryptoGenerator: Generator {}
136136
#[derive(Clone)]
137137
pub struct BlockRng<G: Generator> {
138138
results: G::Output,
139-
index: usize,
140139
/// The *core* part of the RNG, implementing the `generate` function.
141140
pub core: G,
142141
}
143142

144143
// Custom Debug implementation that does not expose the contents of `results`.
145-
impl<G: Generator + fmt::Debug> fmt::Debug for BlockRng<G> {
144+
impl<W: Word, const N: usize, G> fmt::Debug for BlockRng<G>
145+
where
146+
G: Generator<Output = [W; N]> + fmt::Debug,
147+
{
146148
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
147149
fmt.debug_struct("BlockRng")
148150
.field("core", &self.core)
149-
.field("index", &self.index)
151+
.field("index", &self.index())
150152
.finish()
151153
}
152154
}
@@ -157,55 +159,103 @@ impl<G: Generator> Drop for BlockRng<G> {
157159
}
158160
}
159161

160-
impl<W: Copy + Default, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
162+
impl<W: Word + Default, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
161163
/// Create a new `BlockRng` from an existing RNG implementing
162164
/// `Generator`. Results will be generated on first use.
163165
#[inline]
164166
pub fn new(core: G) -> BlockRng<G> {
165-
BlockRng {
166-
core,
167-
index: N,
168-
results: [W::default(); N],
167+
let mut results = [W::default(); N];
168+
results[0] = W::from_usize(N);
169+
BlockRng { core, results }
170+
}
171+
172+
/// Reconstruct from a core and a remaining-results buffer.
173+
///
174+
/// This may be used to deserialize using a `core` and the output of
175+
/// [`Self::remaining_results`].
176+
///
177+
/// Returns `None` if `remaining_results` is too long.
178+
pub fn reconstruct(core: G, remaining_results: &[W]) -> Option<Self> {
179+
let mut results = [W::default(); N];
180+
if remaining_results.len() < N {
181+
let index = N - remaining_results.len();
182+
results[index..].copy_from_slice(remaining_results);
183+
results[0] = W::from_usize(index);
184+
Some(BlockRng { results, core })
185+
} else {
186+
None
169187
}
170188
}
171189
}
172190

173-
impl<W: Clone, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
191+
impl<W: Word, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
174192
/// Get the index into the result buffer.
175193
///
176194
/// If this is equal to or larger than the size of the result buffer then
177195
/// the buffer is "empty" and `generate()` must be called to produce new
178196
/// results.
179197
#[inline(always)]
180198
pub fn index(&self) -> usize {
181-
self.index
199+
self.results[0].into_usize()
200+
}
201+
202+
#[inline(always)]
203+
fn set_index(&mut self, index: usize) {
204+
debug_assert!(0 < index && index <= N);
205+
self.results[0] = W::from_usize(index);
182206
}
183207

184208
/// Reset the number of available results.
185209
/// This will force a new set of results to be generated on next use.
186210
#[inline]
187211
pub fn reset(&mut self) {
188-
self.index = N;
212+
self.set_index(N);
189213
}
190214

191-
/// Generate a new set of results immediately, setting the index to the
192-
/// given value.
215+
/// Updates the index and buffer contents
216+
///
217+
/// If `index == 0`, this marks the buffer as "empty", causing generation on
218+
/// next use.
219+
///
220+
/// If `index > 0`, this generates a new block immediately then sets the
221+
/// index.
193222
#[inline]
194223
pub fn generate_and_set(&mut self, index: usize) {
224+
if index == 0 {
225+
self.set_index(N);
226+
return;
227+
}
228+
195229
assert!(index < N);
196230
self.core.generate(&mut self.results);
197-
self.index = index;
231+
self.set_index(index);
232+
}
233+
234+
/// Access the unused part of the results buffer
235+
///
236+
/// The length of the returned slice is guaranteed to be less than the
237+
/// length of `<Self as Generator>::Output` (i.e. less than `N` where
238+
/// `Output = [W; N]`).
239+
///
240+
/// This is a low-level interface intended for serialization.
241+
/// Results are not marked as consumed.
242+
#[inline]
243+
pub fn remaining_results(&self) -> &[W] {
244+
let index = self.index();
245+
&self.results[index..]
198246
}
199247

200248
/// Generate the next word (e.g. `u32`)
201249
#[inline]
202250
pub fn next_word(&mut self) -> W {
203-
if self.index >= N {
204-
self.generate_and_set(0);
251+
let mut index = self.index();
252+
if index >= N {
253+
self.core.generate(&mut self.results);
254+
index = 0;
205255
}
206256

207-
let value = self.results[self.index].clone();
208-
self.index += 1;
257+
let value = self.results[index];
258+
self.set_index(index + 1);
209259
value
210260
}
211261
}
@@ -214,25 +264,24 @@ impl<const N: usize, G: Generator<Output = [u32; N]>> BlockRng<G> {
214264
/// Generate a `u64` from two `u32` words
215265
#[inline]
216266
pub fn next_u64_from_u32(&mut self) -> u64 {
217-
let read_u64 = |results: &[u32], index| {
218-
let data = &results[index..=index + 1];
219-
(u64::from(data[1]) << 32) | u64::from(data[0])
220-
};
221-
222-
let index = self.index;
267+
let index = self.index();
268+
let (lo, hi);
223269
if index < N - 1 {
224-
self.index += 2;
225-
// Read an u64 from the current index
226-
read_u64(&self.results, index)
270+
lo = self.results[index];
271+
hi = self.results[index + 1];
272+
self.set_index(index + 2);
227273
} else if index >= N {
228-
self.generate_and_set(2);
229-
read_u64(&self.results, 0)
274+
self.core.generate(&mut self.results);
275+
lo = self.results[0];
276+
hi = self.results[1];
277+
self.set_index(2);
230278
} else {
231-
let x = u64::from(self.results[N - 1]);
232-
self.generate_and_set(1);
233-
let y = u64::from(self.results[0]);
234-
(y << 32) | x
279+
lo = self.results[N - 1];
280+
self.core.generate(&mut self.results);
281+
hi = self.results[0];
282+
self.set_index(1);
235283
}
284+
(u64::from(hi) << 32) | u64::from(lo)
236285
}
237286
}
238287

@@ -242,13 +291,15 @@ impl<W: Word, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
242291
pub fn fill_bytes(&mut self, dest: &mut [u8]) {
243292
let mut read_len = 0;
244293
while read_len < dest.len() {
245-
if self.index >= N {
246-
self.generate_and_set(0);
294+
let mut index = self.index();
295+
if index >= N {
296+
self.core.generate(&mut self.results);
297+
index = 0;
247298
}
248299
let (consumed_u32, filled_u8) =
249-
fill_via_chunks(&self.results[self.index..], &mut dest[read_len..]);
300+
fill_via_chunks(&self.results[index..], &mut dest[read_len..]);
250301

251-
self.index += consumed_u32;
302+
self.set_index(index + consumed_u32);
252303
read_len += filled_u8;
253304
}
254305
}

src/le.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,41 @@ pub fn fill_bytes_via_next<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) {
7474
mod word {
7575
pub trait Sealed: Copy {
7676
type Bytes: Sized + AsRef<[u8]>;
77+
7778
fn to_le_bytes(self) -> Self::Bytes;
79+
80+
fn from_usize(val: usize) -> Self;
81+
fn into_usize(self) -> usize;
7882
}
83+
7984
impl Sealed for u32 {
8085
type Bytes = [u8; 4];
8186

8287
fn to_le_bytes(self) -> Self::Bytes {
8388
Self::to_le_bytes(self)
8489
}
90+
91+
fn from_usize(val: usize) -> Self {
92+
val.try_into().unwrap()
93+
}
94+
fn into_usize(self) -> usize {
95+
self.try_into().unwrap()
96+
}
8597
}
98+
8699
impl Sealed for u64 {
87100
type Bytes = [u8; 8];
88101

89102
fn to_le_bytes(self) -> Self::Bytes {
90103
Self::to_le_bytes(self)
91104
}
105+
106+
fn from_usize(val: usize) -> Self {
107+
val.try_into().unwrap()
108+
}
109+
fn into_usize(self) -> usize {
110+
self.try_into().unwrap()
111+
}
92112
}
93113
}
94114

0 commit comments

Comments
 (0)