Skip to main content

nt_string/unicode_string/
strmut.rs

1// Copyright 2023-2026 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) -> &'a 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().next_back()? {
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 length = NtUnicodeStr::try_length_from_u16(buffer)?;
126
127        Ok(Self {
128            raw: RawNtString {
129                length,
130                maximum_length: length,
131                buffer: buffer.as_mut_ptr(),
132            },
133            _lifetime: PhantomData,
134        })
135    }
136
137    /// Creates an [`NtUnicodeStrMut`] from an existing [`u16`] string buffer that contains at least one NUL character.
138    ///
139    /// The string is expected to consist of valid UTF-16 characters.
140    ///
141    /// The string will be terminated at the NUL character.
142    /// An [`NtStringError::NulNotFound`] error is returned if no NUL character could be found.
143    /// As a consequence, this function has *O*(*n*) complexity.
144    ///
145    /// The resulting internal `buffer` of [`NtUnicodeStrMut`] will be NUL-terminated.
146    /// See the [module-level documentation](super) for the implications of that.
147    ///
148    /// Use [`try_from_u16`] if you have a buffer that is not NUL-terminated.
149    /// You can also convert from a NUL-terminated [`U16CStr`] in *O*(1) via the corresponding [`TryFrom`] implementation.
150    ///
151    /// [`try_from_u16`]: Self::try_from_u16
152    pub fn try_from_u16_until_nul(buffer: &mut [u16]) -> Result<Self> {
153        let (length, maximum_length) = NtUnicodeStr::try_length_from_u16_until_nul(buffer)?;
154
155        Ok(Self {
156            raw: RawNtString {
157                length,
158                maximum_length,
159                buffer: buffer.as_mut_ptr(),
160            },
161            _lifetime: PhantomData,
162        })
163    }
164}
165
166impl<'a> Deref for NtUnicodeStrMut<'a> {
167    type Target = NtUnicodeStr<'a>;
168
169    fn deref(&self) -> &Self::Target {
170        // SAFETY: `NtUnicodeStr` and `NtUnicodeStrMut` have the same memory layout,
171        // with the additional constraint of an immutable `buffer` pointer in `NtUnicodeStr`,
172        // so we can safely transmute `NtUnicodeStrMut` to `NtUnicodeStr`.
173        unsafe { mem::transmute(self) }
174    }
175}
176
177impl<'a> fmt::Display for NtUnicodeStrMut<'a> {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        fmt::Display::fmt(self.deref(), f)
180    }
181}
182
183impl<'a> Eq for NtUnicodeStrMut<'a> {}
184
185impl<'a> Ord for NtUnicodeStrMut<'a> {
186    fn cmp(&self, other: &Self) -> Ordering {
187        Ord::cmp(self.deref(), other.deref())
188    }
189}
190
191impl<'a> TryFrom<&'a mut U16CStr> for NtUnicodeStrMut<'a> {
192    type Error = NtStringError;
193
194    /// Converts a mutable [`U16CStr`] reference into an [`NtUnicodeStrMut`].
195    ///
196    /// The internal buffer will be NUL-terminated.
197    /// See the [module-level documentation](super) for the implications of that.
198    fn try_from(value: &'a mut U16CStr) -> Result<Self> {
199        let (length, maximum_length) = NtUnicodeStr::try_length_from_u16_cstr(value)?;
200
201        Ok(Self {
202            raw: RawNtString {
203                length,
204                maximum_length,
205                buffer: value.as_mut_ptr(),
206            },
207            _lifetime: PhantomData,
208        })
209    }
210}
211
212impl<'a> TryFrom<&'a mut U16Str> for NtUnicodeStrMut<'a> {
213    type Error = NtStringError;
214
215    /// Converts a [`U16Str`] reference into an [`NtUnicodeStr`].
216    ///
217    /// The internal buffer will NOT be NUL-terminated.
218    /// See the [module-level documentation](super) for the implications of that.
219    fn try_from(value: &'a mut U16Str) -> Result<Self> {
220        Self::try_from_u16(value.as_mut_slice())
221    }
222}
223
224impl_eq! { NtUnicodeStrMut<'a>, NtUnicodeStrMut<'b> }
225impl_eq! { NtUnicodeStr<'a>, NtUnicodeStrMut<'b> }
226impl_eq! { NtUnicodeStrMut<'a>, NtUnicodeStr<'b> }
227impl_eq! { NtUnicodeStrMut<'a>, str }
228impl_eq! { str, NtUnicodeStrMut<'a> }
229impl_eq! { NtUnicodeStrMut<'a>, &str }
230impl_eq! { &str, NtUnicodeStrMut<'a> }
231
232impl_partial_cmp! { NtUnicodeStrMut<'a>, NtUnicodeStrMut<'b> }
233impl_partial_cmp! { NtUnicodeStr<'a>, NtUnicodeStrMut<'b> }
234impl_partial_cmp! { NtUnicodeStrMut<'a>, NtUnicodeStr<'b> }
235impl_partial_cmp! { NtUnicodeStrMut<'a>, str }
236impl_partial_cmp! { str, NtUnicodeStrMut<'a> }
237impl_partial_cmp! { NtUnicodeStrMut<'a>, &str }
238impl_partial_cmp! { &str, NtUnicodeStrMut<'a> }