1use stuffr::FormatKind;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub struct Levels {
14 pub default: Option<i32>,
15 pub sweep: &'static [i32],
16}
17
18const fn some(default: i32, sweep: &'static [i32]) -> Levels {
19 Levels {
20 default: Some(default),
21 sweep,
22 }
23}
24
25const NONE: Levels = Levels {
26 default: None,
27 sweep: &[],
28};
29
30pub fn levels_of(format: &str) -> Option<Levels> {
36 Some(match format {
37 "gzip" | "zlib" | "deflate" | "xz" | "lzma" | "lzip" => {
38 some(6, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
39 }
40 "bzip2" => some(6, &[1, 2, 3, 4, 5, 6, 7, 8, 9]),
41 "brotli" => some(11, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
42 "zstd" => some(
43 3,
44 &[
45 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
46 ],
47 ),
48 "compress" => some(16, &[9, 10, 11, 12, 13, 14, 15, 16]),
50 "lz4" | "snappy" => NONE,
51 _ => return None,
52 })
53}
54
55pub fn backend_of(codec: &str) -> &'static str {
63 const C_TIER: [(&str, bool); 3] = [
64 ("zstd", cfg!(feature = "zstd-c")),
65 ("xz", cfg!(feature = "xz-c")),
66 ("lzma", cfg!(feature = "c-backed")),
67 ];
68 if C_TIER.iter().any(|&(name, on)| on && name == codec) {
69 "c"
70 } else {
71 "pure"
72 }
73}
74
75pub fn benchable_codecs() -> Vec<&'static str> {
78 let reg = stuffr::registry();
79 reg.matrix()
80 .into_iter()
81 .filter(|row| row.kind == FormatKind::Codec && row.write)
82 .filter(|row| reg.codec(row.id).is_some_and(|c| !c.caps().weak_encoder))
83 .map(|row| row.id.as_str())
84 .collect()
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use std::io::Write;
91 use std::sync::{Arc, Mutex};
92 use stuffr::{EncodeOpts, FormatId, FormatKind};
93
94 #[derive(Clone)]
95 struct Buf(Arc<Mutex<Vec<u8>>>);
96
97 impl Write for Buf {
98 fn write(&mut self, b: &[u8]) -> std::io::Result<usize> {
99 self.0.lock().unwrap().extend_from_slice(b);
100 Ok(b.len())
101 }
102 fn flush(&mut self) -> std::io::Result<()> {
103 Ok(())
104 }
105 }
106
107 fn input() -> Vec<u8> {
108 b"the quick brown fox jumps over the lazy dog. "
109 .iter()
110 .copied()
111 .cycle()
112 .take(4096)
113 .collect()
114 }
115
116 fn encode(name: &'static str, level: Option<i32>) -> Vec<u8> {
117 let codec = stuffr::registry()
118 .codec(FormatId::new(name))
119 .expect("codec registered");
120 let buf = Buf(Arc::default());
121 let opts = EncodeOpts {
122 level,
123 ..EncodeOpts::default()
124 };
125 let mut sink = codec.encoder(Box::new(buf.clone()), &opts).unwrap();
126 sink.write_all(&input()).unwrap();
127 sink.finish().unwrap();
128 buf.0.lock().unwrap().clone()
129 }
130
131 #[test]
132 fn every_benchable_codec_has_a_row() {
133 for name in benchable_codecs() {
134 assert!(levels_of(name).is_some(), "{name} has no level row");
135 }
136 assert!(levels_of("no-such-format").is_none());
137 }
138
139 #[test]
140 fn the_default_label_is_the_encoders_default() {
141 for name in benchable_codecs() {
142 let l = levels_of(name).unwrap();
143 if let Some(d) = l.default {
144 assert_eq!(
145 encode(name, Some(d)),
146 encode(name, None),
147 "{name}: label {d} is not the encoder default"
148 );
149 assert!(l.sweep.contains(&d), "{name}: sweep omits its default");
150 }
151 }
152 }
153
154 #[test]
155 fn every_sweep_level_is_accepted() {
156 for name in benchable_codecs() {
157 let codec = stuffr::registry().codec(FormatId::new(name)).unwrap();
158 let l = levels_of(name).unwrap();
159 assert!(
160 l.sweep.windows(2).all(|w| w[0] < w[1]),
161 "{name}: not ascending"
162 );
163 for &level in l.sweep {
164 let opts = EncodeOpts {
165 level: Some(level),
166 ..EncodeOpts::default()
167 };
168 codec
169 .check_encode_opts(&opts)
170 .unwrap_or_else(|e| panic!("{name} rejects sweep level {level}: {e}"));
171 }
172 }
173 }
174
175 #[test]
176 fn no_weak_encoder_is_benchable() {
177 let reg = stuffr::registry();
178 let benchable = benchable_codecs();
179 for row in reg.matrix() {
180 if reg.codec(row.id).is_some_and(|c| c.caps().weak_encoder) {
181 assert!(
182 !benchable.contains(&row.id.as_str()),
183 "{} has a weak encoder",
184 row.id.as_str()
185 );
186 }
187 }
188 }
189
190 #[test]
191 fn the_backend_follows_the_build() {
192 let zstd_weak = stuffr::registry()
195 .codec(FormatId::new("zstd"))
196 .is_some_and(|c| c.caps().weak_encoder);
197 assert_eq!(backend_of("zstd") == "c", cfg!(feature = "zstd-c"));
198 if cfg!(feature = "zstd-c") {
199 assert!(!zstd_weak);
200 }
201 assert_eq!(backend_of("xz") == "c", cfg!(feature = "xz-c"));
202 assert_eq!(backend_of("lzma") == "c", cfg!(feature = "c-backed"));
203 for name in [
205 "gzip", "zlib", "deflate", "lz4", "bzip2", "brotli", "snappy", "lzip", "compress",
206 ] {
207 assert_eq!(backend_of(name), "pure", "{name}");
208 }
209 }
210
211 #[test]
212 fn no_container_is_benchable() {
213 let b = benchable_codecs();
214 for c in ["tar", "zip", "ar", "cpio"] {
215 assert!(!b.contains(&c), "{c} must not be benchable");
216 }
217 for row in stuffr::registry().matrix() {
218 if row.kind == FormatKind::Container {
219 assert!(!b.contains(&row.id.as_str()));
220 }
221 }
222 }
223}