Skip to content

Commit 0f7466c

Browse files
authored
Add Deref implementation for HSTRING (#3291)
1 parent bca9a76 commit 0f7466c

File tree

10 files changed

+134
-133
lines changed

10 files changed

+134
-133
lines changed

crates/libs/result/src/bstr.rs

+11-21
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,26 @@
11
use super::*;
2+
use core::ops::Deref;
23

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

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

11-
pub fn len(&self) -> usize {
12-
if self.0.is_null() {
10+
fn deref(&self) -> &[u16] {
11+
let len = if self.0.is_null() {
1312
0
1413
} else {
1514
unsafe { SysStringLen(self.0) as usize }
16-
}
17-
}
18-
19-
pub fn as_wide(&self) -> &[u16] {
20-
let len = self.len();
21-
if len != 0 {
22-
unsafe { core::slice::from_raw_parts(self.as_ptr(), len) }
23-
} else {
24-
&[]
25-
}
26-
}
15+
};
2716

28-
pub fn as_ptr(&self) -> *const u16 {
29-
if !self.is_empty() {
30-
self.0
17+
if len > 0 {
18+
unsafe { core::slice::from_raw_parts(self.0, len) }
3119
} else {
20+
// This ensures that if `as_ptr` is called on the slice that the resulting pointer
21+
// will still refer to a null-terminated string.
3222
const EMPTY: [u16; 1] = [0];
33-
EMPTY.as_ptr()
23+
&EMPTY[..0]
3424
}
3525
}
3626
}

crates/libs/result/src/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ mod error_info {
343343
}
344344
}
345345

346-
Some(String::from_utf16_lossy(wide_trim_end(message.as_wide())))
346+
Some(String::from_utf16_lossy(wide_trim_end(&message)))
347347
}
348348

