byte_unit/bit/
decimal.rs

1use rust_decimal::prelude::*;
2
3use super::Bit;
4use crate::{common::is_zero_remainder_decimal, Unit};
5
6/// Associated functions for building `Bit` instances using `Decimal`.
7impl Bit {
8    /// Create a new `Bit` instance from a size in bits.
9    ///
10    /// # Examples
11    ///
12    /// ```
13    /// use byte_unit::Bit;
14    /// use rust_decimal::Decimal;
15    ///
16    /// let bit = Bit::from_decimal(Decimal::from(15000000u64)).unwrap(); // 15 Mb
17    /// ```
18    ///
19    /// # Points to Note
20    ///
21    /// * If the input **size** is too large (the maximum is **10<sup>27</sup> - 1** if the `u128` feature is enabled, or **2<sup>64</sup> - 1** otherwise) or not greater than or equal to **0**, this function will return `None`.
22    /// * The fractional part will be rounded up.
23    #[inline]
24    pub fn from_decimal(size: Decimal) -> Option<Self> {
25        if size >= Decimal::ZERO {
26            #[cfg(feature = "u128")]
27            {
28                let size = size.ceil();
29
30                match size.to_u128() {
31                    Some(n) => Self::from_u128(n),
32                    None => None,
33                }
34            }
35
36            #[cfg(not(feature = "u128"))]
37            {
38                let size = size.ceil();
39
40                size.to_u64().map(Self::from_u64)
41            }
42        } else {
43            None
44        }
45    }
46}
47
48/// Associated functions for building `Bit` instances using `Decimal` (with `Unit`).
49impl Bit {
50    /// Create a new `Bit` instance from a size of bits with a unit.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use byte_unit::{Bit, Unit};
56    /// use rust_decimal::Decimal;
57    ///
58    /// let bit = Bit::from_decimal_with_unit(Decimal::from(15u64), Unit::Mbit).unwrap(); // 15 Mb
59    /// ```
60    ///
61    /// # Points to Note
62    ///
63    /// * If the calculated bit is too large or not greater than or equal to **0**, this function will return `None`.
64    /// * The calculated bit will be rounded up.
65    #[inline]
66    pub fn from_decimal_with_unit(size: Decimal, unit: Unit) -> Option<Self> {
67        let v = {
68            match unit {
69                Unit::Bit => size,
70                _ => match size.checked_mul(Decimal::from(unit.as_bits_u128())) {
71                    Some(v) => v,
72                    None => return None,
73                },
74            }
75        };
76
77        Self::from_decimal(v)
78    }
79}
80
81/// Methods for finding an unit using `Decimal`.
82impl Bit {
83    /// Find the appropriate unit and value that can be used to recover back to this `Bit` precisely.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use byte_unit::{Bit, Unit};
89    ///
90    /// let bit = Bit::from_u64(3670016);
91    ///
92    /// assert_eq!(
93    ///     (3.5f64.try_into().unwrap(), Unit::Mibit),
94    ///     bit.get_recoverable_unit(false, 3)
95    /// );
96    /// ```
97    ///
98    /// ```
99    /// use byte_unit::{Bit, Unit};
100    ///
101    /// let bit = Bit::from_u64(28000000);
102    ///
103    /// assert_eq!(
104    ///     (3.5f64.try_into().unwrap(), Unit::MB),
105    ///     bit.get_recoverable_unit(true, 3)
106    /// );
107    /// ```
108    ///
109    /// ```
110    /// use byte_unit::{Bit, Unit};
111    ///
112    /// let bit = Bit::from_u64(437500);
113    ///
114    /// assert_eq!(
115    ///     (437.5f64.try_into().unwrap(), Unit::Kbit),
116    ///     bit.get_recoverable_unit(false, 3)
117    /// );
118    /// ```
119    ///
120    /// # Points to Note
121    ///
122    /// * `precision` should be smaller or equal to `26` if the `u128` feature is enabled, otherwise `19`. The typical `precision` is `3`.
123    #[inline]
124    pub fn get_recoverable_unit(
125        self,
126        allow_in_bytes: bool,
127        mut precision: usize,
128    ) -> (Decimal, Unit) {
129        let bits_v = self.as_u128();
130        let bits_vd = Decimal::from(bits_v);
131
132        let a = if allow_in_bytes { Unit::get_multiples() } else { Unit::get_multiples_bits() };
133        let mut i = a.len() - 1;
134
135        if precision >= 28 {
136            precision = 28;
137        }
138
139        loop {
140            let unit = a[i];
141
142            let unit_v = unit.as_bits_u128();
143
144            if bits_v >= unit_v {
145                let unit_vd = Decimal::from(unit_v);
146
147                if let Some(quotient) = is_zero_remainder_decimal(bits_vd, unit_vd, precision) {
148                    return (quotient, unit);
149                }
150            }
151
152            if i == 0 {
153                break;
154            }
155
156            i -= 1;
157        }
158
159        (bits_vd, Unit::Bit)
160    }
161}