| 75 | #endif |
| 76 | |
| 77 | GJS_JSAPI_RETURN_CONVENTION |
| 78 | static JSString* gjs_lossy_decode_from_uint8array_slow( |
| 79 | JSContext* cx, const uint8_t* bytes, size_t bytes_len, |
| 80 | const char* from_codeset) { |
| 81 | Gjs::AutoError error; |
| 82 | Gjs::AutoUnref<GCharsetConverter> converter{ |
| 83 | g_charset_converter_new(UTF16_CODESET, from_codeset, &error)}; |
| 84 | |
| 85 | // This should only throw if an encoding is not available. |
| 86 | if (error) |
| 87 | return gjs_throw_type_error_from_gerror(cx, error); |
| 88 | |
| 89 | // This function converts *to* UTF-16, using a std::u16string |
| 90 | // as its buffer. |
| 91 | // |
| 92 | // UTF-16 represents each character with 2 bytes or |
| 93 | // 4 bytes, the best case scenario when converting to |
| 94 | // UTF-16 is that every input byte encodes to two bytes, |
| 95 | // this is typical for ASCII and non-supplementary characters. |
| 96 | // Because we are converting from an unknown encoding |
| 97 | // technically a single byte could be supplementary in |
| 98 | // Unicode (4 bytes) or even represent multiple Unicode characters. |
| 99 | // |
| 100 | // std::u16string does not care about these implementation |
| 101 | // details, its only concern is that is consists of byte pairs. |
| 102 | // Given this, a single UTF-16 character could be represented |
| 103 | // by one or two std::u16string characters. |
| 104 | |
| 105 | // Allocate bytes_len * 2 + 12 as our initial buffer. |
| 106 | // bytes_len * 2 is the "best case" for LATIN1 strings |
| 107 | // and strings which are in the basic multilingual plane. |
| 108 | // Add 12 as a slight cushion and set the minimum allocation |
| 109 | // at 256 to prefer running a single iteration for |
| 110 | // small strings with supplemental plane characters. |
| 111 | // |
| 112 | // When converting Chinese characters, for example, |
| 113 | // some dialectal characters are in the supplemental plane |
| 114 | // Adding a padding of 12 prevents a few dialectal characters |
| 115 | // from requiring a reallocation. |
| 116 | size_t buffer_size = |
| 117 | std::max(bytes_len * 2 + 12, static_cast<size_t>(256u)); |
| 118 | |
| 119 | // Cast data to correct input types |
| 120 | const char* input = reinterpret_cast<const char*>(bytes); |
| 121 | size_t input_len = bytes_len; |
| 122 | |
| 123 | // The base string that we'll append to. |
| 124 | std::u16string output_str; |
| 125 | |
| 126 | do { |
| 127 | Gjs::AutoError local_error; |
| 128 | |
| 129 | // Create a buffer to convert into. |
| 130 | std::unique_ptr<char[]> buffer = std::make_unique<char[]>(buffer_size); |
| 131 | size_t bytes_written = 0, bytes_read = 0; |
| 132 | |
| 133 | g_converter_convert(G_CONVERTER(converter.get()), input, input_len, |
| 134 | buffer.get(), buffer_size, G_CONVERTER_INPUT_AT_END, |
no test coverage detected