1use super::*;
2
3/// A WinRT string ([HSTRING](https://docs.microsoft.com/en-us/windows/win32/winrt/hstring))
4/// is reference-counted and immutable.
5#[repr(transparent)]
6pub struct HSTRING(Option<std::ptr::NonNull<Header>>);
7
8impl HSTRING {
9 /// Create an empty `HSTRING`.
10 ///
11 /// This function does not allocate memory.
12 pub const fn new() -> Self {
13 Self(None)
14 }
15
16 /// Returns `true` if the string is empty.
17 pub const fn is_empty(&self) -> bool {
18 // An empty HSTRING is represented by a null pointer.
19 self.0.is_none()
20 }
21
22 /// Returns the length of the string.
23 pub fn len(&self) -> usize {
24 if let Some(header) = self.get_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 { std::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.get_header() {
39 header.data
40 } else {
41 const EMPTY: [u16; 1] = [0];
42 EMPTY.as_ptr()
43 }
44 }
45
46 /// Create a `HSTRING` from a slice of 16 bit characters (wchars).
47 pub fn from_wide(value: &[u16]) -> Result<Self> {
48 unsafe { Self::from_wide_iter(value.iter().copied(), value.len()) }
49 }
50
51 /// Get the contents of this `HSTRING` as a String lossily.
52 pub fn to_string_lossy(&self) -> std::string::String {
53 std::string::String::from_utf16_lossy(self.as_wide())
54 }
55
56 /// Get the contents of this `HSTRING` as a OsString.
57 #[cfg(windows)]
58 pub fn to_os_string(&self) -> std::ffi::OsString {
59 std::os::windows::ffi::OsStringExt::from_wide(self.as_wide())
60 }
61
62 /// # Safety
63 /// len must not be less than the number of items in the iterator.
64 unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Result<Self> {
65 if len == 0 {
66 return Ok(Self::new());
67 }
68
69 let ptr = Header::alloc(len.try_into()?)?;
70
71 // Place each utf-16 character into the buffer and
72 // increase len as we go along.
73 for (index, wide) in iter.enumerate() {
74 debug_assert!(index < len);
75
76 std::ptr::write((*ptr).data.add(index), wide);
77 (*ptr).len = index as u32 + 1;
78 }
79
80 // Write a 0 byte to the end of the buffer.
81 std::ptr::write((*ptr).data.offset((*ptr).len as isize), 0);
82 Ok(Self(std::ptr::NonNull::new(ptr)))
83 }
84
85 fn get_header(&self) -> Option<&Header> {
86 if let Some(header) = &self.0 {
87 unsafe { Some(header.as_ref()) }
88 } else {
89 None
90 }
91 }
92}
93
94impl RuntimeType for HSTRING {
95 const SIGNATURE: crate::imp::ConstBuffer = crate::imp::ConstBuffer::from_slice(b"string");
96}
97
98impl TypeKind for HSTRING {
99 type TypeKind = ValueType;
100}
101
102impl Default for HSTRING {
103 fn default() -> Self {
104 Self::new()
105 }
106}
107
108impl Clone for HSTRING {
109 fn clone(&self) -> Self {
110 if let Some(header: &Header) = self.get_header() {
111 Self(std::ptr::NonNull::new(ptr:header.duplicate().unwrap()))
112 } else {
113 Self::new()
114 }
115 }
116}
117
118impl Drop for HSTRING {
119 fn drop(&mut self) {
120 if self.is_empty() {
121 return;
122 }
123
124 if let Some(header: NonNull
) = self.0.take() {
125 // REFERENCE_FLAG indicates a string backed by static or stack memory that is
126 // thus not reference-counted and does not need to be freed.
127 unsafe {
128 let header: &Header = header.as_ref();
129 if header.flags & REFERENCE_FLAG == 0 && header.count.release() == 0 {
130 crate::imp::heap_free(ptr:header as *const _ as *mut _);
131 }
132 }
133 }
134 }
135}
136
137unsafe impl Send for HSTRING {}
138unsafe impl Sync for HSTRING {}
139
140impl std::fmt::Display for HSTRING {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 write!(f, "{}", Decode(|| std::char::decode_utf16(self.as_wide().iter().cloned())))
143 }
144}
145
146impl std::fmt::Debug for HSTRING {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 write!(f, "\"{}\"", self)
149 }
150}
151
152impl std::convert::From<&str> for HSTRING {
153 fn from(value: &str) -> Self {
154 unsafe { Self::from_wide_iter(iter:value.encode_utf16(), value.len()).unwrap() }
155 }
156}
157
158impl std::convert::From<std::string::String> for HSTRING {
159 fn from(value: std::string::String) -> Self {
160 value.as_str().into()
161 }
162}
163
164impl std::convert::From<&std::string::String> for HSTRING {
165 fn from(value: &std::string::String) -> Self {
166 value.as_str().into()
167 }
168}
169
170#[cfg(windows)]
171impl std::convert::From<&std::path::Path> for HSTRING {
172 fn from(value: &std::path::Path) -> Self {
173 value.as_os_str().into()
174 }
175}
176
177#[cfg(windows)]
178impl std::convert::From<&std::ffi::OsStr> for HSTRING {
179 fn from(value: &std::ffi::OsStr) -> Self {
180 unsafe { Self::from_wide_iter(std::os::windows::ffi::OsStrExt::encode_wide(value), value.len()).unwrap() }
181 }
182}
183
184#[cfg(windows)]
185impl std::convert::From<std::ffi::OsString> for HSTRING {
186 fn from(value: std::ffi::OsString) -> Self {
187 value.as_os_str().into()
188 }
189}
190
191#[cfg(windows)]
192impl std::convert::From<&std::ffi::OsString> for HSTRING {
193 fn from(value: &std::ffi::OsString) -> Self {
194 value.as_os_str().into()
195 }
196}
197
198impl Eq for HSTRING {}
199
200impl Ord for HSTRING {
201 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
202 self.as_wide().cmp(other.as_wide())
203 }
204}
205
206impl PartialOrd for HSTRING {
207 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
208 Some(self.cmp(other))
209 }
210}
211
212impl PartialEq for HSTRING {
213 fn eq(&self, other: &Self) -> bool {
214 *self.as_wide() == *other.as_wide()
215 }
216}
217
218impl PartialEq<std::string::String> for HSTRING {
219 fn eq(&self, other: &std::string::String) -> bool {
220 *self == **other
221 }
222}
223
224impl PartialEq<std::string::String> for &HSTRING {
225 fn eq(&self, other: &std::string::String) -> bool {
226 **self == **other
227 }
228}
229
230impl PartialEq<&std::string::String> for HSTRING {
231 fn eq(&self, other: &&std::string::String) -> bool {
232 *self == ***other
233 }
234}
235
236impl PartialEq<str> for HSTRING {
237 fn eq(&self, other: &str) -> bool {
238 self.as_wide().iter().copied().eq(other.encode_utf16())
239 }
240}
241
242impl PartialEq<str> for &HSTRING {
243 fn eq(&self, other: &str) -> bool {
244 **self == *other
245 }
246}
247
248impl PartialEq<&str> for HSTRING {
249 fn eq(&self, other: &&str) -> bool {
250 *self == **other
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 &str {
261 fn eq(&self, other: &HSTRING) -> bool {
262 *other == **self
263 }
264}
265
266impl PartialEq<&HSTRING> for str {
267 fn eq(&self, other: &&HSTRING) -> bool {
268 **other == *self
269 }
270}
271
272impl PartialEq<HSTRING> for std::string::String {
273 fn eq(&self, other: &HSTRING) -> bool {
274 *other == **self
275 }
276}
277
278impl PartialEq<HSTRING> for &std::string::String {
279 fn eq(&self, other: &HSTRING) -> bool {
280 *other == ***self
281 }
282}
283
284impl PartialEq<&HSTRING> for std::string::String {
285 fn eq(&self, other: &&HSTRING) -> bool {
286 **other == **self
287 }
288}
289
290#[cfg(windows)]
291impl PartialEq<std::ffi::OsString> for HSTRING {
292 fn eq(&self, other: &std::ffi::OsString) -> bool {
293 *self == **other
294 }
295}
296
297#[cfg(windows)]
298impl PartialEq<std::ffi::OsString> for &HSTRING {
299 fn eq(&self, other: &std::ffi::OsString) -> bool {
300 **self == **other
301 }
302}
303
304#[cfg(windows)]
305impl PartialEq<&std::ffi::OsString> for HSTRING {
306 fn eq(&self, other: &&std::ffi::OsString) -> bool {
307 *self == ***other
308 }
309}
310
311#[cfg(windows)]
312impl PartialEq<std::ffi::OsStr> for HSTRING {
313 fn eq(&self, other: &std::ffi::OsStr) -> bool {
314 self.as_wide().iter().copied().eq(std::os::windows::ffi::OsStrExt::encode_wide(other))
315 }
316}
317
318#[cfg(windows)]
319impl PartialEq<std::ffi::OsStr> for &HSTRING {
320 fn eq(&self, other: &std::ffi::OsStr) -> bool {
321 **self == *other
322 }
323}
324
325#[cfg(windows)]
326impl PartialEq<&std::ffi::OsStr> for HSTRING {
327 fn eq(&self, other: &&std::ffi::OsStr) -> bool {
328 *self == **other
329 }
330}
331
332#[cfg(windows)]
333impl PartialEq<HSTRING> for std::ffi::OsStr {
334 fn eq(&self, other: &HSTRING) -> bool {
335 *other == *self
336 }
337}
338
339#[cfg(windows)]
340impl PartialEq<HSTRING> for &std::ffi::OsStr {
341 fn eq(&self, other: &HSTRING) -> bool {
342 *other == **self
343 }
344}
345
346#[cfg(windows)]
347impl PartialEq<&HSTRING> for std::ffi::OsStr {
348 fn eq(&self, other: &&HSTRING) -> bool {
349 **other == *self
350 }
351}
352
353#[cfg(windows)]
354impl PartialEq<HSTRING> for std::ffi::OsString {
355 fn eq(&self, other: &HSTRING) -> bool {
356 *other == **self
357 }
358}
359
360#[cfg(windows)]
361impl PartialEq<HSTRING> for &std::ffi::OsString {
362 fn eq(&self, other: &HSTRING) -> bool {
363 *other == ***self
364 }
365}
366
367#[cfg(windows)]
368impl PartialEq<&HSTRING> for std::ffi::OsString {
369 fn eq(&self, other: &&HSTRING) -> bool {
370 **other == **self
371 }
372}
373
374impl<'a> std::convert::TryFrom<&'a HSTRING> for std::string::String {
375 type Error = std::string::FromUtf16Error;
376
377 fn try_from(hstring: &HSTRING) -> std::result::Result<Self, Self::Error> {
378 std::string::String::from_utf16(hstring.as_wide())
379 }
380}
381
382impl std::convert::TryFrom<HSTRING> for std::string::String {
383 type Error = std::string::FromUtf16Error;
384
385 fn try_from(hstring: HSTRING) -> std::result::Result<Self, Self::Error> {
386 std::string::String::try_from(&hstring)
387 }
388}
389
390#[cfg(windows)]
391impl<'a> std::convert::From<&'a HSTRING> for std::ffi::OsString {
392 fn from(hstring: &HSTRING) -> Self {
393 hstring.to_os_string()
394 }
395}
396
397#[cfg(windows)]
398impl std::convert::From<HSTRING> for std::ffi::OsString {
399 fn from(hstring: HSTRING) -> Self {
400 Self::from(&hstring)
401 }
402}
403
404impl IntoParam<PCWSTR> for &HSTRING {
405 fn into_param(self) -> Param<PCWSTR> {
406 Param::Owned(PCWSTR(self.as_ptr()))
407 }
408}
409
410const REFERENCE_FLAG: u32 = 1;
411
412#[repr(C)]
413struct Header {
414 flags: u32,
415 len: u32,
416 _0: u32,
417 _1: u32,
418 data: *mut u16,
419 count: crate::imp::RefCount,
420 buffer_start: u16,
421}
422
423impl Header {
424 fn alloc(len: u32) -> Result<*mut Header> {
425 debug_assert!(len != 0);
426 // Allocate enough space for header and two bytes per character.
427 // The space for the terminating null character is already accounted for inside of `Header`.
428 let alloc_size = std::mem::size_of::<Header>() + 2 * len as usize;
429
430 let header = crate::imp::heap_alloc(alloc_size)? as *mut Header;
431
432 // SAFETY: uses `std::ptr::write` (since `header` is unintialized). `Header` is safe to be all zeros.
433 unsafe {
434 header.write(std::mem::MaybeUninit::<Header>::zeroed().assume_init());
435 (*header).len = len;
436 (*header).count = crate::imp::RefCount::new(1);
437 (*header).data = &mut (*header).buffer_start;
438 }
439 Ok(header)
440 }
441
442 fn duplicate(&self) -> Result<*mut Header> {
443 if self.flags & REFERENCE_FLAG == 0 {
444 // If this is not a "fast pass" string then simply increment the reference count.
445 self.count.add_ref();
446 Ok(self as *const Header as *mut Header)
447 } else {
448 // Otherwise, allocate a new string and copy the value into the new string.
449 let copy = Header::alloc(self.len)?;
450 // SAFETY: since we are duplicating the string it is safe to copy all data from self to the initialized `copy`.
451 // We copy `len + 1` characters since `len` does not account for the terminating null character.
452 unsafe {
453 std::ptr::copy_nonoverlapping(self.data, (*copy).data, self.len as usize + 1);
454 }
455 Ok(copy)
456 }
457 }
458}
459