#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::string::{FromUtf16Error, String};
use core::{fmt::Debug, mem::size_of, slice::from_raw_parts};
#[derive(Debug)]
#[repr(C)]
pub struct UnicodeString {
len: u16,
cap: u16,
buf: *const u16,
}
impl UnicodeString {
#[inline]
pub const fn new(len: u16, cap: u16, buf: *const u16) -> Self {
Self { len, cap, buf }
}
#[inline]
pub const fn len(&self) -> usize {
self.len as usize / size_of::<u16>()
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub const fn bytes_len(&self) -> usize {
self.len as usize
}
#[inline]
pub const fn capacity(&self) -> usize {
self.cap as usize
}
#[inline]
pub fn is_null(&self) -> bool {
self.buf.is_null()
}
#[inline]
pub unsafe fn as_slice<'a>(&self) -> &'a [u16] {
from_raw_parts(self.buf, self.len())
}
#[cfg(feature = "alloc")]
pub unsafe fn to_string(&self) -> Result<String, FromUtf16Error> {
String::from_utf16(self.as_slice())
}
#[inline]
pub unsafe fn is_ascii(&self) -> bool {
self.as_slice().iter().all(|b| *b < 128)
}
#[inline]
pub unsafe fn ascii(&self) -> impl Iterator<Item = char> {
assert!(self.is_ascii());
self.as_slice().iter().map(|b| *b as u8 as char)
}
#[inline]
pub unsafe fn utf16(&self) -> impl Iterator<Item = char> {
char::decode_utf16(self.as_slice().iter().cloned()).map(|ch| match ch {
Ok(c) => c,
Err(_) => char::REPLACEMENT_CHARACTER,
})
}
}
impl PartialEq for UnicodeString {
fn eq(&self, other: &Self) -> bool {
if self.is_null() && other.is_null() {
return true;
}
if self.bytes_len() != other.bytes_len() {
false
} else {
unsafe { self.as_slice() == other.as_slice() }
}
}
}
#[macro_export]
macro_rules! unicode_string {
($str:expr) => {
$crate::types::win::UnicodeString::new(
($str.len() * 2) as _,
($str.len() * 2) as _,
$crate::obfstr::wide!($str).as_ptr() as _,
)
};
}