libafl_bolts: improvements to the rands
module, add next_float (#2086)
* rands: use splitmix64 for seeding Seeding with splitmix64 is a good way to avoid starting with low-entropy PRNG states, and is explicitly recommended by the authors of both xoshiro256++ and Romu. While at it, give the xoshiro256++ PRNG its proper name. * rands: use fast_bound() to generate number in range * rands: add top-level choose() * rands: add Rand::next_float() * rands: add Rand::coinflip() helper * libafl: unbreak tests that relied on direct seeding * rands: add SFC64 PRNG SFC64 is a well-established and well-understood PRNG designed by Chris Doty-Humphrey, the author of PractRand. It has been tested quite a lot over the years, and to date has no known weaknesses. Compared to xoshiro256++, it is slightly faster and is likely to be a more future-proof design (xoshiro/xoroshiro family of generators come with quite long history of [flaws][1] found over the years). Compared to Romu, it is slightly slower, but guarantees absense of bias, minimum period of at least 2^64 for any seed, and non-overlapping streams for different seeds. [1]: https://tom-kaitchuck.medium.com/designing-a-new-prng-1c4ffd27124d
This commit is contained in:
parent
5ff709f241
commit
e1b8c9b5d8
@ -4,7 +4,10 @@ use alloc::vec::Vec;
|
|||||||
use core::cmp::max;
|
use core::cmp::max;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl_bolts::{rands::Rand, Named};
|
use libafl_bolts::{
|
||||||
|
rands::{choose, Rand},
|
||||||
|
Named,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -117,7 +120,7 @@ where
|
|||||||
|
|
||||||
let insert_at = state.rand_mut().below(input.terminals().len() as u64) as usize;
|
let insert_at = state.rand_mut().below(input.terminals().len() as u64) as usize;
|
||||||
|
|
||||||
let rand_num = state.rand_mut().next() as usize;
|
let rand_num = state.rand_mut().next();
|
||||||
|
|
||||||
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
|
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
|
||||||
|
|
||||||
@ -134,7 +137,7 @@ where
|
|||||||
meta.map.get(&input.terminals()[insert_at].state).map_or(
|
meta.map.get(&input.terminals()[insert_at].state).map_or(
|
||||||
Ok(MutationResult::Skipped),
|
Ok(MutationResult::Skipped),
|
||||||
|splice_points| {
|
|splice_points| {
|
||||||
let from = splice_points[rand_num % splice_points.len()];
|
let from = *choose(splice_points, rand_num);
|
||||||
|
|
||||||
input.terminals_mut().truncate(insert_at);
|
input.terminals_mut().truncate(insert_at);
|
||||||
input
|
input
|
||||||
|
@ -4,7 +4,10 @@
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min};
|
||||||
|
|
||||||
use libafl_bolts::{rands::Rand, Named};
|
use libafl_bolts::{
|
||||||
|
rands::{choose, fast_bound, Rand},
|
||||||
|
Named,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::Corpus,
|
corpus::Corpus,
|
||||||
@ -17,7 +20,7 @@ use crate::{
|
|||||||
|
|
||||||
const RECURSIVE_REPLACEMENT_DEPTH: [usize; 6] = [2, 4, 8, 16, 32, 64];
|
const RECURSIVE_REPLACEMENT_DEPTH: [usize; 6] = [2, 4, 8, 16, 32, 64];
|
||||||
const MAX_RECURSIVE_REPLACEMENT_LEN: usize = 64 << 10;
|
const MAX_RECURSIVE_REPLACEMENT_LEN: usize = 64 << 10;
|
||||||
const CHOOSE_SUBINPUT_PROB: u64 = 50;
|
const CHOOSE_SUBINPUT_PROB: f64 = 0.5;
|
||||||
|
|
||||||
fn extend_with_random_generalized<S>(
|
fn extend_with_random_generalized<S>(
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
@ -29,10 +32,10 @@ where
|
|||||||
{
|
{
|
||||||
let idx = random_corpus_id!(state.corpus(), state.rand_mut());
|
let idx = random_corpus_id!(state.corpus(), state.rand_mut());
|
||||||
|
|
||||||
if state.rand_mut().below(100) > CHOOSE_SUBINPUT_PROB {
|
if state.rand_mut().coinflip(CHOOSE_SUBINPUT_PROB) {
|
||||||
if state.rand_mut().below(100) < 50 {
|
if state.rand_mut().coinflip(0.5) {
|
||||||
let rand1 = state.rand_mut().next() as usize;
|
let rand1 = state.rand_mut().next();
|
||||||
let rand2 = state.rand_mut().next() as usize;
|
let rand2 = state.rand_mut().next();
|
||||||
|
|
||||||
let other_testcase = state.corpus().get(idx)?.borrow();
|
let other_testcase = state.corpus().get(idx)?.borrow();
|
||||||
if let Some(other) = other_testcase
|
if let Some(other) = other_testcase
|
||||||
@ -48,8 +51,8 @@ where
|
|||||||
{
|
{
|
||||||
gap_indices.push(i);
|
gap_indices.push(i);
|
||||||
}
|
}
|
||||||
let min_idx = gap_indices[rand1 % gap_indices.len()];
|
let min_idx = *choose(&*gap_indices, rand1);
|
||||||
let max_idx = gap_indices[rand2 % gap_indices.len()];
|
let max_idx = *choose(&*gap_indices, rand2);
|
||||||
let (mut min_idx, max_idx) = (min(min_idx, max_idx), max(min_idx, max_idx));
|
let (mut min_idx, max_idx) = (min(min_idx, max_idx), max(min_idx, max_idx));
|
||||||
|
|
||||||
gap_indices.clear();
|
gap_indices.clear();
|
||||||
@ -66,11 +69,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rand1 = state.rand_mut().next() as usize;
|
let rand1 = state.rand_mut().next();
|
||||||
|
|
||||||
if let Some(meta) = state.metadata_map().get::<Tokens>() {
|
if let Some(meta) = state.metadata_map().get::<Tokens>() {
|
||||||
if !meta.tokens().is_empty() {
|
if !meta.tokens().is_empty() {
|
||||||
let tok = &meta.tokens()[rand1 % meta.tokens().len()];
|
let tok = choose(meta.tokens(), rand1);
|
||||||
if items.last() != Some(&GeneralizedItem::Gap) {
|
if items.last() != Some(&GeneralizedItem::Gap) {
|
||||||
items.push(GeneralizedItem::Gap);
|
items.push(GeneralizedItem::Gap);
|
||||||
}
|
}
|
||||||
@ -253,8 +256,8 @@ where
|
|||||||
token_replace = state.rand_mut().below(tokens_len as u64) as usize;
|
token_replace = state.rand_mut().below(tokens_len as u64) as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
let stop_at_first = state.rand_mut().below(100) > 50;
|
let stop_at_first = state.rand_mut().coinflip(0.5);
|
||||||
let mut rand_idx = state.rand_mut().next() as usize;
|
let rand_idx = state.rand_mut().next();
|
||||||
|
|
||||||
let meta = state.metadata_map().get::<Tokens>().unwrap();
|
let meta = state.metadata_map().get::<Tokens>().unwrap();
|
||||||
let token_1 = &meta.tokens()[token_find];
|
let token_1 = &meta.tokens()[token_find];
|
||||||
@ -263,7 +266,7 @@ where
|
|||||||
let mut mutated = MutationResult::Skipped;
|
let mut mutated = MutationResult::Skipped;
|
||||||
|
|
||||||
let gen = generalised_meta.generalized_mut();
|
let gen = generalised_meta.generalized_mut();
|
||||||
rand_idx %= gen.len();
|
let rand_idx = fast_bound(rand_idx, gen.len() as u64) as usize;
|
||||||
|
|
||||||
'first: for item in &mut gen[..rand_idx] {
|
'first: for item in &mut gen[..rand_idx] {
|
||||||
if let GeneralizedItem::Bytes(bytes) = item {
|
if let GeneralizedItem::Bytes(bytes) = item {
|
||||||
|
@ -202,7 +202,7 @@ impl MOpt {
|
|||||||
let mut total_x_now = 0.0;
|
let mut total_x_now = 0.0;
|
||||||
let mut x_sum = 0.0;
|
let mut x_sum = 0.0;
|
||||||
for i in 0..self.operator_num {
|
for i in 0..self.operator_num {
|
||||||
self.x_now[swarm][i] = (self.rand.below(7000) as f64) * 0.0001 + 0.1;
|
self.x_now[swarm][i] = 0.7 * self.rand.next_float() + 0.1;
|
||||||
total_x_now += self.x_now[swarm][i];
|
total_x_now += self.x_now[swarm][i];
|
||||||
self.v_now[swarm][i] = 0.1;
|
self.v_now[swarm][i] = 0.1;
|
||||||
self.l_best[swarm][i] = 0.5;
|
self.l_best[swarm][i] = 0.5;
|
||||||
@ -215,12 +215,8 @@ impl MOpt {
|
|||||||
|
|
||||||
for i in 0..self.operator_num {
|
for i in 0..self.operator_num {
|
||||||
self.v_now[swarm][i] = self.w_now * self.v_now[swarm][i]
|
self.v_now[swarm][i] = self.w_now * self.v_now[swarm][i]
|
||||||
+ (self.rand.below(1000) as f64)
|
+ self.rand.next_float() * (self.l_best[swarm][i] - self.x_now[swarm][i])
|
||||||
* 0.001
|
+ self.rand.next_float() * (self.g_best[i] - self.x_now[swarm][i]);
|
||||||
* (self.l_best[swarm][i] - self.x_now[swarm][i])
|
|
||||||
+ (self.rand.below(1000) as f64)
|
|
||||||
* 0.001
|
|
||||||
* (self.g_best[i] - self.x_now[swarm][i]);
|
|
||||||
self.x_now[swarm][i] += self.v_now[swarm][i];
|
self.x_now[swarm][i] += self.v_now[swarm][i];
|
||||||
|
|
||||||
self.x_now[swarm][i] = self.x_now[swarm][i].clamp(V_MIN, V_MAX);
|
self.x_now[swarm][i] = self.x_now[swarm][i].clamp(V_MIN, V_MAX);
|
||||||
@ -282,12 +278,8 @@ impl MOpt {
|
|||||||
for i in 0..self.operator_num {
|
for i in 0..self.operator_num {
|
||||||
self.probability_now[swarm][i] = 0.0;
|
self.probability_now[swarm][i] = 0.0;
|
||||||
self.v_now[swarm][i] = self.w_now * self.v_now[swarm][i]
|
self.v_now[swarm][i] = self.w_now * self.v_now[swarm][i]
|
||||||
+ (self.rand.below(1000) as f64)
|
+ self.rand.next_float() * (self.l_best[swarm][i] - self.x_now[swarm][i])
|
||||||
* 0.001
|
+ self.rand.next_float() * (self.g_best[i] - self.x_now[swarm][i]);
|
||||||
* (self.l_best[swarm][i] - self.x_now[swarm][i])
|
|
||||||
+ (self.rand.below(1000) as f64)
|
|
||||||
* 0.001
|
|
||||||
* (self.g_best[i] - self.x_now[swarm][i]);
|
|
||||||
self.x_now[swarm][i] += self.v_now[swarm][i];
|
self.x_now[swarm][i] += self.v_now[swarm][i];
|
||||||
|
|
||||||
self.x_now[swarm][i] = self.x_now[swarm][i].clamp(V_MIN, V_MAX);
|
self.x_now[swarm][i] = self.x_now[swarm][i].clamp(V_MIN, V_MAX);
|
||||||
@ -328,8 +320,8 @@ impl MOpt {
|
|||||||
let operator_num = self.operator_num;
|
let operator_num = self.operator_num;
|
||||||
|
|
||||||
// Fetch a random sele value
|
// Fetch a random sele value
|
||||||
let select_prob: f64 = self.probability_now[self.swarm_now][operator_num - 1]
|
let select_prob: f64 =
|
||||||
* ((self.rand.below(10000) as f64) * 0.0001);
|
self.probability_now[self.swarm_now][operator_num - 1] * self.rand.next_float();
|
||||||
|
|
||||||
for i in 0..operator_num {
|
for i in 0..operator_num {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
@ -470,7 +470,7 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use libafl_bolts::rands::{Rand, StdRand, XkcdRand};
|
use libafl_bolts::rands::{StdRand, XkcdRand};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, InMemoryCorpus, Testcase},
|
corpus::{Corpus, InMemoryCorpus, Testcase},
|
||||||
@ -486,8 +486,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mut_scheduled() {
|
fn test_mut_scheduled() {
|
||||||
// With the current impl, seed of 1 will result in a split at pos 2.
|
let rand = XkcdRand::with_seed(0);
|
||||||
let mut rand = XkcdRand::with_seed(5);
|
|
||||||
let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
|
let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
|
||||||
corpus
|
corpus
|
||||||
.add(Testcase::new(vec![b'a', b'b', b'c'].into()))
|
.add(Testcase::new(vec![b'a', b'b', b'c'].into()))
|
||||||
@ -510,21 +509,17 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
rand.set_seed(5);
|
|
||||||
|
|
||||||
let mut splice = SpliceMutator::new();
|
let mut splice = SpliceMutator::new();
|
||||||
splice.mutate(&mut state, &mut input).unwrap();
|
splice.mutate(&mut state, &mut input).unwrap();
|
||||||
|
|
||||||
log::trace!("{:?}", input.bytes());
|
log::trace!("{:?}", input.bytes());
|
||||||
|
|
||||||
// The pre-seeded rand should have spliced at position 2.
|
// The pre-seeded rand should have spliced at position 2.
|
||||||
// TODO: Maybe have a fixed rand for this purpose?
|
|
||||||
assert_eq!(input.bytes(), &[b'a', b'b', b'f']);
|
assert_eq!(input.bytes(), &[b'a', b'b', b'f']);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_havoc() {
|
fn test_havoc() {
|
||||||
// With the current impl, seed of 1 will result in a split at pos 2.
|
|
||||||
let rand = StdRand::with_seed(0x1337);
|
let rand = StdRand::with_seed(0x1337);
|
||||||
let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
|
let mut corpus: InMemoryCorpus<BytesInput> = InMemoryCorpus::new();
|
||||||
corpus
|
corpus
|
||||||
|
@ -165,8 +165,7 @@ where
|
|||||||
// We will sample using the mutation probabilities.
|
// We will sample using the mutation probabilities.
|
||||||
// Doing this outside of the original if branch to make the borrow checker happy.
|
// Doing this outside of the original if branch to make the borrow checker happy.
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
let coin = state.rand_mut().next() as f32 / u64::MAX as f32;
|
let coin = state.rand_mut().next_float() as f32;
|
||||||
debug_assert!(coin <= 1.0_f32);
|
|
||||||
|
|
||||||
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
|
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
|
||||||
let power = metadata
|
let power = metadata
|
||||||
@ -204,8 +203,7 @@ where
|
|||||||
// We will sample using the mutation probabilities.
|
// We will sample using the mutation probabilities.
|
||||||
// Doing this outside of the original if branch to make the borrow checker happy.
|
// Doing this outside of the original if branch to make the borrow checker happy.
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
let coin = state.rand_mut().next() as f32 / u64::MAX as f32;
|
let coin = state.rand_mut().next_float() as f32;
|
||||||
debug_assert!(coin <= 1.0_f32);
|
|
||||||
|
|
||||||
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
|
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
|
@ -112,7 +112,7 @@ where
|
|||||||
CS::State: Debug,
|
CS::State: Debug,
|
||||||
{
|
{
|
||||||
accounting_map: &'a [u32],
|
accounting_map: &'a [u32],
|
||||||
skip_non_favored_prob: u64,
|
skip_non_favored_prob: f64,
|
||||||
inner: MinimizerScheduler<
|
inner: MinimizerScheduler<
|
||||||
CS,
|
CS,
|
||||||
LenTimeMulTestcaseScore<<CS as UsesState>::State>,
|
LenTimeMulTestcaseScore<<CS as UsesState>::State>,
|
||||||
@ -171,7 +171,7 @@ where
|
|||||||
.borrow()
|
.borrow()
|
||||||
.has_metadata::<IsFavoredMetadata>();
|
.has_metadata::<IsFavoredMetadata>();
|
||||||
has
|
has
|
||||||
} && state.rand_mut().below(100) < self.skip_non_favored_prob
|
} && state.rand_mut().coinflip(self.skip_non_favored_prob)
|
||||||
{
|
{
|
||||||
idx = self.inner.base_mut().next(state)?;
|
idx = self.inner.base_mut().next(state)?;
|
||||||
}
|
}
|
||||||
@ -338,7 +338,7 @@ where
|
|||||||
observer: &O,
|
observer: &O,
|
||||||
state: &mut CS::State,
|
state: &mut CS::State,
|
||||||
base: CS,
|
base: CS,
|
||||||
skip_non_favored_prob: u64,
|
skip_non_favored_prob: f64,
|
||||||
accounting_map: &'a [u32],
|
accounting_map: &'a [u32],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match state.metadata_map().get::<TopAccountingMetadata>() {
|
match state.metadata_map().get::<TopAccountingMetadata>() {
|
||||||
|
@ -20,7 +20,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Default probability to skip the non-favored values
|
/// Default probability to skip the non-favored values
|
||||||
pub const DEFAULT_SKIP_NON_FAVORED_PROB: u64 = 95;
|
pub const DEFAULT_SKIP_NON_FAVORED_PROB: f64 = 0.95;
|
||||||
|
|
||||||
/// A testcase metadata saying if a testcase is favored
|
/// A testcase metadata saying if a testcase is favored
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@ -73,7 +73,7 @@ impl Default for TopRatedsMetadata {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MinimizerScheduler<CS, F, M, O> {
|
pub struct MinimizerScheduler<CS, F, M, O> {
|
||||||
base: CS,
|
base: CS,
|
||||||
skip_non_favored_prob: u64,
|
skip_non_favored_prob: f64,
|
||||||
remove_metadata: bool,
|
remove_metadata: bool,
|
||||||
phantom: PhantomData<(F, M, O)>,
|
phantom: PhantomData<(F, M, O)>,
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ where
|
|||||||
.borrow()
|
.borrow()
|
||||||
.has_metadata::<IsFavoredMetadata>();
|
.has_metadata::<IsFavoredMetadata>();
|
||||||
has
|
has
|
||||||
} && state.rand_mut().below(100) < self.skip_non_favored_prob
|
} && state.rand_mut().coinflip(self.skip_non_favored_prob)
|
||||||
{
|
{
|
||||||
idx = self.base.next(state)?;
|
idx = self.base.next(state)?;
|
||||||
}
|
}
|
||||||
@ -406,7 +406,7 @@ where
|
|||||||
/// and has a non-default probability to skip non-faved [`Testcase`]s using (`skip_non_favored_prob`).
|
/// and has a non-default probability to skip non-faved [`Testcase`]s using (`skip_non_favored_prob`).
|
||||||
///
|
///
|
||||||
/// When calling, pass the edges observer which will provided the indexes to minimize over.
|
/// When calling, pass the edges observer which will provided the indexes to minimize over.
|
||||||
pub fn with_skip_prob(_observer: &O, base: CS, skip_non_favored_prob: u64) -> Self {
|
pub fn with_skip_prob(_observer: &O, base: CS, skip_non_favored_prob: f64) -> Self {
|
||||||
require_index_tracking!("MinimizerScheduler", O);
|
require_index_tracking!("MinimizerScheduler", O);
|
||||||
Self {
|
Self {
|
||||||
base,
|
base,
|
||||||
|
@ -162,7 +162,7 @@ where
|
|||||||
"No entries in corpus. This often implies the target is not properly instrumented.",
|
"No entries in corpus. This often implies the target is not properly instrumented.",
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
let rand_prob: f64 = (state.rand_mut().below(100) as f64) / 100.0;
|
let rand_prob: f64 = state.rand_mut().next_float();
|
||||||
let meta = state.metadata_map().get::<ProbabilityMetadata>().unwrap();
|
let meta = state.metadata_map().get::<ProbabilityMetadata>().unwrap();
|
||||||
let threshold = meta.total_probability * rand_prob;
|
let threshold = meta.total_probability * rand_prob;
|
||||||
let mut k: f64 = 0.0;
|
let mut k: f64 = 0.0;
|
||||||
@ -237,8 +237,8 @@ mod tests {
|
|||||||
super::ProbabilityMetadata::register();
|
super::ProbabilityMetadata::register();
|
||||||
}
|
}
|
||||||
|
|
||||||
// the first 3 probabilities will be .69, .86, .44
|
// the first 3 probabilities will be .76, .86, .36
|
||||||
let rand = StdRand::with_seed(12);
|
let rand = StdRand::with_seed(2);
|
||||||
|
|
||||||
let mut scheduler = UniformProbabilitySamplingScheduler::new();
|
let mut scheduler = UniformProbabilitySamplingScheduler::new();
|
||||||
|
|
||||||
|
@ -316,8 +316,8 @@ where
|
|||||||
} else {
|
} else {
|
||||||
let s = random_corpus_id!(state.corpus(), state.rand_mut());
|
let s = random_corpus_id!(state.corpus(), state.rand_mut());
|
||||||
|
|
||||||
// Choose a random value between 0.000000000 and 1.000000000
|
// Choose a random value between 0.0 and 1.0
|
||||||
let probability = state.rand_mut().between(0, 1000000000) as f64 / 1000000000_f64;
|
let probability = state.rand_mut().next_float();
|
||||||
|
|
||||||
let wsmeta = state.metadata_mut::<WeightedScheduleMetadata>()?;
|
let wsmeta = state.metadata_mut::<WeightedScheduleMetadata>()?;
|
||||||
|
|
||||||
|
@ -7,14 +7,48 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
|||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use crate::current_nanos;
|
use crate::current_nanos;
|
||||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
|
||||||
use crate::hash_std;
|
|
||||||
|
|
||||||
/// The standard rand implementation for `LibAFL`.
|
/// The standard rand implementation for `LibAFL`.
|
||||||
/// It is usually the right choice, with very good speed and a reasonable randomness.
|
/// It is usually the right choice, with very good speed and a reasonable randomness.
|
||||||
/// Not cryptographically secure (which is not what you want during fuzzing ;) )
|
/// Not cryptographically secure (which is not what you want during fuzzing ;) )
|
||||||
pub type StdRand = RomuDuoJrRand;
|
pub type StdRand = RomuDuoJrRand;
|
||||||
|
|
||||||
|
/// Choose an item at random from the given iterator, sampling uniformly.
|
||||||
|
///
|
||||||
|
/// Note: the runtime cost is bound by the iterator's [`nth`][`Iterator::nth`] implementation
|
||||||
|
/// * For `Vec`, slice, array, this is O(1)
|
||||||
|
/// * For `HashMap`, `HashSet`, this is O(n)
|
||||||
|
pub fn choose<I>(from: I, rand: u64) -> I::Item
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::IntoIter: ExactSizeIterator,
|
||||||
|
{
|
||||||
|
// create iterator
|
||||||
|
let mut iter = from.into_iter();
|
||||||
|
|
||||||
|
// make sure there is something to choose from
|
||||||
|
debug_assert!(iter.len() > 0, "choosing from an empty iterator");
|
||||||
|
|
||||||
|
// pick a random, valid index
|
||||||
|
let index = fast_bound(rand, iter.len() as u64) as usize;
|
||||||
|
|
||||||
|
// return the item chosen
|
||||||
|
iter.nth(index).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Faster and almost unbiased alternative to `rand % n`.
|
||||||
|
///
|
||||||
|
/// For N-bit bound, probability of getting a biased value is 1/2^(64-N).
|
||||||
|
/// At least 2^2*(64-N) samples are required to detect this amount of bias.
|
||||||
|
///
|
||||||
|
/// See: [An optimal algorithm for bounded random integers](https://github.com/apple/swift/pull/39143).
|
||||||
|
#[must_use]
|
||||||
|
pub fn fast_bound(rand: u64, n: u64) -> u64 {
|
||||||
|
debug_assert_ne!(n, 0);
|
||||||
|
let mul = u128::from(rand).wrapping_mul(u128::from(n));
|
||||||
|
(mul >> 64) as u64
|
||||||
|
}
|
||||||
|
|
||||||
/// Ways to get random around here.
|
/// Ways to get random around here.
|
||||||
/// Please note that these are not cryptographically secure.
|
/// Please note that these are not cryptographically secure.
|
||||||
/// Or, even if some might be by accident, at least they are not seeded in a cryptographically secure fashion.
|
/// Or, even if some might be by accident, at least they are not seeded in a cryptographically secure fashion.
|
||||||
@ -25,26 +59,25 @@ pub trait Rand: Debug + Serialize + DeserializeOwned {
|
|||||||
/// Gets the next 64 bit value
|
/// Gets the next 64 bit value
|
||||||
fn next(&mut self) -> u64;
|
fn next(&mut self) -> u64;
|
||||||
|
|
||||||
|
/// Gets a value between 0.0 (inclusive) and 1.0 (exclusive)
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
fn next_float(&mut self) -> f64 {
|
||||||
|
// both 2^53 and 2^-53 can be represented in f64 exactly
|
||||||
|
const MAX: u64 = 1u64 << 53;
|
||||||
|
const MAX_DIV: f64 = 1.0 / (MAX as f64);
|
||||||
|
let u = self.next() & MAX.wrapping_sub(1);
|
||||||
|
u as f64 * MAX_DIV
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true with specified probability
|
||||||
|
fn coinflip(&mut self, success_prob: f64) -> bool {
|
||||||
|
debug_assert!((0.0..=1.0).contains(&success_prob));
|
||||||
|
self.next_float() < success_prob
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a value below the given 64 bit val (exclusive)
|
/// Gets a value below the given 64 bit val (exclusive)
|
||||||
fn below(&mut self, upper_bound_excl: u64) -> u64 {
|
fn below(&mut self, upper_bound_excl: u64) -> u64 {
|
||||||
if upper_bound_excl <= 1 {
|
fast_bound(self.next(), upper_bound_excl)
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Modulo is biased - we don't want our fuzzing to be biased so let's do it
|
|
||||||
right. See
|
|
||||||
https://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator
|
|
||||||
*/
|
|
||||||
let mut unbiased_rnd: u64;
|
|
||||||
loop {
|
|
||||||
unbiased_rnd = self.next();
|
|
||||||
if unbiased_rnd < (u64::MAX - (u64::MAX % upper_bound_excl)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unbiased_rnd % upper_bound_excl
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a value between the given lower bound (inclusive) and upper bound (inclusive)
|
/// Gets a value between the given lower bound (inclusive) and upper bound (inclusive)
|
||||||
@ -53,27 +86,13 @@ pub trait Rand: Debug + Serialize + DeserializeOwned {
|
|||||||
lower_bound_incl + self.below(upper_bound_incl - lower_bound_incl + 1)
|
lower_bound_incl + self.below(upper_bound_incl - lower_bound_incl + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Choose an item at random from the given iterator, sampling uniformly.
|
/// Convenient variant of [`choose`].
|
||||||
///
|
fn choose<I>(&mut self, from: I) -> I::Item
|
||||||
/// Note: the runtime cost is bound by the iterator's [`nth`][`Iterator::nth`] implementation
|
|
||||||
/// * For `Vec`, slice, array, this is O(1)
|
|
||||||
/// * For `HashMap`, `HashSet`, this is O(n)
|
|
||||||
fn choose<I, E, T>(&mut self, from: I) -> T
|
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = T, IntoIter = E>,
|
I: IntoIterator,
|
||||||
E: ExactSizeIterator + Iterator<Item = T>,
|
I::IntoIter: ExactSizeIterator,
|
||||||
{
|
{
|
||||||
// create iterator
|
choose(from, self.next())
|
||||||
let mut iter = from.into_iter();
|
|
||||||
|
|
||||||
// make sure there is something to choose from
|
|
||||||
debug_assert!(iter.len() > 0, "choosing from an empty iterator");
|
|
||||||
|
|
||||||
// pick a random, valid index
|
|
||||||
let index = self.below(iter.len() as u64) as usize;
|
|
||||||
|
|
||||||
// return the item chosen
|
|
||||||
iter.nth(index).unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,13 +115,22 @@ macro_rules! default_rand {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://prng.di.unimi.it/splitmix64.c
|
||||||
|
fn splitmix64(x: &mut u64) -> u64 {
|
||||||
|
*x = x.wrapping_add(0x9e3779b97f4a7c15);
|
||||||
|
let mut z = *x;
|
||||||
|
z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
|
||||||
|
z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
|
||||||
|
z ^ (z >> 31)
|
||||||
|
}
|
||||||
|
|
||||||
// Derive Default by calling `new(DEFAULT_SEED)` on each of the following Rand types.
|
// Derive Default by calling `new(DEFAULT_SEED)` on each of the following Rand types.
|
||||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
default_rand!(Xoshiro256PlusPlusRand);
|
||||||
default_rand!(Xoshiro256StarRand);
|
|
||||||
default_rand!(XorShift64Rand);
|
default_rand!(XorShift64Rand);
|
||||||
default_rand!(Lehmer64Rand);
|
default_rand!(Lehmer64Rand);
|
||||||
default_rand!(RomuTrioRand);
|
default_rand!(RomuTrioRand);
|
||||||
default_rand!(RomuDuoJrRand);
|
default_rand!(RomuDuoJrRand);
|
||||||
|
default_rand!(Sfc64Rand);
|
||||||
|
|
||||||
/// Initialize Rand types from a source of randomness.
|
/// Initialize Rand types from a source of randomness.
|
||||||
///
|
///
|
||||||
@ -145,112 +173,108 @@ macro_rules! impl_random {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
impl_random!(Xoshiro256PlusPlusRand);
|
||||||
impl_random!(Xoshiro256StarRand);
|
|
||||||
impl_random!(XorShift64Rand);
|
impl_random!(XorShift64Rand);
|
||||||
impl_random!(Lehmer64Rand);
|
impl_random!(Lehmer64Rand);
|
||||||
impl_random!(RomuTrioRand);
|
impl_random!(RomuTrioRand);
|
||||||
impl_random!(RomuDuoJrRand);
|
impl_random!(RomuDuoJrRand);
|
||||||
|
impl_random!(Sfc64Rand);
|
||||||
|
|
||||||
/// XXH3 Based, hopefully speedy, rnd implementation
|
/// xoshiro256++ PRNG: <https://prng.di.unimi.it/>
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Xoshiro256StarRand {
|
pub struct Xoshiro256PlusPlusRand {
|
||||||
rand_seed: [u64; 4],
|
s: [u64; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: re-enable ahash works without alloc
|
impl Rand for Xoshiro256PlusPlusRand {
|
||||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
fn set_seed(&mut self, mut seed: u64) {
|
||||||
impl Rand for Xoshiro256StarRand {
|
self.s[0] = splitmix64(&mut seed);
|
||||||
#[allow(clippy::unreadable_literal)]
|
self.s[1] = splitmix64(&mut seed);
|
||||||
fn set_seed(&mut self, seed: u64) {
|
self.s[2] = splitmix64(&mut seed);
|
||||||
self.rand_seed[0] = hash_std(&seed.to_be_bytes());
|
self.s[3] = splitmix64(&mut seed);
|
||||||
self.rand_seed[1] = self.rand_seed[0] ^ 0x1234567890abcdef;
|
|
||||||
self.rand_seed[2] = self.rand_seed[0] & 0x0123456789abcdef;
|
|
||||||
self.rand_seed[3] = self.rand_seed[0] | 0x01abcde43f567908;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> u64 {
|
fn next(&mut self) -> u64 {
|
||||||
let ret: u64 = self.rand_seed[0]
|
let ret: u64 = self.s[0]
|
||||||
.wrapping_add(self.rand_seed[3])
|
.wrapping_add(self.s[3])
|
||||||
.rotate_left(23)
|
.rotate_left(23)
|
||||||
.wrapping_add(self.rand_seed[0]);
|
.wrapping_add(self.s[0]);
|
||||||
let t: u64 = self.rand_seed[1] << 17;
|
let t: u64 = self.s[1] << 17;
|
||||||
|
|
||||||
self.rand_seed[2] ^= self.rand_seed[0];
|
self.s[2] ^= self.s[0];
|
||||||
self.rand_seed[3] ^= self.rand_seed[1];
|
self.s[3] ^= self.s[1];
|
||||||
self.rand_seed[1] ^= self.rand_seed[2];
|
self.s[1] ^= self.s[2];
|
||||||
self.rand_seed[0] ^= self.rand_seed[3];
|
self.s[0] ^= self.s[3];
|
||||||
|
|
||||||
self.rand_seed[2] ^= t;
|
self.s[2] ^= t;
|
||||||
|
|
||||||
self.rand_seed[3] = self.rand_seed[3].rotate_left(45);
|
self.s[3] = self.s[3].rotate_left(45);
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
impl Xoshiro256PlusPlusRand {
|
||||||
impl Xoshiro256StarRand {
|
/// Creates a new xoshiro256++ rand with the given seed
|
||||||
/// Creates a new Xoshiro rand with the given seed
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_seed(seed: u64) -> Self {
|
pub fn with_seed(seed: u64) -> Self {
|
||||||
let mut rand = Self { rand_seed: [0; 4] };
|
let mut rand = Self { s: [0; 4] };
|
||||||
rand.set_seed(seed); // TODO: Proper random seed?
|
rand.set_seed(seed);
|
||||||
rand
|
rand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// XXH3 Based, hopefully speedy, rnd implementation
|
/// Xorshift64 PRNG
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct XorShift64Rand {
|
pub struct XorShift64Rand {
|
||||||
rand_seed: u64,
|
s: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for XorShift64Rand {
|
impl Rand for XorShift64Rand {
|
||||||
#[allow(clippy::unreadable_literal)]
|
fn set_seed(&mut self, mut seed: u64) {
|
||||||
fn set_seed(&mut self, seed: u64) {
|
self.s = splitmix64(&mut seed) | 1;
|
||||||
self.rand_seed = seed ^ 0x1234567890abcdef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> u64 {
|
fn next(&mut self) -> u64 {
|
||||||
let mut x = self.rand_seed;
|
let mut x = self.s;
|
||||||
x ^= x << 13;
|
x ^= x << 13;
|
||||||
x ^= x >> 7;
|
x ^= x >> 7;
|
||||||
x ^= x << 17;
|
x ^= x << 17;
|
||||||
self.rand_seed = x;
|
self.s = x;
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XorShift64Rand {
|
impl XorShift64Rand {
|
||||||
/// Creates a new Xoshiro rand with the given seed
|
/// Creates a new xorshift64 rand with the given seed
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_seed(seed: u64) -> Self {
|
pub fn with_seed(seed: u64) -> Self {
|
||||||
let mut ret: Self = Self { rand_seed: 0 };
|
let mut ret: Self = Self { s: 0 };
|
||||||
ret.set_seed(seed); // TODO: Proper random seed?
|
ret.set_seed(seed);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// XXH3 Based, hopefully speedy, rnd implementation
|
/// Lehmer64 PRNG
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Lehmer64Rand {
|
pub struct Lehmer64Rand {
|
||||||
rand_seed: u128,
|
s: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for Lehmer64Rand {
|
impl Rand for Lehmer64Rand {
|
||||||
#[allow(clippy::unreadable_literal)]
|
fn set_seed(&mut self, mut seed: u64) {
|
||||||
fn set_seed(&mut self, seed: u64) {
|
let hi = splitmix64(&mut seed);
|
||||||
self.rand_seed = u128::from(seed) ^ 0x1234567890abcdef;
|
let lo = splitmix64(&mut seed) | 1;
|
||||||
|
self.s = u128::from(hi) << 64 | u128::from(lo);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::unreadable_literal)]
|
#[allow(clippy::unreadable_literal)]
|
||||||
fn next(&mut self) -> u64 {
|
fn next(&mut self) -> u64 {
|
||||||
self.rand_seed *= 0xda942042e4dd58b5;
|
self.s *= 0xda942042e4dd58b5;
|
||||||
(self.rand_seed >> 64) as u64
|
(self.s >> 64) as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +282,7 @@ impl Lehmer64Rand {
|
|||||||
/// Creates a new Lehmer rand with the given seed
|
/// Creates a new Lehmer rand with the given seed
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_seed(seed: u64) -> Self {
|
pub fn with_seed(seed: u64) -> Self {
|
||||||
let mut ret: Self = Self { rand_seed: 0 };
|
let mut ret: Self = Self { s: 0 };
|
||||||
ret.set_seed(seed);
|
ret.set_seed(seed);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
@ -288,10 +312,10 @@ impl RomuTrioRand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for RomuTrioRand {
|
impl Rand for RomuTrioRand {
|
||||||
fn set_seed(&mut self, seed: u64) {
|
fn set_seed(&mut self, mut seed: u64) {
|
||||||
self.x_state = seed ^ 0x12345;
|
self.x_state = splitmix64(&mut seed);
|
||||||
self.y_state = seed ^ 0x6789A;
|
self.y_state = splitmix64(&mut seed);
|
||||||
self.z_state = seed ^ 0xBCDEF;
|
self.z_state = splitmix64(&mut seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -328,9 +352,9 @@ impl RomuDuoJrRand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for RomuDuoJrRand {
|
impl Rand for RomuDuoJrRand {
|
||||||
fn set_seed(&mut self, seed: u64) {
|
fn set_seed(&mut self, mut seed: u64) {
|
||||||
self.x_state = seed ^ 0x12345;
|
self.x_state = splitmix64(&mut seed);
|
||||||
self.y_state = seed ^ 0x6789A;
|
self.y_state = splitmix64(&mut seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -343,6 +367,53 @@ impl Rand for RomuDuoJrRand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [SFC64][1] algorithm by Chris Doty-Humphrey.
|
||||||
|
///
|
||||||
|
/// [1]: https://numpy.org/doc/stable/reference/random/bit_generators/sfc64.html
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Sfc64Rand {
|
||||||
|
a: u64,
|
||||||
|
b: u64,
|
||||||
|
c: u64,
|
||||||
|
w: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sfc64Rand {
|
||||||
|
/// Creates a new [`Sfc64Rand`] with the given seed.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_seed(seed: u64) -> Self {
|
||||||
|
let mut s = Sfc64Rand {
|
||||||
|
a: 0,
|
||||||
|
b: 0,
|
||||||
|
c: 0,
|
||||||
|
w: 0,
|
||||||
|
};
|
||||||
|
s.set_seed(seed);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rand for Sfc64Rand {
|
||||||
|
fn set_seed(&mut self, seed: u64) {
|
||||||
|
self.a = seed;
|
||||||
|
self.b = seed;
|
||||||
|
self.c = seed;
|
||||||
|
self.w = 1;
|
||||||
|
for _ in 0..12 {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> u64 {
|
||||||
|
let out = self.a.wrapping_add(self.b).wrapping_add(self.w);
|
||||||
|
self.w = self.w.wrapping_add(1);
|
||||||
|
self.a = self.b ^ (self.b >> 11);
|
||||||
|
self.b = self.c.wrapping_add(self.c << 3);
|
||||||
|
self.c = self.c.rotate_left(24).wrapping_add(out);
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// fake rand, for testing purposes
|
/// fake rand, for testing purposes
|
||||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
@ -351,8 +422,8 @@ pub struct XkcdRand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rand for XkcdRand {
|
impl Rand for XkcdRand {
|
||||||
fn set_seed(&mut self, val: u64) {
|
fn set_seed(&mut self, mut seed: u64) {
|
||||||
self.val = val;
|
self.val = splitmix64(&mut seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> u64 {
|
fn next(&mut self) -> u64 {
|
||||||
@ -363,26 +434,26 @@ impl Rand for XkcdRand {
|
|||||||
/// A test rng that will return the same value (chose by fair dice roll) for testing.
|
/// A test rng that will return the same value (chose by fair dice roll) for testing.
|
||||||
impl XkcdRand {
|
impl XkcdRand {
|
||||||
/// Creates a new [`XkcdRand`] with the rand of 4, [chosen by fair dice roll, guaranteed to be random](https://xkcd.com/221/).
|
/// Creates a new [`XkcdRand`] with the rand of 4, [chosen by fair dice roll, guaranteed to be random](https://xkcd.com/221/).
|
||||||
/// Will always return this seed.
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::with_seed(4)
|
Self { val: 4 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`XkcdRand`] with the given seed. Will always return this seed.
|
/// Creates a new [`XkcdRand`] with the given seed.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_seed(seed: u64) -> Self {
|
pub fn with_seed(seed: u64) -> Self {
|
||||||
Self { val: seed }
|
let mut rand = XkcdRand { val: 0 };
|
||||||
|
rand.set_seed(seed);
|
||||||
|
rand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
//use xxhash_rust::xxh3::xxh3_64_with_seed;
|
use crate::rands::{
|
||||||
|
Rand, RomuDuoJrRand, RomuTrioRand, Sfc64Rand, StdRand, XorShift64Rand,
|
||||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
Xoshiro256PlusPlusRand,
|
||||||
use crate::rands::Xoshiro256StarRand;
|
};
|
||||||
use crate::rands::{Rand, RomuDuoJrRand, RomuTrioRand, StdRand, XorShift64Rand};
|
|
||||||
|
|
||||||
fn test_single_rand<R: Rand>(rand: &mut R) {
|
fn test_single_rand<R: Rand>(rand: &mut R) {
|
||||||
assert_ne!(rand.next(), rand.next());
|
assert_ne!(rand.next(), rand.next());
|
||||||
@ -399,8 +470,8 @@ mod tests {
|
|||||||
test_single_rand(&mut RomuTrioRand::with_seed(0));
|
test_single_rand(&mut RomuTrioRand::with_seed(0));
|
||||||
test_single_rand(&mut RomuDuoJrRand::with_seed(0));
|
test_single_rand(&mut RomuDuoJrRand::with_seed(0));
|
||||||
test_single_rand(&mut XorShift64Rand::with_seed(0));
|
test_single_rand(&mut XorShift64Rand::with_seed(0));
|
||||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
test_single_rand(&mut Xoshiro256PlusPlusRand::with_seed(0));
|
||||||
test_single_rand(&mut Xoshiro256StarRand::with_seed(0));
|
test_single_rand(&mut Sfc64Rand::with_seed(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -432,6 +503,35 @@ mod tests {
|
|||||||
|
|
||||||
log::info!("random value: {}", mutator.rng.next_u32());
|
log::info!("random value: {}", mutator.rng.next_u32());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sfc64_golden_zig() {
|
||||||
|
// https://github.com/ziglang/zig/blob/130fb5cb0fb9039e79450c9db58d6590c5bee3b3/lib/std/Random/Sfc64.zig#L73-L99
|
||||||
|
let golden: [u64; 16] = [
|
||||||
|
0x3acfa029e3cc6041,
|
||||||
|
0xf5b6515bf2ee419c,
|
||||||
|
0x1259635894a29b61,
|
||||||
|
0xb6ae75395f8ebd6,
|
||||||
|
0x225622285ce302e2,
|
||||||
|
0x520d28611395cb21,
|
||||||
|
0xdb909c818901599d,
|
||||||
|
0x8ffd195365216f57,
|
||||||
|
0xe8c4ad5e258ac04a,
|
||||||
|
0x8f8ef2c89fdb63ca,
|
||||||
|
0xf9865b01d98d8e2f,
|
||||||
|
0x46555871a65d08ba,
|
||||||
|
0x66868677c6298fcd,
|
||||||
|
0x2ce15a7e6329f57d,
|
||||||
|
0xb2f1833ca91ca79,
|
||||||
|
0x4b0890ac9bf453ca,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut s = Sfc64Rand::with_seed(0);
|
||||||
|
for v in golden {
|
||||||
|
let u = s.next();
|
||||||
|
assert_eq!(v, u);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
|
@ -2,16 +2,19 @@
|
|||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
use libafl_bolts::rands::{
|
use libafl_bolts::rands::{
|
||||||
Lehmer64Rand, Rand, RomuDuoJrRand, RomuTrioRand, XorShift64Rand, Xoshiro256StarRand,
|
Lehmer64Rand, Rand, RomuDuoJrRand, RomuTrioRand, Sfc64Rand, XorShift64Rand,
|
||||||
|
Xoshiro256PlusPlusRand,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let mut sfc64 = Sfc64Rand::with_seed(1);
|
||||||
let mut xorshift = XorShift64Rand::with_seed(1);
|
let mut xorshift = XorShift64Rand::with_seed(1);
|
||||||
let mut xoshiro = Xoshiro256StarRand::with_seed(1);
|
let mut xoshiro = Xoshiro256PlusPlusRand::with_seed(1);
|
||||||
let mut romu = RomuDuoJrRand::with_seed(1);
|
let mut romu = RomuDuoJrRand::with_seed(1);
|
||||||
let mut lehmer = Lehmer64Rand::with_seed(1);
|
let mut lehmer = Lehmer64Rand::with_seed(1);
|
||||||
let mut romu_trio = RomuTrioRand::with_seed(1);
|
let mut romu_trio = RomuTrioRand::with_seed(1);
|
||||||
|
|
||||||
|
c.bench_function("sfc64", |b| b.iter(|| black_box(sfc64.next())));
|
||||||
c.bench_function("xorshift", |b| b.iter(|| black_box(xorshift.next())));
|
c.bench_function("xorshift", |b| b.iter(|| black_box(xorshift.next())));
|
||||||
c.bench_function("xoshiro", |b| b.iter(|| black_box(xoshiro.next())));
|
c.bench_function("xoshiro", |b| b.iter(|| black_box(xoshiro.next())));
|
||||||
c.bench_function("romu", |b| b.iter(|| black_box(romu.next())));
|
c.bench_function("romu", |b| b.iter(|| black_box(romu.next())));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user