Convert Latin1 encoded bytes to UTF-8 string. Latin1 (ISO-8859-1) maps bytes 0-127 to ASCII and bytes 128-255 to Unicode code points U+0080 to U+00FF.
| 94 | // Latin1 (ISO-8859-1) maps bytes 0-127 to ASCII and bytes 128-255 to |
| 95 | // Unicode code points U+0080 to U+00FF. |
| 96 | inline std::string latin1_to_utf8(const uint8_t *data, size_t length) { |
| 97 | if (length == 0) { |
| 98 | return std::string(); |
| 99 | } |
| 100 | |
| 101 | // Fast path: if all bytes are ASCII, direct copy |
| 102 | if (is_ascii_fallback(reinterpret_cast<const char *>(data), length)) { |
| 103 | return std::string(reinterpret_cast<const char *>(data), length); |
| 104 | } |
| 105 | |
| 106 | // Calculate exact output size to avoid reallocation |
| 107 | // ASCII bytes (< 128) need 1 byte, non-ASCII need 2 bytes in UTF-8 |
| 108 | size_t utf8_len = 0; |
| 109 | for (size_t i = 0; i < length; ++i) { |
| 110 | utf8_len += (data[i] < 128) ? 1 : 2; |
| 111 | } |
| 112 | |
| 113 | std::string result; |
| 114 | result.resize(utf8_len); |
| 115 | char *out = &result[0]; |
| 116 | |
| 117 | for (size_t i = 0; i < length; ++i) { |
| 118 | uint8_t byte = data[i]; |
| 119 | if (byte < 128) { |
| 120 | *out++ = static_cast<char>(byte); |
| 121 | } else { |
| 122 | // Latin1 byte 128-255 maps to U+0080 to U+00FF |
| 123 | // UTF-8 encoding: 110xxxxx 10xxxxxx |
| 124 | *out++ = static_cast<char>(0xC0 | (byte >> 6)); |
| 125 | *out++ = static_cast<char>(0x80 | (byte & 0x3F)); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | return result; |
| 130 | } |
| 131 | |
| 132 | // Convert UTF-16 code units to UTF-8 string. |
| 133 | // Handles surrogate pairs for characters outside BMP. |
no test coverage detected