use std::fmt::{Display,Formatter,Result};
use std::ops::{Add,Sub,Mul,Div};
pub static B: usize = 1;
pub static KB: usize = 1000;
pub static MB: usize = 1000000;
pub static GB: usize = 1000000000;
pub static TB: usize = 1000000000000;
pub static PB: usize = 1000000000000000;
pub static KIB: usize = 1024;
pub static MIB: usize = 1048576;
pub static GIB: usize = 1073741824;
pub static TIB: usize = 1099511627776;
pub static PIB: usize = 1125899906842624;
static UNITS: &'static str = "KMGTPE";
static UNITS_SI: &'static str = "kMGTPE";
static LN_KB: f64 = 6.931471806; static LN_KIB: f64 = 6.907755279;
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct ByteSize {
size: usize
}
impl ByteSize {
#[inline(always)]
pub fn b(size: usize) -> ByteSize {
ByteSize {size: size}
}
#[inline(always)]
pub fn kb(size: usize) -> ByteSize {
ByteSize {size: size * KB}
}
#[inline(always)]
pub fn kib(size: usize) -> ByteSize {
ByteSize {size: size * KIB}
}
#[inline(always)]
pub fn mb(size: usize) -> ByteSize {
ByteSize {size: size * MB}
}
#[inline(always)]
pub fn mib(size: usize) -> ByteSize {
ByteSize {size: size * MIB}
}
#[inline(always)]
pub fn gb(size: usize) -> ByteSize {
ByteSize {size: size * GB}
}
#[inline(always)]
pub fn gib(size: usize) -> ByteSize {
ByteSize {size: size * GIB}
}
#[inline(always)]
pub fn tb(size: usize) -> ByteSize {
ByteSize {size: size * TB}
}
#[inline(always)]
pub fn tib(size: usize) -> ByteSize {
ByteSize {size: size * TIB}
}
#[inline(always)]
pub fn pb(size: usize) -> ByteSize {
ByteSize {size: size * PB}
}
#[inline(always)]
pub fn pib(size: usize) -> ByteSize {
ByteSize {size: size * PIB}
}
#[inline(always)]
pub fn as_usize(&self) -> usize {
self.size
}
pub fn to_string(&self, si: bool) -> String {
let unit = if si { KIB } else { KB };
let unit_base = if si { LN_KIB } else { LN_KB };
let unit_prefix = if si { UNITS_SI.as_bytes() } else { UNITS.as_bytes() };
let unit_suffix = if si { "iB" } else { "B" };
if self.size < unit {
format!("{} B", self.size)
} else {
let exp = match ((self.size as f64).ln() / unit_base) as usize {
e if e == 0 => 1,
e => e
};
format!("{} {}{}", (self.size / unit.pow(exp as u32)),
unit_prefix[exp - 1] as char, unit_suffix)
}
}
}
impl Display for ByteSize {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", self.to_string(false))
}
}
impl Add<usize> for ByteSize {
type Output = ByteSize;
#[inline(always)]
fn add(self, rhs: usize) -> ByteSize {
ByteSize {size: (self.size + rhs)}
}
}
impl Add<ByteSize> for ByteSize {
type Output = ByteSize;
#[inline(always)]
fn add(self, rhs: ByteSize) -> ByteSize {
ByteSize {size: (self.size + rhs.size)}
}
}
impl Sub<usize> for ByteSize {
type Output = ByteSize;
#[inline(always)]
fn sub(self, rhs: usize) -> ByteSize {
ByteSize {size: (self.size - rhs)}
}
}
impl Sub<ByteSize> for ByteSize {
type Output = ByteSize;
#[inline(always)]
fn sub(self, rhs: ByteSize) -> ByteSize {
ByteSize {size: (self.size - rhs.size)}
}
}
impl Mul<usize> for ByteSize {
type Output = ByteSize;
#[inline(always)]
fn mul(self, rhs: usize) -> ByteSize {
ByteSize {size: (self.size * rhs)}
}
}
impl Div<usize> for ByteSize {
type Output = ByteSize;
#[inline(always)]
fn div(self, rhs: usize) -> ByteSize {
ByteSize {size: (self.size / rhs)}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arithmetic() {
let x = ByteSize::mb(1);
let y = ByteSize::kb(100);
assert_eq!(
(x + y).as_usize(),
1100000
);
assert_eq!(
(x + (100*1000)).as_usize(),
1100000
);
assert_eq!(
(x - y).as_usize(),
900000
);
assert_eq!(
(x - (100*1000)).as_usize(),
900000
);
}
#[test]
fn test_comparison() {
assert!(ByteSize::mb(1) == ByteSize::kb(1000));
assert!(ByteSize::mib(1) == ByteSize::kib(1024));
assert!(ByteSize::mb(1) != ByteSize::kib(1024));
assert!(ByteSize::mb(1) < ByteSize::kib(1024));
assert!(ByteSize::b(0) < ByteSize::tib(1));
}
fn assert_display(expected: &str, b: ByteSize) {
assert_eq!(expected, format!("{}", b));
}
#[test]
fn test_display() {
assert_display("215 B", ByteSize::b(215));
assert_display("1 KB", ByteSize::kb(1));
assert_display("301 KB", ByteSize::kb(301));
assert_display("419 MB", ByteSize::mb(419));
assert_display("518 GB", ByteSize::gb(518));
assert_display("815 TB", ByteSize::tb(815));
assert_display("609 PB", ByteSize::pb(609));
}
fn assert_to_string(expected: &str, b: ByteSize, si: bool) {
assert_eq!(expected.to_string(), b.to_string(si));
}
#[test]
fn test_to_string() {
assert_to_string("215 B", ByteSize::b(215), true);
assert_to_string("215 B", ByteSize::b(215), false);
assert_to_string("1 kiB", ByteSize::kib(1), true);
assert_to_string("1 KB", ByteSize::kib(1), false);
assert_to_string("293 kiB", ByteSize::kb(301), true);
assert_to_string("301 KB", ByteSize::kb(301), false);
assert_to_string("1 MiB", ByteSize::mib(1), true);
assert_to_string("1048 KB", ByteSize::mib(1), false);
assert_to_string("399 MiB", ByteSize::mb(419), true);
assert_to_string("419 MB", ByteSize::mb(419), false);
assert_to_string("482 GiB", ByteSize::gb(518), true);
assert_to_string("518 GB", ByteSize::gb(518), false);
assert_to_string("741 TiB", ByteSize::tb(815), true);
assert_to_string("815 TB", ByteSize::tb(815), false);
assert_to_string("540 PiB", ByteSize::pb(609), true);
assert_to_string("609 PB", ByteSize::pb(609), false);
}
}