pub fn format_duration(duration: u64) -> String {
const NANOS_PER_SEC: u64 = 1_000_000_000;
const NANOS_PER_MILLI: u64 = 1_000_000;
let total_nanos = duration;
if total_nanos < 10_000 {
format!("{}ns", total_nanos)
} else if total_nanos <= NANOS_PER_SEC {
let millis = duration as f64 / NANOS_PER_MILLI as f64;
format!("{:.4}ms", millis)
} else {
let seconds = duration as f64 / NANOS_PER_SEC as f64;
format!("{}s", seconds)
}
}
pub fn format_with_underscores(number: u64) -> String {
let num_str = number.to_string();
let mut result = String::new();
let chars: Vec<_> = num_str.chars().rev().collect();
for (i, char) in chars.iter().enumerate() {
if i % 3 == 0 && i != 0 {
result.push('_');
}
result.push(*char);
}
result.chars().rev().collect()
}
pub fn format_with_underscores_f64(n: f64) -> String {
let integer_part = n.trunc() as u64;
let decimal_part = n.fract();
let mut int_with_underscores = integer_part
.to_string()
.chars()
.rev()
.collect::<Vec<_>>()
.chunks(3)
.map(|chunk| chunk.iter().collect::<String>())
.collect::<Vec<_>>()
.join("_")
.chars()
.rev()
.collect::<String>();
if n >= 1000.0 {
return int_with_underscores;
}
let formatted_decimal = if n < 0.01 {
format!("{:.5}", decimal_part)
} else {
format!("{:.2}", decimal_part)
};
if decimal_part != 0.0 {
int_with_underscores.push_str(&formatted_decimal[1..]);
}
int_with_underscores
}
pub const KB: u64 = 1_000;
static UNITS: &str = "KMGTPE";
static LN_KB: f64 = 6.931471806;
pub fn bytes_to_string(bytes: u64) -> String {
let unit = KB;
let unit_base = LN_KB;
let unit_prefix = UNITS.as_bytes();
let unit_suffix = "B";
if bytes < unit {
format!("{} B", bytes)
} else {
let size = bytes as f64;
let exp = match (size.ln() / unit_base) as usize {
0 => 1,
e => e,
};
format!(
"{:.1} {}{}",
(size / unit.pow(exp as u32) as f64),
unit_prefix[exp - 1] as char,
unit_suffix
)
}
}
pub fn format_duration_or_throughput(
duration_ns: u64,
input_size_in_bytes: Option<usize>,
) -> String {
if let Some(input_size_in_bytes) = input_size_in_bytes {
let mut duration_ns: f64 = duration_ns as f64;
let unit = unit_per_second(input_size_in_bytes, &mut duration_ns);
format!("{:>6} {}", format_float(duration_ns), unit)
} else {
format_duration(duration_ns).to_string()
}
}
pub fn format_float(n: f64) -> String {
if n < 10.0 {
format!("{:.4}", n)
} else if n < 100.0 {
format!("{:.3}", n)
} else if n < 1000.0 {
format!("{:.2}", n)
} else if n < 10000.0 {
format!("{:.1}", n)
} else {
format!("{:.0}", n)
}
}
pub fn unit_per_second(bytes: usize, nanoseconds: &mut f64) -> &'static str {
let bytes_per_second = bytes as f64 * (1e9 / *nanoseconds);
let (denominator, unit) = if bytes_per_second < 1000.0 {
(1.0, " B/s")
} else if bytes_per_second < 1000.0 * 1000.0 {
(1000.0, "KB/s")
} else if bytes_per_second < 1000.0 * 1000.0 * 1000.0 {
(1000.0 * 1000.0, "MB/s")
} else {
(1000.0 * 1000.0 * 1000.0, "GB/s")
};
let bytes_per_second = bytes as f64 * (1e9 / *nanoseconds);
*nanoseconds = bytes_per_second / denominator;
unit
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_throughput_test() {
let bytes = 1000;
let mut nanoseconds = 1e9;
assert_eq!(unit_per_second(bytes, &mut nanoseconds), "KB/s");
assert_eq!(
format_duration_or_throughput(1e9 as u64, Some(1000000)),
"1.0000 MB/s"
);
}
#[test]
fn test_format_with_underscores() {
let num = 123456.78;
let formatted = format_with_underscores_f64(num);
assert_eq!(formatted, "123_456");
let num = 987654321.0;
let formatted = format_with_underscores_f64(num);
assert_eq!(formatted, "987_654_321");
let num = 1234.56;
let formatted = format_with_underscores_f64(num);
assert_eq!(formatted, "1_234");
let num = 12345678901234.56;
let formatted = format_with_underscores_f64(num);
assert_eq!(formatted, "12_345_678_901_234");
let num = 0.78;
let formatted = format_with_underscores_f64(num);
assert_eq!(formatted, "0.78");
let num = 0.0;
let formatted = format_with_underscores_f64(num);
assert_eq!(formatted, "0");
let num = 1000.0;
let formatted = format_with_underscores_f64(num);
assert_eq!(formatted, "1_000");
}
}