LibJS: Use non-copying GetUint8ArrayBytes() in Uint8Array.toBase64()

If the ArrayBuffer we're looking at is non-shared, we can simply get a
view onto the underlying bytes and pass that to the Base64 encoder.
This commit is contained in:
Andreas Kling 2025-11-29 14:21:32 +01:00 committed by Tim Flynn
parent 124b4fc06a
commit c1c24e8fd6
Notes: github-actions[bot] 2025-11-29 14:40:50 +00:00
2 changed files with 46 additions and 14 deletions

View file

@ -333,22 +333,33 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::to_base64)
}
// 8. Let toEncode be ? GetUint8ArrayBytes(O).
auto to_encode = TRY(get_uint8_array_bytes(vm, typed_array));
String out_ascii;
// 9. If alphabet is "base64", then
if (alphabet == Alphabet::Base64) {
// a. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64
// encoding specified in section 4 of RFC 4648. Padding is included if and only if omitPadding is false.
out_ascii = MUST(encode_base64(to_encode, omit_padding));
}
// 10. Else,
else {
// a. Assert: alphabet is "base64url".
// b. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64url
// encoding specified in section 5 of RFC 4648. Padding is included if and only if omitPadding is false.
out_ascii = MUST(encode_base64url(to_encode, omit_padding));
// OPTIMIZATION: If the ArrayBuffer is not shared, we can avoid copying the bytes.
if (!typed_array->viewed_array_buffer()->is_shared_array_buffer()
&& !typed_array->viewed_array_buffer()->is_detached()) {
auto to_encode = TRY(get_uint8_array_bytes_view(vm, typed_array));
if (alphabet == Alphabet::Base64) {
out_ascii = MUST(encode_base64(to_encode, omit_padding));
} else {
out_ascii = MUST(encode_base64url(to_encode, omit_padding));
}
} else {
auto to_encode = TRY(get_uint8_array_bytes(vm, typed_array));
// 9. If alphabet is "base64", then
if (alphabet == Alphabet::Base64) {
// a. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64
// encoding specified in section 4 of RFC 4648. Padding is included if and only if omitPadding is false.
out_ascii = MUST(encode_base64(to_encode, omit_padding));
}
// 10. Else,
else {
// a. Assert: alphabet is "base64url".
// b. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64url
// encoding specified in section 5 of RFC 4648. Padding is included if and only if omitPadding is false.
out_ascii = MUST(encode_base64url(to_encode, omit_padding));
}
}
// 11. Return CodePointsToString(outAscii).
@ -438,6 +449,26 @@ ThrowCompletionOr<ByteBuffer> get_uint8_array_bytes(VM& vm, TypedArrayBase const
return bytes;
}
// 23.3.3.2 GetUint8ArrayBytes ( ta ), https://tc39.es/ecma262/#sec-getuint8arraybytes
// NOTE: This is an optimized version that returns a view into the underlying buffer when possible.
// It's only safe to use when the ArrayBuffer is known to not be shared or detached.
ThrowCompletionOr<ReadonlyBytes> get_uint8_array_bytes_view(VM& vm, TypedArrayBase const& typed_array)
{
VERIFY(typed_array.kind() == TypedArrayBase::Kind::Uint8Array);
VERIFY(!typed_array.viewed_array_buffer()->is_shared_array_buffer());
VERIFY(!typed_array.viewed_array_buffer()->is_detached());
auto typed_array_record = make_typed_array_with_buffer_witness_record(typed_array, ArrayBuffer::Order::SeqCst);
if (is_typed_array_out_of_bounds(typed_array_record))
return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
auto length = typed_array_length(typed_array_record);
auto byte_offset = typed_array.byte_offset();
return ReadonlyBytes {
typed_array.viewed_array_buffer()->buffer().data() + byte_offset,
length
};
}
// 23.3.3.3 SetUint8ArrayBytes ( into, bytes ), https://tc39.es/ecma262/#sec-setuint8arraybytes
void set_uint8_array_bytes(TypedArrayBase& into, ReadonlyBytes bytes)
{

View file

@ -50,6 +50,7 @@ struct DecodeResult {
ThrowCompletionOr<GC::Ref<TypedArrayBase>> validate_uint8_array(VM&);
ThrowCompletionOr<ByteBuffer> get_uint8_array_bytes(VM&, TypedArrayBase const&);
ThrowCompletionOr<ReadonlyBytes> get_uint8_array_bytes_view(VM&, TypedArrayBase const&);
void set_uint8_array_bytes(TypedArrayBase&, ReadonlyBytes);
DecodeResult from_base64(VM&, StringView string, Alphabet alphabet, AK::LastChunkHandling last_chunk_handling, Optional<size_t> max_length = {});
DecodeResult from_hex(VM&, StringView string, Optional<size_t> max_length = {});