windows_strings/
literals.rs

1/// A literal UTF-8 string with a trailing null terminator.
2#[macro_export]
3macro_rules! s {
4    ($s:literal) => {
5        $crate::PCSTR::from_raw(::core::concat!($s, '\0').as_ptr())
6    };
7}
8
9/// A literal UTF-16 wide string with a trailing null terminator.
10#[macro_export]
11macro_rules! w {
12    ($s:literal) => {{
13        const INPUT: &[u8] = $s.as_bytes();
14        const OUTPUT_LEN: usize = $crate::utf16_len(INPUT) + 1;
15        const OUTPUT: &[u16; OUTPUT_LEN] = {
16            let mut buffer = [0; OUTPUT_LEN];
17            let mut input_pos = 0;
18            let mut output_pos = 0;
19            while let Some((mut code_point, new_pos)) = $crate::decode_utf8_char(INPUT, input_pos) {
20                input_pos = new_pos;
21                if code_point <= 0xffff {
22                    buffer[output_pos] = code_point as u16;
23                    output_pos += 1;
24                } else {
25                    code_point -= 0x10000;
26                    buffer[output_pos] = 0xd800 + (code_point >> 10) as u16;
27                    output_pos += 1;
28                    buffer[output_pos] = 0xdc00 + (code_point & 0x3ff) as u16;
29                    output_pos += 1;
30                }
31            }
32            &{ buffer }
33        };
34        $crate::PCWSTR::from_raw(OUTPUT.as_ptr())
35    }};
36}
37
38/// A literal HSTRING, length-prefixed wide string with a trailing null terminator.
39#[macro_export]
40macro_rules! h {
41    ($s:literal) => {{
42        const INPUT: &[u8] = $s.as_bytes();
43        const OUTPUT_LEN: usize = $crate::utf16_len(INPUT) + 1;
44        #[allow(clippy::declare_interior_mutable_const)]
45        const RESULT: $crate::HSTRING = {
46            if OUTPUT_LEN == 1 {
47                unsafe { ::core::mem::transmute(::core::ptr::null::<u16>()) }
48            } else {
49                const OUTPUT: $crate::PCWSTR = $crate::w!($s);
50                const HEADER: $crate::HSTRING_HEADER = $crate::HSTRING_HEADER {
51                    flags: 0x11,
52                    len: (OUTPUT_LEN - 1) as u32,
53                    padding1: 0,
54                    padding2: 0,
55                    ptr: OUTPUT.as_ptr(),
56                };
57                // SAFETY: an `HSTRING` is exactly equivalent to a pointer to an `HSTRING_HEADER`
58                unsafe {
59                    ::core::mem::transmute::<&$crate::HSTRING_HEADER, $crate::HSTRING>(&HEADER)
60                }
61            }
62        };
63        #[allow(clippy::borrow_interior_mutable_const)]
64        &RESULT
65    }};
66}
67
68#[doc(hidden)]
69pub const fn decode_utf8_char(bytes: &[u8], mut pos: usize) -> Option<(u32, usize)> {
70    if bytes.len() == pos {
71        return None;
72    }
73    let ch = bytes[pos] as u32;
74    pos += 1;
75    if ch <= 0x7f {
76        return Some((ch, pos));
77    }
78    if (ch & 0xe0) == 0xc0 {
79        if bytes.len() - pos < 1 {
80            return None;
81        }
82        let ch2 = bytes[pos] as u32;
83        pos += 1;
84        if (ch2 & 0xc0) != 0x80 {
85            return None;
86        }
87        let result: u32 = ((ch & 0x1f) << 6) | (ch2 & 0x3f);
88        if result <= 0x7f {
89            return None;
90        }
91        return Some((result, pos));
92    }
93    if (ch & 0xf0) == 0xe0 {
94        if bytes.len() - pos < 2 {
95            return None;
96        }
97        let ch2 = bytes[pos] as u32;
98        pos += 1;
99        let ch3 = bytes[pos] as u32;
100        pos += 1;
101        if (ch2 & 0xc0) != 0x80 || (ch3 & 0xc0) != 0x80 {
102            return None;
103        }
104        let result = ((ch & 0x0f) << 12) | ((ch2 & 0x3f) << 6) | (ch3 & 0x3f);
105        if result <= 0x7ff || (0xd800 <= result && result <= 0xdfff) {
106            return None;
107        }
108        return Some((result, pos));
109    }
110    if (ch & 0xf8) == 0xf0 {
111        if bytes.len() - pos < 3 {
112            return None;
113        }
114        let ch2 = bytes[pos] as u32;
115        pos += 1;
116        let ch3 = bytes[pos] as u32;
117        pos += 1;
118        let ch4 = bytes[pos] as u32;
119        pos += 1;
120        if (ch2 & 0xc0) != 0x80 || (ch3 & 0xc0) != 0x80 || (ch4 & 0xc0) != 0x80 {
121            return None;
122        }
123        let result =
124            ((ch & 0x07) << 18) | ((ch2 & 0x3f) << 12) | ((ch3 & 0x3f) << 6) | (ch4 & 0x3f);
125        if result <= 0xffff || 0x10ffff < result {
126            return None;
127        }
128        return Some((result, pos));
129    }
130    None
131}
132
133#[doc(hidden)]
134#[repr(C)]
135pub struct HSTRING_HEADER {
136    pub flags: u32,
137    pub len: u32,
138    pub padding1: u32,
139    pub padding2: u32,
140    pub ptr: *const u16,
141}
142
143#[doc(hidden)]
144pub const fn utf16_len(bytes: &[u8]) -> usize {
145    let mut pos = 0;
146    let mut len = 0;
147    while let Some((code_point, new_pos)) = decode_utf8_char(bytes, pos) {
148        pos = new_pos;
149        len += if code_point <= 0xffff { 1 } else { 2 };
150    }
151    len
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn test() {
160        assert_eq!(decode_utf8_char(b"123", 0), Some((0x31, 1)));
161        assert_eq!(decode_utf8_char(b"123", 1), Some((0x32, 2)));
162        assert_eq!(decode_utf8_char(b"123", 2), Some((0x33, 3)));
163        assert_eq!(decode_utf8_char(b"123", 3), None);
164        assert_eq!(utf16_len(b"123"), 3);
165        assert_eq!(utf16_len("α & ω".as_bytes()), 5);
166    }
167}