use std::fmt;
#[derive(Clone, Copy)]
pub struct BStr<'a> {
va: u32,
byte_length: u32,
data: &'a [u8],
}
impl<'a> BStr<'a> {
#[inline]
pub fn new(va: u32, byte_length: u32, data: &'a [u8]) -> Self {
Self {
va,
byte_length,
data,
}
}
#[inline]
pub fn empty() -> Self {
Self {
va: 0,
byte_length: 0,
data: &[],
}
}
#[inline]
pub fn va(&self) -> u32 {
self.va
}
#[inline]
pub fn length_prefix_va(&self) -> u32 {
self.va.wrapping_sub(4)
}
#[inline]
pub fn byte_length(&self) -> u32 {
self.byte_length
}
#[inline]
pub fn char_count(&self) -> usize {
(self.byte_length as usize).checked_div(2).unwrap_or(0)
}
#[inline]
pub fn total_binary_size(&self) -> usize {
(self.byte_length as usize)
.saturating_add(4)
.saturating_add(2)
}
#[inline]
pub fn as_bytes(&self) -> &'a [u8] {
self.data
}
#[inline]
pub fn is_empty(&self) -> bool {
self.byte_length == 0
}
pub fn to_string_lossy(&self) -> String {
if self.data.is_empty() {
return String::new();
}
let u16s: Vec<u16> = self
.data
.chunks_exact(2)
.filter_map(|pair| <[u8; 2]>::try_from(pair).ok())
.map(u16::from_le_bytes)
.collect();
String::from_utf16_lossy(&u16s)
}
}
impl fmt::Debug for BStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BStr(0x{:08X}, {:?})", self.va, self.to_string_lossy())
}
}
impl fmt::Display for BStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string_lossy())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty() {
let bstr = BStr::empty();
assert_eq!(bstr.va(), 0);
assert_eq!(bstr.byte_length(), 0);
assert_eq!(bstr.char_count(), 0);
assert!(bstr.is_empty());
assert_eq!(bstr.total_binary_size(), 6); assert_eq!(bstr.to_string_lossy(), "");
}
#[test]
fn test_hello() {
let data: &[u8] = &[0x48, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x6F, 0x00];
let bstr = BStr::new(0x00401004, 10, data);
assert_eq!(bstr.va(), 0x00401004);
assert_eq!(bstr.length_prefix_va(), 0x00401000);
assert_eq!(bstr.byte_length(), 10);
assert_eq!(bstr.char_count(), 5);
assert!(!bstr.is_empty());
assert_eq!(bstr.total_binary_size(), 16); assert_eq!(bstr.to_string_lossy(), "Hello");
}
#[test]
fn test_debug_display() {
let data: &[u8] = &[0x48, 0x00, 0x69, 0x00]; let bstr = BStr::new(0x00401004, 4, data);
assert_eq!(format!("{bstr}"), "Hi");
assert_eq!(format!("{bstr:?}"), "BStr(0x00401004, \"Hi\")");
}
}