wraith/structures/
unicode_string.rs

1//! UNICODE_STRING structure with safe access
2
3use core::slice;
4
5/// raw UNICODE_STRING structure matching Windows definition
6#[repr(C)]
7#[derive(Debug)]
8pub struct UnicodeString {
9    pub length: u16,         // length in bytes (not chars), not including null
10    pub maximum_length: u16, // total buffer size in bytes
11    pub buffer: *mut u16,    // wide string pointer
12}
13
14impl UnicodeString {
15    /// check if string is empty or null
16    pub fn is_empty(&self) -> bool {
17        self.length == 0 || self.buffer.is_null()
18    }
19
20    /// get string as wide char slice (without null terminator)
21    ///
22    /// # Safety
23    /// buffer must be valid for length bytes
24    pub unsafe fn as_slice(&self) -> &[u16] {
25        if self.is_empty() {
26            return &[];
27        }
28        let char_count = (self.length / 2) as usize;
29        // SAFETY: caller guarantees buffer validity
30        unsafe { slice::from_raw_parts(self.buffer, char_count) }
31    }
32
33    /// convert to Rust String
34    ///
35    /// # Safety
36    /// buffer must be valid
37    pub unsafe fn to_string(&self) -> String {
38        let slice = unsafe { self.as_slice() };
39        String::from_utf16_lossy(slice)
40    }
41
42    /// convert to lowercase String for comparison
43    ///
44    /// # Safety
45    /// buffer must be valid
46    pub unsafe fn to_string_lowercase(&self) -> String {
47        unsafe { self.to_string() }.to_lowercase()
48    }
49
50    /// case-insensitive comparison with str
51    ///
52    /// # Safety
53    /// buffer must be valid
54    pub unsafe fn eq_ignore_case(&self, other: &str) -> bool {
55        let self_str = unsafe { self.to_string_lowercase() };
56        self_str == other.to_lowercase()
57    }
58
59    /// case-insensitive comparison with wide string
60    ///
61    /// # Safety
62    /// buffer must be valid
63    pub unsafe fn eq_wide_ignore_case(&self, other: &[u16]) -> bool {
64        let slice = unsafe { self.as_slice() };
65        if slice.len() != other.len() {
66            return false;
67        }
68        slice.iter().zip(other.iter()).all(|(&a, &b)| {
69            // simple ASCII case-insensitive comparison
70            let a_lower = if a >= 'A' as u16 && a <= 'Z' as u16 {
71                a + 32
72            } else {
73                a
74            };
75            let b_lower = if b >= 'A' as u16 && b <= 'Z' as u16 {
76                b + 32
77            } else {
78                b
79            };
80            a_lower == b_lower
81        })
82    }
83}
84
85/// helper to convert &str to wide string for comparison
86pub fn str_to_wide(s: &str) -> Vec<u16> {
87    s.encode_utf16().collect()
88}