1use anyhow as ah;
13use std::fmt::Write;
14use std::time::Duration;
15
16const EIB: u64 = 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
17const PIB: u64 = 1024 * 1024 * 1024 * 1024 * 1024;
18const TIB: u64 = 1024 * 1024 * 1024 * 1024;
19const GIB: u64 = 1024 * 1024 * 1024;
20const MIB: u64 = 1024 * 1024;
21const KIB: u64 = 1024;
22
23const EIBM1: u64 = EIB - 1;
24const PIBM1: u64 = PIB - 1;
25const TIBM1: u64 = TIB - 1;
26const GIBM1: u64 = GIB - 1;
27const MIBM1: u64 = MIB - 1;
28const EB: u64 = 1000 * 1000 * 1000 * 1000 * 1000 * 1000;
31const PB: u64 = 1000 * 1000 * 1000 * 1000 * 1000;
32const TB: u64 = 1000 * 1000 * 1000 * 1000;
33const GB: u64 = 1000 * 1000 * 1000;
34const MB: u64 = 1000 * 1000;
35const KB: u64 = 1000;
36
37const EBM1: u64 = EB - 1;
38const PBM1: u64 = PB - 1;
39const TBM1: u64 = TB - 1;
40const GBM1: u64 = GB - 1;
41const MBM1: u64 = MB - 1;
42
43pub fn prettybytes(count: u64, binary: bool, decimal: bool, bytes: bool) -> String {
44 let mut ret = String::new();
45
46 if !binary && !decimal {
47 return ret;
48 }
49
50 if count < KIB {
51 let _ = write!(ret, "{} bytes", count);
52 return ret;
53 }
54
55 if binary {
56 let _ = match count {
57 EIB..=u64::MAX => write!(ret, "{:.4} EiB", ((count / TIB) as f64) / (MIB as f64)),
58 PIB..=EIBM1 => write!(ret, "{:.4} PiB", ((count / GIB) as f64) / (MIB as f64)),
59 TIB..=PIBM1 => write!(ret, "{:.4} TiB", ((count / MIB) as f64) / (MIB as f64)),
60 GIB..=TIBM1 => write!(ret, "{:.2} GiB", ((count / MIB) as f64) / (KIB as f64)),
61 MIB..=GIBM1 => write!(ret, "{:.1} MiB", (count as f64) / (MIB as f64)),
62 0..=MIBM1 => write!(ret, "{:.1} kiB", (count as f64) / (KIB as f64)),
63 };
64 }
65
66 let paren = if !ret.is_empty() && (decimal || bytes) {
67 ret.push_str(" (");
68 true
69 } else {
70 false
71 };
72
73 if decimal {
74 let _ = match count {
75 EB..=u64::MAX => write!(ret, "{:.4} EB", ((count / TB) as f64) / (MB as f64)),
76 PB..=EBM1 => write!(ret, "{:.4} PB", ((count / GB) as f64) / (MB as f64)),
77 TB..=PBM1 => write!(ret, "{:.4} TB", ((count / MB) as f64) / (MB as f64)),
78 GB..=TBM1 => write!(ret, "{:.2} GB", ((count / MB) as f64) / (KB as f64)),
79 MB..=GBM1 => write!(ret, "{:.1} MB", (count as f64) / (MB as f64)),
80 0..=MBM1 => write!(ret, "{:.1} kB", (count as f64) / (KB as f64)),
81 };
82 }
83
84 if bytes {
85 if decimal {
86 ret.push_str(", ");
87 }
88 let _ = write!(ret, "{} bytes", count);
89 }
90
91 if paren {
92 ret.push(')');
93 }
94
95 ret
96}
97
98fn try_one_parsebytes(s: &str, suffix: &str, factor: u64) -> ah::Result<u64> {
99 let Some(s) = s.strip_suffix(suffix) else {
100 return Err(ah::format_err!("Value suffix does not match."));
101 };
102 let s = s.trim();
103 if let Ok(value) = s.parse::<u64>() {
104 let Some(prod) = value.checked_mul(factor) else {
106 return Err(ah::format_err!("Value integer overflow."));
107 };
108 Ok(prod)
109 } else if let Ok(value) = s.parse::<f64>() {
110 let factor = factor as f64;
112 if value.log2() + factor.log2() >= 61.0 {
113 return Err(ah::format_err!("Value float overflow."));
114 }
115 Ok((value * factor).round() as u64)
116 } else {
117 Err(ah::format_err!("Value is neither integer nor float."))
118 }
119}
120
121pub fn parsebytes(s: &str) -> ah::Result<u64> {
123 let s = s.trim().to_lowercase();
124
125 if let Ok(v) = try_one_parsebytes(&s, "eib", EIB) {
126 Ok(v)
127 } else if let Ok(v) = try_one_parsebytes(&s, "pib", PIB) {
128 Ok(v)
129 } else if let Ok(v) = try_one_parsebytes(&s, "tib", TIB) {
130 Ok(v)
131 } else if let Ok(v) = try_one_parsebytes(&s, "gib", GIB) {
132 Ok(v)
133 } else if let Ok(v) = try_one_parsebytes(&s, "mib", MIB) {
134 Ok(v)
135 } else if let Ok(v) = try_one_parsebytes(&s, "kib", KIB) {
136 Ok(v)
137 } else if let Ok(v) = try_one_parsebytes(&s, "e", EIB) {
138 Ok(v)
139 } else if let Ok(v) = try_one_parsebytes(&s, "p", PIB) {
140 Ok(v)
141 } else if let Ok(v) = try_one_parsebytes(&s, "t", TIB) {
142 Ok(v)
143 } else if let Ok(v) = try_one_parsebytes(&s, "g", GIB) {
144 Ok(v)
145 } else if let Ok(v) = try_one_parsebytes(&s, "m", MIB) {
146 Ok(v)
147 } else if let Ok(v) = try_one_parsebytes(&s, "k", KIB) {
148 Ok(v)
149 } else if let Ok(v) = try_one_parsebytes(&s, "eb", EB) {
150 Ok(v)
151 } else if let Ok(v) = try_one_parsebytes(&s, "pb", PB) {
152 Ok(v)
153 } else if let Ok(v) = try_one_parsebytes(&s, "tb", TB) {
154 Ok(v)
155 } else if let Ok(v) = try_one_parsebytes(&s, "gb", GB) {
156 Ok(v)
157 } else if let Ok(v) = try_one_parsebytes(&s, "mb", MB) {
158 Ok(v)
159 } else if let Ok(v) = try_one_parsebytes(&s, "kb", KB) {
160 Ok(v)
161 } else if let Ok(v) = s.parse::<u64>() {
162 Ok(v)
164 } else {
165 Err(ah::format_err!("Cannot parse byte count: {}", s))
166 }
167}
168
169pub trait Hhmmss {
170 fn hhmmss(&self) -> String;
171}
172
173impl Hhmmss for Duration {
174 fn hhmmss(&self) -> String {
175 let secs = self.as_secs();
176 let secs_lim = (99 * 60 * 60) + (59 * 60) + 59;
177 let lim = if secs > secs_lim { ">" } else { "" };
178 let secs = secs.min(secs_lim);
179 let h = secs / (60 * 60);
180 let rem = secs % (60 * 60);
181 let m = rem / 60;
182 let s = rem % 60;
183 format!("{}{:02}h:{:02}m:{:02}s", lim, h, m, s)
184 }
185}
186
187pub fn fold(input: &[u8], output_size: usize) -> Vec<u8> {
191 let mut output = vec![0; output_size];
192
193 if output_size > 0 {
194 for (i, data) in input.iter().enumerate() {
195 output[i % output_size] ^= data;
196 }
197 }
198
199 output
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_prettybytes() {
208 assert_eq!(prettybytes(42, true, true, false), "42 bytes");
209 assert_eq!(
210 prettybytes(42 * 1024, true, true, false),
211 "42.0 kiB (43.0 kB)"
212 );
213 assert_eq!(
214 prettybytes(42 * 1024 * 1024, true, true, false),
215 "42.0 MiB (44.0 MB)"
216 );
217 assert_eq!(
218 prettybytes(42 * 1024 * 1024 * 1024, true, true, false),
219 "42.00 GiB (45.10 GB)"
220 );
221 assert_eq!(
222 prettybytes(42 * 1024 * 1024 * 1024 * 1024, true, true, false),
223 "42.0000 TiB (46.1795 TB)"
224 );
225 assert_eq!(
226 prettybytes(42 * 1024 * 1024 * 1024 * 1024 * 1024, true, true, false),
227 "42.0000 PiB (47.2878 PB)"
228 );
229 assert_eq!(
230 prettybytes(
231 2 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
232 true,
233 true,
234 false
235 ),
236 "2.0000 EiB (2.3058 EB)"
237 );
238
239 assert_eq!(prettybytes(42, true, false, false), "42 bytes");
240 assert_eq!(prettybytes(42, false, true, false), "42 bytes");
241 assert_eq!(prettybytes(42, false, false, false), "");
242
243 assert_eq!(prettybytes(42 * 1024, true, false, false), "42.0 kiB");
244 assert_eq!(prettybytes(42 * 1024, false, true, false), "43.0 kB");
245 assert_eq!(prettybytes(42 * 1024, false, false, false), "");
246
247 assert_eq!(
248 prettybytes(42 * 1024, true, true, true),
249 "42.0 kiB (43.0 kB, 43008 bytes)"
250 );
251 assert_eq!(
252 prettybytes(42 * 1024, true, false, true),
253 "42.0 kiB (43008 bytes)"
254 );
255 }
256
257 #[test]
258 fn test_parsebytes() {
259 assert_eq!(parsebytes("42").unwrap(), 42);
261
262 assert_eq!(parsebytes("42kib").unwrap(), 42 * 1024);
264 assert_eq!(parsebytes("42 mib").unwrap(), 42 * 1024 * 1024);
265 assert_eq!(parsebytes(" 42 gib ").unwrap(), 42 * 1024 * 1024 * 1024);
266 assert_eq!(parsebytes("42Tib").unwrap(), 42 * 1024 * 1024 * 1024 * 1024);
267 assert_eq!(
268 parsebytes("42PiB").unwrap(),
269 42 * 1024 * 1024 * 1024 * 1024 * 1024
270 );
271 assert_eq!(
272 parsebytes("2 EIB ").unwrap(),
273 2 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
274 );
275
276 assert_eq!(parsebytes("42.5kib").unwrap(), 42 * 1024 + 1024 / 2);
278 assert_eq!(
279 parsebytes("42.5 mib").unwrap(),
280 42 * 1024 * 1024 + 1024 * 1024 / 2
281 );
282 assert_eq!(
283 parsebytes(" 42.5 gib ").unwrap(),
284 42 * 1024 * 1024 * 1024 + 1024 * 1024 * 1024 / 2
285 );
286 assert_eq!(
287 parsebytes("42.5Tib").unwrap(),
288 42 * 1024 * 1024 * 1024 * 1024 + 1024 * 1024 * 1024 * 1024 / 2
289 );
290 assert_eq!(
291 parsebytes("42.5PiB").unwrap(),
292 42 * 1024 * 1024 * 1024 * 1024 * 1024 + 1024 * 1024 * 1024 * 1024 * 1024 / 2
293 );
294 assert_eq!(
295 parsebytes("1.5 EIB ").unwrap(),
296 1024 * 1024 * 1024 * 1024 * 1024 * 1024 + 1024 * 1024 * 1024 * 1024 * 1024 * 1024 / 2
297 );
298
299 assert_eq!(parsebytes("42k").unwrap(), 42 * 1024);
301 assert_eq!(parsebytes("42 m").unwrap(), 42 * 1024 * 1024);
302 assert_eq!(parsebytes(" 42 g ").unwrap(), 42 * 1024 * 1024 * 1024);
303 assert_eq!(parsebytes("42T").unwrap(), 42 * 1024 * 1024 * 1024 * 1024);
304 assert_eq!(
305 parsebytes("42P").unwrap(),
306 42 * 1024 * 1024 * 1024 * 1024 * 1024
307 );
308 assert_eq!(
309 parsebytes("2 E ").unwrap(),
310 2 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
311 );
312
313 assert_eq!(parsebytes("42.5k").unwrap(), 42 * 1024 + 1024 / 2);
315 assert_eq!(
316 parsebytes("42.5 m").unwrap(),
317 42 * 1024 * 1024 + 1024 * 1024 / 2
318 );
319 assert_eq!(
320 parsebytes(" 42.5 g ").unwrap(),
321 42 * 1024 * 1024 * 1024 + 1024 * 1024 * 1024 / 2
322 );
323 assert_eq!(
324 parsebytes("42.5T").unwrap(),
325 42 * 1024 * 1024 * 1024 * 1024 + 1024 * 1024 * 1024 * 1024 / 2
326 );
327 assert_eq!(
328 parsebytes("42.5P").unwrap(),
329 42 * 1024 * 1024 * 1024 * 1024 * 1024 + 1024 * 1024 * 1024 * 1024 * 1024 / 2
330 );
331 assert_eq!(
332 parsebytes("1.5 E ").unwrap(),
333 1024 * 1024 * 1024 * 1024 * 1024 * 1024 + 1024 * 1024 * 1024 * 1024 * 1024 * 1024 / 2
334 );
335
336 assert_eq!(parsebytes("42kb").unwrap(), 42 * 1000);
338 assert_eq!(parsebytes("42 mb").unwrap(), 42 * 1000 * 1000);
339 assert_eq!(parsebytes(" 42 gb ").unwrap(), 42 * 1000 * 1000 * 1000);
340 assert_eq!(parsebytes("42Tb").unwrap(), 42 * 1000 * 1000 * 1000 * 1000);
341 assert_eq!(
342 parsebytes("42PB").unwrap(),
343 42 * 1000 * 1000 * 1000 * 1000 * 1000
344 );
345 assert_eq!(
346 parsebytes("2 EB ").unwrap(),
347 2 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000
348 );
349
350 assert_eq!(parsebytes("42.5kb").unwrap(), 42 * 1000 + 1000 / 2);
352 assert_eq!(
353 parsebytes("42.5 mb").unwrap(),
354 42 * 1000 * 1000 + 1000 * 1000 / 2
355 );
356 assert_eq!(
357 parsebytes(" 42.5 gb ").unwrap(),
358 42 * 1000 * 1000 * 1000 + 1000 * 1000 * 1000 / 2
359 );
360 assert_eq!(
361 parsebytes("42.5Tb").unwrap(),
362 42 * 1000 * 1000 * 1000 * 1000 + 1000 * 1000 * 1000 * 1000 / 2
363 );
364 assert_eq!(
365 parsebytes("42.5PB").unwrap(),
366 42 * 1000 * 1000 * 1000 * 1000 * 1000 + 1000 * 1000 * 1000 * 1000 * 1000 / 2
367 );
368 assert_eq!(
369 parsebytes("1.5 EB ").unwrap(),
370 1000 * 1000 * 1000 * 1000 * 1000 * 1000 + 1000 * 1000 * 1000 * 1000 * 1000 * 1000 / 2
371 );
372 }
373
374 #[test]
375 fn test_hhmmss() {
376 assert_eq!(Duration::from_secs(0).hhmmss(), "00h:00m:00s");
377 assert_eq!(
378 Duration::from_secs((2 * 60 * 60) + (3 * 60) + 4).hhmmss(),
379 "02h:03m:04s"
380 );
381 assert_eq!(
382 Duration::from_secs((23 * 60 * 60) + (59 * 60) + 59).hhmmss(),
383 "23h:59m:59s"
384 );
385 assert_eq!(
386 Duration::from_secs((99 * 60 * 60) + (59 * 60) + 59).hhmmss(),
387 "99h:59m:59s"
388 );
389 assert_eq!(
390 Duration::from_secs((99 * 60 * 60) + (59 * 60) + 59 + 1).hhmmss(),
391 ">99h:59m:59s"
392 );
393 }
394
395 #[test]
396 fn test_fold() {
397 assert_eq!(fold(&[0x55, 0x55, 0xAA, 0xAA], 2), [0xFF, 0xFF]);
398 assert_eq!(fold(&[0x55, 0x55, 0x55, 0x55], 2), [0x00, 0x00]);
399 assert_eq!(fold(&[0x55, 0x55, 0xAA, 0x55], 2), [0xFF, 0x00]);
400 assert_eq!(fold(&[0x55, 0x55, 0x55, 0xAA], 2), [0x00, 0xFF]);
401 assert_eq!(
402 fold(&[0x98, 0xB1, 0x5B, 0x47, 0x8F, 0xF7, 0x9C, 0x6F], 3),
403 [0x43, 0x51, 0xAC]
404 );
405 assert_eq!(fold(&[0x12, 0x34, 0x56, 0x78], 4), [0x12, 0x34, 0x56, 0x78]);
406 assert_eq!(
407 fold(&[0x12, 0x34, 0x56, 0x78], 6),
408 [0x12, 0x34, 0x56, 0x78, 0x00, 0x00]
409 );
410 assert_eq!(fold(&[0x12, 0x34, 0x56, 0x78], 0), []);
411 }
412}
413
414