human_bandwidth/
binary_system.rs

1//! Module to allow the display of bandwidth in
2//! [binary prefix system](https://en.wikipedia.org/wiki/Binary_prefix)
3//!
4//! # Conversion
5//!
6//! Unlike the international system, the binary system uses powers of 2 instead of powers of 10.
7//! More over the base unit here is [Byte](https://en.wikipedia.org/wiki/Byte) (or octet)
8//! per second and not bit per second, for reminder 1 Byte = 8 bits.
9//!
10//! Examples:
11//!
12//! * `1B/s` is equal to `8bps`
13//! * `1kiB/s` is equal to `8.192kbps`
14//! * `1MiBps` is equal to `8.388_608kbps`
15//!
16//! # Example
17//!
18//! ```
19//! use bandwidth::Bandwidth;
20//! use human_bandwidth::binary_system::format_binary_bandwidth;
21//!
22//! let val = Bandwidth::new(0, 32 * 1024 * 1024);
23//! assert_eq!(format_binary_bandwidth(val).to_string(), "4MiB/s");
24//! ```
25
26use core::fmt;
27
28use bandwidth::Bandwidth;
29
30#[cfg(feature = "serde")]
31pub mod serde;
32
33use crate::{item, Error, OverflowOp, Parser};
34
35/// A wrapper type that allows you to [Display](core::fmt::Display) a [`Bandwidth`] in binary prefix system
36#[derive(Debug, Clone)]
37pub struct FormattedBinaryBandwidth(Bandwidth);
38
39impl OverflowOp for u128 {
40    fn mul(self, other: Self) -> Result<Self, Error> {
41        self.checked_mul(other).ok_or(Error::NumberOverflow)
42    }
43    fn add(self, other: Self) -> Result<Self, Error> {
44        self.checked_add(other).ok_or(Error::NumberOverflow)
45    }
46}
47
48/// Convert the fractionnal part of a binary prefix value to the right amount of Bytes per second
49///
50/// The rounding is to the nearest with ties away from 0
51fn parse_binary_fraction(fraction: u64, fraction_cnt: u32, unit: u32) -> Result<u64, Error> {
52    let rounding = 10_u128.pow(fraction_cnt) >> 1;
53    let fraction = (fraction as u128)
54        .checked_shl(10 * unit)
55        .ok_or(Error::NumberOverflow)?;
56    Ok(((fraction + rounding) / 10u128.pow(fraction_cnt)) as u64)
57}
58
59impl Parser<'_> {
60    fn parse_binary_unit(
61        &mut self,
62        n: u64,
63        fraction: u64,
64        fraction_cnt: u32,
65        start: usize,
66        end: usize,
67    ) -> Result<(), Error> {
68        let unit = match &self.src[start..end] {
69            "Bps" | "Byte/s" | "B/s" | "ops" | "o/s" => 0,
70            "kiBps" | "KiBps" | "kiByte/s" | "KiByte/s" | "kiB/s" | "KiB/s" | "kiops" | "Kiops"
71            | "kio/s" | "Kio/s" => 1,
72            "MiBps" | "miBps" | "MiByte/s" | "miByte/s" | "MiB/s" | "miB/s" | "Miops" | "miops"
73            | "Mio/s" | "mio/s" => 2,
74            "GiBps" | "giBps" | "GiByte/s" | "giByte/s" | "GiB/s" | "giB/s" | "Giops" | "giops"
75            | "Gio/s" | "gio/s" => 3,
76            "TiBps" | "tiBps" | "TiByte/s" | "tiByte/s" | "TiB/s" | "tiB/s" | "Tiops" | "tiops"
77            | "Tio/s" | "tio/s" => 4,
78            _ => {
79                return Err(Error::UnknownBinaryUnit {
80                    start,
81                    end,
82                    unit: self.src[start..end].to_string(),
83                    value: n,
84                });
85            }
86        };
87        let bps = (n as u128)
88            .checked_shl(unit * 10)
89            .ok_or(Error::NumberOverflow)? // Converting the unit to Byte per second
90            .add(parse_binary_fraction(fraction, fraction_cnt, unit)? as u128)? // Adding the fractional part
91            .mul(8)?; // Converting to bit per second
92        let (gbps, bps) = ((bps / 1_000_000_000), (bps % 1_000_000_000) as u32);
93        let gbps = if gbps > u64::MAX as u128 {
94            return Err(Error::NumberOverflow);
95        } else {
96            gbps as u64
97        };
98        let new_bandwidth = Bandwidth::new(gbps, bps);
99        self.current += new_bandwidth;
100        Ok(())
101    }
102
103    fn parse_binary(mut self) -> Result<Bandwidth, Error> {
104        let mut n = self.parse_first_char()?.ok_or(Error::Empty)?;
105        let mut decimal = false;
106        let mut fraction: u64 = 0;
107        let mut fraction_cnt: u32 = 0;
108        'outer: loop {
109            let mut off = self.off();
110            while let Some(c) = self.iter.next() {
111                match c {
112                    '0'..='9' => {
113                        if decimal {
114                            if fraction_cnt >= super::FRACTION_PART_LIMIT {
115                                continue;
116                            }
117                            fraction = fraction
118                                .checked_mul(10)
119                                .and_then(|x| x.checked_add(c as u64 - '0' as u64))
120                                .ok_or(Error::NumberOverflow)?;
121                            fraction_cnt += 1;
122                        } else {
123                            n = n
124                                .checked_mul(10)
125                                .and_then(|x| x.checked_add(c as u64 - '0' as u64))
126                                .ok_or(Error::NumberOverflow)?;
127                        }
128                    }
129                    c if c.is_whitespace() => {}
130                    '_' => {}
131                    '.' => {
132                        if decimal {
133                            return Err(Error::InvalidCharacter(off));
134                        }
135                        decimal = true;
136                    }
137                    'a'..='z' | 'A'..='Z' | '/' => {
138                        break;
139                    }
140                    _ => {
141                        return Err(Error::InvalidCharacter(off));
142                    }
143                }
144                off = self.off();
145            }
146            let start = off;
147            let mut off = self.off();
148            while let Some(c) = self.iter.next() {
149                match c {
150                    '0'..='9' => {
151                        self.parse_binary_unit(n, fraction, fraction_cnt, start, off)?;
152                        n = c as u64 - '0' as u64;
153                        fraction = 0;
154                        decimal = false;
155                        fraction_cnt = 0;
156                        continue 'outer;
157                    }
158                    c if c.is_whitespace() => break,
159                    'a'..='z' | 'A'..='Z' | '/' => {}
160                    _ => {
161                        return Err(Error::InvalidCharacter(off));
162                    }
163                }
164                off = self.off();
165            }
166            self.parse_binary_unit(n, fraction, fraction_cnt, start, off)?;
167            n = match self.parse_first_char()? {
168                Some(n) => n,
169                None => return Ok(self.current),
170            };
171            fraction = 0;
172            decimal = false;
173            fraction_cnt = 0;
174        }
175    }
176}
177
178/// Parse bandwidth object `1GiBps 12MiBps 5Bps` or `1.012000005GiBps`
179///
180/// Unlike [`parse_bandwidth`](super::parse_bandwidth), this method expect bandwidth to
181/// be written in [binary prefix system](https://en.wikipedia.org/wiki/Binary_prefix)
182///
183/// The bandwidth object is a concatenation of rate spans. Where each rate
184/// span is an number and a suffix. Supported suffixes:
185///
186/// * `Bps`, `Byte/s`, `B/s`, `ops`, 'o/s` -- Byte per second
187/// * `kiBps`, `kiByte/s`, `kiB/s`, `kiops`, 'kio/s` -- kibiByte per second
188/// * `MiBps`, `MiByte/s`, `MiB/s`, `Miops`, 'Mio/s` -- mebiByte per second
189/// * `GiBps`, `GiByte/s`, `GiB/s`, `Giops`, 'Gio/s` -- gibiByte per second
190/// * `TiBps`, `TiByte/s`, `TiB/s`, `Tiops`, 'Tio/s` -- tebiByte per second
191///
192/// While the number can be integer or decimal, the fractional part less than 1Bps will always be
193/// rounded to the closest (ties away from zero).
194///
195/// # Examples
196///
197/// ```
198/// use bandwidth::Bandwidth;
199/// use human_bandwidth::binary_system::parse_binary_bandwidth;
200///
201/// assert_eq!(parse_binary_bandwidth("9TiBps 420GiBps"), Ok(Bandwidth::new(82772, 609728512)));
202/// assert_eq!(parse_binary_bandwidth("4MiBps"), Ok(Bandwidth::new(0, 4 * 8 * 1024 * 1024)));
203/// assert_eq!(parse_binary_bandwidth("150.024kiBps"),
204///            Ok(Bandwidth::new(0, (150.024 * 1024_f64).round() as u32 * 8)));
205/// // The fractional part less than 1Bps will always be ignored
206/// assert_eq!(parse_binary_bandwidth("150.02456kiBps"),
207///            Ok(Bandwidth::new(0, (150.02456 * 1024_f64).round() as u32 * 8)));
208/// ```
209pub fn parse_binary_bandwidth(s: &str) -> Result<Bandwidth, Error> {
210    Parser::new(s).parse_binary()
211}
212
213/// Formats bandwidth into a human-readable string using the binary prefix system
214///
215/// Note: this format is NOT guaranteed to have same value when using
216/// parse_binary_bandwidth, the decimal part may varie du to the conversion
217/// between binary system and decimal system.
218/// Especially because rounding are tie to even when printing but tie to highest when reading
219///
220///
221/// By default it will format the value with the largest possible unit in decimal form.
222/// If you want to display integer values only, enable the `display-integer` feature.
223///
224/// # Examples
225///
226/// ```
227/// use bandwidth::Bandwidth;
228/// use human_bandwidth::binary_system::format_binary_bandwidth;
229///
230/// // Enabling the `display-integer` feature will display integer values only
231/// # #[cfg(feature = "display-integer")]
232/// # {
233/// let val1 = Bandwidth::new(82772, 609728512);
234/// assert_eq!(format_binary_bandwidth(val1).to_string(), "9TiB/s 420GiB/s");
235/// let val2 = Bandwidth::new(0, 32 * 1024 * 1024);
236/// assert_eq!(format_binary_bandwidth(val2).to_string(), "4MiB/s");
237/// # }
238///
239/// // Disabling the `display-integer` feature will display decimal values
240/// # #[cfg(not(feature = "display-integer"))]
241/// # {
242/// let val1 = Bandwidth::from_bps((9 * 1024 + 512) * 1024 * 1024 * 1024 * 8);
243/// assert_eq!(format_binary_bandwidth(val1).to_string(), "9.5TiB/s");
244/// let val2 = Bandwidth::new(0, 32 * 1024 * 1024);
245/// assert_eq!(format_binary_bandwidth(val2).to_string(), "4MiB/s");
246/// # }
247/// ```
248pub fn format_binary_bandwidth(val: Bandwidth) -> FormattedBinaryBandwidth {
249    FormattedBinaryBandwidth(val)
250}
251
252#[derive(Copy, Clone)]
253#[repr(usize)]
254enum LargestBinaryUnit {
255    Bps = 0,
256    KiBps = 1,
257    MiBps = 2,
258    GiBps = 3,
259    TiBps = 4,
260}
261
262impl fmt::Display for LargestBinaryUnit {
263    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264        match self {
265            LargestBinaryUnit::Bps => f.write_str("B/s"),
266            LargestBinaryUnit::KiBps => f.write_str("kiB/s"),
267            LargestBinaryUnit::MiBps => f.write_str("MiB/s"),
268            LargestBinaryUnit::GiBps => f.write_str("GiB/s"),
269            LargestBinaryUnit::TiBps => f.write_str("TiB/s"),
270        }
271    }
272}
273
274impl FormattedBinaryBandwidth {
275    /// Enabling the `display-integer` feature will display integer values only
276    ///
277    /// This method is preserved for backward compatibility and custom formatting.
278    pub fn fmt_integer(&self, f: &mut fmt::Formatter) -> fmt::Result {
279        let gbps = self.0.as_gbps();
280        let bps = self.0.subgbps_bps();
281
282        if gbps == 0 && bps == 0 {
283            f.write_str("0B/s")?;
284            return Ok(());
285        }
286
287        let total: u64 = gbps * 1_000_000_000 + bps as u64;
288        let total = (total + 4) / 8;
289
290        let tibps = (total / (1024 * 1024 * 1024 * 1024)) as u32;
291        let total = total % (1024 * 1024 * 1024 * 1024);
292
293        let gibps = (total / (1024 * 1024 * 1024)) as u32;
294        let total = total % (1024 * 1024 * 1024);
295
296        let mibps = (total / (1024 * 1024)) as u32;
297        let total = total % (1024 * 1024);
298
299        let kibps = (total / 1024) as u32;
300        let bps = (total % 1024) as u32;
301
302        let started = &mut false;
303        item(f, started, "TiB/s", tibps)?;
304        item(f, started, "GiB/s", gibps)?;
305        item(f, started, "MiB/s", mibps)?;
306        item(f, started, "kiB/s", kibps)?;
307        item(f, started, "B/s", bps)?;
308        Ok(())
309    }
310
311    /// Disabling the `display-integer` feature will display decimal values
312    ///
313    /// This method is preserved for custom formatting.
314    pub fn fmt_decimal(&self, f: &mut fmt::Formatter) -> fmt::Result {
315        let gbps = self.0.as_gbps();
316        let bps = self.0.subgbps_bps();
317
318        if gbps == 0 && bps == 0 {
319            f.write_str("0B/s")?;
320            return Ok(());
321        }
322
323        let total: u64 = gbps * 1_000_000_000 + bps as u64;
324        let total = (total + 4) / 8;
325
326        let tibps = (total / (1024 * 1024 * 1024 * 1024)) as u32;
327        let total = total % (1024 * 1024 * 1024 * 1024);
328
329        let gibps = (total / (1024 * 1024 * 1024)) as u32;
330        let total = total % (1024 * 1024 * 1024);
331
332        let mibps = (total / (1024 * 1024)) as u32;
333        let total = total % (1024 * 1024);
334
335        let kibps = (total / 1024) as u32;
336        let bps = (total % 1024) as u32;
337
338        let largest_unit = if tibps > 0 {
339            LargestBinaryUnit::TiBps
340        } else if gibps > 0 {
341            LargestBinaryUnit::GiBps
342        } else if mibps > 0 {
343            LargestBinaryUnit::MiBps
344        } else if kibps > 0 {
345            LargestBinaryUnit::KiBps
346        } else {
347            LargestBinaryUnit::Bps
348        };
349
350        let values = [bps, kibps, mibps, gibps, tibps];
351        let index = largest_unit as usize;
352
353        let mut value = values[index];
354
355        let mut reminder = 0;
356        let mut i = index;
357        while i > 0 {
358            reminder *= 1024;
359            reminder += values[i - 1] as u64;
360            i -= 1;
361        }
362        let mut zeros = index * 3;
363        let reminder = (reminder as u128) * 1000_u128.pow(index as u32);
364        let rounding = if index == 0 { 0 } else { 1 << (index * 10 - 1) };
365        let loss = reminder % (1 << (index * 10));
366        let mut reminder = (reminder + rounding) >> (index * 10);
367        if loss == rounding && reminder % 2 == 1 {
368            reminder -= 1;
369        }
370        if let Some(precision) = f.precision() {
371            // Rounding with ties to even to match the precision requested
372            let mut rounding_direction = 0;
373            while precision < zeros {
374                let loss = reminder % 10;
375                reminder /= 10;
376                match loss {
377                    0 => {
378                        // rounding_direction does not change
379                    }
380                    1..5 => {
381                        // we are smaller
382                        rounding_direction = -1;
383                    }
384                    5 => {
385                        if rounding_direction == 0 {
386                            // we are perfectly in the middle, so we round toward even
387                            if reminder % 2 == 1 {
388                                reminder += 1;
389                                rounding_direction = 1;
390                            } else {
391                                rounding_direction = -1
392                            }
393                        } else if rounding_direction == -1 {
394                            // we are already smaller than originally
395                            // so we go up
396                            reminder += 1;
397                            rounding_direction = 1;
398                        } else {
399                            // We were bigger than the original
400                            rounding_direction = -1;
401                        }
402                    }
403                    6..10 => {
404                        // we are bigger
405                        reminder += 1;
406                        rounding_direction = 1;
407                    }
408                    _ => unreachable!("The reminder of a divition by 10 is always between 0 and 9"),
409                }
410                zeros -= 1;
411            }
412            if precision == 0 && reminder > 0 {
413                value += reminder as u32;
414                reminder = 0;
415            }
416        } else if reminder != 0 {
417            while reminder % 10 == 0 {
418                reminder /= 10;
419                zeros -= 1;
420            }
421        } else {
422            zeros = 0;
423        }
424        write!(f, "{value}")?;
425        if zeros != 0 || reminder != 0 {
426            write!(f, ".{reminder:0zeros$}", zeros = zeros)?;
427        }
428        write!(f, "{}", largest_unit)
429    }
430}
431
432impl fmt::Display for FormattedBinaryBandwidth {
433    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
434        #[cfg(not(feature = "display-integer"))]
435        self.fmt_decimal(f)?;
436        #[cfg(feature = "display-integer")]
437        self.fmt_integer(f)?;
438        Ok(())
439    }
440}
441
442impl core::ops::Deref for FormattedBinaryBandwidth {
443    type Target = Bandwidth;
444
445    fn deref(&self) -> &Bandwidth {
446        &self.0
447    }
448}
449
450impl core::ops::DerefMut for FormattedBinaryBandwidth {
451    fn deref_mut(&mut self) -> &mut Bandwidth {
452        &mut self.0
453    }
454}
455
456#[cfg(test)]
457mod tests {
458    use super::*;
459    use bandwidth::Bandwidth;
460
461    fn new_bandwidth(tebi: u16, gibi: u16, mibi: u16, kibi: u16, bytes: u16) -> Bandwidth {
462        const KI_B: u64 = 1024 * 8;
463        const MI_B: u64 = 1024 * KI_B;
464        const GI_B: u64 = 1024 * MI_B;
465        const TI_B: u64 = 1024 * GI_B;
466
467        let res: u64 = bytes as u64 * 8
468            + kibi as u64 * KI_B
469            + mibi as u64 * MI_B
470            + gibi as u64 * GI_B
471            + tebi as u64 * TI_B;
472        Bandwidth::new(res / 1_000_000_000, (res % 1_000_000_000) as u32)
473    }
474
475    #[test]
476    fn test_units() {
477        assert_eq!(
478            parse_binary_bandwidth("1Bps"),
479            Ok(new_bandwidth(0, 0, 0, 0, 1))
480        );
481        assert_eq!(
482            parse_binary_bandwidth("2Byte/s"),
483            Ok(new_bandwidth(0, 0, 0, 0, 2))
484        );
485        assert_eq!(
486            parse_binary_bandwidth("15B/s"),
487            Ok(new_bandwidth(0, 0, 0, 0, 15))
488        );
489        assert_eq!(
490            parse_binary_bandwidth("21ops"),
491            Ok(new_bandwidth(0, 0, 0, 0, 21))
492        );
493        assert_eq!(
494            parse_binary_bandwidth("22o/s"),
495            Ok(new_bandwidth(0, 0, 0, 0, 22))
496        );
497        assert_eq!(
498            parse_binary_bandwidth("51kiBps"),
499            Ok(new_bandwidth(0, 0, 0, 51, 0))
500        );
501        assert_eq!(
502            parse_binary_bandwidth("79KiBps"),
503            Ok(new_bandwidth(0, 0, 0, 79, 0))
504        );
505        assert_eq!(
506            parse_binary_bandwidth("81kiByte/s"),
507            Ok(new_bandwidth(0, 0, 0, 81, 0))
508        );
509        assert_eq!(
510            parse_binary_bandwidth("100KiByte/s"),
511            Ok(new_bandwidth(0, 0, 0, 100, 0))
512        );
513        assert_eq!(
514            parse_binary_bandwidth("150kiB/s"),
515            Ok(new_bandwidth(0, 0, 0, 150, 0))
516        );
517        assert_eq!(
518            parse_binary_bandwidth("410KiB/s"),
519            Ok(new_bandwidth(0, 0, 0, 410, 0))
520        );
521        assert_eq!(
522            parse_binary_bandwidth("251kiops"),
523            Ok(new_bandwidth(0, 0, 0, 251, 0))
524        );
525        assert_eq!(
526            parse_binary_bandwidth("279Kiops"),
527            Ok(new_bandwidth(0, 0, 0, 279, 0))
528        );
529        assert_eq!(
530            parse_binary_bandwidth("250kio/s"),
531            Ok(new_bandwidth(0, 0, 0, 250, 0))
532        );
533        assert_eq!(
534            parse_binary_bandwidth("210Kio/s"),
535            Ok(new_bandwidth(0, 0, 0, 210, 0))
536        );
537        assert_eq!(
538            parse_binary_bandwidth("12MiBps"),
539            Ok(new_bandwidth(0, 0, 12, 0, 0))
540        );
541        assert_eq!(
542            parse_binary_bandwidth("16miBps"),
543            Ok(new_bandwidth(0, 0, 16, 0, 0))
544        );
545        assert_eq!(
546            parse_binary_bandwidth("24MiByte/s"),
547            Ok(new_bandwidth(0, 0, 24, 0, 0))
548        );
549        assert_eq!(
550            parse_binary_bandwidth("36miByte/s"),
551            Ok(new_bandwidth(0, 0, 36, 0, 0))
552        );
553        assert_eq!(
554            parse_binary_bandwidth("48MiB/s"),
555            Ok(new_bandwidth(0, 0, 48, 0, 0))
556        );
557        assert_eq!(
558            parse_binary_bandwidth("96miB/s"),
559            Ok(new_bandwidth(0, 0, 96, 0, 0))
560        );
561        assert_eq!(
562            parse_binary_bandwidth("212Miops"),
563            Ok(new_bandwidth(0, 0, 212, 0, 0))
564        );
565        assert_eq!(
566            parse_binary_bandwidth("216miops"),
567            Ok(new_bandwidth(0, 0, 216, 0, 0))
568        );
569        assert_eq!(
570            parse_binary_bandwidth("248Mio/s"),
571            Ok(new_bandwidth(0, 0, 248, 0, 0))
572        );
573        assert_eq!(
574            parse_binary_bandwidth("296mio/s"),
575            Ok(new_bandwidth(0, 0, 296, 0, 0))
576        );
577        assert_eq!(
578            parse_binary_bandwidth("2GiBps"),
579            Ok(new_bandwidth(0, 2, 0, 0, 0))
580        );
581        assert_eq!(
582            parse_binary_bandwidth("4giBps"),
583            Ok(new_bandwidth(0, 4, 0, 0, 0))
584        );
585        assert_eq!(
586            parse_binary_bandwidth("6GiByte/s"),
587            Ok(new_bandwidth(0, 6, 0, 0, 0))
588        );
589        assert_eq!(
590            parse_binary_bandwidth("8giByte/s"),
591            Ok(new_bandwidth(0, 8, 0, 0, 0))
592        );
593        assert_eq!(
594            parse_binary_bandwidth("16GiB/s"),
595            Ok(new_bandwidth(0, 16, 0, 0, 0))
596        );
597        assert_eq!(
598            parse_binary_bandwidth("40giB/s"),
599            Ok(new_bandwidth(0, 40, 0, 0, 0))
600        );
601        assert_eq!(
602            parse_binary_bandwidth("202Giops"),
603            Ok(new_bandwidth(0, 202, 0, 0, 0))
604        );
605        assert_eq!(
606            parse_binary_bandwidth("204giops"),
607            Ok(new_bandwidth(0, 204, 0, 0, 0))
608        );
609        assert_eq!(
610            parse_binary_bandwidth("216Gio/s"),
611            Ok(new_bandwidth(0, 216, 0, 0, 0))
612        );
613        assert_eq!(
614            parse_binary_bandwidth("240gio/s"),
615            Ok(new_bandwidth(0, 240, 0, 0, 0))
616        );
617        assert_eq!(
618            parse_binary_bandwidth("1TiBps"),
619            Ok(new_bandwidth(1, 0, 0, 0, 0))
620        );
621        assert_eq!(
622            parse_binary_bandwidth("2tiBps"),
623            Ok(new_bandwidth(2, 0, 0, 0, 0))
624        );
625        assert_eq!(
626            parse_binary_bandwidth("4TiByte/s"),
627            Ok(new_bandwidth(4, 0, 0, 0, 0))
628        );
629        assert_eq!(
630            parse_binary_bandwidth("8tiByte/s"),
631            Ok(new_bandwidth(8, 0, 0, 0, 0))
632        );
633        assert_eq!(
634            parse_binary_bandwidth("16TiB/s"),
635            Ok(new_bandwidth(16, 0, 0, 0, 0))
636        );
637        assert_eq!(
638            parse_binary_bandwidth("32tiB/s"),
639            Ok(new_bandwidth(32, 0, 0, 0, 0))
640        );
641        assert_eq!(
642            parse_binary_bandwidth("201Tiops"),
643            Ok(new_bandwidth(201, 0, 0, 0, 0))
644        );
645        assert_eq!(
646            parse_binary_bandwidth("202tiops"),
647            Ok(new_bandwidth(202, 0, 0, 0, 0))
648        );
649        assert_eq!(
650            parse_binary_bandwidth("216Tio/s"),
651            Ok(new_bandwidth(216, 0, 0, 0, 0))
652        );
653        assert_eq!(
654            parse_binary_bandwidth("232tio/s"),
655            Ok(new_bandwidth(232, 0, 0, 0, 0))
656        );
657    }
658
659    #[test]
660    fn test_decimal() {
661        assert_eq!(
662            parse_binary_bandwidth("1.5Bps"),
663            Ok(new_bandwidth(0, 0, 0, 0, 2))
664        );
665        assert_eq!(
666            parse_binary_bandwidth("2.5Byte/s"),
667            Ok(new_bandwidth(0, 0, 0, 0, 3))
668        );
669        assert_eq!(
670            parse_binary_bandwidth("15.5B/s"),
671            Ok(new_bandwidth(0, 0, 0, 0, 16))
672        );
673        assert_eq!(
674            parse_binary_bandwidth("51.6kiBps"),
675            Ok(new_bandwidth(0, 0, 0, 51, 614))
676        );
677        assert_eq!(
678            parse_binary_bandwidth("79.78KiBps"),
679            Ok(new_bandwidth(0, 0, 0, 79, 799))
680        );
681        assert_eq!(
682            parse_binary_bandwidth("81.923kiByte/s"),
683            Ok(new_bandwidth(0, 0, 0, 81, 945))
684        );
685        assert_eq!(
686            parse_binary_bandwidth("100.1234KiByte/s"),
687            Ok(new_bandwidth(0, 0, 0, 100, 126))
688        );
689        assert_eq!(
690            parse_binary_bandwidth("150.12345kiB/s"),
691            Ok(new_bandwidth(0, 0, 0, 150, 126))
692        );
693        assert_eq!(
694            parse_binary_bandwidth("410.123456KiB/s"),
695            Ok(new_bandwidth(0, 0, 0, 410, 126))
696        );
697        assert_eq!(
698            parse_binary_bandwidth("12.123MiBps"),
699            Ok(new_bandwidth(0, 0, 12, 125, 975))
700        );
701        assert_eq!(
702            parse_binary_bandwidth("16.1234miBps"),
703            Ok(new_bandwidth(0, 0, 16, 126, 370))
704        );
705        assert_eq!(
706            parse_binary_bandwidth("24.12345MiByte/s"),
707            Ok(new_bandwidth(0, 0, 24, 126, 423))
708        );
709        assert_eq!(
710            parse_binary_bandwidth("36.123456miByte/s"),
711            Ok(new_bandwidth(0, 0, 36, 126, 429))
712        );
713        assert_eq!(
714            parse_binary_bandwidth("48.123MiB/s"),
715            Ok(new_bandwidth(0, 0, 48, 125, 975))
716        );
717        assert_eq!(
718            parse_binary_bandwidth("96.1234miB/s"),
719            Ok(new_bandwidth(0, 0, 96, 126, 370))
720        );
721        assert_eq!(
722            parse_binary_bandwidth("2.123GiBps"),
723            Ok(new_bandwidth(0, 2, 125, 974, 868))
724        );
725        assert_eq!(
726            parse_binary_bandwidth("4.1234giBps"),
727            Ok(new_bandwidth(0, 4, 126, 370, 285))
728        );
729        assert_eq!(
730            parse_binary_bandwidth("6.12345GiByte/s"),
731            Ok(new_bandwidth(0, 6, 126, 422, 724))
732        );
733        assert_eq!(
734            parse_binary_bandwidth("8.123456giByte/s"),
735            Ok(new_bandwidth(0, 8, 126, 428, 1023))
736        );
737        assert_eq!(
738            parse_binary_bandwidth("16.123456789GiB/s"),
739            Ok(new_bandwidth(0, 16, 126, 429, 846))
740        );
741        assert_eq!(
742            parse_binary_bandwidth("40.12345678912giB/s"),
743            Ok(new_bandwidth(0, 40, 126, 429, 846))
744        );
745        assert_eq!(
746            parse_binary_bandwidth("1.123TiBps"),
747            Ok(new_bandwidth(1, 125, 974, 868, 360))
748        );
749        assert_eq!(
750            parse_binary_bandwidth("2.1234tiBps"),
751            Ok(new_bandwidth(2, 126, 370, 285, 84))
752        );
753        assert_eq!(
754            parse_binary_bandwidth("4.12345TiByte/s"),
755            Ok(new_bandwidth(4, 126, 422, 724, 177))
756        );
757        assert_eq!(
758            parse_binary_bandwidth("8.123456tiByte/s"),
759            Ok(new_bandwidth(8, 126, 428, 1022, 639))
760        );
761        assert_eq!(
762            parse_binary_bandwidth("16.123456789TiB/s"),
763            Ok(new_bandwidth(16, 126, 429, 845, 825))
764        );
765        assert_eq!(
766            parse_binary_bandwidth("32.12345678912tiB/s"),
767            Ok(new_bandwidth(32, 126, 429, 845, 957))
768        );
769    }
770
771    #[test]
772    fn test_combo() {
773        assert_eq!(
774            parse_binary_bandwidth("1Bps 2Byte/s 3B/s"),
775            Ok(new_bandwidth(0, 0, 0, 0, 6))
776        );
777        assert_eq!(
778            parse_binary_bandwidth("4kiBps 5KiBps 6kiByte/s"),
779            Ok(new_bandwidth(0, 0, 0, 15, 0))
780        );
781        assert_eq!(
782            parse_binary_bandwidth("7MiBps 8miBps 9MiByte/s"),
783            Ok(new_bandwidth(0, 0, 24, 0, 0))
784        );
785        assert_eq!(
786            parse_binary_bandwidth("10GiBps 11giBps 12GiByte/s"),
787            Ok(new_bandwidth(0, 33, 0, 0, 0))
788        );
789        assert_eq!(
790            parse_binary_bandwidth("13TiBps 14tiBps 15TiByte/s"),
791            Ok(new_bandwidth(42, 0, 0, 0, 0))
792        );
793        assert_eq!(
794            parse_binary_bandwidth("10GiBps 5MiBps 1B/s"),
795            Ok(new_bandwidth(0, 10, 5, 0, 1))
796        );
797        assert_eq!(
798            parse_binary_bandwidth("36MiBps 12kiBps 24Bps"),
799            Ok(new_bandwidth(0, 0, 36, 12, 24))
800        );
801    }
802
803    #[test]
804    fn test_decimal_combo() {
805        assert_eq!(
806            parse_binary_bandwidth("1.1Bps 2.2Byte/s 3.3B/s"),
807            Ok(new_bandwidth(0, 0, 0, 0, 6))
808        );
809        assert_eq!(
810            parse_binary_bandwidth("4.4kiBps 5.5KiBps 6.6kiByte/s"),
811            Ok(new_bandwidth(0, 0, 0, 16, 512))
812        );
813        assert_eq!(
814            parse_binary_bandwidth("7.7MiBps 8.8miBps 9.9MiByte/s"),
815            Ok(new_bandwidth(0, 0, 26, 409, 614))
816        );
817        assert_eq!(
818            parse_binary_bandwidth("10.10GiBps 11.11giBps 12.12GiByte/s"),
819            Ok(new_bandwidth(0, 33, 337, 942, 82))
820        );
821        assert_eq!(
822            parse_binary_bandwidth("13.13TiBps 14.14tiBps 15.15TiByte/s"),
823            Ok(new_bandwidth(42, 430, 81, 942, 82))
824        );
825        assert_eq!(
826            parse_binary_bandwidth("10.1GiBps 5.2MiBps 1.3B/s"),
827            Ok(new_bandwidth(0, 10, 107, 614, 410))
828        );
829        assert_eq!(
830            parse_binary_bandwidth("36.1MiBps 12.2kiBps 24.3Bps"),
831            Ok(new_bandwidth(0, 0, 36, 114, 639))
832        );
833    }
834
835    #[test]
836    fn test_overflow() {
837        // The overflow arrives du to the limits of u64 to read the number, not during the conversion to bandwidth
838        assert_eq!(
839            parse_binary_bandwidth("100_000_000_000_000_000_000Bps"),
840            Err(Error::NumberOverflow)
841        );
842        assert!(parse_binary_bandwidth("10_000_000_000_000_000_000Bps").is_ok());
843        assert_eq!(
844            parse_binary_bandwidth("100_000_000_000_000_000_000kiBps"),
845            Err(Error::NumberOverflow)
846        );
847        assert!(parse_binary_bandwidth("10_000_000_000_000_000_000kiBps").is_ok());
848        assert_eq!(
849            parse_binary_bandwidth("100_000_000_000_000_000_000MiBps"),
850            Err(Error::NumberOverflow)
851        );
852        assert!(parse_binary_bandwidth("10_000_000_000_000_000_000MiBps").is_ok());
853
854        // For GiBps and TiBps, the overflow arrive for smaller number du to the multiplication by 8 (for B/s to bps)
855        assert_eq!(
856            parse_binary_bandwidth("10_000_000_000_000_000_000GiBps"),
857            Err(Error::NumberOverflow)
858        );
859        assert!(parse_binary_bandwidth("1_000_000_000_000_000_000GiBps").is_ok());
860        assert_eq!(
861            parse_binary_bandwidth("10_000_000_000_000_000TiBps"),
862            Err(Error::NumberOverflow)
863        );
864        assert!(parse_binary_bandwidth("1_000_000_000_000_000TiBps").is_ok());
865    }
866
867    #[test]
868    fn test_nice_error_message() {
869        assert_eq!(
870            parse_binary_bandwidth("123").unwrap_err().to_string(),
871            "binary bandwidth unit needed, for example 123MiB/s or 123B/s"
872        );
873        assert_eq!(
874            parse_binary_bandwidth("10 GiBps 1")
875                .unwrap_err()
876                .to_string(),
877            "binary bandwidth unit needed, for example 1MiB/s or 1B/s"
878        );
879        assert_eq!(
880            parse_binary_bandwidth("10 byte/s").unwrap_err().to_string(),
881            "unknown binary bandwidth unit \"byte/s\", \
882                    supported units: B/s, kiB/s, MiB/s, GiB/s, TiB/s"
883        );
884    }
885
886    #[test]
887    fn test_formatted_bandwidth_integer() {
888        struct TestInteger(FormattedBinaryBandwidth);
889        impl From<FormattedBinaryBandwidth> for TestInteger {
890            fn from(fb: FormattedBinaryBandwidth) -> Self {
891                TestInteger(fb)
892            }
893        }
894        impl fmt::Display for TestInteger {
895            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
896                self.0.fmt_integer(f)
897            }
898        }
899        assert_eq!(
900            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 0, 0, 0))).to_string(),
901            "0B/s"
902        );
903        assert_eq!(
904            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 0, 0, 1))).to_string(),
905            "1B/s"
906        );
907        assert_eq!(
908            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 0, 0, 15))).to_string(),
909            "15B/s"
910        );
911        assert_eq!(
912            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 0, 51, 0))).to_string(),
913            "51kiB/s"
914        );
915        assert_eq!(
916            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 32, 0, 0))).to_string(),
917            "32MiB/s"
918        );
919        assert_eq!(
920            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 79, 0, 0))).to_string(),
921            "79MiB/s"
922        );
923        assert_eq!(
924            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 100, 0, 0))).to_string(),
925            "100MiB/s"
926        );
927        assert_eq!(
928            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 150, 0, 0))).to_string(),
929            "150MiB/s"
930        );
931        assert_eq!(
932            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 0, 410, 0, 0))).to_string(),
933            "410MiB/s"
934        );
935        assert_eq!(
936            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 1, 0, 0, 0))).to_string(),
937            "1GiB/s"
938        );
939        assert_eq!(
940            TestInteger::from(format_binary_bandwidth(new_bandwidth(0, 4, 500, 0, 0))).to_string(),
941            "4GiB/s 500MiB/s"
942        );
943        assert_eq!(
944            TestInteger::from(format_binary_bandwidth(new_bandwidth(9, 420, 0, 0, 0))).to_string(),
945            "9TiB/s 420GiB/s"
946        );
947    }
948
949    #[test]
950    fn test_formatted_bandwidth_decimal() {
951        struct TestDecimal(FormattedBinaryBandwidth);
952        impl From<FormattedBinaryBandwidth> for TestDecimal {
953            fn from(fb: FormattedBinaryBandwidth) -> Self {
954                TestDecimal(fb)
955            }
956        }
957        impl fmt::Display for TestDecimal {
958            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
959                self.0.fmt_decimal(f)
960            }
961        }
962        assert_eq!(
963            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 0, 0, 0))).to_string(),
964            "0B/s"
965        );
966        assert_eq!(
967            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 0, 0, 1))).to_string(),
968            "1B/s"
969        );
970        assert_eq!(
971            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 0, 0, 15))).to_string(),
972            "15B/s"
973        );
974        assert_eq!(
975            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 0, 51, 256))).to_string(),
976            "51.25kiB/s"
977        );
978        assert_eq!(
979            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 32, 256, 0))).to_string(),
980            "32.25MiB/s"
981        );
982        assert_eq!(
983            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 79, 0, 5))).to_string(),
984            "79.000005MiB/s"
985        );
986        assert_eq!(
987            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 100, 128, 7)))
988                .to_string(),
989            "100.125007MiB/s"
990        );
991        assert_eq!(
992            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 150, 0, 0))).to_string(),
993            "150MiB/s"
994        );
995        assert_eq!(
996            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 0, 410, 9, 116)))
997                .to_string(),
998            "410.0089MiB/s"
999        );
1000        assert_eq!(
1001            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 1, 0, 0, 0))).to_string(),
1002            "1GiB/s"
1003        );
1004        assert_eq!(
1005            TestDecimal::from(format_binary_bandwidth(new_bandwidth(0, 4, 512, 0, 0))).to_string(),
1006            "4.5GiB/s"
1007        );
1008        assert_eq!(
1009            TestDecimal::from(format_binary_bandwidth(new_bandwidth(8, 768, 0, 0, 0))).to_string(),
1010            "8.75TiB/s"
1011        );
1012        assert_eq!(
1013            "9.375TiB/s",
1014            TestDecimal::from(format_binary_bandwidth(new_bandwidth(9, 384, 0, 0, 0))).to_string(),
1015        );
1016    }
1017
1018    #[test]
1019    fn test_formatted_bandwidth_decimal_with_precision() {
1020        struct TestDecimal(FormattedBinaryBandwidth);
1021        impl From<FormattedBinaryBandwidth> for TestDecimal {
1022            fn from(fb: FormattedBinaryBandwidth) -> Self {
1023                TestDecimal(fb)
1024            }
1025        }
1026        impl fmt::Display for TestDecimal {
1027            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1028                self.0.fmt_decimal(f)
1029            }
1030        }
1031        let bandwidths = [
1032            (new_bandwidth(0, 0, 0, 0, 0), 0, 0, "B/s", 0),
1033            (new_bandwidth(0, 0, 0, 0, 1), 1, 0, "B/s", 0),
1034            (new_bandwidth(0, 0, 0, 0, 15), 15, 0, "B/s", 0),
1035            (new_bandwidth(0, 0, 0, 51, 256), 51, 250, "kiB/s", 3),
1036            (new_bandwidth(0, 0, 32, 256, 0), 32, 250_000, "MiB/s", 6),
1037            (new_bandwidth(0, 0, 79, 0, 5), 79, 5, "MiB/s", 6),
1038            (new_bandwidth(0, 0, 100, 128, 7), 100, 125_007, "MiB/s", 6),
1039            (new_bandwidth(0, 0, 150, 0, 0), 150, 0, "MiB/s", 6),
1040            (new_bandwidth(0, 0, 410, 9, 116), 410, 8_900, "MiB/s", 6),
1041            (new_bandwidth(0, 1, 0, 0, 0), 1, 0, "GiB/s", 9),
1042            (new_bandwidth(0, 4, 512, 0, 0), 4, 500_000_000, "GiB/s", 9),
1043            (
1044                new_bandwidth(8, 768, 0, 0, 0),
1045                8,
1046                750_000_000_000,
1047                "TiB/s",
1048                12,
1049            ),
1050            (
1051                new_bandwidth(9, 384, 0, 0, 0),
1052                9,
1053                375_000_000_000,
1054                "TiB/s",
1055                12,
1056            ),
1057        ];
1058        for precision in 0..7 {
1059            for (bandwidth, int, fract, unit, max_precision) in bandwidths.iter() {
1060                let bandwidth = TestDecimal::from(format_binary_bandwidth(*bandwidth));
1061                let pow = 10_u64.pow((max_precision - precision.min(*max_precision)) as u32);
1062                let fract = if pow != 1 {
1063                    if fract % pow > pow / 2 || fract % pow == pow / 2 && fract / pow % 2 == 1 {
1064                        fract / pow + 1
1065                    } else {
1066                        fract / pow
1067                    }
1068                } else {
1069                    *fract
1070                };
1071                if precision != 0 && *max_precision != 0 {
1072                    assert_eq!(
1073                        format!("{bandwidth:.precision$}"),
1074                        format!(
1075                            "{int}.{fract:0precision$}{unit}",
1076                            precision = precision.min(*max_precision)
1077                        )
1078                    );
1079                } else {
1080                    let int = if fract == 1 { int + 1 } else { *int };
1081                    assert_eq!(format!("{bandwidth:.precision$}"), format!("{int}{unit}"));
1082                }
1083            }
1084        }
1085    }
1086}