Skip to content

Commit c1c24e8

Browse files
awesomeklingtrflynn89
authored andcommitted
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.
1 parent 124b4fc commit c1c24e8

File tree

2 files changed

+46
-14
lines changed

2 files changed

+46
-14
lines changed

Libraries/LibJS/Runtime/Uint8Array.cpp

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -333,22 +333,33 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::to_base64)
333333
}
334334

335335
// 8. Let toEncode be ? GetUint8ArrayBytes(O).
336-
auto to_encode = TRY(get_uint8_array_bytes(vm, typed_array));
337-
338336
String out_ascii;
339337

340-
// 9. If alphabet is "base64", then
341-
if (alphabet == Alphabet::Base64) {
342-
// a. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64
343-
// encoding specified in section 4 of RFC 4648. Padding is included if and only if omitPadding is false.
344-
out_ascii = MUST(encode_base64(to_encode, omit_padding));
345-
}
346-
// 10. Else,
347-
else {
348-
// a. Assert: alphabet is "base64url".
349-
// b. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64url
350-
// encoding specified in section 5 of RFC 4648. Padding is included if and only if omitPadding is false.
351-
out_ascii = MUST(encode_base64url(to_encode, omit_padding));
338+
// OPTIMIZATION: If the ArrayBuffer is not shared, we can avoid copying the bytes.
339+
if (!typed_array->viewed_array_buffer()->is_shared_array_buffer()
340+
&& !typed_array->viewed_array_buffer()->is_detached()) {
341+
auto to_encode = TRY(get_uint8_array_bytes_view(vm, typed_array));
342+
if (alphabet == Alphabet::Base64) {
343+
out_ascii = MUST(encode_base64(to_encode, omit_padding));
344+
} else {
345+
out_ascii = MUST(encode_base64url(to_encode, omit_padding));
346+
}
347+
} else {
348+
auto to_encode = TRY(get_uint8_array_bytes(vm, typed_array));
349+
350+
// 9. If alphabet is "base64", then
351+
if (alphabet == Alphabet::Base64) {
352+
// a. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64
353+
// encoding specified in section 4 of RFC 4648. Padding is included if and only if omitPadding is false.
354+
out_ascii = MUST(encode_base64(to_encode, omit_padding));
355+
}
356+
// 10. Else,
357+
else {
358+
// a. Assert: alphabet is "base64url".
359+
// b. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64url
360+
// encoding specified in section 5 of RFC 4648. Padding is included if and only if omitPadding is false.
361+
out_ascii = MUST(encode_base64url(to_encode, omit_padding));
362+
}
352363
}
353364

354365
// 11. Return CodePointsToString(outAscii).
@@ -438,6 +449,26 @@ ThrowCompletionOr<ByteBuffer> get_uint8_array_bytes(VM& vm, TypedArrayBase const
438449
return bytes;
439450
}
440451

452+
// 23.3.3.2 GetUint8ArrayBytes ( ta ), https://tc39.es/ecma262/#sec-getuint8arraybytes
453+
// NOTE: This is an optimized version that returns a view into the underlying buffer when possible.
454+
// It's only safe to use when the ArrayBuffer is known to not be shared or detached.
455+
ThrowCompletionOr<ReadonlyBytes> get_uint8_array_bytes_view(VM& vm, TypedArrayBase const& typed_array)
456+
{
457+
VERIFY(typed_array.kind() == TypedArrayBase::Kind::Uint8Array);
458+
VERIFY(!typed_array.viewed_array_buffer()->is_shared_array_buffer());
459+
VERIFY(!typed_array.viewed_array_buffer()->is_detached());
460+
auto typed_array_record = make_typed_array_with_buffer_witness_record(typed_array, ArrayBuffer::Order::SeqCst);
461+
if (is_typed_array_out_of_bounds(typed_array_record))
462+
return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
463+
auto length = typed_array_length(typed_array_record);
464+
auto byte_offset = typed_array.byte_offset();
465+
466+
return ReadonlyBytes {
467+
typed_array.viewed_array_buffer()->buffer().data() + byte_offset,
468+
length
469+
};
470+
}
471+
441472
// 23.3.3.3 SetUint8ArrayBytes ( into, bytes ), https://tc39.es/ecma262/#sec-setuint8arraybytes
442473
void set_uint8_array_bytes(TypedArrayBase& into, ReadonlyBytes bytes)
443474
{

Libraries/LibJS/Runtime/Uint8Array.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ struct DecodeResult {
5050

5151
ThrowCompletionOr<GC::Ref<TypedArrayBase>> validate_uint8_array(VM&);
5252
ThrowCompletionOr<ByteBuffer> get_uint8_array_bytes(VM&, TypedArrayBase const&);
53+
ThrowCompletionOr<ReadonlyBytes> get_uint8_array_bytes_view(VM&, TypedArrayBase const&);
5354
void set_uint8_array_bytes(TypedArrayBase&, ReadonlyBytes);
5455
DecodeResult from_base64(VM&, StringView string, Alphabet alphabet, AK::LastChunkHandling last_chunk_handling, Optional<size_t> max_length = {});
5556
DecodeResult from_hex(VM&, StringView string, Optional<size_t> max_length = {});

0 commit comments

Comments
 (0)