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