1 | /// A literal UTF-8 string with a trailing null terminator. |
2 | #[macro_export ] |
3 | macro_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 ] |
11 | macro_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 ] |
40 | macro_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)] |
59 | pub 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)] |
124 | pub 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)] |
133 | pub 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)] |
144 | mod 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 | |