use std::time::Duration;
const NUM_MILLIS_PER_SECOND: u64 = 1000;
const NUM_MICROS_PER_MILLI: u64 = 1000;
const NUM_MICROS_PER_SECOND: u64 = NUM_MICROS_PER_MILLI * NUM_MILLIS_PER_SECOND;
const NUM_NANOS_PER_SECOND: u64 = 1000 * NUM_MICROS_PER_SECOND;
#[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy)]
pub struct Bandwidth {
bits_per_second: u64,
}
impl std::ops::Mul<f64> for Bandwidth {
type Output = Bandwidth;
fn mul(self, rhs: f64) -> Self::Output {
Bandwidth {
bits_per_second: (self.bits_per_second as f64 * rhs).round() as u64,
}
}
}
impl std::ops::Mul<f32> for Bandwidth {
type Output = Bandwidth;
fn mul(self, rhs: f32) -> Self::Output {
self * rhs as f64
}
}
impl std::ops::Sub<Bandwidth> for Bandwidth {
type Output = Option<Bandwidth>;
fn sub(self, rhs: Bandwidth) -> Self::Output {
self.bits_per_second
.checked_sub(rhs.bits_per_second)
.map(|bps| Bandwidth {
bits_per_second: bps,
})
}
}
impl std::ops::Add<Bandwidth> for Bandwidth {
type Output = Bandwidth;
fn add(self, rhs: Bandwidth) -> Self::Output {
Bandwidth {
bits_per_second: self.bits_per_second.add(rhs.bits_per_second),
}
}
}
impl std::ops::Mul<Duration> for Bandwidth {
type Output = u64;
fn mul(self, rhs: Duration) -> Self::Output {
self.to_bytes_per_period(rhs)
}
}
impl Bandwidth {
pub const fn from_bytes_and_time_delta(
bytes: usize, time_delta: Duration,
) -> Self {
if bytes == 0 {
return Bandwidth { bits_per_second: 0 };
}
let mut nanos = time_delta.as_nanos() as u64;
if nanos == 0 {
nanos = 1;
}
let num_nano_bits = 8 * bytes as u64 * NUM_NANOS_PER_SECOND;
if num_nano_bits < nanos {
return Bandwidth { bits_per_second: 1 };
}
Bandwidth {
bits_per_second: num_nano_bits / nanos,
}
}
#[allow(dead_code)]
pub const fn from_bytes_per_second(bytes_per_second: u64) -> Self {
Bandwidth {
bits_per_second: bytes_per_second * 8,
}
}
#[allow(dead_code)]
pub const fn to_bits_per_second(self) -> u64 {
self.bits_per_second
}
pub const fn to_bytes_per_second(self) -> u64 {
self.bits_per_second / 8
}
pub const fn from_kbits_per_second(k_bits_per_second: u64) -> Self {
Bandwidth {
bits_per_second: k_bits_per_second * 1_000,
}
}
#[allow(dead_code)]
pub const fn from_mbits_per_second(m_bits_per_second: u64) -> Self {
Bandwidth::from_kbits_per_second(m_bits_per_second * 1_000)
}
pub const fn infinite() -> Self {
Bandwidth {
bits_per_second: u64::MAX,
}
}
pub const fn zero() -> Self {
Bandwidth { bits_per_second: 0 }
}
pub fn transfer_time(&self, bytes: u64) -> Duration {
if self.bits_per_second == u64::MAX {
return Duration::ZERO;
}
if self.bits_per_second == 0 {
return Duration::ZERO;
}
if let Some(nanos) = bytes.checked_mul(8 * NUM_NANOS_PER_SECOND) {
return Duration::from_nanos(nanos / self.bits_per_second);
}
let nanos = (bytes as u128) * (8 * NUM_NANOS_PER_SECOND) as u128;
let nanos = nanos / (self.bits_per_second as u128);
Duration::from_nanos(nanos.min(u64::MAX as u128) as u64)
}
pub fn to_bytes_per_period(self, time_period: Duration) -> u64 {
if self.bits_per_second == u64::MAX {
if time_period != Duration::ZERO {
return u64::MAX;
} else {
return 0;
}
}
if let Ok(time_nanos) = u64::try_from(time_period.as_nanos()) {
if let Some(bits) = self.bits_per_second.checked_mul(time_nanos) {
return bits / (8 * NUM_NANOS_PER_SECOND);
}
}
let time_nanos = time_period.as_nanos();
let bits = (self.bits_per_second as u128).saturating_mul(time_nanos);
let bytes = bits / (8 * NUM_NANOS_PER_SECOND) as u128;
bytes.min(u64::MAX as u128) as u64
}
}
impl std::fmt::Debug for Bandwidth {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.bits_per_second {
x if x < 1_000_000 => write!(f, "{:.2} Kbps", x as f64 / 1_000.),
x if x < 1_000_000_000 => {
write!(f, "{:.2} Mbps", x as f64 / 1_000_000.)
},
x => write!(f, "{:.2} Gbps", x as f64 / 1_000_000_000.),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructors() {
assert_eq!(Bandwidth::from_bytes_per_second(100).bits_per_second, 800);
let bw = Bandwidth::from_bytes_per_second(100);
assert_eq!(bw.to_bits_per_second(), 800);
assert_eq!(bw.to_bytes_per_second(), 100);
assert_eq!(
Bandwidth::from_kbits_per_second(100).bits_per_second,
100_000
);
assert_eq!(
Bandwidth::from_mbits_per_second(100).bits_per_second,
100_000_000
);
assert_eq!(Bandwidth::infinite().bits_per_second, u64::MAX);
assert_eq!(Bandwidth::zero().bits_per_second, 0);
}
#[test]
fn arithmetic_ops() {
let bw_1k = Bandwidth::from_kbits_per_second(1);
let bw_5k = Bandwidth::from_kbits_per_second(5);
let bw_6k = Bandwidth::from_kbits_per_second(6);
assert_eq!(bw_1k + bw_5k, bw_6k);
assert_eq!(bw_6k - bw_5k, Some(bw_1k));
assert_eq!(bw_6k - bw_6k, Some(Bandwidth::zero()));
assert_eq!(bw_1k - bw_5k, None);
assert_eq!(bw_1k * 6.0f64, bw_6k);
assert_eq!(bw_1k * 6.0f32, bw_6k);
assert_eq!(bw_5k * 0.0, Bandwidth::zero());
assert_eq!(bw_5k * 1.0, bw_5k);
assert_eq!(Bandwidth::infinite() * -1.0, Bandwidth::zero());
assert_eq!((Bandwidth::infinite() * 2.0f64).bits_per_second, u64::MAX);
assert_eq!(
(Bandwidth::infinite() * 0.5f64).bits_per_second,
u64::MAX / 2 + 1
);
}
#[test]
fn from_bytes_and_time_delta() {
assert_eq!(
Bandwidth::from_bytes_and_time_delta(10, Duration::from_millis(1000))
.bits_per_second,
80
);
assert_eq!(
Bandwidth::from_bytes_and_time_delta(10, Duration::from_millis(100))
.bits_per_second,
800
);
assert_eq!(
Bandwidth::from_bytes_and_time_delta(
100,
Duration::from_millis(1000)
)
.bits_per_second,
800
);
}
#[test]
fn transfer_time() {
let one_kbit_sec = Bandwidth::from_kbits_per_second(1);
assert_eq!(one_kbit_sec.transfer_time(0), Duration::ZERO);
assert_eq!(one_kbit_sec.transfer_time(100), Duration::from_millis(800));
}
#[test]
fn transfer_time_overflow() {
let low_bandwidth = Bandwidth::from_kbits_per_second(1);
let large_bytes = u64::MAX;
let result = low_bandwidth.transfer_time(large_bytes);
assert_eq!(result, Duration::from_nanos(u64::MAX));
let one_gbps = Bandwidth::from_mbits_per_second(1_000); let ten_gib = 10 * 1024 * 1024 * 1024;
let expected = Duration::from_nanos(85_899_345_920);
assert_eq!(one_gbps.transfer_time(ten_gib), expected);
}
#[test]
fn transfer_time_infinite() {
let inf = Bandwidth::infinite();
assert_eq!(inf.transfer_time(0), Duration::ZERO);
assert_eq!(inf.transfer_time(1), Duration::ZERO);
assert_eq!(inf.transfer_time(100), Duration::ZERO);
assert_eq!(inf.transfer_time(1024), Duration::ZERO);
assert_eq!(inf.transfer_time(1_000_000), Duration::ZERO);
assert_eq!(inf.transfer_time(u64::MAX), Duration::ZERO);
}
#[test]
fn to_bytes_per_period() {
let one_kbit_sec = Bandwidth::from_kbits_per_second(1);
assert_eq!(
one_kbit_sec.to_bytes_per_period(Duration::from_millis(10_000)),
1250
);
assert_eq!(
one_kbit_sec.to_bytes_per_period(Duration::from_millis(1000)),
125
);
assert_eq!(
one_kbit_sec.to_bytes_per_period(Duration::from_millis(100)),
12
);
assert_eq!(
one_kbit_sec.to_bytes_per_period(Duration::from_millis(10)),
1
);
assert_eq!(
one_kbit_sec.to_bytes_per_period(Duration::from_millis(1)),
0
);
assert_eq!(one_kbit_sec * Duration::from_millis(10_000), 1250);
}
#[test]
fn to_bytes_per_period_high_bandwidth() {
let ten_gbps = Bandwidth::from_mbits_per_second(10_000);
assert_eq!(
ten_gbps.to_bytes_per_period(Duration::from_secs(1)),
1_250_000_000
);
let hundred_gbps = Bandwidth::from_mbits_per_second(100_000);
assert_eq!(
hundred_gbps.to_bytes_per_period(Duration::from_millis(100)),
1_250_000_000
);
let one_tbps = Bandwidth::from_mbits_per_second(1_000_000);
assert_eq!(
one_tbps.to_bytes_per_period(Duration::from_millis(10)),
1_250_000_000
);
}
#[test]
fn to_bytes_per_period_overflow_intermediate() {
let huge_bw = Bandwidth {
bits_per_second: 10_000_000_000_000_000_000,
};
let result = huge_bw.to_bytes_per_period(Duration::from_secs(1));
assert_eq!(result, 1_250_000_000_000_000_000);
}
#[test]
fn to_bytes_per_period_saturate_very_high_bandwidth() {
let very_high_bw = Bandwidth {
bits_per_second: 1u64 << 63, };
let result = very_high_bw.to_bytes_per_period(Duration::from_secs(100));
assert_eq!(result, u64::MAX);
}
#[test]
fn to_bytes_per_period_saturate_long_period() {
let high_bw = Bandwidth {
bits_per_second: u64::MAX / 2,
};
let result = high_bw.to_bytes_per_period(Duration::from_secs(100));
assert_eq!(result, u64::MAX);
}
#[test]
fn to_bytes_per_period_large_no_saturate() {
let high_bw = Bandwidth::from_mbits_per_second(100_000); let one_hour = Duration::from_secs(3600);
let result = high_bw.to_bytes_per_period(one_hour);
assert_eq!(result, 45_000_000_000_000); }
#[test]
fn to_bytes_per_period_infinite() {
let inf = Bandwidth::infinite();
assert_eq!(inf.to_bytes_per_period(Duration::from_secs(1)), u64::MAX);
assert_eq!(inf.to_bytes_per_period(Duration::from_millis(1)), u64::MAX);
assert_eq!(inf.to_bytes_per_period(Duration::ZERO), 0);
assert_eq!(inf * Duration::from_secs(1), u64::MAX);
}
#[test]
fn to_bytes_per_period_duration_exceeds_u64_nanos() {
let bw = Bandwidth::from_mbits_per_second(1000);
let huge_duration = Duration::from_secs(20_000_000_000);
let result = bw.to_bytes_per_period(huge_duration);
assert_eq!(result, 2_500_000_000_000_000_000);
}
#[test]
fn to_bytes_per_period_u128_overflow() {
let huge_bw = Bandwidth {
bits_per_second: u64::MAX - 1,
};
let max_duration = Duration::MAX;
let result = huge_bw.to_bytes_per_period(max_duration);
assert_eq!(result, u64::MAX);
}
#[test]
fn debug() {
assert_eq!(
format!("{:?}", Bandwidth { bits_per_second: 1 }),
"0.00 Kbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 12
}),
"0.01 Kbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 123
}),
"0.12 Kbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 1234
}),
"1.23 Kbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 12345
}),
"12.35 Kbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 123456
}),
"123.46 Kbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 1234567
}),
"1.23 Mbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 12345678
}),
"12.35 Mbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 123456789
}),
"123.46 Mbps"
);
assert_eq!(
format!("{:?}", Bandwidth {
bits_per_second: 1234567890
}),
"1.23 Gbps"
);
}
}