windows_strings/
hstring_builder.rs

1use super::*;
2
3/// An [HSTRING] builder that supports preallocating the `HSTRING` to avoid extra allocations and copies.
4///
5/// This is similar to the `WindowsPreallocateStringBuffer` function but implemented directly in Rust for efficiency.
6/// It is implemented as a separate type since [HSTRING] values are immutable.
7pub struct HStringBuilder(*mut HStringHeader);
8
9impl HStringBuilder {
10    /// Creates a preallocated `HSTRING` value.
11    pub fn new(len: usize) -> Self {
12        let header = HStringHeader::alloc(len.try_into().unwrap());
13
14        if len > 0 {
15            unsafe { core::ptr::write_bytes((*header).data, 0, len) };
16        }
17
18        Self(header)
19    }
20
21    /// Shortens the string by removing any trailing 0 characters.
22    pub fn trim_end(&mut self) {
23        if let Some(header) = self.as_header_mut() {
24            while header.len > 0
25                && unsafe { header.data.offset(header.len as isize - 1).read() == 0 }
26            {
27                header.len -= 1;
28            }
29
30            if header.len == 0 {
31                unsafe {
32                    HStringHeader::free(self.0);
33                }
34                self.0 = core::ptr::null_mut();
35            }
36        }
37    }
38
39    /// Allows the `HSTRING` to be constructed from bytes.
40    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
41        if let Some(header) = self.as_header() {
42            unsafe {
43                core::slice::from_raw_parts_mut(header.data as *mut _, header.len as usize * 2)
44            }
45        } else {
46            &mut []
47        }
48    }
49
50    fn as_header(&self) -> Option<&HStringHeader> {
51        unsafe { self.0.as_ref() }
52    }
53
54    fn as_header_mut(&mut self) -> Option<&mut HStringHeader> {
55        unsafe { self.0.as_mut() }
56    }
57}
58
59impl From<HStringBuilder> for HSTRING {
60    fn from(value: HStringBuilder) -> Self {
61        if let Some(header) = value.as_header() {
62            unsafe { header.data.offset(header.len as isize).write(0) };
63            let result = Self(value.0);
64            core::mem::forget(value);
65            result
66        } else {
67            Self::new()
68        }
69    }
70}
71
72impl core::ops::Deref for HStringBuilder {
73    type Target = [u16];
74
75    fn deref(&self) -> &[u16] {
76        if let Some(header) = self.as_header() {
77            unsafe { core::slice::from_raw_parts(header.data, header.len as usize) }
78        } else {
79            &[]
80        }
81    }
82}
83
84impl core::ops::DerefMut for HStringBuilder {
85    fn deref_mut(&mut self) -> &mut [u16] {
86        if let Some(header) = self.as_header() {
87            unsafe { core::slice::from_raw_parts_mut(header.data, header.len as usize) }
88        } else {
89            &mut []
90        }
91    }
92}
93
94impl Drop for HStringBuilder {
95    fn drop(&mut self) {
96        unsafe {
97            HStringHeader::free(self.0);
98        }
99    }
100}