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;
121 uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
122 uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
123 if constexpr (!match_system(big_endian)) {
124 high_surrogate = u16_swap_bytes(high_surrogate);
125 low_surrogate = u16_swap_bytes(low_surrogate);
127 *utf16_output++ = char16_t(high_surrogate);
128 *utf16_output++ = char16_t(low_surrogate);
134 return utf16_output - start;
137template <endianness big_endian,
typename InputPtr>
138#if SIMDUTF_CPLUSPLUS20
139 requires simdutf::detail::indexes_into_byte_like<InputPtr>
141simdutf_constexpr23 result convert_with_errors(InputPtr data,
size_t len,
142 char16_t *utf16_output) {
144 char16_t *start{utf16_output};
146#if SIMDUTF_CPLUSPLUS23
151 if (pos + 16 <= len) {
154 ::memcpy(&v1, data + pos,
sizeof(uint64_t));
156 ::memcpy(&v2, data + pos +
sizeof(uint64_t),
sizeof(uint64_t));
158 if ((v & 0x8080808080808080) == 0) {
159 size_t final_pos = pos + 16;
160 while (pos < final_pos) {
161 const char16_t byte = uint8_t(data[pos]);
163 !match_system(big_endian) ? u16_swap_bytes(
byte) : byte;
171 auto leading_byte = uint8_t(data[pos]);
172 if (leading_byte < 0b10000000) {
174 *utf16_output++ = !match_system(big_endian)
175 ? char16_t(u16_swap_bytes(leading_byte))
176 : char16_t(leading_byte);
178 }
else if ((leading_byte & 0b11100000) == 0b11000000) {
181 if (pos + 1 >= len) {
182 return result(error_code::TOO_SHORT, pos);
184 if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
185 return result(error_code::TOO_SHORT, pos);
188 uint32_t code_point = (leading_byte & 0b00011111) << 6 |
189 (uint8_t(data[pos + 1]) & 0b00111111);
190 if (code_point < 0x80) {
191 return result(error_code::OVERLONG, pos);
193 if constexpr (!match_system(big_endian)) {
194 code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
196 *utf16_output++ = char16_t(code_point);
198 }
else if ((leading_byte & 0b11110000) == 0b11100000) {
201 if (pos + 2 >= len) {
202 return result(error_code::TOO_SHORT, pos);
205 if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
206 return result(error_code::TOO_SHORT, pos);
208 if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
209 return result(error_code::TOO_SHORT, pos);
212 uint32_t code_point = (leading_byte & 0b00001111) << 12 |
213 (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
214 (uint8_t(data[pos + 2]) & 0b00111111);
215 if (code_point < 0x800) {
216 return result(error_code::OVERLONG, pos);
218 if (0xd7ff < code_point && code_point < 0xe000) {
219 return result(error_code::SURROGATE, pos);
221 if constexpr (!match_system(big_endian)) {
222 code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
224 *utf16_output++ = char16_t(code_point);
226 }
else if ((leading_byte & 0b11111000) == 0b11110000) {
228 if (pos + 3 >= len) {
229 return result(error_code::TOO_SHORT, pos);
231 if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
232 return result(error_code::TOO_SHORT, pos);
234 if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
235 return result(error_code::TOO_SHORT, pos);
237 if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
238 return result(error_code::TOO_SHORT, pos);
242 uint32_t code_point = (leading_byte & 0b00000111) << 18 |
243 (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
244 (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
245 (uint8_t(data[pos + 3]) & 0b00111111);
246 if (code_point <= 0xffff) {
247 return result(error_code::OVERLONG, pos);
249 if (0x10ffff < code_point) {
250 return result(error_code::TOO_LARGE, pos);
252 code_point -= 0x10000;
253 uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
254 uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
255 if constexpr (!match_system(big_endian)) {
256 high_surrogate = u16_swap_bytes(high_surrogate);
257 low_surrogate = u16_swap_bytes(low_surrogate);
259 *utf16_output++ = char16_t(high_surrogate);
260 *utf16_output++ = char16_t(low_surrogate);
264 if ((leading_byte & 0b11000000) == 0b10000000) {
265 return result(error_code::TOO_LONG, pos);
267 return result(error_code::HEADER_BITS, pos);
271 return result(error_code::SUCCESS, utf16_output - start);
289template <endianness endian>
290inline result rewind_and_convert_with_errors(
size_t prior_bytes,
291 const char *buf,
size_t len,
292 char16_t *utf16_output) {
297 size_t how_far_back = prior_bytes;
300 bool found_leading_bytes{
false};
302 for (
size_t i = 0; i <= how_far_back; i++) {
303 unsigned char byte = buf[-
static_cast<std::ptrdiff_t
>(i)];
304 found_leading_bytes = ((
byte & 0b11000000) != 0b10000000);
305 if (found_leading_bytes) {
306 if (i > 0 &&
byte < 128) {
309 return result(error_code::TOO_LONG, 0 - i + 1);
324 if (!found_leading_bytes) {
329 return result(error_code::TOO_LONG, 0 - how_far_back);
331 result res = convert_with_errors<endian>(buf, len + extra_len, utf16_output);
333 res.count -= extra_len;
helpers placed in namespace detail are not a part of the public API