simdutf 9.2.1
Unicode at GB/s.
Loading...
Searching...
No Matches
latin1_to_utf8.h
1#ifndef SIMDUTF_LATIN1_TO_UTF8_H
2#define SIMDUTF_LATIN1_TO_UTF8_H
3
4#include <cstring>
5
6namespace simdutf {
7namespace scalar {
8namespace {
9namespace latin1_to_utf8 {
10
11template <typename InputPtr, typename OutputPtr>
12#if SIMDUTF_CPLUSPLUS20
13 requires(simdutf::detail::indexes_into_byte_like<InputPtr> &&
14 simdutf::detail::index_assignable_from_char<OutputPtr>)
15#endif
16simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
17 OutputPtr utf8_output) {
18 // const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
19 size_t pos = 0;
20 size_t utf8_pos = 0;
21
22 while (pos < len) {
23#if SIMDUTF_CPLUSPLUS23
24 if !consteval
25#endif
26 {
27 // try to convert the next block of 16 ASCII bytes
28 if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
29 // they are ascii
30 uint64_t v1;
31 ::memcpy(&v1, data + pos, sizeof(uint64_t));
32 uint64_t v2;
33 ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
34 uint64_t v{v1 |
35 v2}; // We are only interested in these bits: 1000 1000 1000
36 // 1000, so it makes sense to concatenate everything
37 if ((v & 0x8080808080808080) ==
38 0) { // if NONE of these are set, e.g. all of them are zero, then
39 // everything is ASCII
40 size_t final_pos = pos + 16;
41 while (pos < final_pos) {
42 utf8_output[utf8_pos++] = char(data[pos]);
43 pos++;
44 }
45 continue;
46 }
47 } // if (pos + 16 <= len)
48 } // !consteval scope
49
50 unsigned char byte = data[pos];
51 if ((byte & 0x80) == 0) { // if ASCII
52 // will generate one UTF-8 bytes
53 utf8_output[utf8_pos++] = char(byte);
54 pos++;
55 } else {
56 // will generate two UTF-8 bytes
57 utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
58 utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
59 pos++;
60 }
61 } // while
62 return utf8_pos;
63}
64
65simdutf_really_inline size_t convert(const char *buf, size_t len,
66 char *utf8_output) {
67 return convert(reinterpret_cast<const unsigned char *>(buf), len,
68 utf8_output);
69}
70
71inline size_t convert_safe(const char *buf, size_t len, char *utf8_output,
72 size_t utf8_len) {
73 const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
74 size_t pos = 0;
75 size_t skip_pos = 0;
76 size_t utf8_pos = 0;
77 while (pos < len && utf8_pos < utf8_len) {
78 // try to convert the next block of 16 ASCII bytes
79 if (pos >= skip_pos && pos + 16 <= len &&
80 utf8_pos + 16 <= utf8_len) { // if it is safe to read 16 more bytes,
81 // check that they are ascii
82 uint64_t v1;
83 ::memcpy(&v1, data + pos, sizeof(uint64_t));
84 uint64_t v2;
85 ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
86 uint64_t v{v1 |
87 v2}; // We are only interested in these bits: 1000 1000 1000
88 // 1000, so it makes sense to concatenate everything
89 if ((v & 0x8080808080808080) ==
90 0) { // if NONE of these are set, e.g. all of them are zero, then
91 // everything is ASCII
92 ::memcpy(utf8_output + utf8_pos, buf + pos, 16);
93 utf8_pos += 16;
94 pos += 16;
95 } else {
96 // At least one of the next 16 bytes are not ASCII, we will process them
97 // one by one
98 skip_pos = pos + 16;
99 }
100 } else {
101 const auto byte = data[pos];
102 if ((byte & 0x80) == 0) { // if ASCII
103 // will generate one UTF-8 bytes
104 utf8_output[utf8_pos++] = char(byte);
105 pos++;
106 } else if (utf8_pos + 2 <= utf8_len) {
107 // will generate two UTF-8 bytes
108 utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
109 utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
110 pos++;
111 } else {
112 break;
113 }
114 }
115 }
116 return utf8_pos;
117}
118
119inline full_result convert_safe_with_details(const char *buf, size_t len,
120 char *utf8_output,
121 size_t utf8_len) {
122 const size_t output_count = convert_safe(buf, len, utf8_output, utf8_len);
123 // Recover the consumed input count from the completed output. The runtime
124 // safe converter uses this helper only for its short scalar tail.
125 size_t input_count = 0;
126 size_t counted_output = 0;
127 while (input_count < len) {
128 const size_t width =
129 uint8_t(buf[input_count]) < uint8_t(0x80) ? size_t(1) : size_t(2);
130 if (counted_output + width > output_count) {
131 break;
132 }
133 input_count++;
134 counted_output += width;
135 }
136 return full_result(input_count == len ? error_code::SUCCESS
137 : error_code::OUTPUT_BUFFER_TOO_SMALL,
138 input_count, output_count);
139}
140
141template <typename InputPtr, typename OutputPtr>
142#if SIMDUTF_CPLUSPLUS20
143 requires(simdutf::detail::indexes_into_byte_like<InputPtr> &&
144 simdutf::detail::index_assignable_from_char<OutputPtr>)
145#endif
146simdutf_constexpr23 size_t convert_safe_constexpr(InputPtr data, size_t len,
147 OutputPtr utf8_output,
148 size_t utf8_len) {
149 size_t pos = 0;
150 size_t utf8_pos = 0;
151 while (pos < len && utf8_pos < utf8_len) {
152 const unsigned char byte = data[pos];
153 if ((byte & 0x80) == 0) { // if ASCII
154 // will generate one UTF-8 bytes
155 utf8_output[utf8_pos++] = char(byte);
156 pos++;
157 } else if (utf8_pos + 2 <= utf8_len) {
158 // will generate two UTF-8 bytes
159 utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
160 utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
161 pos++;
162 } else {
163 break;
164 }
165 }
166 return utf8_pos;
167}
168
169template <typename InputPtr, typename OutputPtr>
170#if SIMDUTF_CPLUSPLUS20
171 requires(simdutf::detail::indexes_into_byte_like<InputPtr> &&
172 simdutf::detail::index_assignable_from_char<OutputPtr>)
173#endif
174simdutf_constexpr23 full_result convert_safe_with_details_constexpr(
175 InputPtr data, size_t len, OutputPtr utf8_output, size_t utf8_len) {
176 const size_t output_count =
177 convert_safe_constexpr(data, len, utf8_output, utf8_len);
178 size_t input_count = 0;
179 size_t counted_output = 0;
180 while (input_count < len) {
181 const size_t width =
182 uint8_t(data[input_count]) < uint8_t(0x80) ? size_t(1) : size_t(2);
183 if (counted_output + width > output_count) {
184 break;
185 }
186 input_count++;
187 counted_output += width;
188 }
189 return full_result(input_count == len ? error_code::SUCCESS
190 : error_code::OUTPUT_BUFFER_TOO_SMALL,
191 input_count, output_count);
192}
193
194template <typename InputPtr>
195#if SIMDUTF_CPLUSPLUS20
196 requires simdutf::detail::indexes_into_byte_like<InputPtr>
197#endif
198simdutf_constexpr23 simdutf_warn_unused size_t
199utf8_length_from_latin1(InputPtr input, size_t length) noexcept {
200 size_t answer = length;
201 size_t i = 0;
202
203#if SIMDUTF_CPLUSPLUS23
204 if !consteval
205#endif
206 {
207 auto pop = [](uint64_t v) {
208 return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) *
209 UINT64_C(0x0101010101010101) >>
210 56);
211 };
212 for (; i + 32 <= length; i += 32) {
213 uint64_t v;
214 memcpy(&v, input + i, 8);
215 answer += pop(v);
216 memcpy(&v, input + i + 8, sizeof(v));
217 answer += pop(v);
218 memcpy(&v, input + i + 16, sizeof(v));
219 answer += pop(v);
220 memcpy(&v, input + i + 24, sizeof(v));
221 answer += pop(v);
222 }
223 for (; i + 8 <= length; i += 8) {
224 uint64_t v;
225 memcpy(&v, input + i, sizeof(v));
226 answer += pop(v);
227 }
228 } // !consteval scope
229 for (; i + 1 <= length; i += 1) {
230 answer += static_cast<uint8_t>(input[i]) >> 7;
231 }
232 return answer;
233}
234
235} // namespace latin1_to_utf8
236} // unnamed namespace
237} // namespace scalar
238} // namespace simdutf
239
240#endif
helpers placed in namespace detail are not a part of the public API
simdutf_warn_unused size_t utf8_length_from_latin1(const char *input, size_t length) noexcept
Return the number of bytes that this Latin1 string would require in UTF-8 format.