2020-04-26 17:49:19 -03:00
|
|
|
/*
|
2024-10-04 08:19:50 -03:00
|
|
|
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
2020-04-26 17:49:19 -03:00
|
|
|
*
|
2021-04-22 05:24:48 -03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-04-26 17:49:19 -03:00
|
|
|
*/
|
|
|
|
|
|
2020-08-10 18:50:38 -03:00
|
|
|
#include <AK/Base64.h>
|
2020-04-26 17:49:19 -03:00
|
|
|
#include <AK/Types.h>
|
|
|
|
|
|
2024-07-15 16:25:08 -03:00
|
|
|
#include <simdutf.h>
|
2024-03-24 09:55:53 -03:00
|
|
|
|
2024-07-15 16:25:08 -03:00
|
|
|
namespace AK {
|
2020-10-13 11:48:48 -03:00
|
|
|
|
2024-09-01 12:49:23 -03:00
|
|
|
size_t size_required_to_decode_base64(StringView input)
|
|
|
|
|
{
|
|
|
|
|
return simdutf::maximal_binary_length_from_base64(input.characters_without_null_termination(), input.length());
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 14:03:06 -03:00
|
|
|
size_t size_required_to_decode_base64(Utf16View input)
|
|
|
|
|
{
|
|
|
|
|
if (input.has_ascii_storage())
|
|
|
|
|
return size_required_to_decode_base64(StringView { input.bytes() });
|
|
|
|
|
return simdutf::maximal_binary_length_from_base64(input.utf16_span().data(), input.length_in_code_units());
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-03 08:08:26 -03:00
|
|
|
static constexpr simdutf::last_chunk_handling_options to_simdutf_last_chunk_handling(LastChunkHandling last_chunk_handling)
|
2020-04-26 17:49:19 -03:00
|
|
|
{
|
2025-05-03 08:08:26 -03:00
|
|
|
switch (last_chunk_handling) {
|
|
|
|
|
case LastChunkHandling::Loose:
|
|
|
|
|
return simdutf::last_chunk_handling_options::loose;
|
|
|
|
|
case LastChunkHandling::Strict:
|
|
|
|
|
return simdutf::last_chunk_handling_options::strict;
|
|
|
|
|
case LastChunkHandling::StopBeforePartial:
|
|
|
|
|
return simdutf::last_chunk_handling_options::stop_before_partial;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 14:03:06 -03:00
|
|
|
static Optional<InvalidBase64> base64_error_from_result(simdutf::result result, ByteBuffer& output)
|
|
|
|
|
{
|
|
|
|
|
if (result.error == simdutf::SUCCESS || result.error == simdutf::OUTPUT_BUFFER_TOO_SMALL)
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
output.resize((result.count / 4) * 3);
|
|
|
|
|
|
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
|
|
|
struct DecodeError {
|
|
|
|
|
Base64DecodeError decode_error;
|
|
|
|
|
Error error;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto [decode_error, error] = [&]() -> DecodeError {
|
2026-06-21 14:03:06 -03:00
|
|
|
switch (result.error) {
|
|
|
|
|
case simdutf::BASE64_EXTRA_BITS:
|
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
|
|
|
return { Base64DecodeError::ExtraBits, Error::from_string_literal("Extra bits found at end of chunk") };
|
2026-06-21 14:03:06 -03:00
|
|
|
case simdutf::BASE64_INPUT_REMAINDER:
|
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
|
|
|
return { Base64DecodeError::InputRemainder, Error::from_string_literal("Invalid trailing data") };
|
2026-06-21 14:03:06 -03:00
|
|
|
case simdutf::INVALID_BASE64_CHARACTER:
|
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
|
|
|
return { Base64DecodeError::InvalidCharacter, Error::from_string_literal("Invalid base64 character") };
|
2026-06-21 14:03:06 -03:00
|
|
|
default:
|
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
|
|
|
return { Base64DecodeError::InvalidData, Error::from_string_literal("Invalid base64-encoded data") };
|
2026-06-21 14:03:06 -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
|
|
|
return InvalidBase64 { .decode_error = decode_error, .error = move(error), .valid_input_bytes = result.count };
|
2026-06-21 14:03:06 -03:00
|
|
|
}
|
|
|
|
|
|
2025-05-03 08:08:26 -03:00
|
|
|
static ErrorOr<size_t, InvalidBase64> decode_base64_into_impl(StringView input, ByteBuffer& output, LastChunkHandling last_chunk_handling, simdutf::base64_options options)
|
|
|
|
|
{
|
|
|
|
|
static constexpr auto decode_up_to_bad_character = true;
|
|
|
|
|
auto output_length = output.size();
|
2020-04-26 17:49:19 -03:00
|
|
|
|
2024-09-01 18:41:57 -03:00
|
|
|
auto result = simdutf::base64_to_binary_safe(
|
2024-07-15 16:25:08 -03:00
|
|
|
input.characters_without_null_termination(),
|
|
|
|
|
input.length(),
|
|
|
|
|
reinterpret_cast<char*>(output.data()),
|
2024-09-01 18:41:57 -03:00
|
|
|
output_length,
|
2025-05-03 08:08:26 -03:00
|
|
|
options,
|
|
|
|
|
to_simdutf_last_chunk_handling(last_chunk_handling),
|
|
|
|
|
decode_up_to_bad_character);
|
2024-03-20 13:41:41 -03:00
|
|
|
|
2026-06-21 14:03:06 -03:00
|
|
|
if (auto error = base64_error_from_result(result, output); error.has_value())
|
|
|
|
|
return error.release_value();
|
|
|
|
|
|
|
|
|
|
VERIFY(output_length <= output.size());
|
|
|
|
|
output.resize(output_length);
|
|
|
|
|
|
|
|
|
|
return result.count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ErrorOr<size_t, InvalidBase64> decode_base64_into_impl(Utf16View input, ByteBuffer& output, LastChunkHandling last_chunk_handling, simdutf::base64_options options)
|
|
|
|
|
{
|
|
|
|
|
if (input.has_ascii_storage())
|
|
|
|
|
return decode_base64_into_impl(StringView { input.bytes() }, output, last_chunk_handling, options);
|
|
|
|
|
|
|
|
|
|
static constexpr auto decode_up_to_bad_character = true;
|
|
|
|
|
auto output_length = output.size();
|
|
|
|
|
|
|
|
|
|
auto result = simdutf::base64_to_binary_safe(
|
|
|
|
|
input.utf16_span().data(),
|
|
|
|
|
input.length_in_code_units(),
|
|
|
|
|
reinterpret_cast<char*>(output.data()),
|
|
|
|
|
output_length,
|
|
|
|
|
options,
|
|
|
|
|
to_simdutf_last_chunk_handling(last_chunk_handling),
|
|
|
|
|
decode_up_to_bad_character);
|
|
|
|
|
|
|
|
|
|
if (auto error = base64_error_from_result(result, output); error.has_value())
|
|
|
|
|
return error.release_value();
|
2024-09-01 18:41:57 -03:00
|
|
|
|
|
|
|
|
VERIFY(output_length <= output.size());
|
|
|
|
|
output.resize(output_length);
|
|
|
|
|
|
2025-06-01 07:58:47 -03:00
|
|
|
return result.count;
|
2024-09-01 18:41:57 -03:00
|
|
|
}
|
|
|
|
|
|
2025-05-03 08:08:26 -03:00
|
|
|
static ErrorOr<ByteBuffer> decode_base64_impl(StringView input, LastChunkHandling last_chunk_handling, simdutf::base64_options options)
|
2024-09-01 18:41:57 -03:00
|
|
|
{
|
|
|
|
|
ByteBuffer output;
|
|
|
|
|
TRY(output.try_resize(size_required_to_decode_base64(input)));
|
|
|
|
|
|
2025-05-03 08:08:26 -03:00
|
|
|
if (auto result = decode_base64_into_impl(input, output, last_chunk_handling, options); result.is_error())
|
2024-09-01 18:41:57 -03:00
|
|
|
return result.release_error().error;
|
2020-04-26 17:49:19 -03:00
|
|
|
|
2024-03-20 13:41:41 -03:00
|
|
|
return output;
|
2020-04-26 17:49:19 -03:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 11:57:22 -03:00
|
|
|
static ErrorOr<String> encode_base64_impl(ReadonlyBytes input, simdutf::base64_options options)
|
2020-06-12 23:09:31 -03:00
|
|
|
{
|
2025-11-29 06:09:50 -03:00
|
|
|
if (input.is_empty())
|
|
|
|
|
return String {};
|
|
|
|
|
|
|
|
|
|
u8* buffer = nullptr;
|
|
|
|
|
auto output = TRY(AK::Detail::StringData::create_uninitialized(
|
2026-03-31 11:57:22 -03:00
|
|
|
simdutf::base64_length_from_binary(input.size(), options), buffer));
|
2024-07-15 16:25:08 -03:00
|
|
|
|
2024-08-30 10:41:01 -03:00
|
|
|
simdutf::binary_to_base64(
|
2026-03-31 11:57:22 -03:00
|
|
|
reinterpret_cast<char const*>(input.data()),
|
|
|
|
|
input.size(),
|
2025-11-29 06:09:50 -03:00
|
|
|
reinterpret_cast<char*>(buffer),
|
2024-07-15 16:25:08 -03:00
|
|
|
options);
|
|
|
|
|
|
2025-11-29 06:09:50 -03:00
|
|
|
return String { move(output) };
|
2020-06-12 23:09:31 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-21 14:03:10 -03:00
|
|
|
static Utf16String encode_base64_to_utf16_impl(ReadonlyBytes input, simdutf::base64_options options)
|
|
|
|
|
{
|
|
|
|
|
if (input.is_empty())
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
return Utf16String::create_uninitialized_ascii(
|
|
|
|
|
simdutf::base64_length_from_binary(input.size(), options),
|
|
|
|
|
[&](Bytes buffer) {
|
|
|
|
|
simdutf::binary_to_base64(
|
|
|
|
|
reinterpret_cast<char const*>(input.data()),
|
|
|
|
|
input.size(),
|
|
|
|
|
reinterpret_cast<char*>(buffer.data()),
|
|
|
|
|
options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-03 08:08:26 -03:00
|
|
|
ErrorOr<ByteBuffer> decode_base64(StringView input, LastChunkHandling last_chunk_handling)
|
2024-03-20 09:21:59 -03:00
|
|
|
{
|
2025-05-03 08:08:26 -03:00
|
|
|
return decode_base64_impl(input, last_chunk_handling, simdutf::base64_default);
|
2024-03-20 09:21:59 -03:00
|
|
|
}
|
|
|
|
|
|
2025-05-03 08:08:26 -03:00
|
|
|
ErrorOr<ByteBuffer> decode_base64url(StringView input, LastChunkHandling last_chunk_handling)
|
2024-03-20 09:21:59 -03:00
|
|
|
{
|
2025-05-03 08:08:26 -03:00
|
|
|
return decode_base64_impl(input, last_chunk_handling, simdutf::base64_url);
|
2024-03-20 09:21:59 -03:00
|
|
|
}
|
|
|
|
|
|
2025-05-03 08:08:26 -03:00
|
|
|
ErrorOr<size_t, InvalidBase64> decode_base64_into(StringView input, ByteBuffer& output, LastChunkHandling last_chunk_handling)
|
2024-09-01 18:41:57 -03:00
|
|
|
{
|
2025-05-03 08:08:26 -03:00
|
|
|
return decode_base64_into_impl(input, output, last_chunk_handling, simdutf::base64_default);
|
2024-09-01 18:41:57 -03:00
|
|
|
}
|
|
|
|
|
|
2025-05-03 08:08:26 -03:00
|
|
|
ErrorOr<size_t, InvalidBase64> decode_base64url_into(StringView input, ByteBuffer& output, LastChunkHandling last_chunk_handling)
|
2024-09-01 18:41:57 -03:00
|
|
|
{
|
2025-05-03 08:08:26 -03:00
|
|
|
return decode_base64_into_impl(input, output, last_chunk_handling, simdutf::base64_url);
|
2024-09-01 18:41:57 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-21 14:03:06 -03:00
|
|
|
ErrorOr<size_t, InvalidBase64> decode_base64_into(Utf16View input, ByteBuffer& output, LastChunkHandling last_chunk_handling)
|
|
|
|
|
{
|
|
|
|
|
return decode_base64_into_impl(input, output, last_chunk_handling, simdutf::base64_default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErrorOr<size_t, InvalidBase64> decode_base64url_into(Utf16View input, ByteBuffer& output, LastChunkHandling last_chunk_handling)
|
|
|
|
|
{
|
|
|
|
|
return decode_base64_into_impl(input, output, last_chunk_handling, simdutf::base64_url);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-15 17:12:21 -03:00
|
|
|
ErrorOr<String> encode_base64(ReadonlyBytes input, OmitPadding omit_padding)
|
2024-03-20 09:21:59 -03:00
|
|
|
{
|
2024-07-15 17:12:21 -03:00
|
|
|
auto options = omit_padding == OmitPadding::Yes
|
|
|
|
|
? simdutf::base64_default_no_padding
|
|
|
|
|
: simdutf::base64_default;
|
|
|
|
|
|
|
|
|
|
return encode_base64_impl(input, options);
|
2024-03-20 09:21:59 -03:00
|
|
|
}
|
2024-07-15 16:25:08 -03:00
|
|
|
|
2024-07-15 17:12:21 -03:00
|
|
|
ErrorOr<String> encode_base64url(ReadonlyBytes input, OmitPadding omit_padding)
|
2024-03-20 09:21:59 -03:00
|
|
|
{
|
2024-07-15 17:12:21 -03:00
|
|
|
auto options = omit_padding == OmitPadding::Yes
|
|
|
|
|
? simdutf::base64_url
|
|
|
|
|
: simdutf::base64_url_with_padding;
|
|
|
|
|
|
|
|
|
|
return encode_base64_impl(input, options);
|
2024-03-20 09:21:59 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-21 14:03:10 -03:00
|
|
|
ErrorOr<Utf16String> encode_base64_to_utf16(ReadonlyBytes input, OmitPadding omit_padding)
|
|
|
|
|
{
|
|
|
|
|
auto options = omit_padding == OmitPadding::Yes
|
|
|
|
|
? simdutf::base64_default_no_padding
|
|
|
|
|
: simdutf::base64_default;
|
|
|
|
|
|
|
|
|
|
return encode_base64_to_utf16_impl(input, options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErrorOr<Utf16String> encode_base64url_to_utf16(ReadonlyBytes input, OmitPadding omit_padding)
|
|
|
|
|
{
|
|
|
|
|
auto options = omit_padding == OmitPadding::Yes
|
|
|
|
|
? simdutf::base64_url
|
|
|
|
|
: simdutf::base64_url_with_padding;
|
|
|
|
|
|
|
|
|
|
return encode_base64_to_utf16_impl(input, options);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-26 17:49:19 -03:00
|
|
|
}
|