-
Notifications
You must be signed in to change notification settings - Fork 432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement BlockRngCore
ISAAC and ISAAC-64
#325
Conversation
rand-core/src/impls.rs
Outdated
/// | ||
/// This is similar to [`BlockRng`], but specialized for algorithms that operate | ||
/// on `u64` values (rare). Hopefully specialization will one day allow | ||
/// `BlockRng` to support both, so that this wrapper can be removed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "rare" part may change... I also don't know that it's worth talking about this future direction here. Also since all the implementations are different, is there any point making BlockRng
support both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove the speculation...
The idea was once that things like ReseedingRng
can then be used for both 32- and 64-bit RNGs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which is a problem in itself. I wonder whether many people will complain about the new ReseedingRng
being incompatible with many PRNGs... maybe not: I'm struggling to find any references which are not simply copies of the source
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The servo one is little more than a reshuffle of Rand code, so more like one, and that's reseeding ChaCha, so we're fine.
Is ISAAC going to be deprecated over HC128? |
In my opinion it already is, with |
Well, at least it's not yet official according to the documentation. |
Good point, I'll make a reference to HC-128 in the documentation |
Have you tried just deriving Serialize and Deserialize on Block Core? It should automagically only derive where |
c43e348
to
6912a12
Compare
@LinearZoetrope I have pushed my (unsuccessful) attempt to derive Serialize and Deserialize on
Are you interested in taking a look? |
6912a12
to
c48e4cc
Compare
23d85d5
to
8a2deb0
Compare
8a2deb0
to
58fd61d
Compare
🎉 Finally have Serde re-implemented and a green bill from the CI. Updated the description in the first post. |
58fd61d
to
4d09662
Compare
(and relative links are really inconsistent)
4d09662
to
bbd1b38
Compare
src/prng/isaac_array.rs
Outdated
#[derive(Copy, Clone)] | ||
#[allow(missing_debug_implementations)] | ||
#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] | ||
pub struct IsaacArray<T> where T: Default + Copy { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default
and Copy
should never be used in trait bounds on a data structure -- only on impls and methods. The only code that requires these bounds are the Deserialize and Default impls, so the bounds should be on those impls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for reviewing, I hope I got them right now. It compiles...
rand_core/src/lib.rs
Outdated
@@ -44,6 +44,8 @@ | |||
|
|||
#[cfg(feature="std")] extern crate core; | |||
#[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; | |||
#[cfg(feature="serde-1")] extern crate serde; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a weak preference for serde1
as the name of the Serde feature instead of serde-1
, to be consistent with crates that enable optional Serde support through an optional dependency on the serde1
shim.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I see serde-1
seems to be used by a couple of crates to add the optional dependency. serde1
does not seem to have any reverse dependencies?
I don't like serde-1
, and serde1
also does not seem ideal compared to just serde
.
For rand_core
, we could name the feature just serde
with:
serde = { version = "1", optional = true, features = ["derive"] }
.
For rand
I see the serde feature as temporary, until we split of the PRNGs into a separate crate. There seems to be some movement in rust-lang/cargo#1286.
@dhardy What do you think? Does it make sense to use serde
as the feature name in rand_core
, and at some point in the future both crates can use that name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well if serde
never has another breaking release we don't need to append any number, but since it had quite a few before 1.0 appending a number seemed prudent. If @dtolnay prefers serde1
that is fine, except that it's a breaking change (we added the serde-1
feature for the Rand 0.4 release).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
except that it's a breaking change (we added the
serde-1
feature for the Rand 0.4 release).
I don't think we did? https://github.com/rust-lang-nursery/rand/blob/0.4/Cargo.toml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, then we aren't constrained and can use serde1
as suggested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about making the public fields private? I think the only accessors actually needed should be to core
and a "reset" function (for ChaCha::set_counter).
rand_core/src/impls.rs
Outdated
serialize = "R::Results: Serialize", | ||
deserialize = "R::Results: Deserialize<'de>")))] | ||
pub results: R::Results, | ||
pub index: usize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment that 0 is never stored? next_u32
relies on this.
rand_core/src/impls.rs
Outdated
#[cfg_attr(feature="serde-1", serde(bound( | ||
serialize = "R::Results: Serialize", | ||
deserialize = "R::Results: Deserialize<'de>")))] | ||
pub results: R::Results, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public fields should be documented — but I think perhaps we should not make these public (just add necessary accessor fns and a "reset" function).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But than I have to do things properly... But it really cleans things up, so good idea.
rand_core/src/impls.rs
Outdated
deserialize = "R::Results: Deserialize<'de>")))] | ||
pub results: R::Results, | ||
pub index: usize, | ||
pub half_used: bool, // true if only half of the previous result is used |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think fill_bytes
should reset half_used = false
? In fact I think the x86 version could also use the left-over bytes easily (because LE means the bytes are contiguous); the other version could also but would need an extra copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I think it only happened to pass the tests because it then it generates a new set of results in-between, which resets half_used
.
I don't like to change the logic at the moment t.b.h.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think tests could catch this anyway; there's no memory unsafety, just the possibility of skipping one word of output and using another twice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I though we tested for it in test_isaac64_true_values_mixed
, but that tests only mixing next_u64
and next_u32
.
rand_core/src/lib.rs
Outdated
@@ -44,6 +44,8 @@ | |||
|
|||
#[cfg(feature="std")] extern crate core; | |||
#[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; | |||
#[cfg(feature="serde-1")] extern crate serde; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, then we aren't constrained and can use serde1
as suggested.
a2f85ec
to
1d6cd6c
Compare
1d6cd6c
to
35ddc87
Compare
rand_core/src/impls.rs
Outdated
|
||
/// Return a mutable reference the wrapped `BlockRngCore`. | ||
pub fn inner(&mut self) -> &mut R { | ||
&mut self.core |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really we should have mutable and immutable accessors, i.e. fn inner(&self) -> &R
and fn inner_mut
.
I'm surprised you didn't call this core
though (you can shadow the name).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did initially, but I then thought this would make more sense for users, who don't see the field name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just thought it aligned well with BlockRngCore
but doesn't matter.
rand_core/src/impls.rs
Outdated
@@ -424,6 +463,7 @@ where <R as BlockRngCore>::Results: AsRef<[u64]> | |||
#[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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need this in the other implementation of fill_bytes
too!
src/prng/isaac64.rs
Outdated
index: RAND_SIZE, // generate on first use | ||
half_used: false, | ||
}) | ||
Isaac64Rng(BlockRng64::new(Isaac64Core::new_from_u64(seed))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need the separate Isaac64Core::new_from_u64
function, but if you do want it you don't need key
on the line above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sloppy work... I am sorry. Modified the commit.
35ddc87
to
f9f0ea6
Compare
Looks good 👍 |
Implement `BlockRngCore` ISAAC and ISAAC-64
Updated description:
This is the part I left out of #281.
This PR implements
BlockRngCore
for ISAAC and ISAAC-64. This meant changes in bothrand
andrand_core
.rand_core
:BlockRng64
wrapper, becauseBlockRng
only works foru32
. The implementation is mostly just of copy of the one inIsaac64Rng
. Only thefill_bytes
part uses the same direct filling trick asBlockRng
. This does not have any advantage at all forIsaac64Rng
, because it generates 2kb at a time, and the copy cost is negligible compared to the time that takes. But as this is supposed to be a generic wrapper, it seems to make sense.serde-1
feature to deriveSerialize
andDeserialize
for theBlockRng
wrappers.rand
:IsaacArray
wrapper to supportAsRef
and other traits on theBlockRng::Results
type ofIsaac{64}Rng
.isaac_serde
toisaac_array
, because both deal with the problem of bad support for large arrays.IsaacRng
andIsaac64Rng
is as much as possible just a refactor of existing functionality. I believe the only small intended change is that the results type does not useWrapping<T>
.First I thought this PR had problems that would take me forever to figure out, but thanks to help from @dtolnay they vanished overnight 😄. Now I think it would be very nice to get this in the 0.5 release, for three reasons:
BlockRng
. We recommend RNGs to implement Serde, then we should also make it possible to do so.Isaac{64}Rng
.BlockRngCore
PR by implementing it for ISAAC (making it possible to also use it withReseedingRng
etc.).Original post:
This is the part I left out of #281.
There are a few things which make implementing
BlockRngCore
not straight-forward.BlockRng64
wrapper, becauseBlockRng
only works foru32
.Asref()
is not implemented for arrays with more than 32 elements. This adds anIsaacArray
as work-around.BlockRng
inrand-core
serialisable? I did some effort, but implementing serialize, and especially deserialize, by hand becomes ugly quickly. cc @LinearZoetropeSo this is not yet mergeable, but I am hoping for some input 😄.