Skip to content

Commit

Permalink
bstr
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr committed Sep 23, 2024
1 parent 1ce0906 commit 19ac08a
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 62 deletions.
32 changes: 11 additions & 21 deletions crates/libs/result/src/bstr.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,26 @@
use super::*;
use core::ops::Deref;

#[repr(transparent)]
pub struct BasicString(*const u16);

impl BasicString {
pub fn is_empty(&self) -> bool {
self.len() == 0
}
impl Deref for BasicString {
type Target = [u16];

pub fn len(&self) -> usize {
if self.0.is_null() {
fn deref(&self) -> &[u16] {
let len = if self.0.is_null() {
0
} else {
unsafe { SysStringLen(self.0) as usize }
}
}

pub fn as_wide(&self) -> &[u16] {
let len = self.len();
if len != 0 {
unsafe { core::slice::from_raw_parts(self.as_ptr(), len) }
} else {
&[]
}
}
};

pub fn as_ptr(&self) -> *const u16 {
if !self.is_empty() {
self.0
if len > 0 {
unsafe { core::slice::from_raw_parts(self.0, len) }
} else {
// This ensures that if `as_ptr` is called on the slice that the resulting pointer
// will still refer to a null-terminated string.
const EMPTY: [u16; 1] = [0];
EMPTY.as_ptr()
&EMPTY[..0]
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/libs/result/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ mod error_info {
}
}

Some(String::from_utf16_lossy(wide_trim_end(message.as_wide())))
Some(String::from_utf16_lossy(wide_trim_end(&message)))
}

pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
Expand Down
64 changes: 27 additions & 37 deletions crates/libs/strings/src/bstr.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::*;
use core::ops::Deref;

/// A BSTR string ([BSTR](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/automat/string-manipulation-functions))
/// is a length-prefixed wide string.
Expand All @@ -13,35 +14,6 @@ impl BSTR {
Self(core::ptr::null_mut())
}

/// Returns `true` if the string is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}

/// Returns the length of the string.
pub fn len(&self) -> usize {
if self.0.is_null() {
0
} else {
unsafe { bindings::SysStringLen(self.0) as usize }
}
}

/// Get the string as 16-bit wide characters (wchars).
pub fn as_wide(&self) -> &[u16] {
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
}

/// Returns a raw pointer to the `BSTR` buffer.
pub fn as_ptr(&self) -> *const u16 {
if !self.is_empty() {
self.0
} else {
const EMPTY: [u16; 1] = [0];
EMPTY.as_ptr()
}
}

/// Create a `BSTR` from a slice of 16 bit characters (wchars).
pub fn from_wide(value: &[u16]) -> Self {
if value.is_empty() {
Expand Down Expand Up @@ -75,9 +47,30 @@ impl BSTR {
}
}

impl Deref for BSTR {
type Target = [u16];

fn deref(&self) -> &[u16] {
let len = if self.0.is_null() {
0
} else {
unsafe { bindings::SysStringLen(self.0) as usize }
};

if len > 0 {
unsafe { core::slice::from_raw_parts(self.0, len) }
} else {
// This ensures that if `as_ptr` is called on the slice that the resulting pointer
// will still refer to a null-terminated string.
const EMPTY: [u16; 1] = [0];
&EMPTY[..0]
}
}
}

impl Clone for BSTR {
fn clone(&self) -> Self {
Self::from_wide(self.as_wide())
Self::from_wide(self)
}
}

Expand All @@ -104,7 +97,7 @@ impl<'a> TryFrom<&'a BSTR> for String {
type Error = alloc::string::FromUtf16Error;

fn try_from(value: &BSTR) -> core::result::Result<Self, Self::Error> {
String::from_utf16(value.as_wide())
String::from_utf16(value)
}
}

Expand All @@ -127,7 +120,7 @@ impl core::fmt::Display for BSTR {
core::write!(
f,
"{}",
Decode(|| core::char::decode_utf16(self.as_wide().iter().cloned()))
Decode(|| core::char::decode_utf16(self.iter().cloned()))
)
}
}
Expand All @@ -140,7 +133,7 @@ impl core::fmt::Debug for BSTR {

impl PartialEq for BSTR {
fn eq(&self, other: &Self) -> bool {
self.as_wide() == other.as_wide()
self.deref() == other.deref()
}
}

Expand All @@ -160,10 +153,7 @@ impl PartialEq<BSTR> for String {

impl<T: AsRef<str> + ?Sized> PartialEq<T> for BSTR {
fn eq(&self, other: &T) -> bool {
self.as_wide()
.iter()
.copied()
.eq(other.as_ref().encode_utf16())
self.iter().copied().eq(other.as_ref().encode_utf16())
}
}

Expand Down
35 changes: 32 additions & 3 deletions crates/tests/misc/strings/tests/bstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ fn clone() {
assert!(a.is_empty());
assert!(a.len() == 0);
assert_eq!(a.len(), 0);
assert_eq!(a.as_wide().len(), 0);
assert_eq!(a.len(), 0);

let wide = &[0x68, 0x65, 0x6c, 0x6c, 0x6f];
let a = BSTR::from_wide(wide);
assert!(!a.is_empty());
assert!(a.len() == 5);
assert_eq!(a.as_wide().len(), 5);
assert_eq!(a.as_wide(), wide);
assert_eq!(a.len(), 5);
assert_eq!(*a, *wide);
assert_eq!(a, "hello");

let a: BSTR = "".into();
Expand All @@ -60,3 +60,32 @@ fn interop() -> Result<()> {
Ok(())
}
}

#[test]
fn deref_as_slice() {
let deref = BSTR::from("0123456789");
assert!(!deref.is_empty());
assert_eq!(deref.len(), 10);
assert_eq!(BSTR::from_wide(&deref[..=3]), "0123");
assert!(deref.ends_with(&deref[7..=9]));
assert_eq!(deref.get(5), Some(b'5' as u16).as_ref());
let ptr = PCWSTR(deref.as_ptr());
assert_eq!(deref.cmp(&deref), std::cmp::Ordering::Equal);

unsafe {
assert_eq!(*ptr.as_wide(), *deref);
}

let empty = BSTR::new();
assert!(empty.is_empty());
assert_eq!(empty.len(), 0);
assert_eq!(*empty, []);

unsafe {
assert_eq!(wcslen(empty.as_ptr()), 0);
}
}

extern "C" {
pub fn wcslen(s: *const u16) -> usize;
}

0 comments on commit 19ac08a

Please sign in to comment.