1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
pub use arbitrary::{Arbitrary, Error as ArbitraryError, Unstructured};
use canonical::{Canon, Id, Sink};
const FUZZ_ITERATIONS: usize = 128;
fn hash<T: Hash>(t: T) -> u64 {
let mut hasher = DefaultHasher::new();
t.hash(&mut hasher);
hasher.finish()
}
pub fn fuzz_canon<C>()
where
C: Canon + Arbitrary + PartialEq + std::fmt::Debug,
{
fuzz_canon_iterations::<C>(FUZZ_ITERATIONS)
}
pub fn fuzz_canon_iterations<C>(iterations: usize)
where
C: Canon + Arbitrary + PartialEq + std::fmt::Debug,
{
let mut entropy = 0;
for _ in 0..iterations {
let mut bytes = vec![];
let canon = {
loop {
match C::arbitrary(&mut Unstructured::new(&bytes)) {
Ok(t) => break t,
Err(_) => {
entropy += 1;
bytes.extend_from_slice(&hash(entropy).to_be_bytes());
}
}
}
};
let claimed_len = canon.encoded_len();
let mut buffer_a = vec![];
buffer_a.resize_with(claimed_len + 1, || 0xff);
let mut buffer_b = vec![];
buffer_b.resize_with(claimed_len + 1, || 0x00);
let mut sink_a = Sink::new(&mut buffer_a[..]);
let mut sink_b = Sink::new(&mut buffer_b[..]);
canon.encode(&mut sink_a);
canon.encode(&mut sink_b);
let mut valid = true;
if buffer_a[claimed_len] != 0xff
|| buffer_b[claimed_len] != 0x00
|| (claimed_len > 0
&& buffer_a[claimed_len - 1] != buffer_b[claimed_len - 1])
{
valid = false
}
if !valid {
for i in 0..claimed_len {
if buffer_a[i] != buffer_b[i] {
panic!("claimed {}, wrote {}", claimed_len, i - 1)
}
}
}
let id = Id::new(&canon);
let restored = id.reify().unwrap();
assert!(canon == restored);
}
}