2020-05-03 17:41:34 -03:00
|
|
|
|
/*
|
2024-10-04 08:19:50 -03:00
|
|
|
|
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
2024-11-01 08:14:53 -03:00
|
|
|
|
* Copyright (c) 2022, Jelle Raaijmakers <jelle@ladybird.org>
|
2023-02-17 14:45:08 -03:00
|
|
|
|
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
|
2024-05-27 12:57:12 -03:00
|
|
|
|
* Copyright (c) 2024, Simon Wanner <simon@skyrising.xyz>
|
2020-05-03 17:41:34 -03:00
|
|
|
|
*
|
2021-04-22 05:24:48 -03:00
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-05-03 17:41:34 -03:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <AK/StringBuilder.h>
|
Libraries: Clean up UTF-16 source text paths
Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.
Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
2026-06-21 14:03:19 -03:00
|
|
|
|
#include <AK/Utf16StringBuilder.h>
|
2022-03-27 03:48:25 -03:00
|
|
|
|
#include <AK/Utf8View.h>
|
2020-05-03 17:41:34 -03:00
|
|
|
|
#include <LibTextCodec/Decoder.h>
|
2026-06-19 19:51:56 -03:00
|
|
|
|
#include <RustFFI.h>
|
2020-05-03 17:41:34 -03:00
|
|
|
|
|
|
|
|
|
|
namespace TextCodec {
|
|
|
|
|
|
|
2020-12-27 18:44:38 -03:00
|
|
|
|
namespace {
|
2025-05-13 08:06:33 -03:00
|
|
|
|
|
2026-06-19 19:51:56 -03:00
|
|
|
|
class RustDecoder final : public Decoder {
|
|
|
|
|
|
public:
|
|
|
|
|
|
explicit RustDecoder(StringView encoding)
|
|
|
|
|
|
: m_encoding(encoding)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
virtual ErrorOr<String> to_utf8(StringView input, IgnoreBOM, ErrorMode) override;
|
2026-06-19 19:51:56 -03:00
|
|
|
|
virtual ErrorOr<size_t> length_in_utf16_code_units(StringView input) override;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
virtual ErrorOr<void> process(StringView input, Function<ErrorOr<void>(u32)> on_code_point) override;
|
|
|
|
|
|
|
|
|
|
|
|
StringView m_encoding;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class UTF8Decoder final : public Decoder {
|
|
|
|
|
|
public:
|
|
|
|
|
|
virtual ErrorOr<void> process(StringView, Function<ErrorOr<void>(u32)> on_code_point) override;
|
2026-06-22 17:07:40 -03:00
|
|
|
|
virtual ErrorOr<String> to_utf8(StringView, IgnoreBOM, ErrorMode) override;
|
2026-06-19 19:51:56 -03:00
|
|
|
|
virtual ErrorOr<size_t> length_in_utf16_code_units(StringView) override;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class UTF16BEDecoder final : public Decoder {
|
|
|
|
|
|
public:
|
2026-06-22 17:07:40 -03:00
|
|
|
|
virtual ErrorOr<String> to_utf8(StringView, IgnoreBOM, ErrorMode) override;
|
2026-06-19 19:51:56 -03:00
|
|
|
|
virtual ErrorOr<size_t> length_in_utf16_code_units(StringView) override;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
virtual ErrorOr<void> process(StringView, Function<ErrorOr<void>(u32)>) override;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class UTF16LEDecoder final : public Decoder {
|
|
|
|
|
|
public:
|
2026-06-22 17:07:40 -03:00
|
|
|
|
virtual ErrorOr<String> to_utf8(StringView, IgnoreBOM, ErrorMode) override;
|
2026-06-19 19:51:56 -03:00
|
|
|
|
virtual ErrorOr<size_t> length_in_utf16_code_units(StringView) override;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
virtual ErrorOr<void> process(StringView, Function<ErrorOr<void>(u32)>) override;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Latin1Decoder final : public Decoder {
|
|
|
|
|
|
public:
|
|
|
|
|
|
virtual ErrorOr<void> process(StringView, Function<ErrorOr<void>(u32)> on_code_point) override;
|
|
|
|
|
|
virtual ErrorOr<size_t> length_in_utf16_code_units(StringView) override;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2022-01-28 17:52:06 -03:00
|
|
|
|
UTF8Decoder s_utf8_decoder;
|
|
|
|
|
|
UTF16BEDecoder s_utf16be_decoder;
|
2022-03-08 10:27:11 -03:00
|
|
|
|
UTF16LEDecoder s_utf16le_decoder;
|
2026-06-19 19:51:56 -03:00
|
|
|
|
Latin1Decoder s_latin1_decoder;
|
|
|
|
|
|
|
|
|
|
|
|
RustDecoder s_gb18030_decoder { "gb18030"sv };
|
|
|
|
|
|
RustDecoder s_big5_decoder { "Big5"sv };
|
|
|
|
|
|
RustDecoder s_euc_jp_decoder { "EUC-JP"sv };
|
|
|
|
|
|
RustDecoder s_iso_2022_jp_decoder { "ISO-2022-JP"sv };
|
|
|
|
|
|
RustDecoder s_shift_jis_decoder { "Shift_JIS"sv };
|
|
|
|
|
|
RustDecoder s_euc_kr_decoder { "EUC-KR"sv };
|
|
|
|
|
|
RustDecoder s_ibm866_decoder { "IBM866"sv };
|
|
|
|
|
|
RustDecoder s_latin2_decoder { "ISO-8859-2"sv };
|
|
|
|
|
|
RustDecoder s_latin3_decoder { "ISO-8859-3"sv };
|
|
|
|
|
|
RustDecoder s_latin4_decoder { "ISO-8859-4"sv };
|
|
|
|
|
|
RustDecoder s_latin_cyrillic_decoder { "ISO-8859-5"sv };
|
|
|
|
|
|
RustDecoder s_latin_arabic_decoder { "ISO-8859-6"sv };
|
|
|
|
|
|
RustDecoder s_latin_greek_decoder { "ISO-8859-7"sv };
|
|
|
|
|
|
RustDecoder s_latin_hebrew_decoder { "ISO-8859-8"sv };
|
|
|
|
|
|
RustDecoder s_latin6_decoder { "ISO-8859-10"sv };
|
|
|
|
|
|
RustDecoder s_latin7_decoder { "ISO-8859-13"sv };
|
|
|
|
|
|
RustDecoder s_latin8_decoder { "ISO-8859-14"sv };
|
|
|
|
|
|
RustDecoder s_latin9_decoder { "ISO-8859-15"sv };
|
|
|
|
|
|
RustDecoder s_latin10_decoder { "ISO-8859-16"sv };
|
|
|
|
|
|
RustDecoder s_centraleurope_decoder { "windows-1250"sv };
|
|
|
|
|
|
RustDecoder s_cyrillic_decoder { "windows-1251"sv };
|
|
|
|
|
|
RustDecoder s_hebrew_decoder { "windows-1255"sv };
|
|
|
|
|
|
RustDecoder s_koi8r_decoder { "KOI8-R"sv };
|
|
|
|
|
|
RustDecoder s_koi8u_decoder { "KOI8-U"sv };
|
|
|
|
|
|
RustDecoder s_mac_roman_decoder { "macintosh"sv };
|
|
|
|
|
|
RustDecoder s_windows874_decoder { "windows-874"sv };
|
|
|
|
|
|
RustDecoder s_windows1252_decoder { "windows-1252"sv };
|
|
|
|
|
|
RustDecoder s_windows1253_decoder { "windows-1253"sv };
|
|
|
|
|
|
RustDecoder s_turkish_decoder { "windows-1254"sv };
|
|
|
|
|
|
RustDecoder s_windows1256_decoder { "windows-1256"sv };
|
|
|
|
|
|
RustDecoder s_windows1257_decoder { "windows-1257"sv };
|
|
|
|
|
|
RustDecoder s_windows1258_decoder { "windows-1258"sv };
|
|
|
|
|
|
RustDecoder s_mac_cyrillic_decoder { "x-mac-cyrillic"sv };
|
|
|
|
|
|
RustDecoder s_x_user_defined_decoder { "x-user-defined"sv };
|
|
|
|
|
|
RustDecoder s_replacement_decoder { "replacement"sv };
|
|
|
|
|
|
|
|
|
|
|
|
struct DecodeContext {
|
|
|
|
|
|
StringBuilder builder;
|
|
|
|
|
|
ErrorOr<void> result {};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void append_decoded_bytes(void* context, u8 const* data, size_t length)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto& decode_context = *static_cast<DecodeContext*>(context);
|
|
|
|
|
|
if (decode_context.result.is_error())
|
|
|
|
|
|
return;
|
|
|
|
|
|
decode_context.result = decode_context.builder.try_append(StringView { data, length });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ErrorOr<String> rust_decode_to_utf8(StringView encoding, StringView input, IgnoreBOM ignore_bom, ErrorMode error_mode)
|
2026-06-19 19:51:56 -03:00
|
|
|
|
{
|
|
|
|
|
|
DecodeContext context { .builder = StringBuilder(input.length()) };
|
|
|
|
|
|
auto succeeded = FFI::textcodec_rust_decode_to_utf8(
|
|
|
|
|
|
reinterpret_cast<u8 const*>(encoding.characters_without_null_termination()),
|
|
|
|
|
|
encoding.length(),
|
|
|
|
|
|
reinterpret_cast<u8 const*>(input.characters_without_null_termination()),
|
|
|
|
|
|
input.length(),
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ignore_bom == IgnoreBOM::No,
|
|
|
|
|
|
error_mode == ErrorMode::Fatal,
|
2026-06-19 19:51:56 -03:00
|
|
|
|
&context,
|
|
|
|
|
|
append_decoded_bytes);
|
|
|
|
|
|
if (!succeeded)
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return Error::from_string_literal("Failed to decode input");
|
2026-06-19 19:51:56 -03:00
|
|
|
|
TRY(context.result);
|
|
|
|
|
|
return context.builder.to_string_without_validation();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ErrorOr<void> rust_process(StringView encoding, StringView input, IgnoreBOM ignore_bom, Function<ErrorOr<void>(u32)> on_code_point)
|
2026-06-19 19:51:56 -03:00
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
auto utf8 = TRY(rust_decode_to_utf8(encoding, input, ignore_bom, ErrorMode::Replacement));
|
2026-06-19 19:51:56 -03:00
|
|
|
|
for (auto code_point : Utf8View { utf8 })
|
|
|
|
|
|
TRY(on_code_point(code_point));
|
|
|
|
|
|
return {};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ErrorOr<size_t> rust_length_in_utf16_code_units(StringView encoding, StringView input, IgnoreBOM ignore_bom)
|
2026-06-19 19:51:56 -03:00
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
auto utf8 = TRY(rust_decode_to_utf8(encoding, input, ignore_bom, ErrorMode::Replacement));
|
2026-06-19 19:51:56 -03:00
|
|
|
|
size_t length = 0;
|
|
|
|
|
|
for (auto code_point : Utf8View { utf8 })
|
|
|
|
|
|
length += code_point <= 0xffff ? 1 : 2;
|
|
|
|
|
|
return length;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Optional<StringView> get_static_encoding_name_from_rust(StringView label)
|
|
|
|
|
|
{
|
|
|
|
|
|
u8 const* encoding_name = nullptr;
|
|
|
|
|
|
size_t encoding_name_length = 0;
|
|
|
|
|
|
auto succeeded = FFI::textcodec_rust_get_standardized_encoding(
|
|
|
|
|
|
reinterpret_cast<u8 const*>(label.characters_without_null_termination()),
|
|
|
|
|
|
label.length(),
|
|
|
|
|
|
&encoding_name,
|
|
|
|
|
|
&encoding_name_length);
|
|
|
|
|
|
if (!succeeded)
|
|
|
|
|
|
return {};
|
|
|
|
|
|
return StringView { encoding_name, encoding_name_length };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ErrorOr<String> rust_streaming_decode_to_utf8(FFI::TextCodecRustStreamingDecoder* decoder, ReadonlyBytes input, bool last, ErrorMode error_mode)
|
2026-06-19 19:51:56 -03:00
|
|
|
|
{
|
|
|
|
|
|
DecodeContext context { .builder = StringBuilder(input.size()) };
|
|
|
|
|
|
auto succeeded = FFI::textcodec_rust_streaming_decoder_decode_to_utf8(
|
|
|
|
|
|
decoder,
|
|
|
|
|
|
input.data(),
|
|
|
|
|
|
input.size(),
|
|
|
|
|
|
last,
|
2026-06-22 17:07:40 -03:00
|
|
|
|
error_mode == ErrorMode::Fatal,
|
2026-06-19 19:51:56 -03:00
|
|
|
|
&context,
|
|
|
|
|
|
append_decoded_bytes);
|
|
|
|
|
|
if (!succeeded)
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return Error::from_string_literal("Failed to decode input");
|
2026-06-19 19:51:56 -03:00
|
|
|
|
TRY(context.result);
|
|
|
|
|
|
return context.builder.to_string_without_validation();
|
|
|
|
|
|
}
|
2024-05-27 12:57:12 -03:00
|
|
|
|
|
2020-12-27 18:44:38 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-06-02 10:56:36 -03:00
|
|
|
|
Optional<Decoder&> decoder_for(StringView label)
|
2020-05-03 17:41:34 -03:00
|
|
|
|
{
|
2024-06-02 10:56:36 -03:00
|
|
|
|
auto encoding = get_standardized_encoding(label);
|
|
|
|
|
|
return encoding.has_value() ? decoder_for_exact_name(encoding.value()) : Optional<Decoder&> {};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Optional<Decoder&> decoder_for_exact_name(StringView encoding)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-1"sv))
|
|
|
|
|
|
return s_latin1_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1252"sv))
|
|
|
|
|
|
return s_windows1252_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("utf-8"sv))
|
|
|
|
|
|
return s_utf8_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("utf-16be"sv))
|
|
|
|
|
|
return s_utf16be_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("utf-16le"sv))
|
|
|
|
|
|
return s_utf16le_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("big5"sv))
|
|
|
|
|
|
return s_big5_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("euc-jp"sv))
|
|
|
|
|
|
return s_euc_jp_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("euc-kr"sv))
|
|
|
|
|
|
return s_euc_kr_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("gbk"sv))
|
|
|
|
|
|
return s_gb18030_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("gb18030"sv))
|
|
|
|
|
|
return s_gb18030_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("ibm866"sv))
|
|
|
|
|
|
return s_ibm866_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-2022-jp"sv))
|
|
|
|
|
|
return s_iso_2022_jp_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-2"sv))
|
|
|
|
|
|
return s_latin2_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-3"sv))
|
|
|
|
|
|
return s_latin3_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-4"sv))
|
|
|
|
|
|
return s_latin4_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-5"sv))
|
|
|
|
|
|
return s_latin_cyrillic_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-6"sv))
|
|
|
|
|
|
return s_latin_arabic_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-7"sv))
|
|
|
|
|
|
return s_latin_greek_decoder;
|
|
|
|
|
|
if (encoding.is_one_of_ignoring_ascii_case("iso-8859-8"sv, "iso-8859-8-i"sv))
|
|
|
|
|
|
return s_latin_hebrew_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-10"sv))
|
|
|
|
|
|
return s_latin6_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-13"sv))
|
|
|
|
|
|
return s_latin7_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-14"sv))
|
|
|
|
|
|
return s_latin8_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-15"sv))
|
|
|
|
|
|
return s_latin9_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("iso-8859-16"sv))
|
|
|
|
|
|
return s_latin10_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("koi8-r"sv))
|
|
|
|
|
|
return s_koi8r_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("koi8-u"sv))
|
|
|
|
|
|
return s_koi8u_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("macintosh"sv))
|
|
|
|
|
|
return s_mac_roman_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("replacement"sv))
|
|
|
|
|
|
return s_replacement_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("shift_jis"sv))
|
|
|
|
|
|
return s_shift_jis_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-874"sv))
|
|
|
|
|
|
return s_windows874_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1250"sv))
|
|
|
|
|
|
return s_centraleurope_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1251"sv))
|
|
|
|
|
|
return s_cyrillic_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1253"sv))
|
|
|
|
|
|
return s_windows1253_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1254"sv))
|
|
|
|
|
|
return s_turkish_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1255"sv))
|
|
|
|
|
|
return s_hebrew_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1256"sv))
|
|
|
|
|
|
return s_windows1256_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1257"sv))
|
|
|
|
|
|
return s_windows1257_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("windows-1258"sv))
|
|
|
|
|
|
return s_windows1258_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("x-mac-cyrillic"sv))
|
|
|
|
|
|
return s_mac_cyrillic_decoder;
|
|
|
|
|
|
if (encoding.equals_ignoring_ascii_case("x-user-defined"sv))
|
|
|
|
|
|
return s_x_user_defined_decoder;
|
2026-06-19 19:51:56 -03:00
|
|
|
|
|
2024-06-02 10:56:36 -03:00
|
|
|
|
dbgln("TextCodec: No decoder implemented for encoding '{}'", encoding);
|
2023-02-17 14:45:08 -03:00
|
|
|
|
return {};
|
2020-05-03 17:41:34 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-20 20:09:28 -03:00
|
|
|
|
Optional<StringView> get_standardized_encoding(StringView encoding)
|
2020-11-13 08:14:02 -03:00
|
|
|
|
{
|
2026-06-19 19:51:56 -03:00
|
|
|
|
auto standardized_encoding = get_static_encoding_name_from_rust(encoding);
|
|
|
|
|
|
if (!standardized_encoding.has_value())
|
|
|
|
|
|
dbgln("TextCodec: Unrecognized encoding: {}", encoding);
|
|
|
|
|
|
return standardized_encoding;
|
2020-11-13 08:14:02 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-11 17:58:06 -03:00
|
|
|
|
// https://encoding.spec.whatwg.org/#bom-sniff
|
2023-02-17 16:53:51 -03:00
|
|
|
|
Optional<Decoder&> bom_sniff_to_decoder(StringView input)
|
2022-02-11 17:58:06 -03:00
|
|
|
|
{
|
|
|
|
|
|
// 1. Let BOM be the result of peeking 3 bytes from ioQueue, converted to a byte sequence.
|
|
|
|
|
|
// 2. For each of the rows in the table below, starting with the first one and going down,
|
|
|
|
|
|
// if BOM starts with the bytes given in the first column, then return the encoding given
|
|
|
|
|
|
// in the cell in the second column of that row. Otherwise, return null.
|
|
|
|
|
|
|
|
|
|
|
|
// Byte Order Mark | Encoding
|
|
|
|
|
|
// --------------------------
|
|
|
|
|
|
// 0xEF 0xBB 0xBF | UTF-8
|
|
|
|
|
|
// 0xFE 0xFF | UTF-16BE
|
|
|
|
|
|
// 0xFF 0xFE | UTF-16LE
|
|
|
|
|
|
|
|
|
|
|
|
auto bytes = input.bytes();
|
|
|
|
|
|
if (bytes.size() < 2)
|
2023-02-17 16:53:51 -03:00
|
|
|
|
return {};
|
2022-02-11 17:58:06 -03:00
|
|
|
|
|
|
|
|
|
|
auto first_byte = bytes[0];
|
|
|
|
|
|
|
|
|
|
|
|
switch (first_byte) {
|
|
|
|
|
|
case 0xEF: // UTF-8
|
|
|
|
|
|
if (bytes.size() < 3)
|
2023-02-17 16:53:51 -03:00
|
|
|
|
return {};
|
|
|
|
|
|
if (bytes[1] == 0xBB && bytes[2] == 0xBF)
|
|
|
|
|
|
return s_utf8_decoder;
|
|
|
|
|
|
return {};
|
2022-02-11 17:58:06 -03:00
|
|
|
|
case 0xFE: // UTF-16BE
|
2023-02-17 16:53:51 -03:00
|
|
|
|
if (bytes[1] == 0xFF)
|
|
|
|
|
|
return s_utf16be_decoder;
|
|
|
|
|
|
return {};
|
2022-02-11 17:58:06 -03:00
|
|
|
|
case 0xFF: // UTF-16LE
|
2023-02-17 16:53:51 -03:00
|
|
|
|
if (bytes[1] == 0xFE)
|
|
|
|
|
|
return s_utf16le_decoder;
|
|
|
|
|
|
return {};
|
2022-02-11 17:58:06 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-17 16:53:51 -03:00
|
|
|
|
return {};
|
2022-02-11 17:58:06 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-11 18:02:29 -03:00
|
|
|
|
// https://encoding.spec.whatwg.org/#decode
|
2023-02-17 17:15:10 -03:00
|
|
|
|
ErrorOr<String> convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder& fallback_decoder, StringView input)
|
2022-02-11 18:02:29 -03:00
|
|
|
|
{
|
|
|
|
|
|
Decoder* actual_decoder = &fallback_decoder;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. Let BOMEncoding be the result of BOM sniffing ioQueue.
|
|
|
|
|
|
// 2. If BOMEncoding is non-null:
|
2023-02-17 16:53:51 -03:00
|
|
|
|
if (auto unicode_decoder = bom_sniff_to_decoder(input); unicode_decoder.has_value()) {
|
2022-02-11 18:02:29 -03:00
|
|
|
|
// 1. Set encoding to BOMEncoding.
|
2023-02-17 16:53:51 -03:00
|
|
|
|
actual_decoder = &unicode_decoder.value();
|
2022-02-11 18:02:29 -03:00
|
|
|
|
|
|
|
|
|
|
// 2. Read three bytes from ioQueue, if BOMEncoding is UTF-8; otherwise read two bytes. (Do nothing with those bytes.)
|
|
|
|
|
|
// FIXME: I imagine this will be pretty slow for large inputs, as it's regenerating the input without the first 2/3 bytes.
|
2023-02-17 16:53:51 -03:00
|
|
|
|
input = input.substring_view(&unicode_decoder.value() == &s_utf8_decoder ? 3 : 2);
|
2022-02-11 18:02:29 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VERIFY(actual_decoder);
|
|
|
|
|
|
|
2023-05-11 12:22:41 -03:00
|
|
|
|
// 3. Process a queue with an instance of encoding’s decoder, ioQueue, output, and "replacement".
|
|
|
|
|
|
// FIXME: This isn't the exact same as the spec, which is written in terms of I/O queues.
|
2026-06-22 17:07:40 -03:00
|
|
|
|
auto output = TRY(actual_decoder->to_utf8(input, IgnoreBOM::No, ErrorMode::Replacement));
|
2023-05-11 12:22:41 -03:00
|
|
|
|
|
2022-02-11 18:02:29 -03:00
|
|
|
|
// 4. Return output.
|
2023-05-11 12:22:41 -03:00
|
|
|
|
return output;
|
2022-02-11 18:02:29 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
Libraries: Clean up UTF-16 source text paths
Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.
Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
2026-06-21 14:03:19 -03:00
|
|
|
|
ErrorOr<Utf16String> convert_input_to_utf16_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder& fallback_decoder, StringView input)
|
|
|
|
|
|
{
|
|
|
|
|
|
Decoder* actual_decoder = &fallback_decoder;
|
|
|
|
|
|
|
|
|
|
|
|
if (auto unicode_decoder = bom_sniff_to_decoder(input); unicode_decoder.has_value()) {
|
|
|
|
|
|
actual_decoder = &unicode_decoder.value();
|
|
|
|
|
|
input = input.substring_view(&unicode_decoder.value() == &s_utf8_decoder ? 3 : 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VERIFY(actual_decoder);
|
|
|
|
|
|
return actual_decoder->to_utf16(input);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 06:03:51 -03:00
|
|
|
|
ErrorOr<size_t> convert_input_to_utf16_length_using_given_decoder_unless_there_is_a_byte_order_mark(Decoder& fallback_decoder, StringView input)
|
|
|
|
|
|
{
|
|
|
|
|
|
Decoder* actual_decoder = &fallback_decoder;
|
|
|
|
|
|
|
|
|
|
|
|
if (auto unicode_decoder = bom_sniff_to_decoder(input); unicode_decoder.has_value()) {
|
|
|
|
|
|
actual_decoder = &unicode_decoder.value();
|
|
|
|
|
|
input = input.substring_view(&unicode_decoder.value() == &s_utf8_decoder ? 3 : 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VERIFY(actual_decoder);
|
|
|
|
|
|
return actual_decoder->length_in_utf16_code_units(input);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-18 12:25:33 -03:00
|
|
|
|
// https://encoding.spec.whatwg.org/#get-an-output-encoding
|
|
|
|
|
|
StringView get_output_encoding(StringView encoding)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. If encoding is replacement or UTF-16BE/LE, then return UTF-8.
|
|
|
|
|
|
if (encoding.is_one_of_ignoring_ascii_case("replacement"sv, "utf-16le"sv, "utf-16be"sv))
|
|
|
|
|
|
return "UTF-8"sv;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. Return encoding.
|
|
|
|
|
|
return encoding;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 16:58:47 -03:00
|
|
|
|
ErrorOr<String> Decoder::to_utf8(StringView input, IgnoreBOM, ErrorMode)
|
2021-08-29 08:44:28 -03:00
|
|
|
|
{
|
|
|
|
|
|
StringBuilder builder(input.length());
|
2023-02-17 17:15:10 -03:00
|
|
|
|
TRY(process(input, [&builder](u32 c) { return builder.try_append_code_point(c); }));
|
2023-12-29 11:30:15 -03:00
|
|
|
|
return builder.to_string_without_validation();
|
2021-08-29 08:44:28 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
Libraries: Clean up UTF-16 source text paths
Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.
Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
2026-06-21 14:03:19 -03:00
|
|
|
|
ErrorOr<Utf16String> Decoder::to_utf16(StringView input)
|
|
|
|
|
|
{
|
|
|
|
|
|
Utf16StringBuilder builder;
|
|
|
|
|
|
TRY(process(input, [&builder](u32 c) -> ErrorOr<void> {
|
|
|
|
|
|
builder.append_code_point(c);
|
|
|
|
|
|
return {};
|
|
|
|
|
|
}));
|
|
|
|
|
|
return builder.to_string();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 06:03:51 -03:00
|
|
|
|
ErrorOr<size_t> Decoder::length_in_utf16_code_units(StringView input)
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t length = 0;
|
|
|
|
|
|
TRY(process(input, [&](u32 code_point) -> ErrorOr<void> {
|
|
|
|
|
|
length += code_point <= 0xffff ? 1 : 2;
|
|
|
|
|
|
return {};
|
|
|
|
|
|
}));
|
|
|
|
|
|
return length;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-17 14:05:53 -03:00
|
|
|
|
ErrorOr<void> Decoder::process_code_points(StringView input, Function<ErrorOr<void>(u32)> on_code_point)
|
|
|
|
|
|
{
|
|
|
|
|
|
return process(input, move(on_code_point));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ErrorOr<String> RustDecoder::to_utf8(StringView input, IgnoreBOM ignore_bom, ErrorMode error_mode)
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_decode_to_utf8(m_encoding, input, ignore_bom, error_mode);
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 19:51:56 -03:00
|
|
|
|
ErrorOr<size_t> RustDecoder::length_in_utf16_code_units(StringView input)
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_length_in_utf16_code_units(m_encoding, input, IgnoreBOM::Yes);
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 19:51:56 -03:00
|
|
|
|
ErrorOr<void> RustDecoder::process(StringView input, Function<ErrorOr<void>(u32)> on_code_point)
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_process(m_encoding, input, IgnoreBOM::Yes, move(on_code_point));
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 19:51:56 -03:00
|
|
|
|
ErrorOr<void> Latin1Decoder::process(StringView input, Function<ErrorOr<void>(u32)> on_code_point)
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
{
|
2026-06-19 19:51:56 -03:00
|
|
|
|
for (u8 ch : input)
|
|
|
|
|
|
TRY(on_code_point(ch));
|
|
|
|
|
|
return {};
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 19:51:56 -03:00
|
|
|
|
ErrorOr<size_t> Latin1Decoder::length_in_utf16_code_units(StringView input)
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
{
|
2026-06-19 19:51:56 -03:00
|
|
|
|
return input.length();
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
StreamingDecoder::StreamingDecoder(StringView encoding, IgnoreBOM ignore_bom, ErrorMode error_mode)
|
|
|
|
|
|
: m_error_mode(error_mode)
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
{
|
2026-06-19 19:51:56 -03:00
|
|
|
|
m_decoder = FFI::textcodec_rust_streaming_decoder_new(
|
|
|
|
|
|
reinterpret_cast<u8 const*>(encoding.characters_without_null_termination()),
|
|
|
|
|
|
encoding.length(),
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ignore_bom == IgnoreBOM::No);
|
2026-06-19 19:51:56 -03:00
|
|
|
|
VERIFY(m_decoder);
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 19:51:56 -03:00
|
|
|
|
StreamingDecoder::~StreamingDecoder()
|
2026-06-19 19:50:19 -03:00
|
|
|
|
{
|
2026-06-19 19:51:56 -03:00
|
|
|
|
FFI::textcodec_rust_streaming_decoder_free(static_cast<FFI::TextCodecRustStreamingDecoder*>(m_decoder));
|
2026-06-19 19:50:19 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
ErrorOr<String> StreamingDecoder::to_utf8(ReadonlyBytes input)
|
|
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_streaming_decode_to_utf8(static_cast<FFI::TextCodecRustStreamingDecoder*>(m_decoder), input, false, m_error_mode);
|
LibTextCodec: Add streaming decoder
Introduce a StreamingDecoder wrapper that lets callers feed bytes to a
Decoder one chunk at a time. It buffers any incomplete trailing byte
sequence at the end of a chunk and prepends it to the next chunk, so a
multi-byte code point split across a chunk boundary is decoded correctly
once the next chunk arrives.
To support that, add an incomplete_tail_length() virtual on Decoder
returning the number of trailing bytes that form an incomplete sequence
per the Encoding Standard's decoder handler byte ranges, with overrides
for UTF-8, UTF-16BE, UTF-16LE, GB18030, Big5, EUC-JP, ISO-2022-JP,
Shift_JIS, and EUC-KR. The default implementation returns 0, which keeps
single-byte legacy decoders correct.
This is the foundation for the upcoming incremental HTML parser, which
needs to decode network response bodies as they arrive.
2026-04-27 15:00:25 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ErrorOr<String> StreamingDecoder::finish()
|
|
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_streaming_decode_to_utf8(static_cast<FFI::TextCodecRustStreamingDecoder*>(m_decoder), {}, true, m_error_mode);
|
2026-06-03 06:03:51 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-17 16:09:29 -03:00
|
|
|
|
ErrorOr<void> UTF8Decoder::process(StringView input, Function<ErrorOr<void>(u32)> on_code_point)
|
|
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_process("UTF-8"sv, input, IgnoreBOM::Yes, move(on_code_point));
|
2026-05-17 16:09:29 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ErrorOr<String> UTF8Decoder::to_utf8(StringView input, IgnoreBOM ignore_bom, ErrorMode error_mode)
|
2020-05-03 17:41:34 -03:00
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_decode_to_utf8("UTF-8"sv, input, ignore_bom, error_mode);
|
2020-05-03 17:41:34 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 06:03:51 -03:00
|
|
|
|
ErrorOr<size_t> UTF8Decoder::length_in_utf16_code_units(StringView input)
|
|
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_length_in_utf16_code_units("UTF-8"sv, input, IgnoreBOM::No);
|
2026-06-03 06:03:51 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-17 14:05:53 -03:00
|
|
|
|
ErrorOr<void> UTF16BEDecoder::process(StringView input, Function<ErrorOr<void>(u32)> on_code_point)
|
|
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_process("UTF-16BE"sv, input, IgnoreBOM::No, move(on_code_point));
|
2026-05-17 14:05:53 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ErrorOr<String> UTF16BEDecoder::to_utf8(StringView input, IgnoreBOM ignore_bom, ErrorMode error_mode)
|
2021-08-29 08:44:28 -03:00
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_decode_to_utf8("UTF-16BE"sv, input, ignore_bom, error_mode);
|
2022-03-08 10:27:11 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 06:03:51 -03:00
|
|
|
|
ErrorOr<size_t> UTF16BEDecoder::length_in_utf16_code_units(StringView input)
|
|
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_length_in_utf16_code_units("UTF-16BE"sv, input, IgnoreBOM::No);
|
2026-06-03 06:03:51 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-17 14:05:53 -03:00
|
|
|
|
ErrorOr<void> UTF16LEDecoder::process(StringView input, Function<ErrorOr<void>(u32)> on_code_point)
|
|
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_process("UTF-16LE"sv, input, IgnoreBOM::No, move(on_code_point));
|
2026-05-17 14:05:53 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-22 17:07:40 -03:00
|
|
|
|
ErrorOr<String> UTF16LEDecoder::to_utf8(StringView input, IgnoreBOM ignore_bom, ErrorMode error_mode)
|
2022-03-08 10:27:11 -03:00
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_decode_to_utf8("UTF-16LE"sv, input, ignore_bom, error_mode);
|
2021-02-16 13:31:22 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 06:03:51 -03:00
|
|
|
|
ErrorOr<size_t> UTF16LEDecoder::length_in_utf16_code_units(StringView input)
|
|
|
|
|
|
{
|
2026-06-22 17:07:40 -03:00
|
|
|
|
return rust_length_in_utf16_code_units("UTF-16LE"sv, input, IgnoreBOM::No);
|
2026-06-03 06:03:51 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-24 14:20:51 -03:00
|
|
|
|
// https://infra.spec.whatwg.org/#isomorphic-decode
|
|
|
|
|
|
String isomorphic_decode(StringView input)
|
|
|
|
|
|
{
|
|
|
|
|
|
// To isomorphic decode a byte sequence input, return a string whose code point length is equal to input’s length
|
|
|
|
|
|
// and whose code points have the same values as the values of input’s bytes, in the same order.
|
|
|
|
|
|
// NB: This is essentially spec-speak for "Decode as ISO-8859-1 / Latin-1".
|
|
|
|
|
|
StringBuilder builder(input.length());
|
|
|
|
|
|
|
|
|
|
|
|
for (auto byte : input.bytes())
|
|
|
|
|
|
builder.append_code_point(byte);
|
|
|
|
|
|
|
|
|
|
|
|
return builder.to_string_without_validation();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-05-03 17:41:34 -03:00
|
|
|
|
}
|