#[derive(Clone, Debug)]
pub struct Rng(u64);
impl Rng {
pub fn new(seed: u64) -> Rng {
Rng(if seed == 0 { 0x9e3779b97f4a7c15 } else { seed })
}
pub fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
self.0 = x;
x.wrapping_mul(0x2545f4914f6cdd1d)
}
pub fn next_u32(&mut self) -> u32 {
(self.next_u64() >> 32) as u32
}
pub fn byte(&mut self) -> u8 {
(self.next_u64() >> 56) as u8
}
pub fn below(&mut self, n: usize) -> usize {
if n == 0 {
0
} else {
(self.next_u64() % n as u64) as usize
}
}
pub fn range(&mut self, lo: usize, hi: usize) -> usize {
lo + self.below(hi.saturating_sub(lo) + 1)
}
pub fn chance(&mut self, num: u32, den: u32) -> bool {
self.next_u32() % den < num
}
pub fn pick<'a, T>(&mut self, items: &'a [T]) -> &'a T {
&items[self.below(items.len())]
}
}
pub fn random_chunk(rng: &mut Rng, max_len: usize) -> Vec<u8> {
let len = rng.below(max_len + 1);
(0..len).map(|_| rng.byte()).collect()
}
pub fn sequence_shaped(rng: &mut Rng) -> Vec<u8> {
let mut out = vec![0x1b];
match rng.below(6) {
0 | 1 => {
out.push(b'[');
if rng.chance(1, 4) {
out.push(*rng.pick(b"?><="));
}
let nparams = rng.below(7);
for i in 0..nparams {
if i > 0 {
out.push(*rng.pick(b";;:"));
}
push_number(rng, &mut out);
}
if rng.chance(1, 8) {
out.push(*rng.pick(b" !\"#$%&'"));
}
if !rng.chance(1, 6) {
out.push(rng.range(0x40, 0x7e) as u8);
}
}
2 => {
out.push(b']');
push_number(rng, &mut out);
out.push(b';');
for _ in 0..rng.below(24) {
out.push(rng.range(0x20, 0x7e) as u8);
}
match rng.below(3) {
0 => out.push(0x07),
1 => out.extend_from_slice(b"\x1b\\"),
_ => {} }
}
3 => {
out.push(*rng.pick(b"P_^X"));
for _ in 0..rng.below(24) {
out.push(rng.byte());
}
if !rng.chance(1, 3) {
out.extend_from_slice(b"\x1b\\");
}
}
4 => {
out.push(b'O');
out.push(rng.range(0x40, 0x7e) as u8);
}
_ => out.push(rng.byte()),
}
out
}
fn push_number(rng: &mut Rng, out: &mut Vec<u8>) {
match rng.below(8) {
0 => out.extend_from_slice(
format!("{}", (rng.next_u32() as u64).saturating_mul(1 << 20)).as_bytes(),
),
1 => {
for _ in 0..rng.range(30, 300) {
out.push(b'0' + (rng.below(10) as u8));
}
}
_ => out.extend_from_slice(format!("{}", rng.below(10000)).as_bytes()),
}
}
pub fn truncated_utf8(rng: &mut Rng) -> Vec<u8> {
const POOL: &[&str] = &[
"hello",
"héllo",
"café",
"日本語",
"中文字",
"한국어",
"🎉",
"🧪",
"👍🏽",
"e\u{301}",
"a\u{300}\u{316}",
"Ω≈ç√",
"𝔘𝔫𝔦",
];
let mut s = String::new();
for _ in 0..rng.range(1, 5) {
s.push_str(rng.pick(POOL));
}
let mut bytes = s.into_bytes();
if rng.chance(2, 3) && !bytes.is_empty() {
let cut = rng.range(1, bytes.len());
bytes.truncate(cut);
}
bytes
}
pub fn hostile_corpus(seed: u64, count: usize) -> Vec<Vec<u8>> {
let mut rng = Rng::new(seed);
let mut corpus: Vec<Vec<u8>> = vec![
b"".to_vec(),
b"\x1b".to_vec(),
b"\x1b\x1b\x1b".to_vec(),
b"\x1b[".to_vec(),
b"\x1b[;;;;;;;;;;;;;".to_vec(),
b"\x1b[999999999999999999999999H".to_vec(),
b"\x1b[38;2;300;300;300m".to_vec(),
b"\x1b[38;5m".to_vec(),
b"\x1b[38m".to_vec(),
b"\x1b]8;;".to_vec(),
b"\x1b]0;title never ends".to_vec(),
b"\x1bP+q544e\x1b".to_vec(),
b"\xff\xfe\xfd".to_vec(),
b"\xc3".to_vec(), b"\xe2\x82".to_vec(), b"\xf0\x9f\x8e".to_vec(), b"\x80\x80\x80".to_vec(), b"\xed\xa0\x80".to_vec(), b"\xc0\xaf".to_vec(), b"\x18\x1a".to_vec(), vec![0x1b; 512],
vec![b'A'; 4096],
];
while corpus.len() < count {
let chunk = match rng.below(4) {
0 => random_chunk(&mut rng, 64),
1 | 2 => sequence_shaped(&mut rng),
_ => truncated_utf8(&mut rng),
};
corpus.push(chunk);
}
corpus
}
pub fn random_splits(rng: &mut Rng, bytes: &[u8], max: usize) -> Vec<Vec<u8>> {
let mut out = Vec::new();
let mut i = 0;
while i < bytes.len() {
let n = rng.range(1, max.max(1)).min(bytes.len() - i);
out.push(bytes[i..i + n].to_vec());
i += n;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rng_is_deterministic() {
let a: Vec<u64> = {
let mut r = Rng::new(42);
(0..8).map(|_| r.next_u64()).collect()
};
let b: Vec<u64> = {
let mut r = Rng::new(42);
(0..8).map(|_| r.next_u64()).collect()
};
assert_eq!(a, b);
let c: Vec<u64> = {
let mut r = Rng::new(43);
(0..8).map(|_| r.next_u64()).collect()
};
assert_ne!(a, c);
}
#[test]
fn zero_seed_does_not_lock() {
let mut r = Rng::new(0);
assert_ne!(r.next_u64(), 0);
}
#[test]
fn corpus_is_reproducible_and_sized() {
let a = hostile_corpus(7, 100);
let b = hostile_corpus(7, 100);
assert_eq!(a, b);
assert_eq!(a.len(), 100);
}
#[test]
fn splits_reassemble() {
let mut rng = Rng::new(9);
let data: Vec<u8> = (0..=255).collect();
let parts = random_splits(&mut rng, &data, 7);
let joined: Vec<u8> = parts.concat();
assert_eq!(joined, data);
assert!(parts.iter().all(|p| !p.is_empty() && p.len() <= 7));
}
}