From a28961a92df6b87a9b83742219f4a5ab4cf2bdfa Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Mon, 23 Dec 2024 16:20:02 +1300 Subject: [PATCH] LibWeb/Streams: Fully implement BYOB operations for ReadableStream As far as I can tell there is no change in WPT from this implementation. But these steps should be more effecient than using the non BYOB steps as we can make direct use of the provided buffer to the byte stream. --- Libraries/LibWeb/Streams/ReadableStream.cpp | 69 ++++++++++++++++----- Libraries/LibWeb/Streams/ReadableStream.h | 2 + 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/Libraries/LibWeb/Streams/ReadableStream.cpp b/Libraries/LibWeb/Streams/ReadableStream.cpp index a0283b88579..742277848c6 100644 --- a/Libraries/LibWeb/Streams/ReadableStream.cpp +++ b/Libraries/LibWeb/Streams/ReadableStream.cpp @@ -15,9 +15,12 @@ #include #include #include +#include #include #include #include +#include +#include #include namespace Web::Streams { @@ -280,8 +283,10 @@ WebIDL::ExceptionOr ReadableStream::pull_from_bytes(ByteBuffer bytes) // 3. Let desiredSize be available. auto desired_size = available; - // FIXME: 4. If stream’s current BYOB request view is non-null, then set desiredSize to stream’s current BYOB request - // view's byte length. + // 4. If stream’s current BYOB request view is non-null, then set desiredSize to stream’s current BYOB request + // view's byte length. + if (auto byob_view = current_byob_request_view()) + desired_size = byob_view->byte_length(); // 5. Let pullSize be the smaller value of available and desiredSize. auto pull_size = min(available, desired_size); @@ -293,11 +298,16 @@ WebIDL::ExceptionOr ReadableStream::pull_from_bytes(ByteBuffer bytes) if (pull_size != available) bytes = MUST(bytes.slice(pull_size, available - pull_size)); - // FIXME: 8. If stream’s current BYOB request view is non-null, then: - // 1. Write pulled into stream’s current BYOB request view. - // 2. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], pullSize). + // 8. If stream’s current BYOB request view is non-null, then: + if (auto byob_view = current_byob_request_view()) { + // 1. Write pulled into stream’s current BYOB request view. + byob_view->write(pulled); + + // 2. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], pullSize). + TRY(readable_byte_stream_controller_respond(controller, pull_size)); + } // 9. Otherwise, - { + else { // 1. Set view to the result of creating a Uint8Array from pulled in stream’s relevant Realm. auto array_buffer = JS::ArrayBuffer::create(realm, move(pulled)); auto view = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer); @@ -309,6 +319,23 @@ WebIDL::ExceptionOr ReadableStream::pull_from_bytes(ByteBuffer bytes) return {}; } +// https://streams.spec.whatwg.org/#readablestream-current-byob-request-view +GC::Ptr ReadableStream::current_byob_request_view() +{ + // 1. Assert: stream.[[controller]] implements ReadableByteStreamController. + VERIFY(m_controller->has>()); + + // 2. Let byobRequest be ! ReadableByteStreamControllerGetBYOBRequest(stream.[[controller]]). + auto byob_request = readable_byte_stream_controller_get_byob_request(m_controller->get>()); + + // 3. If byobRequest is null, then return null. + if (!byob_request) + return {}; + + // 4. Return byobRequest.[[view]]. + return byob_request->view(); +} + // https://streams.spec.whatwg.org/#readablestream-enqueue WebIDL::ExceptionOr ReadableStream::enqueue(JS::Value chunk) { @@ -317,7 +344,7 @@ WebIDL::ExceptionOr ReadableStream::enqueue(JS::Value chunk) // 1. If stream.[[controller]] implements ReadableStreamDefaultController, if (m_controller->has>()) { // 1. Perform ! ReadableStreamDefaultControllerEnqueue(stream.[[controller]], chunk). - return readable_stream_default_controller_enqueue(m_controller->get>(), chunk); + MUST(readable_stream_default_controller_enqueue(m_controller->get>(), chunk)); } // 2. Otherwise, else { @@ -325,23 +352,31 @@ WebIDL::ExceptionOr ReadableStream::enqueue(JS::Value chunk) VERIFY(m_controller->has>()); auto readable_byte_controller = m_controller->get>(); - // FIXME: 2. Assert: chunk is an ArrayBufferView. + // 2. Assert: chunk is an ArrayBufferView. + VERIFY(chunk.is_object()); + auto chunk_view = heap().allocate(chunk.as_object()); // 3. Let byobView be the current BYOB request view for stream. - // FIXME: This is not what the spec means by 'current BYOB request view' - auto byob_view = readable_byte_controller->raw_byob_request(); + auto byob_view = current_byob_request_view(); // 4. If byobView is non-null, and chunk.[[ViewedArrayBuffer]] is byobView.[[ViewedArrayBuffer]], then: - if (byob_view) { - // FIXME: 1. Assert: chunk.[[ByteOffset]] is byobView.[[ByteOffset]]. - // FIXME: 2. Assert: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]]. - // FIXME: 3. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], chunk.[[ByteLength]]). - TODO(); - } + if (byob_view && chunk_view->viewed_array_buffer() == byob_view->viewed_array_buffer()) { + // 1. Assert: chunk.[[ByteOffset]] is byobView.[[ByteOffset]]. + VERIFY(chunk_view->byte_offset() == byob_view->byte_offset()); + // 2. Assert: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]]. + VERIFY(chunk_view->byte_length() <= byob_view->byte_length()); + + // 3. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], chunk.[[ByteLength]]). + TRY(readable_byte_stream_controller_respond(readable_byte_controller, chunk_view->byte_length())); + } // 5. Otherwise, perform ? ReadableByteStreamControllerEnqueue(stream.[[controller]], chunk). - return readable_byte_stream_controller_enqueue(readable_byte_controller, chunk); + else { + TRY(readable_byte_stream_controller_enqueue(readable_byte_controller, chunk)); + } } + + return {}; } // https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support diff --git a/Libraries/LibWeb/Streams/ReadableStream.h b/Libraries/LibWeb/Streams/ReadableStream.h index 3959c82bbe5..5cfc1e66dce 100644 --- a/Libraries/LibWeb/Streams/ReadableStream.h +++ b/Libraries/LibWeb/Streams/ReadableStream.h @@ -111,6 +111,8 @@ public: void set_up_with_byte_reading_support(GC::Ptr = {}, GC::Ptr = {}, double high_water_mark = 0); GC::Ref piped_through(GC::Ref, bool prevent_close = false, bool prevent_abort = false, bool prevent_cancel = false, JS::Value signal = JS::js_undefined()); + GC::Ptr current_byob_request_view(); + private: explicit ReadableStream(JS::Realm&);