nt_string/unicode_string/
strmut.rs

1// Copyright 2023 Colin Finck <colin@reactos.org>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use core::cmp::Ordering;
5use core::marker::PhantomData;
6use core::ops::Deref;
7use core::{fmt, mem, slice};
8
9use widestring::{U16CStr, U16Str};
10
11use crate::error::Result;
12use crate::helpers::RawNtString;
13use crate::NtStringError;
14
15use super::{impl_eq, impl_partial_cmp, NtUnicodeStr};
16
17/// A mutable reference to a `UNICODE_STRING` (equivalent of `&mut str`).
18///
19/// See the [module-level documentation](super) for more details.
20#[derive(Debug)]
21#[repr(transparent)]
22pub struct NtUnicodeStrMut<'a> {
23    raw: RawNtString<*mut u16>,
24    _lifetime: PhantomData<&'a ()>,
25}
26
27impl<'a> NtUnicodeStrMut<'a> {
28    /// Returns a `*mut NtUnicodeStrMut` pointer
29    /// (mainly for non-Rust interfaces that expect a mutable `UNICODE_STRING*`).
30    pub fn as_mut_ptr(&mut self) -> *mut Self {
31        self as *mut Self
32    }
33
34    /// Returns a mutable slice to the raw `u16` codepoints of the string.
35    pub fn as_mut_slice(&mut self) -> &'a mut [u16] {
36        unsafe { slice::from_raw_parts_mut(self.raw.buffer, self.len_in_elements()) }
37    }
38
39    /// Returns a mutable [`U16Str`] reference for this string.
40    ///
41    /// The [`U16Str`] will only contain the characters and not the NUL terminator.
42    pub fn as_mut_u16str(&mut self) -> &'a U16Str {
43        U16Str::from_slice_mut(self.as_mut_slice())
44    }
45
46    /// Returns an immutable [`NtUnicodeStr`] reference for this string.
47    pub fn as_unicode_str(&'a self) -> &NtUnicodeStr<'a> {
48        self.deref()
49    }
50
51    /// Truncates this string, removing all contents.
52    ///
53    /// While this means the string will have a length of zero, it does not touch its capacity.
54    pub fn clear(&mut self) {
55        self.raw.length = 0;
56    }
57
58    /// Creates an [`NtUnicodeStrMut`] from a [`u16`] string buffer, a byte length of the string,
59    /// and a buffer capacity in bytes (also known as "maximum length").
60    ///
61    /// The string is expected to consist of valid UTF-16 characters.
62    /// The buffer may or may not be NUL-terminated.
63    /// In any case, `length` does NOT include the terminating NUL character.
64    ///
65    /// This function is `unsafe` and you are advised to use any of the safe `try_from_*`
66    /// functions over this one if possible.
67    ///
68    /// # Safety
69    ///
70    /// Behavior is undefined if any of the following conditions are violated:
71    ///
72    /// * `length` must be less than or equal to `maximum_length`.
73    /// * `buffer` must be valid for at least `maximum_length` bytes.
74    /// * `buffer` must point to `length` consecutive properly initialized bytes.
75    /// * `buffer` must be valid for the duration of lifetime `'a`.
76    pub const unsafe fn from_raw_parts(buffer: *mut u16, length: u16, maximum_length: u16) -> Self {
77        debug_assert!(length <= maximum_length);
78
79        Self {
80            raw: RawNtString {
81                length,
82                maximum_length,
83                buffer,
84            },
85            _lifetime: PhantomData,
86        }
87    }
88
89    /// Removes the last character from the string and returns it.
90    ///
91    /// An [`NtStringError::UnpairedUtf16Surrogate`] error is returned if the last character is an unpaired surrogate.
92    /// In that case, the unpaired surrogate codepoint is removed from the string anyway.
93    ///
94    /// `None` is returned if this string is empty.
95    ///
96    /// [`NtStringError::UnpairedUtf16Surrogate`]: crate::error::NtStringError::UnpairedUtf16Surrogate
97    pub fn pop(&mut self) -> Option<Result<char>> {
98        match self.chars().rev().next()? {
99            Ok(c) => {
100                self.raw.length -= (c.len_utf16() * mem::size_of::<u16>()) as u16;
101                Some(Ok(c))
102            }
103            Err(e) => {
104                // An unpaired surrogate is always a single character.
105                self.raw.length -= mem::size_of::<u16>() as u16;
106                Some(Err(e))
107            }
108        }
109    }
110
111    /// Creates an [`NtUnicodeStrMut`] from an existing [`u16`] string buffer without a terminating NUL character.
112    ///
113    /// The string is expected to consist of valid UTF-16 characters.
114    ///
115    /// The given buffer becomes the internal buffer of the [`NtUnicodeStrMut`] and therefore won't be NUL-terminated.
116    /// See the [module-level documentation](super) for the implications of that.
117    ///
118    /// This function has *O*(1) complexity.
119    ///
120    /// If you have a NUL-terminated buffer, either use [`try_from_u16_until_nul`] or convert from a [`U16CStr`]
121    /// using the corresponding [`TryFrom`] implementation.
122    ///
123    /// [`try_from_u16_until_nul`]: Self::try_from_u16_until_nul
124    pub fn try_from_u16(buffer: &mut [u16]) -> Result<Self> {
125        let unicode_str = NtUnicodeStr::try_from_u16(buffer)?;
126
127        // SAFETY: `unicode_str` was created from a mutable `buffer` and
128        // `NtUnicodeStr` and `NtUnicodeStrMut` have the same memory layout,
129        // so we can safely transmute `NtUnicodeStr` to `NtUnicodeStrMut`.
130        let unicode_str_mut = unsafe { mem::transmute(unicode_str) };
131
132        Ok(unicode_str_mut)
133    }
134
135    /// Creates an [`NtUnicodeStrMut`] from an existing [`u16`] string buffer that contains at least one NUL character.
136    ///
137    /// The string is expected to consist of valid UTF-16 characters.
138    ///
139    /// The string will be terminated at the NUL character.
140    /// An [`NtStringError::NulNotFound`] error is returned if no NUL character could be found.
141    /// As a consequence, this function has *O*(*n*) complexity.
142    ///
143    /// The resulting internal `buffer` of [`NtUnicodeStrMut`] will be NUL-terminated.
144    /// See the [module-level documentation](super) for the implications of that.
145    ///
146    /// Use [`try_from_u16`] if you have a buffer that is not NUL-terminated.
147    /// You can also convert from a NUL-terminated [`U16CStr`] in *O*(1) via the corresponding [`TryFrom`] implementation.
148    ///
149    /// [`try_from_u16`]: Self::try_from_u16
150    pub fn try_from_u16_until_nul(buffer: &mut [u16]) -> Result<Self> {
151        let unicode_str = NtUnicodeStr::try_from_u16_until_nul(buffer)?;
152
153        // SAFETY: `unicode_str` was created from a mutable `buffer` and
154        // `NtUnicodeStr` and `NtUnicodeStrMut` have the same memory layout,
155        // so we can safely transmute `NtUnicodeStr` to `NtUnicodeStrMut`.
156        let unicode_str_mut = unsafe { mem::transmute(unicode_str) };
157
158        Ok(unicode_str_mut)
159    }
160}
161
162impl<'a> Deref for NtUnicodeStrMut<'a> {
163    type Target = NtUnicodeStr<'a>;
164
165    fn deref(&self) -> &Self::Target {
166        // SAFETY: `NtUnicodeStr` and `NtUnicodeStrMut` have the same memory layout,
167        // with the additional constraint of an immutable `buffer` pointer in `NtUnicodeStr`,
168        // so we can safely transmute `NtUnicodeStrMut` to `NtUnicodeStr`.
169        unsafe { mem::transmute(self) }
170    }
171}
172
173impl<'a> fmt::Display for NtUnicodeStrMut<'a> {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        fmt::Display::fmt(self.deref(), f)
176    }
177}
178
179impl<'a> Eq for NtUnicodeStrMut<'a> {}
180
181impl<'a> Ord for NtUnicodeStrMut<'a> {
182    fn cmp(&self, other: &Self) -> Ordering {
183        Ord::cmp(self.deref(), other.deref())
184    }
185}
186
187impl<'a> TryFrom<&'a mut U16CStr> for NtUnicodeStrMut<'a> {
188    type Error = NtStringError;
189
190    /// Converts a mutable [`U16CStr`] reference into an [`NtUnicodeStrMut`].
191    ///
192    /// The internal buffer will be NUL-terminated.
193    /// See the [module-level documentation](super) for the implications of that.
194    fn try_from(value: &'a mut U16CStr) -> Result<Self> {
195        let unicode_str = NtUnicodeStr::try_from(&*value)?;
196
197        // SAFETY: `unicode_str` was created from a mutable `value` and
198        // `NtUnicodeStr` and `NtUnicodeStrMut` have the same memory layout,
199        // so we can safely transmute `NtUnicodeStr` to `NtUnicodeStrMut`.
200        let unicode_str_mut = unsafe { mem::transmute(unicode_str) };
201
202        Ok(unicode_str_mut)
203    }
204}
205
206impl<'a> TryFrom<&'a mut U16Str> for NtUnicodeStrMut<'a> {
207    type Error = NtStringError;
208
209    /// Converts a [`U16Str`] reference into an [`NtUnicodeStr`].
210    ///
211    /// The internal buffer will NOT be NUL-terminated.
212    /// See the [module-level documentation](super) for the implications of that.
213    fn try_from(value: &'a mut U16Str) -> Result<Self> {
214        Self::try_from_u16(value.as_mut_slice())
215    }
216}
217
218impl_eq! { NtUnicodeStrMut<'a>, NtUnicodeStrMut<'b> }
219impl_eq! { NtUnicodeStr<'a>, NtUnicodeStrMut<'b> }
220impl_eq! { NtUnicodeStrMut<'a>, NtUnicodeStr<'b> }
221impl_eq! { NtUnicodeStrMut<'a>, str }
222impl_eq! { str, NtUnicodeStrMut<'a> }
223impl_eq! { NtUnicodeStrMut<'a>, &str }
224impl_eq! { &str, NtUnicodeStrMut<'a> }
225
226impl_partial_cmp! { NtUnicodeStrMut<'a>, NtUnicodeStrMut<'b> }
227impl_partial_cmp! { NtUnicodeStr<'a>, NtUnicodeStrMut<'b> }
228impl_partial_cmp! { NtUnicodeStrMut<'a>, NtUnicodeStr<'b> }
229impl_partial_cmp! { NtUnicodeStrMut<'a>, str }
230impl_partial_cmp! { str, NtUnicodeStrMut<'a> }
231impl_partial_cmp! { NtUnicodeStrMut<'a>, &str }
232impl_partial_cmp! { &str, NtUnicodeStrMut<'a> }