| 146 | namespace ada::idna { |
| 147 | |
| 148 | size_t utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) { |
| 149 | const uint8_t* data = reinterpret_cast<const uint8_t*>(buf); |
| 150 | size_t pos = 0; |
| 151 | const char32_t* start{utf32_output}; |
| 152 | while (pos < len) { |
| 153 | // try to convert the next block of 16 ASCII bytes |
| 154 | if (pos + 16 <= len) { // if it is safe to read 16 more |
| 155 | // bytes, check that they are ascii |
| 156 | uint64_t v1; |
| 157 | std::memcpy(&v1, data + pos, sizeof(uint64_t)); |
| 158 | uint64_t v2; |
| 159 | std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); |
| 160 | uint64_t v{v1 | v2}; |
| 161 | if ((v & 0x8080808080808080) == 0) { |
| 162 | size_t final_pos = pos + 16; |
| 163 | while (pos < final_pos) { |
| 164 | *utf32_output++ = char32_t(buf[pos]); |
| 165 | pos++; |
| 166 | } |
| 167 | continue; |
| 168 | } |
| 169 | } |
| 170 | uint8_t leading_byte = data[pos]; // leading byte |
| 171 | if (leading_byte < 0b10000000) { |
| 172 | // converting one ASCII byte !!! |
| 173 | *utf32_output++ = char32_t(leading_byte); |
| 174 | pos++; |
| 175 | } else if ((leading_byte & 0b11100000) == 0b11000000) { |
| 176 | // We have a two-byte UTF-8 |
| 177 | if (pos + 1 >= len) { |
| 178 | return 0; |
| 179 | } // minimal bound checking |
| 180 | if ((data[pos + 1] & 0b11000000) != 0b10000000) { |
| 181 | return 0; |
| 182 | } |
| 183 | // range check |
| 184 | uint32_t code_point = |
| 185 | (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); |
| 186 | if (code_point < 0x80 || 0x7ff < code_point) { |
| 187 | return 0; |
| 188 | } |
| 189 | *utf32_output++ = char32_t(code_point); |
| 190 | pos += 2; |
| 191 | } else if ((leading_byte & 0b11110000) == 0b11100000) { |
| 192 | // We have a three-byte UTF-8 |
| 193 | if (pos + 2 >= len) { |
| 194 | return 0; |
| 195 | } // minimal bound checking |
| 196 | |
| 197 | if ((data[pos + 1] & 0b11000000) != 0b10000000) { |
| 198 | return 0; |
| 199 | } |
| 200 | if ((data[pos + 2] & 0b11000000) != 0b10000000) { |
| 201 | return 0; |
| 202 | } |
| 203 | // range check |
| 204 | uint32_t code_point = (leading_byte & 0b00001111) << 12 | |
| 205 | (data[pos + 1] & 0b00111111) << 6 | |