use chrono::DateTime;
use super::stats::byte_stats;
use super::types::{
DecodedValue, Endian, format_binary, format_hex, format_octal, read_u16, read_u32, read_u64,
};
fn format_timestamp(secs: i64) -> Option<String> {
if !(0..=4_102_444_800).contains(&secs) {
return None;
}
DateTime::from_timestamp(secs, 0).map(|dt| dt.format("%Y-%m-%d %H:%M:%S UTC").to_string())
}
pub fn decode_selection(bytes: &[u8], endian: Endian) -> Vec<DecodedValue> {
let mut results = Vec::new();
if bytes.is_empty() {
return results;
}
results.push(DecodedValue::new("Hex", format_hex(bytes)));
results.push(DecodedValue::new("Binary", format_binary(bytes)));
results.push(DecodedValue::new("Octal", format_octal(bytes)));
results.push(DecodedValue::new("", ""));
if bytes.len() >= 1 {
results.push(DecodedValue::new("u8", format!("{}", bytes[0])));
results.push(DecodedValue::new("i8", format!("{}", bytes[0] as i8)));
}
if bytes.len() >= 2 {
let v = read_u16(bytes, endian);
results.push(DecodedValue::new(
format!("u16 {}", endian.label()),
format!("{}", v),
));
results.push(DecodedValue::new(
format!("i16 {}", endian.label()),
format!("{}", v as i16),
));
}
if bytes.len() >= 4 {
let v = read_u32(bytes, endian);
results.push(DecodedValue::new(
format!("u32 {}", endian.label()),
format!("{}", v),
));
results.push(DecodedValue::new(
format!("i32 {}", endian.label()),
format!("{}", v as i32),
));
results.push(DecodedValue::new(
format!("f32 {}", endian.label()),
format!("{}", f32::from_bits(v)),
));
}
if bytes.len() >= 8 {
let v = read_u64(bytes, endian);
results.push(DecodedValue::new(
format!("u64 {}", endian.label()),
format!("{}", v),
));
results.push(DecodedValue::new(
format!("i64 {}", endian.label()),
format!("{}", v as i64),
));
results.push(DecodedValue::new(
format!("f64 {}", endian.label()),
format!("{}", f64::from_bits(v)),
));
if let Some(ts_str) = format_timestamp(v as i64) {
results.push(DecodedValue::new("Timestamp", ts_str));
}
if let Some(ts_str) = format_timestamp(read_u32(bytes, endian) as i64) {
results.push(DecodedValue::new("Timestamp32", ts_str));
}
} else if bytes.len() >= 4 {
if let Some(ts_str) = format_timestamp(read_u32(bytes, endian) as i64) {
results.push(DecodedValue::new("Timestamp", ts_str));
}
}
results.push(DecodedValue::new("", ""));
let ascii: String = bytes
.iter()
.take(64)
.map(|&b| {
if b.is_ascii_graphic() || b == b' ' {
b as char
} else {
'.'
}
})
.collect();
results.push(DecodedValue::new("ASCII", format!("\"{}\"", ascii)));
let utf8 = String::from_utf8_lossy(&bytes[..bytes.len().min(64)]);
results.push(DecodedValue::new("UTF-8", format!("\"{}\"", utf8)));
results.push(DecodedValue::new("", "")); results.push(DecodedValue::new(
"Length",
format!("{} bytes", bytes.len()),
));
if bytes.len() >= 2 {
let stats = byte_stats(bytes);
results.push(DecodedValue::new("Entropy", stats.entropy_display()));
results.push(DecodedValue::new("Compress", stats.compress_display()));
results.push(DecodedValue::new("Sparsity", stats.sparsity_display()));
results.push(DecodedValue::new("Unique", stats.unique_display()));
}
results
}