wraith/structures/
unicode_string.rs

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