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(::std::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 const RESULT: $crate::HSTRING = {
45 if OUTPUT_LEN == 1 {
46 unsafe { ::std::mem::transmute(::std::ptr::null::<u16>()) }
47 } else {
48 const OUTPUT: $crate::PCWSTR = $crate::w!($s);
49 const HEADER: $crate::HSTRING_HEADER = $crate::HSTRING_HEADER { flags: 0x11, len: (OUTPUT_LEN - 1) as u32, padding1: 0, padding2: 0, ptr: OUTPUT.as_ptr() };
50 // SAFETY: an `HSTRING` is exactly equivalent to a pointer to an `HSTRING_HEADER`
51 unsafe { ::std::mem::transmute::<&$crate::HSTRING_HEADER, $crate::HSTRING>(&HEADER) }
52 }
53 };
54 &RESULT
55 }};
56}
57
58#[doc(hidden)]
59pub const fn decode_utf8_char(bytes: &[u8], mut pos: usize) -> Option<(u32, usize)> {
60 if bytes.len() == pos {
61 return None;
62 }
63 let ch = bytes[pos] as u32;
64 pos += 1;
65 if ch <= 0x7f {
66 return Some((ch, pos));
67 }
68 if (ch & 0xe0) == 0xc0 {
69 if bytes.len() - pos < 1 {
70 return None;
71 }
72 let ch2 = bytes[pos] as u32;
73 pos += 1;
74 if (ch2 & 0xc0) != 0x80 {
75 return None;
76 }
77 let result: u32 = ((ch & 0x1f) << 6) | (ch2 & 0x3f);
78 if result <= 0x7f {
79 return None;
80 }
81 return Some((result, pos));
82 }
83 if (ch & 0xf0) == 0xe0 {
84 if bytes.len() - pos < 2 {
85 return None;
86 }
87 let ch2 = bytes[pos] as u32;
88 pos += 1;
89 let ch3 = bytes[pos] as u32;
90 pos += 1;
91 if (ch2 & 0xc0) != 0x80 || (ch3 & 0xc0) != 0x80 {
92 return None;
93 }
94 let result = ((ch & 0x0f) << 12) | ((ch2 & 0x3f) << 6) | (ch3 & 0x3f);
95 if result <= 0x7ff || (0xd800 <= result && result <= 0xdfff) {
96 return None;
97 }
98 return Some((result, pos));
99 }
100 if (ch & 0xf8) == 0xf0 {
101 if bytes.len() - pos < 3 {
102 return None;
103 }
104 let ch2 = bytes[pos] as u32;
105 pos += 1;
106 let ch3 = bytes[pos] as u32;
107 pos += 1;
108 let ch4 = bytes[pos] as u32;
109 pos += 1;
110 if (ch2 & 0xc0) != 0x80 || (ch3 & 0xc0) != 0x80 || (ch4 & 0xc0) != 0x80 {
111 return None;
112 }
113 let result = ((ch & 0x07) << 18) | ((ch2 & 0x3f) << 12) | ((ch3 & 0x3f) << 6) | (ch4 & 0x3f);
114 if result <= 0xffff || 0x10ffff < result {
115 return None;
116 }
117 return Some((result, pos));
118 }
119 None
120}
121
122#[doc(hidden)]
123#[repr(C)]
124pub struct HSTRING_HEADER {
125 pub flags: u32,
126 pub len: u32,
127 pub padding1: u32,
128 pub padding2: u32,
129 pub ptr: *const u16,
130}
131
132#[doc(hidden)]
133pub const fn utf16_len(bytes: &[u8]) -> usize {
134 let mut pos: usize = 0;
135 let mut len: usize = 0;
136 while let Some((code_point: u32, new_pos: usize)) = decode_utf8_char(bytes, pos) {
137 pos = new_pos;
138 len += if code_point <= 0xffff { 1 } else { 2 };
139 }
140 len
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test() {
149 assert_eq!(decode_utf8_char(b"123", 0), Some((0x31, 1)));
150 assert_eq!(decode_utf8_char(b"123", 1), Some((0x32, 2)));
151 assert_eq!(decode_utf8_char(b"123", 2), Some((0x33, 3)));
152 assert_eq!(decode_utf8_char(b"123", 3), None);
153 assert_eq!(utf16_len(b"123"), 3);
154 assert_eq!(utf16_len("α & ω".as_bytes()), 5);
155 }
156}
157