1 | use 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)] |
6 | pub struct HSTRING(Option<std::ptr::NonNull<Header>>); |
7 | |
8 | impl 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 | |
94 | impl RuntimeType for HSTRING { |
95 | const SIGNATURE: crate::imp::ConstBuffer = crate::imp::ConstBuffer::from_slice(b"string" ); |
96 | } |
97 | |
98 | impl TypeKind for HSTRING { |
99 | type TypeKind = ValueType; |
100 | } |
101 | |
102 | impl Default for HSTRING { |
103 | fn default() -> Self { |
104 | Self::new() |
105 | } |
106 | } |
107 | |
108 | impl 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 | |
118 | impl 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 | |
137 | unsafe impl Send for HSTRING {} |
138 | unsafe impl Sync for HSTRING {} |
139 | |
140 | impl 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 | |
146 | impl std::fmt::Debug for HSTRING { |
147 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
148 | write!(f, " \"{}\"" , self) |
149 | } |
150 | } |
151 | |
152 | impl 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 | |
158 | impl std::convert::From<std::string::String> for HSTRING { |
159 | fn from(value: std::string::String) -> Self { |
160 | value.as_str().into() |
161 | } |
162 | } |
163 | |
164 | impl 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)] |
171 | impl 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)] |
178 | impl 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)] |
185 | impl 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)] |
192 | impl 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 | |
198 | impl Eq for HSTRING {} |
199 | |
200 | impl Ord for HSTRING { |
201 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
202 | self.as_wide().cmp(other.as_wide()) |
203 | } |
204 | } |
205 | |
206 | impl PartialOrd for HSTRING { |
207 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
208 | Some(self.cmp(other)) |
209 | } |
210 | } |
211 | |
212 | impl PartialEq for HSTRING { |
213 | fn eq(&self, other: &Self) -> bool { |
214 | *self.as_wide() == *other.as_wide() |
215 | } |
216 | } |
217 | |
218 | impl PartialEq<std::string::String> for HSTRING { |
219 | fn eq(&self, other: &std::string::String) -> bool { |
220 | *self == **other |
221 | } |
222 | } |
223 | |
224 | impl PartialEq<std::string::String> for &HSTRING { |
225 | fn eq(&self, other: &std::string::String) -> bool { |
226 | **self == **other |
227 | } |
228 | } |
229 | |
230 | impl PartialEq<&std::string::String> for HSTRING { |
231 | fn eq(&self, other: &&std::string::String) -> bool { |
232 | *self == ***other |
233 | } |
234 | } |
235 | |
236 | impl PartialEq<str> for HSTRING { |
237 | fn eq(&self, other: &str) -> bool { |
238 | self.as_wide().iter().copied().eq(other.encode_utf16()) |
239 | } |
240 | } |
241 | |
242 | impl PartialEq<str> for &HSTRING { |
243 | fn eq(&self, other: &str) -> bool { |
244 | **self == *other |
245 | } |
246 | } |
247 | |
248 | impl PartialEq<&str> for HSTRING { |
249 | fn eq(&self, other: &&str) -> bool { |
250 | *self == **other |
251 | } |
252 | } |
253 | |
254 | impl PartialEq<HSTRING> for str { |
255 | fn eq(&self, other: &HSTRING) -> bool { |
256 | *other == *self |
257 | } |
258 | } |
259 | |
260 | impl PartialEq<HSTRING> for &str { |
261 | fn eq(&self, other: &HSTRING) -> bool { |
262 | *other == **self |
263 | } |
264 | } |
265 | |
266 | impl PartialEq<&HSTRING> for str { |
267 | fn eq(&self, other: &&HSTRING) -> bool { |
268 | **other == *self |
269 | } |
270 | } |
271 | |
272 | impl PartialEq<HSTRING> for std::string::String { |
273 | fn eq(&self, other: &HSTRING) -> bool { |
274 | *other == **self |
275 | } |
276 | } |
277 | |
278 | impl PartialEq<HSTRING> for &std::string::String { |
279 | fn eq(&self, other: &HSTRING) -> bool { |
280 | *other == ***self |
281 | } |
282 | } |
283 | |
284 | impl PartialEq<&HSTRING> for std::string::String { |
285 | fn eq(&self, other: &&HSTRING) -> bool { |
286 | **other == **self |
287 | } |
288 | } |
289 | |
290 | #[cfg (windows)] |
291 | impl PartialEq<std::ffi::OsString> for HSTRING { |
292 | fn eq(&self, other: &std::ffi::OsString) -> bool { |
293 | *self == **other |
294 | } |
295 | } |
296 | |
297 | #[cfg (windows)] |
298 | impl PartialEq<std::ffi::OsString> for &HSTRING { |
299 | fn eq(&self, other: &std::ffi::OsString) -> bool { |
300 | **self == **other |
301 | } |
302 | } |
303 | |
304 | #[cfg (windows)] |
305 | impl PartialEq<&std::ffi::OsString> for HSTRING { |
306 | fn eq(&self, other: &&std::ffi::OsString) -> bool { |
307 | *self == ***other |
308 | } |
309 | } |
310 | |
311 | #[cfg (windows)] |
312 | impl 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)] |
319 | impl PartialEq<std::ffi::OsStr> for &HSTRING { |
320 | fn eq(&self, other: &std::ffi::OsStr) -> bool { |
321 | **self == *other |
322 | } |
323 | } |
324 | |
325 | #[cfg (windows)] |
326 | impl PartialEq<&std::ffi::OsStr> for HSTRING { |
327 | fn eq(&self, other: &&std::ffi::OsStr) -> bool { |
328 | *self == **other |
329 | } |
330 | } |
331 | |
332 | #[cfg (windows)] |
333 | impl PartialEq<HSTRING> for std::ffi::OsStr { |
334 | fn eq(&self, other: &HSTRING) -> bool { |
335 | *other == *self |
336 | } |
337 | } |
338 | |
339 | #[cfg (windows)] |
340 | impl PartialEq<HSTRING> for &std::ffi::OsStr { |
341 | fn eq(&self, other: &HSTRING) -> bool { |
342 | *other == **self |
343 | } |
344 | } |
345 | |
346 | #[cfg (windows)] |
347 | impl PartialEq<&HSTRING> for std::ffi::OsStr { |
348 | fn eq(&self, other: &&HSTRING) -> bool { |
349 | **other == *self |
350 | } |
351 | } |
352 | |
353 | #[cfg (windows)] |
354 | impl PartialEq<HSTRING> for std::ffi::OsString { |
355 | fn eq(&self, other: &HSTRING) -> bool { |
356 | *other == **self |
357 | } |
358 | } |
359 | |
360 | #[cfg (windows)] |
361 | impl PartialEq<HSTRING> for &std::ffi::OsString { |
362 | fn eq(&self, other: &HSTRING) -> bool { |
363 | *other == ***self |
364 | } |
365 | } |
366 | |
367 | #[cfg (windows)] |
368 | impl PartialEq<&HSTRING> for std::ffi::OsString { |
369 | fn eq(&self, other: &&HSTRING) -> bool { |
370 | **other == **self |
371 | } |
372 | } |
373 | |
374 | impl<'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 | |
382 | impl 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)] |
391 | impl<'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)] |
398 | impl std::convert::From<HSTRING> for std::ffi::OsString { |
399 | fn from(hstring: HSTRING) -> Self { |
400 | Self::from(&hstring) |
401 | } |
402 | } |
403 | |
404 | impl IntoParam<PCWSTR> for &HSTRING { |
405 | fn into_param(self) -> Param<PCWSTR> { |
406 | Param::Owned(PCWSTR(self.as_ptr())) |
407 | } |
408 | } |
409 | |
410 | const REFERENCE_FLAG: u32 = 1; |
411 | |
412 | #[repr (C)] |
413 | struct 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 | |
423 | impl 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 | |