// SPDX-License-Identifier: GPL-2.0-only
// Port of boofuzz/primitives/random_data.py at 518c13904fc32e7f2cc88c9dec934e509062953e.
// Upstream seeds a fresh random.Random(0) per mutations() call for
// reproducibility; the port materializes the same MT19937 sequence eagerly
// so indexed candidate access stays positionally stable.
///|
/// Random data candidates from upstream's deterministic Random(0) stream.
/// Step mode fixes each length at min_length + i*step without consuming the
/// generator; otherwise each candidate draws one randint for its length and
/// one randint(0, 255) per byte. Candidate identity is stable, so the
/// upstream count quirk (mutations loop over get_num_mutations, which also
/// counts fuzz_values) is intentionally not reproduced: the random sequence
/// has exactly max_mutations entries and user fuzz_values are appended after.
pub fn Field::random_data(
default_value? : Bytes = b"",
min_length? : Int = 0,
max_length? : Int = 1,
max_mutations? : Int = 25,
step? : Int,
fuzzable? : Bool = true,
fuzz_values? : Array[Bytes] = [],
) -> Field raise ModelError {
guard min_length >= 0 && max_length >= min_length else {
raise Invalid("invalid random data length range")
}
guard max_mutations >= 0 else { raise Invalid("negative max_mutations") }
let step_value = step.unwrap_or(0)
guard step_value >= 0 else { raise Invalid("negative random data step") }
let count = if step_value > 0 {
(max_length - min_length) / step_value + 1
} else {
max_mutations
}
let generator = Mt19937::from_seed(0L)
let saved : Array[Bytes] = []
let mut total_bytes = 0L
for i in 0.. 0 {
min_length + i * step_value
} else {
generator.randint(min_length, max_length)
}
total_bytes += length.to_int64()
guard total_bytes <= 20000000L else {
raise Invalid("random data candidates exceed the byte budget")
}
let bytes = Bytes::makei(length, _ => generator.randint(0, 255).to_byte())
saved.push(bytes)
}
let base : Field = {
value: default_value,
count: if fuzzable {
saved.length()
} else {
0
},
candidate: fn(i) { saved[i] },
candidate_length: i => saved[i].length().to_int64(),
}
Field::with_fuzz_values(base, fuzz_values, fuzzable)
}