byte_unit/byte/
decimal.rs

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