1#ifndef SIMDUTF_UTF8_TO_UTF16_H
2#define SIMDUTF_UTF8_TO_UTF16_H
9namespace utf8_to_utf16 {
11template <endianness big_endian,
typename InputPtr>
12#if SIMDUTF_CPLUSPLUS20
13 requires simdutf::detail::indexes_into_byte_like<InputPtr>
15simdutf_constexpr23
size_t convert(InputPtr data,
size_t len,
16 char16_t *utf16_output) {
18 char16_t *start{utf16_output};
20#if SIMDUTF_CPLUSPLUS23
25 if (pos + 16 <= len) {
28 ::memcpy(&v1, data + pos,
sizeof(uint64_t));
30 ::memcpy(&v2, data + pos +
sizeof(uint64_t),
sizeof(uint64_t));
32 if ((v & 0x8080808080808080) == 0) {
33 size_t final_pos = pos + 16;
34 while (pos < final_pos) {
35 *utf16_output++ = !match_system(big_endian)
36 ? char16_t(u16_swap_bytes(data[pos]))
37 : char16_t(data[pos]);
45 uint8_t leading_byte = data[pos];
46 if (leading_byte < 0b10000000) {
48 *utf16_output++ = !match_system(big_endian)
49 ? char16_t(u16_swap_bytes(leading_byte))
50 : char16_t(leading_byte);
52 }
else if ((leading_byte & 0b11100000) == 0b11000000) {
58 if ((data[pos + 1] & 0b11000000) != 0b10000000) {
63 (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
64 if (code_point < 0x80) {
67 if constexpr (!match_system(big_endian)) {
68 code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
70 *utf16_output++ = char16_t(code_point);
72 }
else if ((leading_byte & 0b11110000) == 0b11100000) {
79 if ((data[pos + 1] & 0b11000000) != 0b10000000) {
82 if ((data[pos + 2] & 0b11000000) != 0b10000000) {
86 uint32_t code_point = (leading_byte & 0b00001111) << 12 |
87 (data[pos + 1] & 0b00111111) << 6 |
88 (data[pos + 2] & 0b00111111);
89 if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
92 if constexpr (!match_system(big_endian)) {
93 code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
95 *utf16_output++ = char16_t(code_point);
97 }
else if ((leading_byte & 0b11111000) == 0b11110000) {
102 if ((data[pos + 1] & 0b11000000) != 0b10000000) {
105 if ((data[pos + 2] & 0b11000000) != 0b10000000) {
108 if ((data[pos + 3] & 0b11000000) != 0b10000000) {
113 uint32_t code_point = (leading_byte & 0b00000111) << 18 |
114 (data[pos + 1] & 0b00111111) << 12 |
115 (data[pos + 2] & 0b00111111) << 6 |
116 (data[pos + 3] & 0b00111111);
117 if (code_point <= 0xffff || 0x10ffff < code_point) {
120 code_point -= 0x10000;
124 uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
125 uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
126 if constexpr (!match_system(big_endian)) {
127 high_surrogate = u16_swap_bytes(high_surrogate);
128 low_surrogate = u16_swap_bytes(low_surrogate);
130 *utf16_output++ = char16_t(high_surrogate);
131 *utf16_output++ = char16_t(low_surrogate);
137 return utf16_output - start;
140template <endianness big_endian,
typename InputPtr>
141#if SIMDUTF_CPLUSPLUS20
142 requires simdutf::detail::indexes_into_byte_like<InputPtr>
144simdutf_constexpr23 result convert_with_errors(InputPtr data,
size_t len,
145 char16_t *utf16_output) {
147 char16_t *start{utf16_output};
149#if SIMDUTF_CPLUSPLUS23
154 if (pos + 16 <= len) {
157 ::memcpy(&v1, data + pos,
sizeof(uint64_t));
159 ::memcpy(&v2, data + pos +
sizeof(uint64_t),
sizeof(uint64_t));
161 if ((v & 0x8080808080808080) == 0) {
162 size_t final_pos = pos + 16;
163 while (pos < final_pos) {
164 const char16_t byte = uint8_t(data[pos]);
166 !match_system(big_endian) ? u16_swap_bytes(
byte) : byte;
174 auto leading_byte = uint8_t(data[pos]);
175 if (leading_byte < 0b10000000) {
177 *utf16_output++ = !match_system(big_endian)
178 ? char16_t(u16_swap_bytes(leading_byte))
179 : char16_t(leading_byte);
181 }
else if ((leading_byte & 0b11100000) == 0b11000000) {
184 if (pos + 1 >= len) {
185 return result(error_code::TOO_SHORT, pos);
187 if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
188 return result(error_code::TOO_SHORT, pos);
191 uint32_t code_point = (leading_byte & 0b00011111) << 6 |
192 (uint8_t(data[pos + 1]) & 0b00111111);
193 if (code_point < 0x80) {
194 return result(error_code::OVERLONG, pos);
196 if constexpr (!match_system(big_endian)) {
197 code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
199 *utf16_output++ = char16_t(code_point);
201 }
else if ((leading_byte & 0b11110000) == 0b11100000) {
204 if (pos + 2 >= len) {
205 return result(error_code::TOO_SHORT, pos);
208 if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
209 return result(error_code::TOO_SHORT, pos);
211 if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
212 return result(error_code::TOO_SHORT, pos);
215 uint32_t code_point = (leading_byte & 0b00001111) << 12 |
216 (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
217 (uint8_t(data[pos + 2]) & 0b00111111);
218 if (code_point < 0x800) {
219 return result(error_code::OVERLONG, pos);
221 if (0xd7ff < code_point && code_point < 0xe000) {
222 return result(error_code::SURROGATE, pos);
224 if constexpr (!match_system(big_endian)) {
225 code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
227 *utf16_output++ = char16_t(code_point);
229 }
else if ((leading_byte & 0b11111000) == 0b11110000) {
231 if (pos + 3 >= len) {
232 return result(error_code::TOO_SHORT, pos);
234 if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
235 return result(error_code::TOO_SHORT, pos);
237 if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
238 return result(error_code::TOO_SHORT, pos);
240 if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
241 return result(error_code::TOO_SHORT, pos);
245 uint32_t code_point = (leading_byte & 0b00000111) << 18 |
246 (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
247 (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
248 (uint8_t(data[pos + 3]) & 0b00111111);
249 if (code_point <= 0xffff) {
250 return result(error_code::OVERLONG, pos);
252 if (0x10ffff < code_point) {
253 return result(error_code::TOO_LARGE, pos);
255 code_point -= 0x10000;
259 uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
260 uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
261 if constexpr (!match_system(big_endian)) {
262 high_surrogate = u16_swap_bytes(high_surrogate);
263 low_surrogate = u16_swap_bytes(low_surrogate);
265 *utf16_output++ = char16_t(high_surrogate);
266 *utf16_output++ = char16_t(low_surrogate);
270 if ((leading_byte & 0b11000000) == 0b10000000) {
271 return result(error_code::TOO_LONG, pos);
273 return result(error_code::HEADER_BITS, pos);
277 return result(error_code::SUCCESS, utf16_output - start);
295template <endianness endian>
296inline result rewind_and_convert_with_errors(
size_t prior_bytes,
297 const char *buf,
size_t len,
298 char16_t *utf16_output) {
303 size_t how_far_back = prior_bytes;
306 bool found_leading_bytes{
false};
308 for (
size_t i = 0; i <= how_far_back; i++) {
309 unsigned char byte = buf[-
static_cast<std::ptrdiff_t
>(i)];
310 found_leading_bytes = ((
byte & 0b11000000) != 0b10000000);
311 if (found_leading_bytes) {
312 if (i > 0 &&
byte < 128) {
315 return result(error_code::TOO_LONG, 0 - i + 1);
330 if (!found_leading_bytes) {
335 return result(error_code::TOO_LONG, 0 - how_far_back);
337 result res = convert_with_errors<endian>(buf, len + extra_len, utf16_output);
339 res.count -= extra_len;
helpers placed in namespace detail are not a part of the public API