Skip to main content

byte_unit/byte/
decimal.rs

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