ladybird/AK/Base64.cpp
Timothy Flynn bfc9dc447f AK+LibWeb: Replace our home-grown base64 encoder/decoders with simdutf
We currently have 2 base64 coders: one in AK, another in LibWeb for a
"forgiving" implementation. ECMA-262 has an upcoming proposal which will
require a third implementation.

Instead, let's use the base64 implementation that is used by Node.js and
recommended by the upcoming proposal. It handles forgiving decoding as
well.

Our users of AK's implementation should be fine with the forgiving
implementation. The AK impl originally had naive forgiving behavior, but
that was removed solely for performance reasons.

Using http://mattmahoney.net/dc/enwik8.zip (100MB unzipped) as a test,
performance of our old home-grown implementations vs. the simdutf
implementation (on Linux x64):

                Encode    Decode
AK base64       0.226s    0.169s
LibWeb base64   N/A       1.244s
simdutf         0.161s    0.047s
2024-07-16 10:27:39 +02:00

78 lines
2.1 KiB
C++

/*
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#define AK_DONT_REPLACE_STD
#include <AK/Base64.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <simdutf.h>
namespace AK {
static ErrorOr<ByteBuffer> decode_base64_impl(StringView input, simdutf::base64_options options)
{
ByteBuffer output;
TRY(output.try_resize(simdutf::maximal_binary_length_from_base64(input.characters_without_null_termination(), input.length())));
auto result = simdutf::base64_to_binary(
input.characters_without_null_termination(),
input.length(),
reinterpret_cast<char*>(output.data()),
options);
if (result.error != simdutf::SUCCESS)
return Error::from_string_literal("Invalid base64-encoded data");
output.resize(result.count);
return output;
}
static ErrorOr<String> encode_base64_impl(StringView input, simdutf::base64_options options)
{
Vector<u8> output;
// simdutf does not append padding to base64url encodings. We use the default encoding option here to allocate room
// for the padding characters that we will later append ourselves if necessary.
TRY(output.try_resize(simdutf::base64_length_from_binary(input.length(), simdutf::base64_default)));
auto size_written = simdutf::binary_to_base64(
input.characters_without_null_termination(),
input.length(),
reinterpret_cast<char*>(output.data()),
options);
if (options == simdutf::base64_url) {
for (size_t i = size_written; i < output.size(); ++i)
output[i] = '=';
}
return String::from_utf8_without_validation(output);
}
ErrorOr<ByteBuffer> decode_base64(StringView input)
{
return decode_base64_impl(input, simdutf::base64_default);
}
ErrorOr<ByteBuffer> decode_base64url(StringView input)
{
return decode_base64_impl(input, simdutf::base64_url);
}
ErrorOr<String> encode_base64(ReadonlyBytes input)
{
return encode_base64_impl(input, simdutf::base64_default);
}
ErrorOr<String> encode_base64url(ReadonlyBytes input)
{
return encode_base64_impl(input, simdutf::base64_url);
}
}