mod parse;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
use std::fmt::{Debug, Display, Formatter, Result};
use std::ops::{Add, AddAssign, Mul, MulAssign};
pub const B: u64 = 1;
pub const KB: u64 = 1_000;
pub const MB: u64 = 1_000_000;
pub const GB: u64 = 1_000_000_000;
pub const TB: u64 = 1_000_000_000_000;
pub const PB: u64 = 1_000_000_000_000_000;
pub const KIB: u64 = 1_024;
pub const MIB: u64 = 1_048_576;
pub const GIB: u64 = 1_073_741_824;
pub const TIB: u64 = 1_099_511_627_776;
pub const PIB: u64 = 1_125_899_906_842_624;
static UNITS: &str = "KMGTPE";
static UNITS_SI: &str = "kMGTPE";
static LN_KB: f64 = 6.931471806; static LN_KIB: f64 = 6.907755279;
pub fn kb<V: Into<u64>>(size: V) -> u64 {
size.into() * KB
}
pub fn kib<V: Into<u64>>(size: V) -> u64 {
size.into() * KIB
}
pub fn mb<V: Into<u64>>(size: V) -> u64 {
size.into() * MB
}
pub fn mib<V: Into<u64>>(size: V) -> u64 {
size.into() * MIB
}
pub fn gb<V: Into<u64>>(size: V) -> u64 {
size.into() * GB
}
pub fn gib<V: Into<u64>>(size: V) -> u64 {
size.into() * GIB
}
pub fn tb<V: Into<u64>>(size: V) -> u64 {
size.into() * TB
}
pub fn tib<V: Into<u64>>(size: V) -> u64 {
size.into() * TIB
}
pub fn pb<V: Into<u64>>(size: V) -> u64 {
size.into() * PB
}
pub fn pib<V: Into<u64>>(size: V) -> u64 {
size.into() * PIB
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[allow(warnings)]
pub struct readable_byte(pub u64);
impl readable_byte {
#[inline(always)]
pub const fn b(size: u64) -> readable_byte {
readable_byte(size)
}
#[inline(always)]
pub const fn kb(size: u64) -> readable_byte {
readable_byte(size * KB)
}
#[inline(always)]
pub const fn kib(size: u64) -> readable_byte {
readable_byte(size * KIB)
}
#[inline(always)]
pub const fn mb(size: u64) -> readable_byte {
readable_byte(size * MB)
}
#[inline(always)]
pub const fn mib(size: u64) -> readable_byte {
readable_byte(size * MIB)
}
#[inline(always)]
pub const fn gb(size: u64) -> readable_byte {
readable_byte(size * GB)
}
#[inline(always)]
pub const fn gib(size: u64) -> readable_byte {
readable_byte(size * GIB)
}
#[inline(always)]
pub const fn tb(size: u64) -> readable_byte {
readable_byte(size * TB)
}
#[inline(always)]
pub const fn tib(size: u64) -> readable_byte {
readable_byte(size * TIB)
}
#[inline(always)]
pub const fn pb(size: u64) -> readable_byte {
readable_byte(size * PB)
}
#[inline(always)]
pub const fn pib(size: u64) -> readable_byte {
readable_byte(size * PIB)
}
#[inline(always)]
pub fn as_u64(&self) -> u64 {
self.0
}
#[inline(always)]
pub fn to_string_as(&self, si_unit: bool) -> String {
to_string_u64(self.0, si_unit)
}
}
#[inline(always)]
pub fn to_string_u64(bytes: u64, si_prefix: bool) -> String {
let unit = if si_prefix { KIB } else { KB };
let unit_base = if si_prefix { LN_KIB } else { LN_KB };
let unit_prefix = if si_prefix {
UNITS_SI.as_bytes()
} else {
UNITS.as_bytes()
};
let unit_suffix = if si_prefix { "iB" } else { "B" };
if bytes < unit {
format!("{} B", bytes)
} else {
let size = bytes as f64;
let exp = match (size.ln() / unit_base) as usize {
e if e == 0 => 1,
e => e,
};
format!(
"{:.1} {}{}",
(size / unit.pow(exp as u32) as f64),
unit_prefix[exp - 1] as char,
unit_suffix
)
}
}
impl Display for readable_byte {
fn fmt(&self, f: &mut Formatter) -> Result {
f.pad(&to_string_u64(self.0, false))
}
}
impl Debug for readable_byte {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", self)
}
}
macro_rules! commutative_op {
($t:ty) => {
impl Add<readable_byte> for $t {
type Output = readable_byte;
#[inline(always)]
fn add(self, rhs: readable_byte) -> readable_byte {
readable_byte(rhs.0 + (self as u64))
}
}
impl Mul<readable_byte> for $t {
type Output = readable_byte;
#[inline(always)]
fn mul(self, rhs: readable_byte) -> readable_byte {
readable_byte(rhs.0 * (self as u64))
}
}
};
}
commutative_op!(u64);
commutative_op!(u32);
commutative_op!(u16);
commutative_op!(u8);
impl Add<readable_byte> for readable_byte {
type Output = readable_byte;
#[inline(always)]
fn add(self, rhs: readable_byte) -> readable_byte {
readable_byte(self.0 + rhs.0)
}
}
impl AddAssign<readable_byte> for readable_byte {
#[inline(always)]
fn add_assign(&mut self, rhs: readable_byte) {
self.0 += rhs.0
}
}
impl<T> Add<T> for readable_byte
where
T: Into<u64>,
{
type Output = readable_byte;
#[inline(always)]
fn add(self, rhs: T) -> readable_byte {
readable_byte(self.0 + (rhs.into() as u64))
}
}
impl<T> AddAssign<T> for readable_byte
where
T: Into<u64>,
{
#[inline(always)]
fn add_assign(&mut self, rhs: T) {
self.0 += rhs.into() as u64;
}
}
impl<T> std::ops::Sub<T> for readable_byte
where
T: Into<u64>,
{
type Output = readable_byte;
#[inline(always)]
fn sub(self, rhs: T) -> readable_byte {
readable_byte(self.0 - (rhs.into() as u64))
}
}
impl<T> std::ops::SubAssign<T> for readable_byte
where
T: Into<u64>,
{
#[inline(always)]
fn sub_assign(&mut self, rhs: T) {
self.0 -= rhs.into() as u64;
}
}
impl<T> Mul<T> for readable_byte
where
T: Into<u64>,
{
type Output = readable_byte;
#[inline(always)]
fn mul(self, rhs: T) -> readable_byte {
readable_byte(self.0 * (rhs.into() as u64))
}
}
impl<T> MulAssign<T> for readable_byte
where
T: Into<u64>,
{
#[inline(always)]
fn mul_assign(&mut self, rhs: T) {
self.0 *= rhs.into() as u64;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arithmetic_op() {
let mut x = readable_byte::mb(1);
let y = readable_byte::kb(100);
assert_eq!((x + y).as_u64(), 1_100_000u64);
assert_eq!((x + (100 * 1000) as u64).as_u64(), 1_100_000);
assert_eq!((x * 2u64).as_u64(), 2_000_000);
x += y;
assert_eq!(x.as_u64(), 1_100_000);
x *= 2u64;
assert_eq!(x.as_u64(), 2_200_000);
}
#[test]
fn test_arithmetic_primitives() {
let mut x = readable_byte::mb(1);
assert_eq!((x + MB as u64).as_u64(), 2_000_000);
assert_eq!((x + MB as u32).as_u64(), 2_000_000);
assert_eq!((x + KB as u16).as_u64(), 1_001_000);
assert_eq!((x + B as u8).as_u64(), 1_000_001);
x += MB as u64;
x += MB as u32;
x += 10 as u16;
x += 1 as u8;
assert_eq!(x.as_u64(), 3_000_011);
}
#[test]
fn test_comparison() {
assert!(readable_byte::mb(1) == readable_byte::kb(1000));
assert!(readable_byte::mib(1) == readable_byte::kib(1024));
assert!(readable_byte::mb(1) != readable_byte::kib(1024));
assert!(readable_byte::mb(1) < readable_byte::kib(1024));
assert!(readable_byte::b(0) < readable_byte::tib(1));
}
fn assert_display(expected: &str, b: readable_byte) {
assert_eq!(expected, format!("{}", b));
}
#[test]
fn test_display() {
assert_display("215 B", readable_byte::b(215));
assert_display("1.0 KB", readable_byte::kb(1));
assert_display("301.0 KB", readable_byte::kb(301));
assert_display("419.0 MB", readable_byte::mb(419));
assert_display("518.0 GB", readable_byte::gb(518));
assert_display("815.0 TB", readable_byte::tb(815));
assert_display("609.0 PB", readable_byte::pb(609));
}
#[test]
fn test_display_alignment() {
assert_eq!("|357 B |", format!("|{:10}|", readable_byte(357)));
assert_eq!("| 357 B|", format!("|{:>10}|", readable_byte(357)));
assert_eq!("|357 B |", format!("|{:<10}|", readable_byte(357)));
assert_eq!("| 357 B |", format!("|{:^10}|", readable_byte(357)));
assert_eq!("|-----357 B|", format!("|{:->10}|", readable_byte(357)));
assert_eq!("|357 B-----|", format!("|{:-<10}|", readable_byte(357)));
assert_eq!("|--357 B---|", format!("|{:-^10}|", readable_byte(357)));
}
fn assert_to_string_u64(expected: &str, b: readable_byte, si: bool) {
assert_eq!(expected.to_string(), b.to_string_as(si));
}
#[test]
fn test_to_string_as() {
assert_to_string_u64("215 B", readable_byte::b(215), true);
assert_to_string_u64("215 B", readable_byte::b(215), false);
assert_to_string_u64("1.0 kiB", readable_byte::kib(1), true);
assert_to_string_u64("1.0 KB", readable_byte::kib(1), false);
assert_to_string_u64("293.9 kiB", readable_byte::kb(301), true);
assert_to_string_u64("301.0 KB", readable_byte::kb(301), false);
assert_to_string_u64("1.0 MiB", readable_byte::mib(1), true);
assert_to_string_u64("1048.6 KB", readable_byte::mib(1), false);
assert_to_string_u64("1.9 GiB", readable_byte::mib(1907), true);
assert_to_string_u64("2.0 GB", readable_byte::mib(1908), false);
assert_to_string_u64("399.6 MiB", readable_byte::mb(419), true);
assert_to_string_u64("419.0 MB", readable_byte::mb(419), false);
assert_to_string_u64("482.4 GiB", readable_byte::gb(518), true);
assert_to_string_u64("518.0 GB", readable_byte::gb(518), false);
assert_to_string_u64("741.2 TiB", readable_byte::tb(815), true);
assert_to_string_u64("815.0 TB", readable_byte::tb(815), false);
assert_to_string_u64("540.9 PiB", readable_byte::pb(609), true);
assert_to_string_u64("609.0 PB", readable_byte::pb(609), false);
}
#[test]
fn test_default() {
assert_eq!(readable_byte::b(0), readable_byte::default());
}
#[test]
fn test_to_string_u64() {
assert_to_string_u64("88.0 PB", readable_byte::pb(88), false);
}
}