use std::ops::RangeBounds;
use crate::prelude::*;
pub fn format_bytes(bytes: usize) -> String {
const UNITS: [&str; 6] = ["B", "KB", "MB", "GB", "TB", "PB"];
#[allow(clippy::cast_precision_loss)]
let mut size = bytes as f64;
let mut unit_idx = 0;
while size >= 1000.0 && unit_idx < UNITS.len() - 1 {
size /= 1000.0;
unit_idx += 1;
}
let unit = UNITS[unit_idx];
format!("{size:.2} {unit}")
}
pub fn hexdump(raw_data: &[u8]) -> String {
if raw_data.is_empty() {
return String::new();
}
let mut buffer = String::with_capacity(raw_data.len() * 3);
for byte in raw_data {
use std::fmt::Write;
let _ = write!(&mut buffer, "{byte:02X} ");
}
buffer.pop();
buffer
}
pub fn hexdump_range(raw_data: &[u8], range: impl RangeBounds<usize>) -> Result<String> {
use std::ops::Bound::Excluded;
use std::ops::Bound::Included;
use std::ops::Bound::Unbounded;
let len = raw_data.len();
let start = match range.start_bound() {
Included(&n) => n,
Excluded(&n) => n + 1,
Unbounded => 0,
};
let end = match range.end_bound() {
Included(&n) => n + 1,
Excluded(&n) => n,
Unbounded => len,
};
if start > len || end > len {
bail!("Range out of bounds: {start}..{end} for length {len}");
}
if start > end {
bail!("Invalid range: start {start} > end {end}");
}
let slice: &[u8] = &raw_data[start..end];
Ok(hexdump(slice))
}
pub fn typename<T>() -> &'static str {
let ty = std::any::type_name::<T>();
if let Some(index) = ty.find("GM")
&& !ty.contains('<')
{
return &ty[index..];
}
ty.strip_prefix("libgm::wad::elements::").unwrap_or(ty)
}