349349
pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {

crates/libs/strings/src/bstr.rs

+27-37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use core::ops::Deref;
23

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

16-
/// Returns `true` if the string is empty.
17-
pub fn is_empty(&self) -> bool {
18-
self.len() == 0
19-
}
20-
21-
/// Returns the length of the string.
22-
pub fn len(&self) -> usize {
23-
if self.0.is_null() {
24-
0
25-
} else {
26-
unsafe { bindings::SysStringLen(self.0) as usize }
27-
}
28-
}
29-
30-
/// Get the string as 16-bit wide characters (wchars).
31-
pub fn as_wide(&self) -> &[u16] {
32-
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
33-
}
34-
35-
/// Returns a raw pointer to the `BSTR` buffer.
36-
pub fn as_ptr(&self) -> *const u16 {
37-
if !self.is_empty() {
38-
self.0
39-
} else {
40-
const EMPTY: [u16; 1] = [0];
41-
EMPTY.as_ptr()
42-
}
43-
}
44-
4517
/// Create a `BSTR` from a slice of 16 bit characters (wchars).
4618
pub fn from_wide(value: &[u16]) -> Self {
4719
if value.is_empty() {
@@ -75,9 +47,30 @@ impl BSTR {
7547
}
7648
}
7749

50+
impl Deref for BSTR {
51+
type Target = [u16];
52+
53+
fn deref(&self) -> &[u16] {
54+
let len = if self.0.is_null() {
55+
0
56+
} else {
57+
unsafe { bindings::SysStringLen(self.0) as usize }
58+
};
59+
60+
if len > 0 {
61+
unsafe { core::slice::from_raw_parts(self.0, len) }
62+
} else {
63+
// This ensures that if `as_ptr` is called on the slice that the resulting pointer
64+
// will still refer to a null-terminated string.
65+
const EMPTY: [u16; 1] = [0];
66+
&EMPTY[..0]
67+
}
68+
}
69+
}
70+
7871
impl Clone for BSTR {
7972
fn clone(&self) -> Self {
80-
Self::from_wide(self.as_wide())
73+
Self::from_wide(self)
8174
}
8275
}
8376

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

10699
fn try_from(value: &BSTR) -> core::result::Result<Self, Self::Error> {
107-
String::from_utf16(value.as_wide())
100+
String::from_utf16(value)
108101
}
109102
}
110103

@@ -127,7 +120,7 @@ impl core::fmt::Display for BSTR {
127120
core::write!(
128121
f,
129122
"{}",
130-
Decode(|| core::char::decode_utf16(self.as_wide().iter().cloned()))
123+
Decode(|| core::char::decode_utf16(self.iter().cloned()))
131124
)
132125
}
133126
}
@@ -140,7 +133,7 @@ impl core::fmt::Debug for BSTR {
140133

141134
impl PartialEq for BSTR {
142135
fn eq(&self, other: &Self) -> bool {
143-
self.as_wide() == other.as_wide()
136+
self.deref() == other.deref()
144137
}
145138
}
146139

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

161154
impl<T: AsRef<str> + ?Sized> PartialEq<T> for BSTR {
162155
fn eq(&self, other: &T) -> bool {
163-
self.as_wide()
164-
.iter()
165-
.copied()
166-
.eq(other.as_ref().encode_utf16())
156+
self.iter().copied().eq(other.as_ref().encode_utf16())
167157
}
168158
}
169159

crates/libs/strings/src/hstring.rs

+25-40
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use core::ops::Deref;
23

34
/// An ([HSTRING](https://docs.microsoft.com/en-us/windows/win32/winrt/hstring))
45
/// is a reference-counted and immutable UTF-16 string type.
@@ -13,50 +14,20 @@ impl HSTRING {
1314
Self(core::ptr::null_mut())
1415
}
1516

16-
/// Returns `true` if the string is empty.
17-
pub fn is_empty(&self) -> bool {
18-
// An empty HSTRING is represented by a null pointer.
19-
self.0.is_null()
20-
}
21-
22-
/// Returns the length of the string. The length is measured in `u16`s (UTF-16 code units), not including the terminating null character.
23-
pub fn len(&self) -> usize {
24-
if let Some(header) = self.as_header() {
25-
header.len as usize
26-
} else {
27-
0
28-
}
29-
}
30-
31-
/// Get the string as 16-bit wide characters (wchars).
32-
pub fn as_wide(&self) -> &[u16] {
33-
unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
34-
}
35-
36-
/// Returns a raw pointer to the `HSTRING` buffer.
37-
pub fn as_ptr(&self) -> *const u16 {
38-
if let Some(header) = self.as_header() {
39-
header.data
40-
} else {
41-
const EMPTY: [u16; 1] = [0];
42-
EMPTY.as_ptr()
43-
}
44-
}
45-
4617
/// Create a `HSTRING` from a slice of 16 bit characters (wchars).
4718
pub fn from_wide(value: &[u16]) -> Self {
4819
unsafe { Self::from_wide_iter(value.iter().copied(), value.len()) }
4920
}
5021

5122
/// Get the contents of this `HSTRING` as a String lossily.
5223
pub fn to_string_lossy(&self) -> String {
53-
String::from_utf16_lossy(self.as_wide())
24+
String::from_utf16_lossy(self)
5425
}
5526

5627
/// Get the contents of this `HSTRING` as a OsString.
5728
#[cfg(feature = "std")]
5829
pub fn to_os_string(&self) -> std::ffi::OsString {
59-
std::os::windows::ffi::OsStringExt::from_wide(self.as_wide())
30+
std::os::windows::ffi::OsStringExt::from_wide(self)
6031
}
6132

6233
/// # Safety
@@ -87,6 +58,21 @@ impl HSTRING {
8758
}
8859
}
8960

61+
impl Deref for HSTRING {
62+
type Target = [u16];
63+
64+
fn deref(&self) -> &[u16] {
65+
if let Some(header) = self.as_header() {
66+
unsafe { core::slice::from_raw_parts(header.data, header.len as usize) }
67+
} else {
68+
// This ensures that if `as_ptr` is called on the slice that the resulting pointer
69+
// will still refer to a null-terminated string.
70+
const EMPTY: [u16; 1] = [0];
71+
&EMPTY[..0]
72+
}
73+
}
74+
}
75+
9076
impl Default for HSTRING {
9177
fn default() -> Self {
9278
Self::new()
@@ -125,7 +111,7 @@ impl core::fmt::Display for HSTRING {
125111
write!(
126112
f,
127113
"{}",
128-
Decode(|| core::char::decode_utf16(self.as_wide().iter().cloned()))
114+
Decode(|| core::char::decode_utf16(self.iter().cloned()))
129115
)
130116
}
131117
}
@@ -191,13 +177,13 @@ impl Eq for HSTRING {}
191177

192178
impl Ord for HSTRING {
193179
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
194-
self.as_wide().cmp(other.as_wide())
180+
self.deref().cmp(other)
195181
}
196182
}
197183

