100 lines
2.8 KiB
Rust
100 lines
2.8 KiB
Rust
use core::fmt;
|
|
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
pub struct FormatSizeOptions {}
|
|
|
|
pub struct FormatSize(u64, FormatSizeOptions);
|
|
|
|
pub struct CharsWriter<'a> {
|
|
buffer: &'a mut [u8],
|
|
pos: usize,
|
|
}
|
|
|
|
impl<'a> CharsWriter<'a> {
|
|
pub const fn new(buffer: &'a mut [u8]) -> Self {
|
|
Self { buffer, pos: 0 }
|
|
}
|
|
|
|
pub fn push_char(&mut self, ch: char) -> bool {
|
|
let w = ch.len_utf8();
|
|
if self.pos + w < self.buffer.len() {
|
|
ch.encode_utf8(&mut self.buffer[self.pos..]);
|
|
self.pos += w;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn as_str(&self) -> &str {
|
|
// Safe, the implementation only allows full valid chars to be inserted
|
|
unsafe { core::str::from_utf8_unchecked(&self.buffer[..self.pos]) }
|
|
}
|
|
}
|
|
|
|
impl fmt::Write for CharsWriter<'_> {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
for ch in s.chars() {
|
|
if !self.push_char(ch) {
|
|
break;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl FormatSize {
|
|
pub fn default(size: u64) -> Self {
|
|
Self(size, FormatSizeOptions::default())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for FormatSize {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
use fmt::Write;
|
|
|
|
let mut buffer = [0; 128];
|
|
let mut writer = CharsWriter::new(&mut buffer);
|
|
let ilog2 = self.0.checked_ilog2().unwrap_or(0);
|
|
let (dot, div, suffix) = match ilog2 / 10 {
|
|
0 => (false, 1, "B"),
|
|
1 => (true, 1024, " KiB"),
|
|
2 => (true, 1024 * 1024, " MiB"),
|
|
3 => (true, 1024 * 1024 * 1024, " GiB"),
|
|
_ => (true, 1024 * 1024 * 1024 * 1024, " TiB"),
|
|
};
|
|
let integer = self.0 / div;
|
|
if dot && integer < 100 {
|
|
let fractional = (((self.0 * 1000) / div) % 1000) / 10;
|
|
write!(writer, "{integer}.{fractional:02}{suffix}")?;
|
|
} else {
|
|
write!(writer, "{integer}{suffix}")?;
|
|
}
|
|
|
|
let chars = writer.as_str();
|
|
fmt::Display::fmt(chars, f)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use alloc::format;
|
|
|
|
use crate::fmt::FormatSize;
|
|
|
|
#[test]
|
|
fn test_fmt_size() {
|
|
assert_eq!(format!("{}", FormatSize::default(1023)), "1023B");
|
|
assert_eq!(format!("{}", FormatSize::default(1024)), "1.00 KiB");
|
|
assert_eq!(format!("{}", FormatSize::default(1025)), "1.00 KiB");
|
|
assert_eq!(format!("{}", FormatSize::default(1111)), "1.08 KiB");
|
|
assert_eq!(format!("{}", FormatSize::default(5000)), "4.88 KiB");
|
|
assert_eq!(format!("{}", FormatSize::default(5000000)), "4.76 MiB");
|
|
assert_eq!(format!("{}", FormatSize::default(5000000000)), "4.65 GiB");
|
|
assert_eq!(
|
|
format!("{}", FormatSize::default(5000000000000)),
|
|
"4.54 TiB"
|
|
);
|
|
}
|
|
}
|