• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ia0 / data-encoding / #1030

28 Jun 2025 04:09PM UTC coverage: 52.853%. Remained the same
#1030

push

web-flow
Fix lint in xtask (#144)

491 of 929 relevant lines covered (52.85%)

2.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
lib/fuzz/examples/analyze.rs
1
use std::collections::{BTreeMap, HashMap, HashSet};
2

3
use data_encoding_fuzz::cmd;
4

5
fn main() {
6
    let path = cmd::path(false);
7
    let target = cmd::target(&path);
8
    let mut stats = Stats::new(std::env::args().skip(2));
9
    for entry in std::fs::read_dir(path).unwrap() {
10
        stats.merge(&cmd::execute(&target, &std::fs::read(entry.unwrap().path()).unwrap()));
11
    }
12
    stats.print();
13
}
14

15
struct Stats {
16
    buckets: Vec<(String, Bucket)>,
17
    filters: Vec<(String, Filter)>,
18
    stats: HashMap<Vec<Option<usize>>, HashMap<&'static str, Stat>>,
19
}
20

21
#[derive(Clone, Copy, Default)]
22
struct Stat {
23
    sum: f64,
24
    len: usize,
25
}
26

27
#[derive(Clone, Copy)]
28
enum Bucket {
29
    Lin(usize),
30
    Exp(usize),
31
}
32

33
#[derive(Clone, Copy)]
34
enum Filter {
35
    Is(usize),
36
    Eq(usize),
37
    Lt(usize),
38
    Gt(usize),
39
}
40

41
impl Stats {
42
    fn new(args: impl Iterator<Item = String>) -> Self {
×
43
        let mut buckets = BTreeMap::new();
×
44
        let mut filters = Vec::new();
×
45
        for arg in args {
×
46
            let Some((name, value)) = arg.split_once(['+', '*', '!', '=', '<', '>']) else {
×
47
                panic!("{arg:?} does not contain an operator: + * ! = < >");
×
48
            };
49
            if !name.bytes().all(|x| b"abcdefghijklmnopqrstuvwxz_".contains(&x)) {
×
50
                panic!("{name:?} is not a name");
×
51
            }
52
            let Ok(value) = value.parse::<usize>() else {
×
53
                panic!("{value:?} is not a value");
×
54
            };
55
            let op = match arg.as_bytes()[name.len()] {
×
56
                b'+' => Ok(Bucket::Lin(value)),
×
57
                b'*' => Ok(Bucket::Exp(value)),
×
58
                b'!' => Err(Filter::Is(value)),
×
59
                b'=' => Err(Filter::Eq(value)),
×
60
                b'<' => Err(Filter::Lt(value)),
×
61
                b'>' => Err(Filter::Gt(value)),
×
62
                _ => unreachable!(),
63
            };
64
            match op {
×
65
                Ok(bucket) => {
×
66
                    if buckets.insert(name.to_string(), bucket).is_some() {
×
67
                        panic!("duplicate bucket for {name}");
×
68
                    }
69
                }
70
                Err(filter) => filters.push((name.to_string(), filter)),
×
71
            }
72
        }
73
        let buckets: Vec<_> = buckets.into_iter().collect();
×
74
        Stats { buckets, filters, stats: HashMap::new() }
×
75
    }
76

77
    fn merge(&mut self, stat: &HashMap<&'static str, usize>) {
78
        let Stats { buckets, filters, stats } = self;
79
        let slot = buckets.iter().map(|(k, b)| stat.get(k.as_str()).map(|&v| b.slot(v))).collect();
80
        if !filters.iter().all(|x| x.1.contains(stat.get(x.0.as_str()).copied())) {
81
            return;
82
        }
83
        let stats = stats.entry(slot).or_default();
84
        for (&key, &value) in stat {
85
            stats.entry(key).or_default().merge(value);
86
        }
87
        stats.entry(COUNT).or_default().merge(1);
88
    }
89

90
    fn print(self) {
91
        // Compute slot headers.
92
        let slot_hdrs: Vec<_> = self.buckets.iter().map(|x| x.0.as_str()).collect();
93
        assert!(slot_hdrs.is_sorted());
94

95
        // Compute stat headers.
96
        let mut stat_hdrs = HashSet::new();
97
        for stats in self.stats.values() {
98
            stat_hdrs.extend(stats.keys().copied());
99
        }
100
        let mut stat_hdrs: Vec<_> = stat_hdrs.into_iter().collect();
101
        stat_hdrs.sort();
102
        let stat_hdrs = stat_hdrs;
103

104
        // Compute columns (a column is a slot and its stat).
105
        let mut cols = Vec::new();
106
        for (k, v) in self.stats {
107
            let mut t = Vec::new();
108
            for &x in &stat_hdrs {
109
                t.push(v.get(x).copied());
110
            }
111
            cols.push((k, t));
112
        }
113
        cols.sort_by(|x, y| x.0.cmp(&y.0));
114

115
        // Compute matrix.
116
        let mut matrix = vec![Vec::new(); slot_hdrs.len() + stat_hdrs.len()];
117
        let n = slot_hdrs.len();
118
        for (i, h) in slot_hdrs.iter().enumerate() {
119
            matrix[i].push(h.to_string());
120
        }
121
        for (i, h) in stat_hdrs.iter().enumerate() {
122
            matrix[n + i].push(h.to_string());
123
        }
124
        for (slot, stat) in cols {
125
            for (i, x) in slot.into_iter().enumerate() {
126
                matrix[i].push(x.map_or("-".to_string(), |x| format!("{x}..")));
127
            }
128
            for (i, x) in stat.into_iter().enumerate() {
129
                let cell = match x {
130
                    Some(x) if stat_hdrs[i] == COUNT => x.len.to_string(),
131
                    Some(x) => format!("{:.2}", x.average()),
132
                    None => "-".to_string(),
133
                };
134
                matrix[n + i].push(cell);
135
            }
136
        }
137

138
        // Print matrix.
139
        print_matrix(matrix);
140
    }
141
}
142

143
impl Stat {
144
    fn merge(&mut self, value: usize) {
145
        self.sum += value as f64;
146
        self.len += 1;
147
    }
148

149
    fn average(self) -> f64 {
150
        self.sum / self.len as f64
151
    }
152
}
153

154
impl Bucket {
155
    fn slot(self, mut value: usize) -> usize {
156
        match self {
157
            Bucket::Lin(delta) => value / delta * delta,
158
            Bucket::Exp(base) => {
159
                let mut slot = 0;
160
                while 0 < value {
161
                    value /= base;
162
                    slot = if slot == 0 { 1 } else { slot * base };
163
                }
164
                slot
165
            }
166
        }
167
    }
168
}
169

170
impl Filter {
171
    fn contains(self, value: Option<usize>) -> bool {
172
        match self {
173
            Filter::Is(x) => value.is_some() as usize == x,
174
            Filter::Eq(x) => value.is_some_and(|y| y == x),
175
            Filter::Lt(x) => value.is_some_and(|y| y < x),
176
            Filter::Gt(x) => value.is_some_and(|y| y > x),
177
        }
178
    }
179
}
180

181
const COUNT: &str = "-- count --";
182

183
fn align(x: &str, n: usize) {
184
    for _ in 0 .. n.saturating_sub(x.len()) {
185
        print!(" ");
186
    }
187
    print!("{x}")
188
}
189

190
fn print_matrix(mut m: Vec<Vec<String>>) {
191
    let Some(n) = m.iter().map(|r| r.len()).max() else { return };
192
    m.iter_mut().for_each(|x| x.resize(n, String::new()));
193
    let w: Vec<_> =
194
        (0 .. n).map(|i| m.iter().map(|x| x[i].len()).max().unwrap() + (i != 0) as usize).collect();
195
    for x in m {
196
        for i in 0 .. n {
197
            align(&x[i], w[i]);
198
        }
199
        println!();
200
    }
201
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc