windows_strings/
hstring.rs

1use super::*;
2use core::ops::Deref;
3
4/// An ([HSTRING](https://docs.microsoft.com/en-us/windows/win32/winrt/hstring))
5/// is a reference-counted and immutable UTF-16 string type.
6#[repr(transparent)]
7pub struct HSTRING(pub(crate) *mut HStringHeader);
8
9impl HSTRING {
10    /// Create an empty `HSTRING`.
11    ///
12    /// This function does not allocate memory.
13    pub const fn new() -> Self {
14        Self(core::ptr::null_mut())
15    }
16
17    /// Create a `HSTRING` from a slice of 16 bit characters (wchars).
18    pub fn from_wide(value: &[u16]) -> Self {
19        unsafe { Self::from_wide_iter(value.iter().copied(), value.len()) }
20    }
21
22    /// Get the contents of this `HSTRING` as a String lossily.
23    pub fn to_string_lossy(&self) -> String {
24        String::from_utf16_lossy(self)
25    }
26
27    /// Get the contents of this `HSTRING` as a OsString.
28    #[cfg(feature = "std")]
29    pub fn to_os_string(&self) -> std::ffi::OsString {
30        std::os::windows::ffi::OsStringExt::from_wide(self)
31    }
32
33    /// # Safety
34    /// len must not be less than the number of items in the iterator.
35    unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Self {
36        if len == 0 {
37            return Self::new();
38        }
39
40        let ptr = HStringHeader::alloc(len.try_into().unwrap());
41
42        // Place each utf-16 character into the buffer and
43        // increase len as we go along.
44        for (index, wide) in iter.enumerate() {
45            debug_assert!(index < len);
46
47            unsafe {
48                (*ptr).data.add(index).write(wide);
49                (*ptr).len = index as u32 + 1;
50            }
51        }
52
53        unsafe {
54            // Write a 0 byte to the end of the buffer.
55            (*ptr).data.offset((*ptr).len as isize).write(0);
56        }
57        Self(ptr)
58    }
59
60    fn as_header(&self) -> Option<&HStringHeader> {
61        unsafe { self.0.as_ref() }
62    }
63}
64
65impl Deref for HSTRING {
66    type Target = [u16];
67
68    fn deref(&self) -> &[u16] {
69        if let Some(header) = self.as_header() {
70            unsafe { core::slice::from_raw_parts(header.data, header.len as usize) }
71        } else {
72            // This ensures that if `as_ptr` is called on the slice that the resulting pointer
73            // will still refer to a null-terminated string.
74            const EMPTY: [u16; 1] = [0];
75            &EMPTY[..0]
76        }
77    }
78}
79
80impl Default for HSTRING {
81    fn default() -> Self {
82        Self::new()
83    }
84}
85
86impl Clone for HSTRING {
87    fn clone(&self) -> Self {
88        if let Some(header) = self.as_header() {
89            Self(header.duplicate())
90        } else {
91            Self::new()
92        }
93    }
94}
95
96impl Drop for HSTRING {
97    fn drop(&mut self) {
98        if let Some(header) = self.as_header() {
99            // HSTRING_REFERENCE_FLAG indicates a string backed by static or stack memory that is
100            // thus not reference-counted and does not need to be freed.
101            unsafe {
102                if header.flags & HSTRING_REFERENCE_FLAG == 0 && header.count.release() == 0 {
103                    HStringHeader::free(self.0);
104                }
105            }
106        }
107    }
108}
109
110unsafe impl Send for HSTRING {}
111unsafe impl Sync for HSTRING {}
112
113impl core::fmt::Display for HSTRING {
114    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115        write!(
116            f,
117            "{}",
118            Decode(|| core::char::decode_utf16(self.iter().cloned()))
119        )
120    }
121}
122
123impl core::fmt::Debug for HSTRING {
124    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
125        write!(f, "\"{}\"", self)
126    }
127}
128
129impl From<&str> for HSTRING {
130    fn from(value: &str) -> Self {
131        unsafe { Self::from_wide_iter(value.encode_utf16(), value.len()) }
132    }
133}
134
135impl From<String> for HSTRING {
136    fn from(value: String) -> Self {
137        value.as_str().into()
138    }
139}
140
141impl From<&String> for HSTRING {
142    fn from(value: &String) -> Self {
143        value.as_str().into()
144    }
145}
146
147#[cfg(feature = "std")]
148impl From<&std::path::Path> for HSTRING {
149    fn from(value: &std::path::Path) -> Self {
150        value.as_os_str().into()
151    }
152}
153
154#[cfg(feature = "std")]
155impl From<&std::ffi::OsStr> for HSTRING {
156    fn from(value: &std::ffi::OsStr) -> Self {
157        unsafe {
158            Self::from_wide_iter(
159                std::os::windows::ffi::OsStrExt::encode_wide(value),
160                value.len(),
161            )
162        }
163    }
164}
165
166#[cfg(feature = "std")]
167impl From<std::ffi::OsString> for HSTRING {
168    fn from(value: std::ffi::OsString) -> Self {
169        value.as_os_str().into()
170    }
171}
172
173#[cfg(feature = "std")]
174impl From<&std::ffi::OsString> for HSTRING {
175    fn from(value: &std::ffi::OsString) -> Self {
176        value.as_os_str().into()
177    }
178}
179
180impl Eq for HSTRING {}
181
182impl Ord for HSTRING {
183    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
184        self.deref().cmp(other)
185    }
186}
187
188impl core::hash::Hash for HSTRING {
189    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
190        self.deref().hash(hasher)
191    }
192}
193
194impl PartialOrd for HSTRING {
195    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
196        Some(self.cmp(other))
197    }
198}
199
200impl PartialEq for HSTRING {
201    fn eq(&self, other: &Self) -> bool {
202        self.deref() == other.deref()
203    }
204}
205
206impl PartialEq<String> for HSTRING {
207    fn eq(&self, other: &String) -> bool {
208        *self == **other
209    }
210}
211
212impl PartialEq<String> for &HSTRING {
213    fn eq(&self, other: &String) -> bool {
214        **self == **other
215    }
216}
217
218impl PartialEq<&String> for HSTRING {
219    fn eq(&self, other: &&String) -> bool {
220        *self == ***other
221    }
222}
223
224impl PartialEq<str> for HSTRING {
225    fn eq(&self, other: &str) -> bool {
226        self.iter().copied().eq(other.encode_utf16())
227    }
228}
229
230impl PartialEq<str> for &HSTRING {
231    fn eq(&self, other: &str) -> bool {
232        **self == *other
233    }
234}
235
236impl PartialEq<&str> for HSTRING {
237    fn eq(&self, other: &&str) -> bool {
238        *self == **other
239    }
240}
241
242impl PartialEq<HSTRING> for str {
243    fn eq(&self, other: &HSTRING) -> bool {
244        *other == *self
245    }
246}
247
248impl PartialEq<HSTRING> for &str {
249    fn eq(&self, other: &HSTRING) -> bool {
250        *other == **self
251    }
252}
253
254impl PartialEq<&HSTRING> for str {
255    fn eq(&self, other: &&HSTRING) -> bool {
256        **other == *self
257    }
258}
259
260impl PartialEq<HSTRING> for String {
261    fn eq(&self, other: &HSTRING) -> bool {
262        *other == **self
263    }
264}
265
266impl PartialEq<HSTRING> for &String {
267    fn eq(&self, other: &HSTRING) -> bool {
268        *other == ***self
269    }
270}
271
272impl PartialEq<&HSTRING> for String {
273    fn eq(&self, other: &&HSTRING) -> bool {
274        **other == **self
275    }
276}
277
278#[cfg(feature = "std")]
279impl PartialEq<std::ffi::OsString> for HSTRING {
280    fn eq(&self, other: &std::ffi::OsString) -> bool {
281        *self == **other
282    }
283}
284
285#[cfg(feature = "std")]
286impl PartialEq<std::ffi::OsString> for &HSTRING {
287    fn eq(&self, other: &std::ffi::OsString) -> bool {
288        **self == **other
289    }
290}
291
292#[cfg(feature = "std")]
293impl PartialEq<&std::ffi::OsString> for HSTRING {
294    fn eq(&self, other: &&std::ffi::OsString) -> bool {
295        *self == ***other
296    }
297}
298
299#[cfg(feature = "std")]
300impl PartialEq<std::ffi::OsStr> for HSTRING {
301    fn eq(&self, other: &std::ffi::OsStr) -> bool {
302        self.iter()
303            .copied()
304            .eq(std::os::windows::ffi::OsStrExt::encode_wide(other))
305    }
306}
307
308#[cfg(feature = "std")]
309impl PartialEq<std::ffi::OsStr> for &HSTRING {
310    fn eq(&self, other: &std::ffi::OsStr) -> bool {
311        **self == *other
312    }
313}
314
315#[cfg(feature = "std")]
316impl PartialEq<&std::ffi::OsStr> for HSTRING {
317    fn eq(&self, other: &&std::ffi::OsStr) -> bool {
318        *self == **other
319    }
320}
321
322#[cfg(feature = "std")]
323impl PartialEq<HSTRING> for std::ffi::OsStr {
324    fn eq(&self, other: &HSTRING) -> bool {
325        *other == *self
326    }
327}
328
329#[cfg(feature = "std")]
330impl PartialEq<HSTRING> for &std::ffi::OsStr {
331    fn eq(&self, other: &HSTRING) -> bool {
332        *other == **self
333    }
334}
335
336#[cfg(feature = "std")]
337impl PartialEq<&HSTRING> for std::ffi::OsStr {
338    fn eq(&self, other: &&HSTRING) -> bool {
339        **other == *self
340    }
341}
342
343#[cfg(feature = "std")]
344impl PartialEq<HSTRING> for std::ffi::OsString {
345    fn eq(&self, other: &HSTRING) -> bool {
346        *other == **self
347    }
348}
349
350#[cfg(feature = "std")]
351impl PartialEq<HSTRING> for &std::ffi::OsString {
352    fn eq(&self, other: &HSTRING) -> bool {
353        *other == ***self
354    }
355}
356
357#[cfg(feature = "std")]
358impl PartialEq<&HSTRING> for std::ffi::OsString {
359    fn eq(&self, other: &&HSTRING) -> bool {
360        **other == **self
361    }
362}
363
364impl TryFrom<&HSTRING> for String {
365    type Error = alloc::string::FromUtf16Error;
366
367    fn try_from(hstring: &HSTRING) -> core::result::Result<Self, Self::Error> {
368        String::from_utf16(hstring)
369    }
370}
371
372impl TryFrom<HSTRING> for String {
373    type Error = alloc::string::FromUtf16Error;
374
375    fn try_from(hstring: HSTRING) -> core::result::Result<Self, Self::Error> {
376        String::try_from(&hstring)
377    }
378}
379
380#[cfg(feature = "std")]
381impl From<&HSTRING> for std::ffi::OsString {
382    fn from(hstring: &HSTRING) -> Self {
383        hstring.to_os_string()
384    }
385}
386
387#[cfg(feature = "std")]
388impl From<HSTRING> for std::ffi::OsString {
389    fn from(hstring: HSTRING) -> Self {
390        Self::from(&hstring)
391    }
392}