198184
impl core::hash::Hash for HSTRING {
199185
fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
200-
self.as_wide().hash(hasher)
186+
self.deref().hash(hasher)
201187
}
202188
}
203189

@@ -209,7 +195,7 @@ impl PartialOrd for HSTRING {
209195

210196
impl PartialEq for HSTRING {
211197
fn eq(&self, other: &Self) -> bool {
212-
*self.as_wide() == *other.as_wide()
198+
self.deref() == other.deref()
213199
}
214200
}
215201

@@ -233,7 +219,7 @@ impl PartialEq<&String> for HSTRING {
233219

234220
impl PartialEq<str> for HSTRING {
235221
fn eq(&self, other: &str) -> bool {
236-
self.as_wide().iter().copied().eq(other.encode_utf16())
222+
self.iter().copied().eq(other.encode_utf16())
237223
}
238224
}
239225

@@ -309,8 +295,7 @@ impl PartialEq<&std::ffi::OsString> for HSTRING {
309295
#[cfg(feature = "std")]
310296
impl PartialEq<std::ffi::OsStr> for HSTRING {
311297
fn eq(&self, other: &std::ffi::OsStr) -> bool {
312-
self.as_wide()
313-
.iter()
298+
self.iter()
314299
.copied()
315300
.eq(std::os::windows::ffi::OsStrExt::encode_wide(other))
316301
}
@@ -376,7 +361,7 @@ impl<'a> TryFrom<&'a HSTRING> for String {
376361
type Error = alloc::string::FromUtf16Error;
377362

378363
fn try_from(hstring: &HSTRING) -> core::result::Result<Self, Self::Error> {
379-
String::from_utf16(hstring.as_wide())
364+
String::from_utf16(hstring)
380365
}
381366
}
382367

crates/tests/misc/literals/tests/win.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ fn test() {
4747
fn into() {
4848
let a = h!("");
4949
assert!(a.is_empty());
50-
assert!(!a.as_ptr().is_null());
51-
assert!(a.as_wide().is_empty());
5250
let b = PCWSTR(a.as_ptr());
5351
// Even though an empty HSTRING is internally represented by a null pointer, the PCWSTR
5452
// will still be a non-null pointer to a null terminated empty string.
@@ -80,7 +78,7 @@ fn assert_hstring(left: &HSTRING, right: &[u16]) {
8078
unsafe { wcslen(PCWSTR::from_raw(left.as_ptr())) },
8179
right.len() - 1
8280
);
83-
let left = unsafe { std::slice::from_raw_parts(left.as_wide().as_ptr(), right.len()) };
81+
let left = unsafe { std::slice::from_raw_parts(left.as_ptr(), right.len()) };
8482
assert_eq!(left, right);
8583
}
8684

crates/tests/misc/string_param/tests/pwstr.rs

+1-17
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,4 @@
1-
use windows::{core::*, Win32::Foundation::*, Win32::UI::Shell::*};
2-
3-
#[test]
4-
fn error() {
5-
unsafe {
6-
SetLastError(ERROR_BUSY_DRIVE);
7-
8-
let utf8 = "test\0".as_bytes();
9-
let utf16 = HSTRING::from("test\0");
10-
let utf16 = utf16.as_wide();
11-
let len = 5;
12-
assert_eq!(utf8.len(), len);
13-
assert_eq!(utf16.len(), len);
14-
15-
assert_eq!(GetLastError(), ERROR_BUSY_DRIVE);
16-
}
17-
}
1+
use windows::{core::*, Win32::UI::Shell::*};
182

193
#[test]
204
fn convert() {

0 commit comments

Comments
 (0)