Skip to main content

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    /// Creates an empty `HSTRING` without allocating.
11    pub const fn new() -> Self {
12        Self(core::ptr::null_mut())
13    }
14
15    /// Creates an `HSTRING` from UTF-16 code units.
16    pub fn from_wide(value: &[u16]) -> Self {
17        unsafe { Self::from_wide_iter(value.iter().copied(), value.len()) }
18    }
19
20    /// Converts the string to a lossy UTF-8 `String`.
21    pub fn to_string_lossy(&self) -> String {
22        String::from_utf16_lossy(self)
23    }
24
25    /// Converts the string to an `OsString`.
26    #[cfg(all(feature = "std", windows))]
27    pub fn to_os_string(&self) -> std::ffi::OsString {
28        std::os::windows::ffi::OsStringExt::from_wide(self)
29    }
30
31    /// Returns a display adapter for the string.
32    pub fn display(&self) -> impl core::fmt::Display + '_ {
33        Decode(move || core::char::decode_utf16(self.iter().copied()))
34    }
35
36    /// # Safety
37    /// `len` must not be less than the number of items in `iter`.
38    unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Self {
39        if len == 0 {
40            return Self::new();
41        }
42
43        let ptr = HStringHeader::alloc(len.try_into().unwrap());
44
45        for (index, wide) in iter.enumerate() {
46            debug_assert!(index < len);
47
48            unsafe {
49                (*ptr).data.add(index).write(wide);
50                (*ptr).len = index as u32 + 1;
51            }
52        }
53
54        unsafe {
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            // Keep `as_ptr` on the empty slice null-terminated.
73            const EMPTY: [u16; 1] = [0];
74            &EMPTY[..0]
75        }
76    }
77}
78
79impl Default for HSTRING {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85impl Clone for HSTRING {
86    fn clone(&self) -> Self {
87        if let Some(header) = self.as_header() {
88            Self(header.duplicate())
89        } else {
90            Self::new()
91        }
92    }
93}
94
95impl Drop for HSTRING {
96    fn drop(&mut self) {
97        if let Some(header) = self.as_header() {
98            // Fast-pass strings are borrowed and not reference-counted.
99            unsafe {
100                if header.flags & HSTRING_REFERENCE_FLAG == 0 && header.count.release() == 0 {
101                    HStringHeader::free(self.0);
102                }
103            }
104        }
105    }
106}
107
108unsafe impl Send for HSTRING {}
109unsafe impl Sync for HSTRING {}
110
111impl core::fmt::Debug for HSTRING {
112    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
113        write!(f, "{}", self.display())
114    }
115}
116
117impl From<&str> for HSTRING {
118    fn from(value: &str) -> Self {
119        unsafe { Self::from_wide_iter(value.encode_utf16(), value.len()) }
120    }
121}
122
123impl From<String> for HSTRING {
124    fn from(value: String) -> Self {
125        value.as_str().into()
126    }
127}
128
129impl From<&String> for HSTRING {
130    fn from(value: &String) -> Self {
131        value.as_str().into()
132    }
133}
134
135#[cfg(all(feature = "std", windows))]
136impl From<&std::path::Path> for HSTRING {
137    fn from(value: &std::path::Path) -> Self {
138        value.as_os_str().into()
139    }
140}
141
142#[cfg(all(feature = "std", windows))]
143impl From<&std::ffi::OsStr> for HSTRING {
144    fn from(value: &std::ffi::OsStr) -> Self {
145        unsafe {
146            Self::from_wide_iter(
147                std::os::windows::ffi::OsStrExt::encode_wide(value),
148                value.len(),
149            )
150        }
151    }
152}
153
154#[cfg(all(feature = "std", windows))]
155impl From<std::ffi::OsString> for HSTRING {
156    fn from(value: std::ffi::OsString) -> Self {
157        value.as_os_str().into()
158    }
159}
160
161#[cfg(all(feature = "std", windows))]
162impl From<&std::ffi::OsString> for HSTRING {
163    fn from(value: &std::ffi::OsString) -> Self {
164        value.as_os_str().into()
165    }
166}
167
168impl Eq for HSTRING {}
169
170impl Ord for HSTRING {
171    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
172        self.deref().cmp(other)
173    }
174}
175
176impl core::hash::Hash for HSTRING {
177    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
178        self.deref().hash(hasher);
179    }
180}
181
182impl PartialOrd for HSTRING {
183    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
184        Some(self.cmp(other))
185    }
186}
187
188impl PartialEq for HSTRING {
189    fn eq(&self, other: &Self) -> bool {
190        self.deref() == other.deref()
191    }
192}
193
194impl PartialEq<String> for HSTRING {
195    fn eq(&self, other: &String) -> bool {
196        *self == **other
197    }
198}
199
200impl PartialEq<String> for &HSTRING {
201    fn eq(&self, other: &String) -> bool {
202        **self == **other
203    }
204}
205
206impl PartialEq<&String> for HSTRING {
207    fn eq(&self, other: &&String) -> bool {
208        *self == ***other
209    }
210}
211
212impl PartialEq<str> for HSTRING {
213    fn eq(&self, other: &str) -> bool {
214        self.iter().copied().eq(other.encode_utf16())
215    }
216}
217
218impl PartialEq<str> for &HSTRING {
219    fn eq(&self, other: &str) -> bool {
220        **self == *other
221    }
222}
223
224impl PartialEq<&str> for HSTRING {
225    fn eq(&self, other: &&str) -> bool {
226        *self == **other
227    }
228}
229
230impl PartialEq<HSTRING> for str {
231    fn eq(&self, other: &HSTRING) -> bool {
232        *other == *self
233    }
234}
235
236impl PartialEq<HSTRING> for &str {
237    fn eq(&self, other: &HSTRING) -> bool {
238        *other == **self
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 String {
249    fn eq(&self, other: &HSTRING) -> bool {
250        *other == **self
251    }
252}
253
254impl PartialEq<HSTRING> for &String {
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
266#[cfg(all(feature = "std", windows))]
267impl PartialEq<std::ffi::OsString> for HSTRING {
268    fn eq(&self, other: &std::ffi::OsString) -> bool {
269        *self == **other
270    }
271}
272
273#[cfg(all(feature = "std", windows))]
274impl PartialEq<std::ffi::OsString> for &HSTRING {
275    fn eq(&self, other: &std::ffi::OsString) -> bool {
276        **self == **other
277    }
278}
279
280#[cfg(all(feature = "std", windows))]
281impl PartialEq<&std::ffi::OsString> for HSTRING {
282    fn eq(&self, other: &&std::ffi::OsString) -> bool {
283        *self == ***other
284    }
285}
286
287#[cfg(all(feature = "std", windows))]
288impl PartialEq<std::ffi::OsStr> for HSTRING {
289    fn eq(&self, other: &std::ffi::OsStr) -> bool {
290        self.iter()
291            .copied()
292            .eq(std::os::windows::ffi::OsStrExt::encode_wide(other))
293    }
294}
295
296#[cfg(all(feature = "std", windows))]
297impl PartialEq<std::ffi::OsStr> for &HSTRING {
298    fn eq(&self, other: &std::ffi::OsStr) -> bool {
299        **self == *other
300    }
301}
302
303#[cfg(all(feature = "std", windows))]
304impl PartialEq<&std::ffi::OsStr> for HSTRING {
305    fn eq(&self, other: &&std::ffi::OsStr) -> bool {
306        *self == **other
307    }
308}
309
310#[cfg(all(feature = "std", windows))]
311impl PartialEq<HSTRING> for std::ffi::OsStr {
312    fn eq(&self, other: &HSTRING) -> bool {
313        *other == *self
314    }
315}
316
317#[cfg(all(feature = "std", windows))]
318impl PartialEq<HSTRING> for &std::ffi::OsStr {
319    fn eq(&self, other: &HSTRING) -> bool {
320        *other == **self
321    }
322}
323
324#[cfg(all(feature = "std", windows))]
325impl PartialEq<&HSTRING> for std::ffi::OsStr {
326    fn eq(&self, other: &&HSTRING) -> bool {
327        **other == *self
328    }
329}
330
331#[cfg(all(feature = "std", windows))]
332impl PartialEq<HSTRING> for std::ffi::OsString {
333    fn eq(&self, other: &HSTRING) -> bool {
334        *other == **self
335    }
336}
337
338#[cfg(all(feature = "std", windows))]
339impl PartialEq<HSTRING> for &std::ffi::OsString {
340    fn eq(&self, other: &HSTRING) -> bool {
341        *other == ***self
342    }
343}
344
345#[cfg(all(feature = "std", windows))]
346impl PartialEq<&HSTRING> for std::ffi::OsString {
347    fn eq(&self, other: &&HSTRING) -> bool {
348        **other == **self
349    }
350}
351
352impl TryFrom<&HSTRING> for String {
353    type Error = alloc::string::FromUtf16Error;
354
355    fn try_from(hstring: &HSTRING) -> Result<Self, Self::Error> {
356        Self::from_utf16(hstring)
357    }
358}
359
360impl TryFrom<HSTRING> for String {
361    type Error = alloc::string::FromUtf16Error;
362
363    fn try_from(hstring: HSTRING) -> Result<Self, Self::Error> {
364        Self::try_from(&hstring)
365    }
366}
367
368#[cfg(all(feature = "std", windows))]
369impl From<&HSTRING> for std::ffi::OsString {
370    fn from(hstring: &HSTRING) -> Self {
371        hstring.to_os_string()
372    }
373}
374
375#[cfg(all(feature = "std", windows))]
376impl From<HSTRING> for std::ffi::OsString {
377    fn from(hstring: HSTRING) -> Self {
378        Self::from(&hstring)
379    }
380}