simdutf 9.0.0
Unicode at GB/s.
Loading...
Searching...
No Matches
utf8_to_latin1.h
1#ifndef SIMDUTF_UTF8_TO_LATIN1_H
2#define SIMDUTF_UTF8_TO_LATIN1_H
3
4#include <cstring>
5
6namespace simdutf {
7namespace scalar {
8namespace {
9namespace utf8_to_latin1 {
10
11template <typename InputPtr, typename OutputPtr>
12#if SIMDUTF_CPLUSPLUS20
13 requires(simdutf::detail::indexes_into_byte_like<InputPtr> &&
14 simdutf::detail::indexes_into_byte_like<OutputPtr>)
15#endif
16simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
17 OutputPtr latin_output) {
18 size_t pos = 0;
19 auto start = latin_output;
20
21 while (pos < len) {
22#if SIMDUTF_CPLUSPLUS23
23 if !consteval
24#endif
25 {
26 // try to convert the next block of 16 ASCII bytes
27 if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
28 // they are ascii
29 uint64_t v1;
30 ::memcpy(&v1, data + pos, sizeof(uint64_t));
31 uint64_t v2;
32 ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
33 uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000
34 // 1000 1000 .... etc
35 if ((v & 0x8080808080808080) ==
36 0) { // if NONE of these are set, e.g. all of them are zero, then
37 // everything is ASCII
38 size_t final_pos = pos + 16;
39 while (pos < final_pos) {
40 *latin_output++ = char(data[pos]);
41 pos++;
42 }
43 continue;
44 }
45 }
46 }
47
48 // suppose it is not an all ASCII byte sequence
49 uint8_t leading_byte = data[pos]; // leading byte
50 if (leading_byte < 0b10000000) {
51 // converting one ASCII byte !!!
52 *latin_output++ = char(leading_byte);
53 pos++;
54 } else if ((leading_byte & 0b11100000) ==
55 0b11000000) { // the first three bits indicate:
56 // We have a two-byte UTF-8
57 if (pos + 1 >= len) {
58 return 0;
59 } // minimal bound checking
60 if ((data[pos + 1] & 0b11000000) != 0b10000000) {
61 return 0;
62 } // checks if the next byte is a valid continuation byte in UTF-8. A
63 // valid continuation byte starts with 10.
64 // range check -
65 uint32_t code_point =
66 (leading_byte & 0b00011111) << 6 |
67 (data[pos + 1] &
68 0b00111111); // assembles the Unicode code point from the two bytes.
69 // It does this by discarding the leading 110 and 10
70 // bits from the two bytes, shifting the remaining bits
71 // of the first byte, and then combining the results
72 // with a bitwise OR operation.
73 if (code_point < 0x80 || 0xFF < code_point) {
74 return 0; // We only care about the range 129-255 which is Non-ASCII
75 // latin1 characters. A code_point beneath 0x80 is invalid as
76 // it is already covered by bytes whose leading bit is zero.
77 }
78 *latin_output++ = char(code_point);
79 pos += 2;
80 } else {
81 return 0;
82 }
83 }
84 return latin_output - start;
85}
86
87template <typename InputPtr>
88#if SIMDUTF_CPLUSPLUS20
89 requires simdutf::detail::indexes_into_byte_like<InputPtr>
90#endif
91simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len,
92 char *latin_output) {
93 size_t pos = 0;
94 char *start{latin_output};
95
96 while (pos < len) {
97#if SIMDUTF_CPLUSPLUS23
98 if !consteval
99#endif
100 {
101 // try to convert the next block of 16 ASCII bytes
102 if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
103 // they are ascii
104 uint64_t v1;
105 ::memcpy(&v1, data + pos, sizeof(uint64_t));
106 uint64_t v2;
107 ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
108 uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000
109 // 1000 1000...etc
110 if ((v & 0x8080808080808080) ==
111 0) { // if NONE of these are set, e.g. all of them are zero, then
112 // everything is ASCII
113 size_t final_pos = pos + 16;
114 while (pos < final_pos) {
115 *latin_output++ = char(data[pos]);
116 pos++;
117 }
118 continue;
119 }
120 }
121 }
122 // suppose it is not an all ASCII byte sequence
123 uint8_t leading_byte = data[pos]; // leading byte
124 if (leading_byte < 0b10000000) {
125 // converting one ASCII byte !!!
126 *latin_output++ = char(leading_byte);
127 pos++;
128 } else if ((leading_byte & 0b11100000) ==
129 0b11000000) { // the first three bits indicate:
130 // We have a two-byte UTF-8
131 if (pos + 1 >= len) {
132 return result(error_code::TOO_SHORT, pos);
133 } // minimal bound checking
134 if ((data[pos + 1] & 0b11000000) != 0b10000000) {
135 return result(error_code::TOO_SHORT, pos);
136 } // checks if the next byte is a valid continuation byte in UTF-8. A
137 // valid continuation byte starts with 10.
138 // range check -
139 uint32_t code_point =
140 (leading_byte & 0b00011111) << 6 |
141 (data[pos + 1] &
142 0b00111111); // assembles the Unicode code point from the two bytes.
143 // It does this by discarding the leading 110 and 10
144 // bits from the two bytes, shifting the remaining bits
145 // of the first byte, and then combining the results
146 // with a bitwise OR operation.
147 if (code_point < 0x80) {
148 return result(error_code::OVERLONG, pos);
149 }
150 if (0xFF < code_point) {
151 return result(error_code::TOO_LARGE, pos);
152 } // We only care about the range 129-255 which is Non-ASCII latin1
153 // characters
154 *latin_output++ = char(code_point);
155 pos += 2;
156 } else if ((leading_byte & 0b11110000) == 0b11100000) {
157 // We have a three-byte UTF-8
158 return result(error_code::TOO_LARGE, pos);
159 } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
160 // we have a 4-byte UTF-8 word.
161 return result(error_code::TOO_LARGE, pos);
162 } else {
163 // we either have too many continuation bytes or an invalid leading byte
164 if ((leading_byte & 0b11000000) == 0b10000000) {
165 return result(error_code::TOO_LONG, pos);
166 }
167
168 return result(error_code::HEADER_BITS, pos);
169 }
170 }
171 return result(error_code::SUCCESS, latin_output - start);
172}
173
174inline result rewind_and_convert_with_errors(size_t prior_bytes,
175 const char *buf, size_t len,
176 char *latin1_output) {
177 size_t extra_len{0};
178 // We potentially need to go back in time and find a leading byte.
179 // In theory '3' would be sufficient, but sometimes the error can go back
180 // quite far.
181 size_t how_far_back = prior_bytes;
182 // size_t how_far_back = 3; // 3 bytes in the past + current position
183 // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
184 bool found_leading_bytes{false};
185 // important: it is i <= how_far_back and not 'i < how_far_back'.
186 for (size_t i = 0; i <= how_far_back; i++) {
187 unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
188 found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
189 if (found_leading_bytes) {
190 if (i > 0 && byte < 128) {
191 // If we had to go back and the leading byte is ascii
192 // then we can stop right away.
193 return result(error_code::TOO_LONG, 0 - i + 1);
194 }
195 buf -= i;
196 extra_len = i;
197 break;
198 }
199 }
200 //
201 // It is possible for this function to return a negative count in its result.
202 // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
203 // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
204 // unsigned integral type of the result of the sizeof operator
205 //
206 // An unsigned type will simply wrap round arithmetically (well defined).
207 //
208 if (!found_leading_bytes) {
209 // If how_far_back == 3, we may have four consecutive continuation bytes!!!
210 // [....] [continuation] [continuation] [continuation] | [buf is
211 // continuation] Or we possibly have a stream that does not start with a
212 // leading byte.
213 return result(error_code::TOO_LONG, 0 - how_far_back);
214 }
215 result res = convert_with_errors(buf, len + extra_len, latin1_output);
216 if (res.error) {
217 res.count -= extra_len;
218 }
219 return res;
220}
221
222} // namespace utf8_to_latin1
223} // unnamed namespace
224} // namespace scalar
225} // namespace simdutf
226
227#endif
helpers placed in namespace detail are not a part of the public API