#![no_std]
#[macro_use]
extern crate alloc;
mod byte_unit;
mod macros;
pub use crate::byte_unit::ByteUnit;
use alloc::fmt::{self, Display, Formatter};
use alloc::str::FromStr;
use alloc::string::String;
#[derive(Debug, PartialEq, Eq)]
pub enum ByteError {
ValueIncorrect(String),
UnitIncorrect(String),
}
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
pub struct Byte {
bytes: u128,
}
impl Display for Byte {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_fmt(format_args!("{}", self.bytes))
}
}
impl FromStr for Byte {
type Err = ByteError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Byte::from_str(s)
}
}
impl Byte {
#[inline]
pub fn from_unit(value: f64, unit: ByteUnit) -> Result<Byte, ByteError> {
if value < 0f64 {
return Err(ByteError::ValueIncorrect(format!(
"The value `{}` for creating a `Byte` instance is negative.",
value
)));
}
let bytes = get_bytes(value, unit);
Ok(Byte {
bytes,
})
}
#[inline]
pub fn from_bytes(bytes: u128) -> Byte {
Byte {
bytes,
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str<S: AsRef<str>>(s: S) -> Result<Byte, ByteError> {
let s = s.as_ref().trim();
let mut chars = s.chars();
let mut value = match chars.next() {
Some(c) => {
match c {
'0'..='9' => f64::from(c as u8 - b'0'),
_ => {
return Err(ByteError::ValueIncorrect(format!(
"The character {:?} is not a number.",
c
)))
}
}
}
None => return Err(ByteError::ValueIncorrect(String::from("No value."))),
};
let c = 'outer: loop {
match chars.next() {
Some(c) => {
match c {
'0'..='9' => {
value = value * 10.0 + f64::from(c as u8 - b'0');
}
'.' => {
let mut i = 0.1;
loop {
match chars.next() {
Some(c) => {
if c >= '0' && c <= '9' {
value += f64::from(c as u8 - b'0') * i;
i /= 10.0;
} else {
if (i * 10.0) as u8 == 1 {
return Err(ByteError::ValueIncorrect(format!(
"The character {:?} is not a number.",
c
)));
}
match c {
' ' => {
loop {
match chars.next() {
Some(c) => {
match c {
' ' => (),
_ => break 'outer Some(c),
}
}
None => break 'outer None,
}
}
}
_ => break 'outer Some(c),
}
}
}
None => {
if (i * 10.0) as u8 == 1 {
return Err(ByteError::ValueIncorrect(format!(
"The character {:?} is not a number.",
c
)));
}
break 'outer None;
}
}
}
}
' ' => {
loop {
match chars.next() {
Some(c) => {
match c {
' ' => (),
_ => break 'outer Some(c),
}
}
None => break 'outer None,
}
}
}
_ => break 'outer Some(c),
}
}
None => break None,
}
};
let unit = match c {
Some(c) => {
match c.to_ascii_uppercase() {
'B' => if chars.next().is_some() {
return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. No character is expected.", c)));
} else {
ByteUnit::B
}
'K' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'I' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'B' => ByteUnit::KiB,
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' is expected.", c)))
}
None => ByteUnit::KiB
}
'B' => if chars.next().is_some() {
return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. No character is expected.", c)));
} else {
ByteUnit::KB
}
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' or an 'i' is expected.", c)))
}
None => ByteUnit::KB
}
'M' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'I' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'B' => ByteUnit::MiB,
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' is expected.", c)))
}
None => ByteUnit::MiB
}
'B' => if chars.next().is_some() {
return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. No character is expected.", c)));
} else {
ByteUnit::MB
},
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' or an 'i' is expected.", c)))
}
None => ByteUnit::MB
},
'G' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'I' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'B' => ByteUnit::GiB,
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' is expected.", c)))
}
None => ByteUnit::GiB
}
'B' => if chars.next().is_some() {
return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. No character is expected.", c)));
} else {
ByteUnit::GB
},
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' or an 'i' is expected.", c)))
}
None => ByteUnit::GB
},
'T' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'I' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'B' => ByteUnit::TiB,
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' is expected.", c)))
}
None => ByteUnit::TiB
}
'B' => if chars.next().is_some() {
return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. No character is expected.", c)));
} else {
ByteUnit::TB
},
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' or an 'i' is expected.", c)))
}
None => ByteUnit::TB
},
'P' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'I' => match chars.next() {
Some(c) => match c.to_ascii_uppercase() {
'B' => ByteUnit::PiB,
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' is expected.", c)))
}
None => ByteUnit::PiB
}
'B' => if chars.next().is_some() {
return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. No character is expected.", c)));
} else {
ByteUnit::PB
},
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B' or an 'i' is expected.", c)))
}
None => ByteUnit::PB
},
_ => return Err(ByteError::UnitIncorrect(format!("The character {:?} is incorrect. A 'B', a 'K', a 'M', a 'G', a 'T', a 'P' or no character is expected.", c)))
}
}
None => ByteUnit::B
};
let bytes = get_bytes(value, unit);
Ok(Byte {
bytes,
})
}
#[inline]
pub fn get_bytes(&self) -> u128 {
self.bytes
}
#[allow(clippy::unnecessary_cast)]
pub fn get_appropriate_unit(&self, binary_multiples: bool) -> AdjustedByte {
let bytes = self.bytes;
if binary_multiples {
if bytes > n_pib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_pib_bytes!() as f64,
unit: ByteUnit::PiB,
}
} else if bytes > n_tib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_tib_bytes!() as f64,
unit: ByteUnit::TiB,
}
} else if bytes > n_gib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_gib_bytes!() as f64,
unit: ByteUnit::GiB,
}
} else if bytes > n_mib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_mib_bytes!() as f64,
unit: ByteUnit::MiB,
}
} else if bytes > n_kib_bytes!() {
AdjustedByte {
value: bytes as f64 / n_kib_bytes!() as f64,
unit: ByteUnit::KiB,
}
} else {
AdjustedByte {
value: bytes as f64,
unit: ByteUnit::B,
}
}
} else if bytes > n_pb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_pb_bytes!() as f64,
unit: ByteUnit::PB,
}
} else if bytes > n_tb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_tb_bytes!() as f64,
unit: ByteUnit::TB,
}
} else if bytes > n_gb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_gb_bytes!() as f64,
unit: ByteUnit::GB,
}
} else if bytes > n_mb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_mb_bytes!() as f64,
unit: ByteUnit::MB,
}
} else if bytes > n_kb_bytes!() {
AdjustedByte {
value: bytes as f64 / n_kb_bytes!() as f64,
unit: ByteUnit::KB,
}
} else {
AdjustedByte {
value: bytes as f64,
unit: ByteUnit::B,
}
}
}
#[allow(clippy::unnecessary_cast)]
pub fn get_adjusted_unit(&self, unit: ByteUnit) -> AdjustedByte {
let bytes = self.bytes;
let value = match unit {
ByteUnit::B => bytes as f64,
ByteUnit::KB => bytes as f64 / n_kb_bytes!() as f64,
ByteUnit::KiB => bytes as f64 / n_kib_bytes!() as f64,
ByteUnit::MB => bytes as f64 / n_mb_bytes!() as f64,
ByteUnit::MiB => bytes as f64 / n_mib_bytes!() as f64,
ByteUnit::GB => bytes as f64 / n_gb_bytes!() as f64,
ByteUnit::GiB => bytes as f64 / n_gib_bytes!() as f64,
ByteUnit::TB => bytes as f64 / n_tb_bytes!() as f64,
ByteUnit::TiB => bytes as f64 / n_tib_bytes!() as f64,
ByteUnit::PB => bytes as f64 / n_pb_bytes!() as f64,
ByteUnit::PiB => bytes as f64 / n_pib_bytes!() as f64,
};
AdjustedByte {
value,
unit,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct AdjustedByte {
value: f64,
unit: ByteUnit,
}
impl PartialEq for AdjustedByte {
#[inline]
fn eq(&self, other: &AdjustedByte) -> bool {
if self.value == other.value && self.unit == other.unit {
return true;
}
let self_value = get_bytes(self.value, self.unit);
let other_value = get_bytes(other.value, other.unit);
self_value == other_value
}
}
impl Eq for AdjustedByte {}
impl AdjustedByte {
#[inline]
pub fn format(&self, fractional_digits: usize) -> String {
format!("{:.*} {}", fractional_digits, self.value, self.unit)
}
#[inline]
pub fn get_value(&self) -> f64 {
self.value
}
#[inline]
pub fn get_unit(&self) -> ByteUnit {
self.unit
}
}
impl Display for AdjustedByte {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_fmt(format_args!("{:.2} ", self.value))?;
Display::fmt(&self.unit, f)
}
}
fn get_bytes(value: f64, unit: ByteUnit) -> u128 {
match unit {
ByteUnit::B => value as u128,
ByteUnit::KB => n_kb_bytes!(value, f64),
ByteUnit::KiB => n_kib_bytes!(value, f64),
ByteUnit::MB => n_mb_bytes!(value, f64),
ByteUnit::MiB => n_mib_bytes!(value, f64),
ByteUnit::GB => n_gb_bytes!(value, f64),
ByteUnit::GiB => n_gib_bytes!(value, f64),
ByteUnit::TB => n_tb_bytes!(value, f64),
ByteUnit::TiB => n_gib_bytes!(value, f64),
ByteUnit::PB => n_pb_bytes!(value, f64),
ByteUnit::PiB => n_pib_bytes!(value, f64),
}
}