Skip to main content

ferrijs_std/stream_web/readable/
byte_controller.rs

1use std::collections::VecDeque;
2
3use crate::utils::{
4    error_messages::ERROR_MSG_ARRAY_BUFFER_DETACHED,
5    option::{Null, Undefined},
6    primordials::{BasePrimordials, Primordial},
7    result::ResultExt,
8};
9use rquickjs::{
10    class::{OwnedBorrow, OwnedBorrowMut, Trace, Tracer},
11    function::Constructor,
12    methods,
13    prelude::{Opt, This},
14    ArrayBuffer, Class, Ctx, Error, Exception, Function, IntoJs, JsLifetime, Object, Promise,
15    Result, TypedArray, Value,
16};
17
18use crate::stream_web::{
19    readable::{
20        byob_reader::{ArrayConstructorPrimordials, ReadableStreamReadIntoRequest, ViewBytes},
21        controller::{
22            ReadableStreamController, ReadableStreamControllerClass, ReadableStreamControllerOwned,
23        },
24        default_controller::ReadableStreamDefaultControllerOwned,
25        default_reader::ReadableStreamReadRequest,
26        objects::{
27            ReadableByteStreamObjects, ReadableStreamBYOBObjects, ReadableStreamClassObjects,
28            ReadableStreamDefaultReaderObjects, ReadableStreamObjects,
29        },
30        reader::ReadableStreamReader,
31        stream::{
32            algorithms::{CancelAlgorithm, PullAlgorithm, StartAlgorithm},
33            source::UnderlyingSource,
34            ReadableStream, ReadableStreamClass, ReadableStreamOwned, ReadableStreamState,
35        },
36    },
37    utils::{
38        class_from_owned_borrow_mut,
39        promise::{promise_resolved_with, upon_promise},
40        UnwrapOrUndefined,
41    },
42};
43
44#[derive(JsLifetime)]
45#[rquickjs::class]
46pub struct ReadableByteStreamController<'js> {
47    auto_allocate_chunk_size: Option<usize>,
48    byob_request: Option<Class<'js, ReadableStreamBYOBRequest<'js>>>,
49    cancel_algorithm: Option<CancelAlgorithm<'js>>,
50    close_requested: bool,
51    pull_again: bool,
52    pull_algorithm: Option<PullAlgorithm<'js>>,
53    pulling: bool,
54    pub(super) pending_pull_intos: VecDeque<PullIntoDescriptor<'js>>,
55    queue: VecDeque<ReadableByteStreamQueueEntry<'js>>,
56    queue_total_size: usize,
57    started: bool,
58    strategy_hwm: f64,
59    pub(super) stream: ReadableStreamClass<'js>,
60
61    pub(super) array_constructor_primordials: ArrayConstructorPrimordials<'js>,
62    constructor_array_buffer: Constructor<'js>,
63    pub(super) function_array_buffer_is_view: Function<'js>,
64}
65
66impl<'js> Trace<'js> for ReadableByteStreamController<'js> {
67    fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
68        self.auto_allocate_chunk_size.trace(tracer);
69        self.byob_request.trace(tracer);
70        self.cancel_algorithm.trace(tracer);
71        self.pull_algorithm.trace(tracer);
72        self.pending_pull_intos.trace(tracer);
73        self.queue.trace(tracer);
74        self.queue_total_size.trace(tracer);
75        self.started.trace(tracer);
76        self.strategy_hwm.trace(tracer);
77        self.stream.trace(tracer);
78        self.array_constructor_primordials.trace(tracer);
79        self.constructor_array_buffer.trace(tracer);
80        self.function_array_buffer_is_view.trace(tracer);
81    }
82}
83
84pub type ReadableByteStreamControllerClass<'js> = Class<'js, ReadableByteStreamController<'js>>;
85pub(crate) type ReadableByteStreamControllerOwned<'js> =
86    OwnedBorrowMut<'js, ReadableByteStreamController<'js>>;
87
88impl<'js> ReadableByteStreamController<'js> {
89    // SetUpReadableByteStreamControllerFromUnderlyingSource
90    pub(super) fn set_up_readable_byte_stream_controller_from_underlying_source(
91        ctx: &Ctx<'js>,
92        stream: ReadableStreamOwned<'js>,
93        underlying_source: Null<Undefined<Object<'js>>>,
94        underlying_source_dict: UnderlyingSource<'js>,
95        high_water_mark: f64,
96    ) -> Result<()> {
97        let (start_algorithm, pull_algorithm, cancel_algorithm, auto_allocate_chunk_size) = (
98            // If underlyingSourceDict["start"] exists, then set startAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["start"] with argument list
99            // « controller » and callback this value underlyingSource.
100            underlying_source_dict
101                .start
102                .map(|f| StartAlgorithm::Function {
103                    f,
104                    underlying_source: underlying_source.clone(),
105                })
106                .unwrap_or(StartAlgorithm::ReturnUndefined),
107            // If underlyingSourceDict["pull"] exists, then set pullAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["pull"] with argument list
108            // « controller » and callback this value underlyingSource.
109            underlying_source_dict
110                .pull
111                .map(|f| PullAlgorithm::Function {
112                    f,
113                    underlying_source: underlying_source.clone(),
114                })
115                .unwrap_or(PullAlgorithm::ReturnPromiseUndefined),
116            // If underlyingSourceDict["cancel"] exists, then set cancelAlgorithm to an algorithm which takes an argument reason and returns the result of invoking underlyingSourceDict["cancel"] with argument list
117            // « reason » and callback this value underlyingSource.
118            underlying_source_dict
119                .cancel
120                .map(|f| CancelAlgorithm::Function {
121                    f,
122                    underlying_source,
123                })
124                .unwrap_or(CancelAlgorithm::ReturnPromiseUndefined),
125            // Let autoAllocateChunkSize be underlyingSourceDict["autoAllocateChunkSize"], if it exists, or undefined otherwise.
126            underlying_source_dict.auto_allocate_chunk_size,
127        );
128
129        // If autoAllocateChunkSize is 0, then throw a TypeError exception.
130        if auto_allocate_chunk_size == Some(0) {
131            return Err(Exception::throw_type(
132                ctx,
133                "autoAllocateChunkSize must be greater than 0",
134            ));
135        }
136
137        Self::set_up_readable_byte_stream_controller(
138            ctx.clone(),
139            stream,
140            start_algorithm,
141            pull_algorithm,
142            cancel_algorithm,
143            high_water_mark,
144            auto_allocate_chunk_size,
145        )?;
146
147        Ok(())
148    }
149
150    pub(super) fn set_up_readable_byte_stream_controller(
151        ctx: Ctx<'js>,
152        stream: ReadableStreamOwned<'js>,
153        start_algorithm: StartAlgorithm<'js>,
154        pull_algorithm: PullAlgorithm<'js>,
155        cancel_algorithm: CancelAlgorithm<'js>,
156        high_water_mark: f64,
157        auto_allocate_chunk_size: Option<usize>,
158    ) -> Result<Class<'js, Self>> {
159        let (stream_class, mut stream) = class_from_owned_borrow_mut(stream);
160
161        let array_constructor_primordials = ArrayConstructorPrimordials::get(&ctx)?.clone();
162        let BasePrimordials {
163            constructor_array_buffer,
164            function_array_buffer_is_view,
165            ..
166        } = &*BasePrimordials::get(&ctx)?;
167
168        let controller = Self {
169            // Set controller.[[stream]] to stream.
170            stream: stream_class,
171
172            // Set controller.[[pullAgain]] and controller.[[pulling]] to false.
173            pull_again: false,
174            pulling: false,
175
176            // Set controller.[[byobRequest]] to null.
177            byob_request: None,
178
179            // Perform ! ResetQueue(controller).
180            queue: VecDeque::new(),
181            queue_total_size: 0,
182
183            // Set controller.[[closeRequested]] and controller.[[started]] to false.
184            close_requested: false,
185            started: false,
186
187            // Set controller.[[strategyHWM]] to highWaterMark.
188            strategy_hwm: high_water_mark,
189
190            // Set controller.[[pullAlgorithm]] to pullAlgorithm.
191            pull_algorithm: Some(pull_algorithm),
192            cancel_algorithm: Some(cancel_algorithm),
193
194            // Set controller.[[autoAllocateChunkSize]] to autoAllocateChunkSize.
195            auto_allocate_chunk_size,
196
197            pending_pull_intos: VecDeque::new(),
198
199            array_constructor_primordials,
200            constructor_array_buffer: constructor_array_buffer.clone(),
201            function_array_buffer_is_view: function_array_buffer_is_view.clone(),
202        };
203
204        let controller_class = Class::instance(ctx.clone(), controller)?;
205
206        // Set stream.[[controller]] to controller.
207        stream.controller =
208            ReadableStreamControllerClass::ReadableStreamByteController(controller_class.clone());
209
210        let objects =
211            ReadableStreamObjects::new_byte(stream, OwnedBorrowMut::from_class(controller_class))
212                .refresh_reader();
213
214        let promise_primordials = objects.stream.promise_primordials.clone();
215
216        // Let startResult be the result of performing startAlgorithm.
217        let (start_result, objects_class) =
218            Self::start_algorithm(ctx.clone(), objects, start_algorithm)?;
219
220        // Let startPromise be a promise resolved with startResult.
221        let start_promise = promise_resolved_with(&ctx, &promise_primordials, Ok(start_result))?;
222
223        let _ = upon_promise::<Value<'js>, _>(ctx.clone(), start_promise, {
224            let objects_class = objects_class.clone();
225            move |ctx, result| {
226                let mut objects =
227                    ReadableStreamObjects::from_class_no_reader(objects_class).refresh_reader();
228                match result {
229                    // Upon fulfillment of startPromise,
230                    Ok(_) => {
231                        // Set controller.[[started]] to true.
232                        objects.controller.started = true;
233                        // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
234                        Self::readable_byte_stream_controller_call_pull_if_needed(ctx, objects)?;
235                        Ok(())
236                    },
237                    // Upon rejection of startPromise with reason r,
238                    Err(r) => {
239                        // Perform ! ReadableByteStreamControllerError(controller, r).
240                        Self::readable_byte_stream_controller_error(objects, r)?;
241                        Ok(())
242                    },
243                }
244            }
245        })?;
246
247        Ok(objects_class.controller)
248    }
249
250    fn readable_byte_stream_controller_call_pull_if_needed<R: ReadableStreamReader<'js>>(
251        ctx: Ctx<'js>,
252        objects: ReadableByteStreamObjects<'js, R>,
253    ) -> Result<ReadableByteStreamObjects<'js, R>> {
254        // Let shouldPull be ! ReadableByteStreamControllerShouldCallPull(controller).
255        let (should_pull, mut objects) =
256            Self::readable_byte_stream_controller_should_call_pull(objects);
257
258        // If shouldPull is false, return.
259        if !should_pull {
260            return Ok(objects);
261        }
262
263        // If controller.[[pulling]] is true,
264        if objects.controller.pulling {
265            // Set controller.[[pullAgain]] to true.
266            objects.controller.pull_again = true;
267
268            // Return.
269            return Ok(objects);
270        }
271
272        // Set controller.[[pulling]] to true.
273        objects.controller.pulling = true;
274
275        // Let pullPromise be the result of performing controller.[[pullAlgorithm]].
276        let (pull_promise, objects_class) = Self::pull_algorithm(ctx.clone(), objects)?;
277
278        upon_promise::<Value<'js>, ()>(ctx, pull_promise, {
279            let objects_class = objects_class.clone();
280            move |ctx, result| {
281                let mut objects =
282                    ReadableStreamObjects::from_class_no_reader(objects_class).refresh_reader();
283                match result {
284                    // Upon fulfillment of pullPromise,
285                    Ok(_) => {
286                        // Set controller.[[pulling]] to false.
287                        objects.controller.pulling = false;
288                        // If controller.[[pullAgain]] is true,
289                        if objects.controller.pull_again {
290                            // Set controller.[[pullAgain]] to false.
291                            objects.controller.pull_again = false;
292                            // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
293                            Self::readable_byte_stream_controller_call_pull_if_needed(
294                                ctx, objects,
295                            )?;
296                        };
297                        Ok(())
298                    },
299                    // Upon rejection of pullPromise with reason e,
300                    Err(e) => {
301                        // Perform ! ReadableByteStreamControllerError(controller, e).
302                        Self::readable_byte_stream_controller_error(objects, e)?;
303                        Ok(())
304                    },
305                }
306            }
307        })?;
308
309        Ok(ReadableStreamObjects::from_class(objects_class))
310    }
311
312    fn readable_byte_stream_controller_should_call_pull<R: ReadableStreamReader<'js>>(
313        mut objects: ReadableByteStreamObjects<'js, R>,
314    ) -> (bool, ReadableByteStreamObjects<'js, R>) {
315        // Let stream be controller.[[stream]].
316        match objects.stream.state {
317            ReadableStreamState::Readable => {},
318            // If stream.[[state]] is not "readable", return false.
319            _ => return (false, objects),
320        }
321
322        // If controller.[[closeRequested]] is true, return false.
323        if objects.controller.close_requested {
324            return (false, objects);
325        }
326
327        // If controller.[[started]] is false, return false.
328        if !objects.controller.started {
329            return (false, objects);
330        }
331
332        let (mut has_read_requests, mut has_read_into_requests) = (false, false);
333        objects = objects
334            .with_reader(
335                |objects| {
336                    // If ! ReadableStreamHasDefaultReader(stream) is true and ! ReadableStreamGetNumReadRequests(stream) > 0, return true.
337                    if ReadableStream::readable_stream_get_num_read_requests(&objects.reader) > 0 {
338                        has_read_requests = true;
339                    }
340                    Ok(objects)
341                },
342                |objects| {
343                    // If ! ReadableStreamHasBYOBReader(stream) is true and ! ReadableStreamGetNumReadIntoRequests(stream) > 0, return true.
344                    if ReadableStream::readable_stream_get_num_read_into_requests(&objects.reader)
345                        > 0
346                    {
347                        has_read_into_requests = true;
348                    }
349                    Ok(objects)
350                },
351                Ok,
352            )
353            .unwrap();
354
355        if has_read_requests || has_read_into_requests {
356            return (true, objects);
357        }
358
359        // Let desiredSize be ! ReadableByteStreamControllerGetDesiredSize(controller).
360        let desired_size = objects
361            .controller
362            .readable_byte_stream_controller_get_desired_size(&objects.stream);
363
364        // Assert: desiredSize is not null.
365        if desired_size.0.expect("desired_size must not be null") > 0.0 {
366            // If desiredSize > 0, return true.
367            return (true, objects);
368        }
369
370        // Return false.
371        (false, objects)
372    }
373
374    pub(super) fn readable_byte_stream_controller_error<R: ReadableStreamReader<'js>>(
375        // Let stream be controller.[[stream]].
376        mut objects: ReadableByteStreamObjects<'js, R>,
377        e: Value<'js>,
378    ) -> Result<ReadableByteStreamObjects<'js, R>> {
379        // If stream.[[state]] is not "readable", return.
380        if !matches!(objects.stream.state, ReadableStreamState::Readable) {
381            return Ok(objects);
382        };
383
384        // Perform ! ReadableByteStreamControllerClearPendingPullIntos(controller).
385        objects
386            .controller
387            .readable_byte_stream_controller_clear_pending_pull_intos();
388
389        // Perform ! ResetQueue(controller).
390        objects.controller.reset_queue();
391
392        // Perform ! ReadableByteStreamControllerClearAlgorithms(controller).
393        objects
394            .controller
395            .readable_byte_stream_controller_clear_algorithms();
396
397        // Perform ! ReadableStreamError(stream, e).
398        ReadableStream::readable_stream_error(objects, e)
399    }
400
401    fn readable_byte_stream_controller_clear_pending_pull_intos(&mut self) {
402        // Perform ! ReadableByteStreamControllerInvalidateBYOBRequest(controller).
403        self.readable_byte_stream_controller_invalidate_byob_request();
404
405        // Set controller.[[pendingPullIntos]] to a new empty list.
406        self.pending_pull_intos.clear();
407    }
408
409    fn readable_byte_stream_controller_invalidate_byob_request(&mut self) {
410        let byob_request = match self.byob_request {
411            // If controller.[[byobRequest]] is null, return.
412            None => return,
413            Some(ref byob_request) => byob_request.clone(),
414        };
415        let mut byob_request = OwnedBorrowMut::from_class(byob_request);
416        byob_request.controller = None;
417        byob_request.view = None;
418
419        self.byob_request = None;
420    }
421
422    fn readable_byte_stream_controller_clear_algorithms(&mut self) {
423        self.pull_algorithm = None;
424        self.cancel_algorithm = None;
425    }
426
427    pub(super) fn readable_byte_stream_controller_get_byob_request(
428        ctx: Ctx<'js>,
429        controller: OwnedBorrowMut<'js, Self>,
430    ) -> Result<(
431        Null<Class<'js, ReadableStreamBYOBRequest<'js>>>,
432        OwnedBorrowMut<'js, Self>,
433    )> {
434        // If controller.[[byobRequest]] is null and controller.[[pendingPullIntos]] is not empty,
435        if controller.byob_request.is_none() && !controller.pending_pull_intos.is_empty() {
436            // Let firstDescriptor be controller.[[pendingPullIntos]][0].
437            let first_descriptor = &controller.pending_pull_intos[0];
438
439            // Let view be ! Construct(%Uint8Array%, « firstDescriptor’s buffer, firstDescriptor’s byte offset + firstDescriptor’s bytes filled, firstDescriptor’s byte length − firstDescriptor’s bytes filled »).
440            let view = ViewBytes::from_value(
441                &ctx,
442                &controller.function_array_buffer_is_view,
443                Some(
444                    &controller
445                        .array_constructor_primordials
446                        .constructor_uint8array
447                        .construct((
448                            first_descriptor.buffer.clone(),
449                            first_descriptor.byte_offset + first_descriptor.bytes_filled,
450                            first_descriptor.byte_length - first_descriptor.bytes_filled,
451                        ))?,
452                ),
453            )?;
454
455            let (controller_class, mut controller) = class_from_owned_borrow_mut(controller);
456
457            // Let byobRequest be a new ReadableStreamBYOBRequest.
458            let byob_request = ReadableStreamBYOBRequest {
459                // Set byobRequest.[[controller]] to controller.
460                controller: Some(controller_class),
461                // Set byobRequest.[[view]] to view.
462                view: Some(view),
463            };
464
465            // Set controller.[[byobRequest]] to byobRequest.
466            controller.byob_request = Some(Class::instance(ctx, byob_request)?);
467
468            Ok((Null(controller.byob_request.clone()), controller))
469        } else {
470            // Return controller.[[byobRequest]].
471            Ok((Null(controller.byob_request.clone()), controller))
472        }
473    }
474
475    fn readable_byte_stream_controller_get_desired_size(
476        &self,
477        stream: &ReadableStream<'js>,
478    ) -> Null<f64> {
479        // Let state be controller.[[stream]].[[state]].
480        match stream.state {
481            // If state is "errored", return null.
482            ReadableStreamState::Errored(_) => Null(None),
483            // If state is "closed", return 0.
484            ReadableStreamState::Closed => Null(Some(0.0)),
485            // Return controller.[[strategyHWM]] − controller.[[queueTotalSize]].
486            _ => Null(Some(self.strategy_hwm - self.queue_total_size as f64)),
487        }
488    }
489
490    fn reset_queue(&mut self) {
491        // Set container.[[queue]] to a new empty list.
492        self.queue.clear();
493        // Set container.[[queueTotalSize]] to 0.
494        self.queue_total_size = 0;
495    }
496
497    pub(super) fn readable_byte_stream_controller_close<R: ReadableStreamReader<'js>>(
498        ctx: Ctx<'js>,
499        // Let stream be controller.[[stream]].
500        mut objects: ReadableByteStreamObjects<'js, R>,
501    ) -> Result<ReadableByteStreamObjects<'js, R>> {
502        // If controller.[[closeRequested]] is true or stream.[[state]] is not "readable", return.
503        if objects.controller.close_requested
504            || !matches!(objects.stream.state, ReadableStreamState::Readable)
505        {
506            return Ok(objects);
507        }
508
509        // If controller.[[queueTotalSize]] > 0,
510        if objects.controller.queue_total_size > 0 {
511            // Set controller.[[closeRequested]] to true.
512            objects.controller.close_requested = true;
513            // Return.
514            return Ok(objects);
515        }
516
517        // If controller.[[pendingPullIntos]] is not empty,
518        // Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
519        if let Some(first_pending_pull_into) = objects.controller.pending_pull_intos.front() {
520            // If the remainder after dividing firstPendingPullInto’s bytes filled by firstPendingPullInto’s element size is not 0,
521            if first_pending_pull_into.bytes_filled % first_pending_pull_into.element_size != 0 {
522                // Let e be a new TypeError exception.
523                let e: Value = objects
524                    .stream
525                    .constructor_type_error
526                    .call(("Insufficient bytes to fill elements in the given buffer",))?;
527                Self::readable_byte_stream_controller_error(objects, e.clone())?;
528                return Err(ctx.throw(e));
529            }
530        }
531
532        // Perform ! ReadableByteStreamControllerClearAlgorithms(controller).
533        objects
534            .controller
535            .readable_byte_stream_controller_clear_algorithms();
536
537        // Perform ! ReadableStreamClose(stream).
538        ReadableStream::readable_stream_close(ctx, objects)
539    }
540
541    pub(super) fn readable_byte_stream_controller_enqueue<R: ReadableStreamReader<'js>>(
542        ctx: &Ctx<'js>,
543        // Let stream be controller.[[stream]].
544        objects: ReadableByteStreamObjects<'js, R>,
545        chunk: ViewBytes<'js>,
546    ) -> Result<ReadableByteStreamObjects<'js, R>> {
547        Self::readable_byte_stream_controller_enqueue_impl(
548            ctx, objects, chunk, /*skip_transfer=*/ false,
549        )
550    }
551
552    /// Like [`readable_byte_stream_controller_enqueue`] but skips the
553    /// spec-mandated `TransferArrayBuffer(chunk)` step on the incoming
554    /// chunk. Used by producers that already own the backing allocation
555    /// and want to hand it to the stream without QuickJS copying or
556    /// detaching it (e.g. `Blob.stream()`, where the backing
557    /// `ArrayBuffer` must survive multiple `.stream()` calls).
558    ///
559    /// Safety vs. correctness: the chunk we enqueue is NOT detached from
560    /// the producer's perspective, so both producer and consumer see the
561    /// same underlying bytes. This matches the existing non-isolation
562    /// behaviour of `Blob.arrayBuffer()` / `Blob.bytes()`, which already
563    /// return handles that alias the blob's storage. Pending BYOB
564    /// transfers of reader-provided buffers are unaffected — those are
565    /// separate buffers and still use spec-mandated transfer.
566    pub(super) fn readable_byte_stream_controller_enqueue_borrowed<R: ReadableStreamReader<'js>>(
567        ctx: &Ctx<'js>,
568        objects: ReadableByteStreamObjects<'js, R>,
569        chunk: ViewBytes<'js>,
570    ) -> Result<ReadableByteStreamObjects<'js, R>> {
571        Self::readable_byte_stream_controller_enqueue_impl(
572            ctx, objects, chunk, /*skip_transfer=*/ true,
573        )
574    }
575
576    fn readable_byte_stream_controller_enqueue_impl<R: ReadableStreamReader<'js>>(
577        ctx: &Ctx<'js>,
578        mut objects: ReadableByteStreamObjects<'js, R>,
579        chunk: ViewBytes<'js>,
580        skip_transfer: bool,
581    ) -> Result<ReadableByteStreamObjects<'js, R>> {
582        // If controller.[[closeRequested]] is true or stream.[[state]] is not "readable", return.
583        if objects.controller.close_requested
584            || !matches!(objects.stream.state, ReadableStreamState::Readable)
585        {
586            return Ok(objects);
587        };
588
589        // Let buffer be chunk.[[ViewedArrayBuffer]].
590        // Let byteOffset be chunk.[[ByteOffset]].
591        // Let byteLength be chunk.[[ByteLength]].
592        let (buffer, byte_length, byte_offset) = chunk.get_array_buffer()?;
593
594        // If ! IsDetachedBuffer(buffer) is true, throw a TypeError exception.
595        buffer.as_raw().ok_or(Exception::throw_type(
596            ctx,
597            "chunk's buffer is detached and so cannot be enqueued",
598        ))?;
599
600        // Let transferredBuffer be ? TransferArrayBuffer(buffer).
601        // (When `skip_transfer` is true, the caller guarantees that the
602        // buffer is already owned exclusively by the stream for the
603        // purposes of this enqueue — see
604        // `readable_byte_stream_controller_enqueue_borrowed`.)
605        let transferred_buffer = if skip_transfer {
606            buffer
607        } else {
608            transfer_array_buffer(buffer)?
609        };
610
611        // If controller.[[pendingPullIntos]] is not empty,
612        // Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
613        if !objects.controller.pending_pull_intos.is_empty() {
614            // If ! IsDetachedBuffer(firstPendingPullInto’s buffer) is true, throw a TypeError exception.
615            objects.controller.pending_pull_intos[0]
616                    .buffer
617                    .as_raw()
618                    .or_throw_type(
619                        ctx,
620                        "The BYOB request's buffer has been detached and so cannot be filled with an enqueued chunk",
621                    )?;
622
623            // Perform ! ReadableByteStreamControllerInvalidateBYOBRequest(controller).
624            objects
625                .controller
626                .readable_byte_stream_controller_invalidate_byob_request();
627
628            // Set firstPendingPullInto’s buffer to ! TransferArrayBuffer(firstPendingPullInto’s buffer).
629            objects.controller.pending_pull_intos[0].buffer =
630                transfer_array_buffer(objects.controller.pending_pull_intos[0].buffer.clone())?;
631
632            // If firstPendingPullInto’s reader type is "none", perform ? ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller, firstPendingPullInto).
633            if let PullIntoDescriptorReaderType::None =
634                objects.controller.pending_pull_intos[0].reader_type
635            {
636                objects = Self::readable_byte_stream_enqueue_detached_pull_into_to_queue(
637                    ctx.clone(),
638                    objects,
639                    0,
640                )?;
641            }
642        }
643
644        objects = objects.with_reader(
645            // If ! ReadableStreamHasDefaultReader(stream) is true,
646            |mut objects| {
647                // Perform ! ReadableByteStreamControllerProcessReadRequestsUsingQueue(controller).
648                objects = Self::readable_byte_stream_controller_process_read_requests_using_queue(
649                    objects, ctx,
650                )?;
651
652                // If ! ReadableStreamGetNumReadRequests(stream) is 0,
653                if ReadableStream::readable_stream_get_num_read_requests(&objects.reader) == 0 {
654                    // Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength).
655                    objects
656                        .controller
657                        .readable_byte_stream_controller_enqueue_chunk_to_queue(
658                            transferred_buffer.clone(),
659                            byte_offset,
660                            byte_length,
661                        )
662                } else {
663                    // Otherwise,
664                    // If controller.[[pendingPullIntos]] is not empty,
665                    if !objects.controller.pending_pull_intos.is_empty() {
666                        // Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
667                        objects
668                            .controller
669                            .readable_byte_stream_controller_shift_pending_pull_into();
670                    }
671
672                    // Let transferredView be ! Construct(%Uint8Array%, « transferredBuffer, byteOffset, byteLength »).
673                    let transferred_view = ViewBytes::from_value(
674                        ctx,
675                        &objects.controller.function_array_buffer_is_view,
676                        Some(
677                            &objects
678                                .controller
679                                .array_constructor_primordials
680                                .constructor_uint8array
681                                .construct((
682                                    transferred_buffer.clone(),
683                                    byte_offset,
684                                    byte_length,
685                                ))?,
686                        ),
687                    );
688
689                    // Perform ! ReadableStreamFulfillReadRequest(stream, transferredView, false).
690                    objects = ReadableStream::readable_stream_fulfill_read_request(
691                        ctx,
692                        objects,
693                        transferred_view.into_js(ctx)?,
694                        false,
695                    )?;
696                }
697
698                Ok(objects)
699            },
700            |mut objects| {
701                // Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true,
702                // Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength).
703                objects
704                    .controller
705                    .readable_byte_stream_controller_enqueue_chunk_to_queue(
706                        transferred_buffer.clone(),
707                        byte_offset,
708                        byte_length,
709                    );
710                // Perform ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
711
712                Self::readable_byte_stream_controller_process_pull_into_descriptors_using_queue(
713                    ctx, objects,
714                )
715            },
716            |mut objects| {
717                // Otherwise,
718                // Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength).
719                objects
720                    .controller
721                    .readable_byte_stream_controller_enqueue_chunk_to_queue(
722                        transferred_buffer.clone(),
723                        byte_offset,
724                        byte_length,
725                    );
726
727                Ok(objects)
728            },
729        )?;
730
731        // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
732        Self::readable_byte_stream_controller_call_pull_if_needed(ctx.clone(), objects)
733    }
734
735    fn readable_byte_stream_enqueue_detached_pull_into_to_queue<R: ReadableStreamReader<'js>>(
736        ctx: Ctx<'js>,
737        mut objects: ReadableByteStreamObjects<'js, R>,
738        pull_into_descriptor_index: usize,
739    ) -> Result<ReadableByteStreamObjects<'js, R>> {
740        let pull_into_descriptor =
741            &objects.controller.pending_pull_intos[pull_into_descriptor_index];
742        // If pullIntoDescriptor’s bytes filled > 0, perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller, pullIntoDescriptor’s buffer, pullIntoDescriptor’s byte offset, pullIntoDescriptor’s bytes filled).
743        if pull_into_descriptor.bytes_filled > 0 {
744            let buffer = pull_into_descriptor.buffer.clone();
745            let byte_offset = pull_into_descriptor.byte_offset;
746            let bytes_filled = pull_into_descriptor.bytes_filled;
747            objects = Self::readable_byte_stream_controller_enqueue_cloned_chunk_to_queue(
748                ctx,
749                objects,
750                &buffer,
751                byte_offset,
752                bytes_filled,
753            )?;
754        }
755
756        // Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
757        objects
758            .controller
759            .readable_byte_stream_controller_shift_pending_pull_into();
760
761        Ok(objects)
762    }
763
764    fn readable_byte_stream_controller_process_read_requests_using_queue(
765        mut objects: ReadableStreamDefaultReaderObjects<'js, OwnedBorrowMut<'js, Self>>,
766        ctx: &Ctx<'js>,
767    ) -> Result<ReadableStreamDefaultReaderObjects<'js, OwnedBorrowMut<'js, Self>>> {
768        // While reader.[[readRequests]] is not empty,
769        while !objects.reader.read_requests.is_empty() {
770            // If controller.[[queueTotalSize]] is 0, return.
771            if objects.controller.queue_total_size == 0 {
772                return Ok(objects);
773            }
774
775            // Let readRequest be reader.[[readRequests]][0].
776            // Remove readRequest from reader.[[readRequests]].
777            let read_request = objects.reader.read_requests.pop_front().unwrap();
778            // Perform ! ReadableByteStreamControllerFillReadRequestFromQueue(controller, readRequest).
779            objects = Self::readable_byte_stream_controller_fill_read_request_from_queue(
780                ctx,
781                objects,
782                read_request,
783            )?;
784        }
785
786        Ok(objects)
787    }
788
789    fn readable_byte_stream_controller_shift_pending_pull_into(
790        &mut self,
791    ) -> PullIntoDescriptor<'js> {
792        // Invalidate byobRequest since the first pending pull-into is being removed
793        self.readable_byte_stream_controller_invalidate_byob_request();
794        // Let descriptor be controller.[[pendingPullIntos]][0].
795        // Remove descriptor from controller.[[pendingPullIntos]].
796        // Return descriptor.
797        self.pending_pull_intos.pop_front().expect(
798            "ReadableByteStreamControllerShiftPendingPullInto called on empty pendingPullIntos",
799        )
800    }
801
802    fn readable_byte_stream_controller_enqueue_chunk_to_queue(
803        &mut self,
804        buffer: ArrayBuffer<'js>,
805        byte_offset: usize,
806        byte_length: usize,
807    ) {
808        // Append a new readable byte stream queue entry with buffer buffer, byte offset byteOffset, and byte length byteLength to controller.[[queue]].
809        self.queue.push_back(ReadableByteStreamQueueEntry {
810            buffer,
811            byte_offset,
812            byte_length,
813        });
814
815        // Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]] + byteLength.
816        self.queue_total_size += byte_length;
817    }
818
819    fn readable_byte_stream_controller_process_pull_into_descriptors_using_queue<
820        R: ReadableStreamReader<'js>,
821    >(
822        ctx: &Ctx<'js>,
823        mut objects: ReadableByteStreamObjects<'js, R>,
824    ) -> Result<ReadableByteStreamObjects<'js, R>> {
825        // While controller.[[pendingPullIntos]] is not empty,
826        while !objects.controller.pending_pull_intos.is_empty() {
827            // If controller.[[queueTotalSize]] is 0, return.
828            if objects.controller.queue_total_size == 0 {
829                return Ok(objects);
830            }
831
832            // Let pullIntoDescriptor be controller.[[pendingPullIntos]][0].
833            let mut pull_into_descriptor_ref = PullIntoDescriptorRefMut::Index(0);
834
835            // If ! ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) is true,
836            if objects
837                .controller
838                .readable_byte_stream_controller_fill_pull_into_descriptor_from_queue(
839                    ctx,
840                    &mut pull_into_descriptor_ref,
841                )?
842            {
843                // Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
844                let pull_into_descriptor = objects
845                    .controller
846                    .readable_byte_stream_controller_shift_pending_pull_into();
847
848                // Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], pullIntoDescriptor).
849                objects = Self::readable_byte_stream_controller_commit_pull_into_descriptor(
850                    ctx.clone(),
851                    objects,
852                    pull_into_descriptor,
853                )?;
854            }
855        }
856        Ok(objects)
857    }
858
859    fn readable_byte_stream_controller_enqueue_cloned_chunk_to_queue<
860        R: ReadableStreamReader<'js>,
861    >(
862        ctx: Ctx<'js>,
863        mut objects: ReadableByteStreamObjects<'js, R>,
864        buffer: &ArrayBuffer<'js>,
865        byte_offset: usize,
866        byte_length: usize,
867    ) -> Result<ReadableByteStreamObjects<'js, R>> {
868        // Let cloneResult be CloneArrayBuffer(buffer, byteOffset, byteLength, %ArrayBuffer%).
869        let clone_result = match ArrayBuffer::new_copy(
870            ctx.clone(),
871            &buffer.as_bytes().expect(
872                "ReadableByteStreamControllerEnqueueClonedChunkToQueue called on detached buffer",
873            )[byte_offset..byte_offset + byte_length],
874        ) {
875            Ok(clone_result) => clone_result,
876            Err(Error::Exception) => {
877                let err = ctx.catch();
878                Self::readable_byte_stream_controller_error(objects, err.clone())?;
879                return Err(ctx.throw(err));
880            },
881            Err(err) => return Err(err),
882        };
883
884        // Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(controller, cloneResult.[[Value]], 0, byteLength).
885        objects
886            .controller
887            .readable_byte_stream_controller_enqueue_chunk_to_queue(clone_result, 0, byte_length);
888
889        Ok(objects)
890    }
891
892    fn readable_byte_stream_controller_fill_read_request_from_queue(
893        ctx: &Ctx<'js>,
894        mut objects: ReadableStreamDefaultReaderObjects<'js, OwnedBorrowMut<'js, Self>>,
895        read_request: impl ReadableStreamReadRequest<'js>,
896    ) -> Result<ReadableStreamDefaultReaderObjects<'js, OwnedBorrowMut<'js, Self>>> {
897        let entry = {
898            // Assert: controller.[[queueTotalSize]] > 0.
899            // Let entry be controller.[[queue]][0].
900            // Remove entry from controller.[[queue]].
901            let entry = objects.controller.queue.pop_front().expect(
902                "ReadableByteStreamControllerFillReadRequestFromQueue called with empty queue",
903            );
904
905            // Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]] − entry’s byte length.
906            objects.controller.queue_total_size -= entry.byte_length;
907
908            entry
909        };
910
911        // Perform ! ReadableByteStreamControllerHandleQueueDrain(controller).
912        objects = Self::readable_byte_stream_controller_handle_queue_drain(ctx.clone(), objects)?;
913
914        // Let view be ! Construct(%Uint8Array%, « entry’s buffer, entry’s byte offset, entry’s byte length »).
915        let view: TypedArray<u8> = objects
916            .controller
917            .array_constructor_primordials
918            .constructor_uint8array
919            .construct((entry.buffer, entry.byte_offset, entry.byte_length))?;
920
921        // Perform readRequest’s chunk steps, given view.
922        read_request.chunk_steps_typed(objects, view.into_value())
923    }
924
925    fn readable_byte_stream_controller_fill_pull_into_descriptor_from_queue<'a>(
926        &'a mut self,
927        ctx: &Ctx<'js>,
928        pull_into_descriptor_ref: &mut PullIntoDescriptorRefMut<'js, 'a>,
929    ) -> Result<bool> {
930        let (mut total_bytes_to_copy_remaining, ready) = {
931            let pull_into_descriptor = match pull_into_descriptor_ref {
932                PullIntoDescriptorRefMut::Index(i) => &mut self.pending_pull_intos[*i],
933                PullIntoDescriptorRefMut::Owned(r) => r,
934            };
935            // Let maxBytesToCopy be min(controller.[[queueTotalSize]], pullIntoDescriptor’s byte length − pullIntoDescriptor’s bytes filled).
936            let max_bytes_to_copy: usize = std::cmp::min(
937                self.queue_total_size,
938                pull_into_descriptor.byte_length - pull_into_descriptor.bytes_filled,
939            );
940
941            // Let maxBytesFilled be pullIntoDescriptor’s bytes filled + maxBytesToCopy.
942            let max_bytes_filled = pull_into_descriptor.bytes_filled + max_bytes_to_copy;
943
944            // Let totalBytesToCopyRemaining be maxBytesToCopy.
945            let mut total_bytes_to_copy_remaining = max_bytes_to_copy;
946
947            // Let ready be false.
948            let mut ready = false;
949
950            // Let remainderBytes be the remainder after dividing maxBytesFilled by pullIntoDescriptor’s element size.
951            let remainder_bytes = max_bytes_filled % pull_into_descriptor.element_size;
952
953            // Let maxAlignedBytes be maxBytesFilled − remainderBytes.
954            let max_aligned_bytes = max_bytes_filled - remainder_bytes;
955
956            // If maxAlignedBytes ≥ pullIntoDescriptor’s minimum fill,
957            if max_aligned_bytes >= pull_into_descriptor.minimum_fill {
958                // Set totalBytesToCopyRemaining to maxAlignedBytes − pullIntoDescriptor’s bytes filled.
959                total_bytes_to_copy_remaining =
960                    max_aligned_bytes - pull_into_descriptor.bytes_filled;
961                // Set ready to true.
962                ready = true
963            }
964
965            (total_bytes_to_copy_remaining, ready)
966        };
967
968        // Let queue be controller.[[queue]].
969        // While totalBytesToCopyRemaining > 0,
970        while total_bytes_to_copy_remaining > 0 {
971            let bytes_to_copy = {
972                let pull_into_descriptor = match pull_into_descriptor_ref {
973                    PullIntoDescriptorRefMut::Index(i) => &mut self.pending_pull_intos[*i],
974                    PullIntoDescriptorRefMut::Owned(r) => r,
975                };
976
977                // Let headOfQueue be queue[0].
978                let head_of_queue = self
979                    .queue
980                    .front_mut()
981                    .expect("empty queue with bytes to copy");
982                // Let bytesToCopy be min(totalBytesToCopyRemaining, headOfQueue’s byte length).
983                let bytes_to_copy: usize =
984                    std::cmp::min(total_bytes_to_copy_remaining, head_of_queue.byte_length);
985                // Let destStart be pullIntoDescriptor’s byte offset + pullIntoDescriptor’s bytes filled.
986                let dest_start: usize =
987                    pull_into_descriptor.byte_offset + pull_into_descriptor.bytes_filled;
988                // Perform ! CopyDataBlockBytes(pullIntoDescriptor’s buffer.[[ArrayBufferData]], destStart, headOfQueue’s buffer.[[ArrayBufferData]], headOfQueue’s byte offset, bytesToCopy).
989                copy_data_block_bytes(
990                    ctx,
991                    &pull_into_descriptor.buffer,
992                    dest_start,
993                    &head_of_queue.buffer,
994                    head_of_queue.byte_offset,
995                    bytes_to_copy,
996                )?;
997                if head_of_queue.byte_length == bytes_to_copy {
998                    // If headOfQueue’s byte length is bytesToCopy,
999                    // Remove queue[0].
1000                    self.queue.pop_front();
1001                } else {
1002                    // Otherwise,
1003                    // Set headOfQueue’s byte offset to headOfQueue’s byte offset + bytesToCopy.
1004                    head_of_queue.byte_offset += bytes_to_copy;
1005                    // Set headOfQueue’s byte length to headOfQueue’s byte length − bytesToCopy.
1006                    head_of_queue.byte_length -= bytes_to_copy
1007                }
1008
1009                // Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]] − bytesToCopy.
1010                self.queue_total_size -= bytes_to_copy;
1011
1012                bytes_to_copy
1013            };
1014
1015            // Perform ! ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesToCopy, pullIntoDescriptor).
1016            self.readable_byte_stream_controller_fill_head_pull_into_descriptor(
1017                bytes_to_copy,
1018                pull_into_descriptor_ref,
1019            );
1020
1021            // Set totalBytesToCopyRemaining to totalBytesToCopyRemaining − bytesToCopy.
1022            total_bytes_to_copy_remaining -= bytes_to_copy
1023        }
1024
1025        Ok(ready)
1026    }
1027
1028    fn readable_byte_stream_controller_commit_pull_into_descriptor<R: ReadableStreamReader<'js>>(
1029        ctx: Ctx<'js>,
1030        objects: ReadableByteStreamObjects<'js, R>,
1031        pull_into_descriptor: PullIntoDescriptor<'js>,
1032    ) -> Result<ReadableByteStreamObjects<'js, R>> {
1033        // Let done be false.
1034        let mut done = false;
1035        // If stream.[[state]] is "closed",
1036        if matches!(objects.stream.state, ReadableStreamState::Closed) {
1037            // Set done to true.
1038            done = true
1039        }
1040
1041        let reader_type = pull_into_descriptor.reader_type;
1042
1043        // Let filledView be ! ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
1044        let filled_view = Self::readable_byte_stream_controller_convert_pull_into_descriptor(
1045            ctx.clone(),
1046            &objects.stream.function_array_buffer_is_view,
1047            pull_into_descriptor,
1048        )?;
1049
1050        if let PullIntoDescriptorReaderType::Default = reader_type {
1051            // If pullIntoDescriptor’s reader type is "default",
1052            objects.with_assert_default_reader(|objects| {
1053                // Perform ! ReadableStreamFulfillReadRequest(stream, filledView, done).
1054                ReadableStream::readable_stream_fulfill_read_request(
1055                    &ctx,
1056                    objects,
1057                    filled_view.into_js(&ctx)?,
1058                    done,
1059                )
1060            })
1061        } else {
1062            // Otherwise,
1063            objects.with_assert_byob_reader(|objects| {
1064                // Perform ! ReadableStreamFulfillReadIntoRequest(stream, filledView, done).
1065                ReadableStream::readable_stream_fulfill_read_into_request(
1066                    &ctx,
1067                    objects,
1068                    filled_view,
1069                    done,
1070                )
1071            })
1072        }
1073    }
1074
1075    fn readable_byte_stream_controller_handle_queue_drain<R: ReadableStreamReader<'js>>(
1076        ctx: Ctx<'js>,
1077        mut objects: ReadableByteStreamObjects<'js, R>,
1078    ) -> Result<ReadableByteStreamObjects<'js, R>> {
1079        // If controller.[[queueTotalSize]] is 0 and controller.[[closeRequested]] is true,
1080        if objects.controller.queue_total_size == 0 && objects.controller.close_requested {
1081            // Perform ! ReadableByteStreamControllerClearAlgorithms(controller).
1082            objects
1083                .controller
1084                .readable_byte_stream_controller_clear_algorithms();
1085            // Perform ! ReadableStreamClose(controller.[[stream]]).
1086            ReadableStream::readable_stream_close(ctx, objects)
1087        } else {
1088            // Otherwise,
1089            // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
1090            Self::readable_byte_stream_controller_call_pull_if_needed(ctx.clone(), objects)
1091        }
1092    }
1093
1094    fn readable_byte_stream_controller_convert_pull_into_descriptor(
1095        ctx: Ctx<'js>,
1096        function_array_buffer_is_view: &Function<'js>,
1097        pull_into_descriptor: PullIntoDescriptor<'js>,
1098    ) -> Result<ViewBytes<'js>> {
1099        let PullIntoDescriptor {
1100            // Let bytesFilled be pullIntoDescriptor’s bytes filled.
1101            bytes_filled,
1102            // Let elementSize be pullIntoDescriptor’s element size.
1103            element_size,
1104            byte_offset,
1105            buffer,
1106            ..
1107        } = pull_into_descriptor;
1108        // Let buffer be ! TransferArrayBuffer(pullIntoDescriptor’s buffer).
1109        let buffer = transfer_array_buffer(buffer);
1110        // Return ! Construct(pullIntoDescriptor’s view constructor, « buffer, pullIntoDescriptor’s byte offset, bytesFilled ÷ elementSize »).
1111        let view: Object = pull_into_descriptor.view_constructor.construct((
1112            buffer,
1113            byte_offset,
1114            bytes_filled / element_size,
1115        ))?;
1116        ViewBytes::from_object(&ctx, function_array_buffer_is_view, &view)
1117    }
1118
1119    pub(super) fn readable_byte_stream_controller_pull_into(
1120        ctx: &Ctx<'js>,
1121        // Let stream be controller.[[stream]].
1122        mut objects: ReadableStreamBYOBObjects<'js>,
1123        view: ViewBytes<'js>,
1124        min: u64,
1125        read_into_request: impl ReadableStreamReadIntoRequest<'js> + 'js,
1126    ) -> Result<ReadableStreamBYOBObjects<'js>> {
1127        // Set elementSize to the element size specified in the typed array constructors table for view.[[TypedArrayName]].
1128        // Set ctor to the constructor specified in the typed array constructors table for view.[[TypedArrayName]].
1129        let (element_size, ctor) = (
1130            view.element_size(),
1131            objects
1132                .controller
1133                .array_constructor_primordials
1134                .for_view_bytes(&view),
1135        );
1136
1137        // Let minimumFill be min × elementSize.
1138        let minimum_fill: usize = (min as usize) * element_size;
1139
1140        // Let byteOffset be view.[[ByteOffset]].
1141        // Let byteLength be view.[[ByteLength]].
1142        let (buffer, byte_length, byte_offset) = view.get_array_buffer()?;
1143
1144        // Let bufferResult be TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
1145        let buffer_result = transfer_array_buffer(buffer);
1146        let buffer = match buffer_result {
1147            // If bufferResult is an abrupt completion,
1148            Err(Error::Exception) => {
1149                // Perform readIntoRequest’s error steps, given bufferResult.[[Value]].
1150                objects = read_into_request.error_steps(objects, ctx.catch())?;
1151                // Return.
1152                return Ok(objects);
1153            },
1154            Err(err) => return Err(err),
1155            // Let buffer be bufferResult.[[Value]].
1156            Ok(buffer) => buffer,
1157        };
1158
1159        let buffer_byte_length = buffer.len();
1160        // Let pullIntoDescriptor be a new pull-into descriptor with
1161        let mut pull_into_descriptor = PullIntoDescriptor {
1162            buffer,
1163            buffer_byte_length,
1164            byte_offset,
1165            byte_length,
1166            bytes_filled: 0,
1167            minimum_fill,
1168            element_size,
1169            view_constructor: ctor.clone(),
1170            reader_type: PullIntoDescriptorReaderType::Byob,
1171        };
1172
1173        // If controller.[[pendingPullIntos]] is not empty,
1174        if !objects.controller.pending_pull_intos.is_empty() {
1175            // Append pullIntoDescriptor to controller.[[pendingPullIntos]].
1176            objects
1177                .controller
1178                .pending_pull_intos
1179                .push_back(pull_into_descriptor);
1180
1181            // Perform ! ReadableStreamAddReadIntoRequest(stream, readIntoRequest).
1182            ReadableStream::readable_stream_add_read_into_request(
1183                &mut objects.reader,
1184                read_into_request,
1185            );
1186
1187            // Return.
1188            return Ok(objects);
1189        }
1190
1191        // If stream.[[state]] is "closed",
1192        if matches!(objects.stream.state, ReadableStreamState::Closed) {
1193            // Let emptyView be ! Construct(ctor, « pullIntoDescriptor’s buffer, pullIntoDescriptor’s byte offset, 0 »).
1194            let empty_view: Value<'js> = ctor.construct((
1195                pull_into_descriptor.buffer,
1196                pull_into_descriptor.byte_offset,
1197                0,
1198            ))?;
1199
1200            // Perform readIntoRequest’s close steps, given emptyView.
1201            objects = read_into_request.close_steps(objects, empty_view)?;
1202
1203            // Return.
1204            return Ok(objects);
1205        }
1206
1207        // If controller.[[queueTotalSize]] > 0,
1208        if objects.controller.queue_total_size > 0 {
1209            // If ! ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) is true,
1210            if objects
1211                .controller
1212                .readable_byte_stream_controller_fill_pull_into_descriptor_from_queue(
1213                    ctx,
1214                    &mut PullIntoDescriptorRefMut::Owned(&mut pull_into_descriptor),
1215                )?
1216            {
1217                // Let filledView be ! ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
1218                let filled_view = objects
1219                    .controller
1220                    .readable_byte_steam_controller_convert_pull_into_descriptor(
1221                        pull_into_descriptor,
1222                    )?;
1223
1224                // Perform ! ReadableByteStreamControllerHandleQueueDrain(controller).
1225                objects =
1226                    Self::readable_byte_stream_controller_handle_queue_drain(ctx.clone(), objects)?;
1227
1228                // Perform readIntoRequest’s chunk steps, given filledView.
1229                // Return.
1230                return read_into_request.chunk_steps(objects, filled_view);
1231            }
1232
1233            // If controller.[[closeRequested]] is true,
1234            if objects.controller.close_requested {
1235                // Let e be a TypeError exception.
1236                let e: Value = objects
1237                    .stream
1238                    .constructor_type_error
1239                    .call(("Insufficient bytes to fill elements in the given buffer",))?;
1240
1241                // Perform ! ReadableByteStreamControllerError(controller, e).
1242                objects = Self::readable_byte_stream_controller_error(objects, e.clone())?;
1243
1244                // Perform readIntoRequest’s error steps, given e.
1245                // Return.
1246                return read_into_request.error_steps(objects, e);
1247            }
1248        }
1249
1250        // Append pullIntoDescriptor to controller.[[pendingPullIntos]].
1251        objects
1252            .controller
1253            .pending_pull_intos
1254            .push_back(pull_into_descriptor);
1255
1256        // Perform ! ReadableStreamAddReadIntoRequest(stream, readIntoRequest).
1257        ReadableStream::readable_stream_add_read_into_request(
1258            &mut objects.reader,
1259            read_into_request,
1260        );
1261
1262        // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
1263        Self::readable_byte_stream_controller_call_pull_if_needed(ctx.clone(), objects)
1264    }
1265
1266    fn readable_byte_steam_controller_convert_pull_into_descriptor(
1267        &mut self,
1268        pull_into_descriptor: PullIntoDescriptor<'js>,
1269    ) -> Result<Value<'js>> {
1270        // Let bytesFilled be pullIntoDescriptor’s bytes filled.
1271        let bytes_filled = pull_into_descriptor.bytes_filled;
1272
1273        // Let elementSize be pullIntoDescriptor’s element size.
1274        let element_size = pull_into_descriptor.element_size;
1275
1276        // Let buffer be ! TransferArrayBuffer(pullIntoDescriptor’s buffer).
1277        let buffer = transfer_array_buffer(pull_into_descriptor.buffer)?;
1278
1279        // Return ! Construct(pullIntoDescriptor’s view constructor, « buffer, pullIntoDescriptor’s byte offset, bytesFilled ÷ elementSize »).
1280        pull_into_descriptor.view_constructor.construct((
1281            buffer,
1282            pull_into_descriptor.byte_offset,
1283            bytes_filled / element_size,
1284        ))
1285    }
1286
1287    pub(super) fn readable_byte_stream_controller_respond<R: ReadableStreamReader<'js>>(
1288        ctx: Ctx<'js>,
1289        mut objects: ReadableByteStreamObjects<'js, R>,
1290        bytes_written: usize,
1291    ) -> Result<()> {
1292        // Let firstDescriptor be controller.[[pendingPullIntos]][0].
1293        let first_descriptor = &mut objects.controller.pending_pull_intos[0];
1294
1295        // Let state be controller.[[stream]].[[state]].
1296        match objects.stream.state {
1297            // If state is "closed",
1298            ReadableStreamState::Closed => {
1299                // If bytesWritten is not 0, throw a TypeError exception.
1300                if bytes_written != 0 {
1301                    return Err(Exception::throw_type(
1302                        &ctx,
1303                        "bytesWritten must be 0 when calling respond() on a closed stream",
1304                    ));
1305                }
1306            },
1307            // Otherwise,
1308            _ => {
1309                // If bytesWritten is 0, throw a TypeError exception.
1310                if bytes_written == 0 {
1311                    return Err(Exception::throw_type(
1312                        &ctx,
1313                        "bytesWritten must be greater than 0 when calling respond() on a readable stream",
1314                    ));
1315                }
1316
1317                // If firstDescriptor’s bytes filled + bytesWritten > firstDescriptor’s byte length, throw a RangeError exception.
1318                if first_descriptor.bytes_filled + bytes_written > first_descriptor.byte_length {
1319                    return Err(Exception::throw_range(&ctx, "bytesWritten out of range'"));
1320                }
1321            },
1322        };
1323
1324        // Set firstDescriptor’s buffer to ! TransferArrayBuffer(firstDescriptor’s buffer).
1325        first_descriptor.buffer = transfer_array_buffer(first_descriptor.buffer.clone())?;
1326
1327        // Perform ? ReadableByteStreamControllerRespondInternal(controller, bytesWritten).
1328        Self::readable_byte_stream_controller_respond_internal(ctx, objects, bytes_written)
1329    }
1330
1331    fn readable_byte_stream_controller_respond_internal<R: ReadableStreamReader<'js>>(
1332        ctx: Ctx<'js>,
1333        mut objects: ReadableByteStreamObjects<'js, R>,
1334        bytes_written: usize,
1335    ) -> Result<()> {
1336        // Let firstDescriptor be controller.[[pendingPullIntos]][0].
1337        let first_descriptor_index = 0;
1338
1339        // Perform ! ReadableByteStreamControllerInvalidateBYOBRequest(controller).
1340        objects
1341            .controller
1342            .readable_byte_stream_controller_invalidate_byob_request();
1343
1344        // Let state be controller.[[stream]].[[state]].
1345        match objects.stream.state {
1346            // If state is "closed",
1347            ReadableStreamState::Closed => {
1348                // Perform ! ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor).
1349                objects = Self::readable_byte_stream_controller_respond_in_closed_state(
1350                    ctx.clone(),
1351                    objects,
1352                    first_descriptor_index,
1353                )?;
1354            },
1355            // Otherwise
1356            _ => {
1357                // Perform ? ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, firstDescriptor).
1358                objects = Self::readable_byte_stream_controller_respond_in_readable_state(
1359                    ctx.clone(),
1360                    objects,
1361                    bytes_written,
1362                    first_descriptor_index,
1363                )?
1364            },
1365        };
1366
1367        _ = Self::readable_byte_stream_controller_call_pull_if_needed(ctx, objects)?;
1368        Ok(())
1369    }
1370
1371    fn readable_byte_stream_controller_respond_in_closed_state<R: ReadableStreamReader<'js>>(
1372        ctx: Ctx<'js>,
1373        // Let stream be controller.[[stream]].
1374        mut objects: ReadableByteStreamObjects<'js, R>,
1375        first_descriptor_index: usize,
1376    ) -> Result<ReadableByteStreamObjects<'js, R>> {
1377        // If firstDescriptor’s reader type is "none", perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
1378        if let PullIntoDescriptorReaderType::None =
1379            objects.controller.pending_pull_intos[first_descriptor_index].reader_type
1380        {
1381            objects
1382                .controller
1383                .readable_byte_stream_controller_shift_pending_pull_into();
1384        }
1385
1386        // If ! ReadableStreamHasBYOBReader(stream) is true,
1387        objects.with_reader(
1388            Ok,
1389            |mut objects| {
1390                // While ! ReadableStreamGetNumReadIntoRequests(stream) > 0,
1391                while ReadableStream::readable_stream_get_num_read_into_requests(&objects.reader)
1392                    > 0
1393                {
1394                    // Let pullIntoDescriptor be ! ReadableByteStreamControllerShiftPendingPullInto(controller).
1395                    let pull_into_descriptor = objects
1396                        .controller
1397                        .readable_byte_stream_controller_shift_pending_pull_into();
1398
1399                    // Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor).
1400                    objects = Self::readable_byte_stream_controller_commit_pull_into_descriptor(
1401                        ctx.clone(),
1402                        objects,
1403                        pull_into_descriptor,
1404                    )?;
1405                }
1406
1407                Ok(objects)
1408            },
1409            Ok,
1410        )
1411    }
1412
1413    fn readable_byte_stream_controller_respond_in_readable_state<R: ReadableStreamReader<'js>>(
1414        ctx: Ctx<'js>,
1415        // Let stream be controller.[[stream]].
1416        mut objects: ReadableByteStreamObjects<'js, R>,
1417        bytes_written: usize,
1418        pull_into_descriptor_index: usize,
1419    ) -> Result<ReadableByteStreamObjects<'js, R>> {
1420        // Perform ! ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesWritten, pullIntoDescriptor).
1421        objects
1422            .controller
1423            .readable_byte_stream_controller_fill_head_pull_into_descriptor(
1424                bytes_written,
1425                &mut PullIntoDescriptorRefMut::Index(pull_into_descriptor_index),
1426            );
1427
1428        // If pullIntoDescriptor’s reader type is "none",
1429        if let PullIntoDescriptorReaderType::None =
1430            objects.controller.pending_pull_intos[pull_into_descriptor_index].reader_type
1431        {
1432            // Perform ? ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller, pullIntoDescriptor).
1433            objects = Self::readable_byte_stream_enqueue_detached_pull_into_to_queue(
1434                ctx.clone(),
1435                objects,
1436                pull_into_descriptor_index,
1437            )?;
1438            // Perform ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
1439            // Return.
1440            return Self::readable_byte_stream_controller_process_pull_into_descriptors_using_queue(
1441                &ctx, objects,
1442            );
1443        }
1444
1445        // If pullIntoDescriptor’s bytes filled < pullIntoDescriptor’s minimum fill, return.
1446        if objects.controller.pending_pull_intos[pull_into_descriptor_index].bytes_filled
1447            < objects.controller.pending_pull_intos[pull_into_descriptor_index].minimum_fill
1448        {
1449            return Ok(objects);
1450        }
1451
1452        // Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
1453        let mut pull_into_descriptor = objects
1454            .controller
1455            .readable_byte_stream_controller_shift_pending_pull_into();
1456
1457        // Let remainderSize be the remainder after dividing pullIntoDescriptor’s bytes filled by pullIntoDescriptor’s element size.
1458        let remainder_size = pull_into_descriptor.bytes_filled % pull_into_descriptor.element_size;
1459
1460        // If remainderSize > 0,
1461        if remainder_size > 0 {
1462            // Let end be pullIntoDescriptor’s byte offset + pullIntoDescriptor’s bytes filled.
1463            let end = pull_into_descriptor.byte_offset + pull_into_descriptor.bytes_filled;
1464
1465            let buffer = pull_into_descriptor.buffer.clone();
1466
1467            // Perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller, pullIntoDescriptor’s buffer, end − remainderSize, remainderSize).
1468            objects = Self::readable_byte_stream_controller_enqueue_cloned_chunk_to_queue(
1469                ctx.clone(),
1470                objects,
1471                &buffer,
1472                end - remainder_size,
1473                remainder_size,
1474            )?;
1475        }
1476
1477        // Set pullIntoDescriptor’s bytes filled to pullIntoDescriptor’s bytes filled − remainderSize.
1478        pull_into_descriptor.bytes_filled -= remainder_size;
1479
1480        // Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], pullIntoDescriptor).
1481        objects = Self::readable_byte_stream_controller_commit_pull_into_descriptor(
1482            ctx.clone(),
1483            objects,
1484            pull_into_descriptor,
1485        )?;
1486
1487        // Perform ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
1488        Self::readable_byte_stream_controller_process_pull_into_descriptors_using_queue(
1489            &ctx, objects,
1490        )
1491    }
1492
1493    pub(super) fn readable_byte_stream_controller_respond_with_new_view<
1494        R: ReadableStreamReader<'js>,
1495    >(
1496        ctx: Ctx<'js>,
1497        mut objects: ReadableByteStreamObjects<'js, R>,
1498        view: ViewBytes<'js>,
1499    ) -> Result<()> {
1500        // Let firstDescriptor be controller.[[pendingPullIntos]][0].
1501        let first_descriptor_index = 0;
1502
1503        let (buffer, byte_length, byte_offset) = view.get_array_buffer()?;
1504
1505        // Let state be controller.[[stream]].[[state]].
1506        match objects.stream.state {
1507            // If state is "closed",
1508            ReadableStreamState::Closed => {
1509                // If view.[[ByteLength]] is not 0, throw a TypeError exception.
1510                if byte_length != 0 {
1511                    return Err(Exception::throw_type(&ctx, "The view's length must be 0 when calling respondWithNewView() on a closed stream"));
1512                }
1513            },
1514            // Otherwise
1515            _ => {
1516                // If view.[[ByteLength]] is 0, throw a TypeError exception.
1517                if byte_length == 0 {
1518                    return Err(Exception::throw_type(&ctx, "The view's length must be greater than 0 when calling respondWithNewView() on a readable stream"));
1519                }
1520            },
1521        };
1522
1523        {
1524            let first_descriptor =
1525                &mut objects.controller.pending_pull_intos[first_descriptor_index];
1526
1527            // If firstDescriptor’s byte offset + firstDescriptor’ bytes filled is not view.[[ByteOffset]], throw a RangeError exception.
1528            if first_descriptor.byte_offset + first_descriptor.bytes_filled != byte_offset {
1529                return Err(Exception::throw_range(
1530                    &ctx,
1531                    "The region specified by view does not match byobRequest",
1532                ));
1533            };
1534
1535            // If firstDescriptor’s buffer byte length is not view.[[ViewedArrayBuffer]].[[ByteLength]], throw a RangeError exception.
1536            if first_descriptor.buffer_byte_length != buffer.len() {
1537                return Err(Exception::throw_range(
1538                    &ctx,
1539                    "The buffer of view has different capacity than byobRequest",
1540                ));
1541            };
1542
1543            // If firstDescriptor’s bytes filled + view.[[ByteLength]] > firstDescriptor’s byte length, throw a RangeError exception.
1544            if first_descriptor.bytes_filled + byte_length > first_descriptor.byte_length {
1545                return Err(Exception::throw_range(
1546                    &ctx,
1547                    "The region specified by view is larger than byobRequest",
1548                ));
1549            }
1550
1551            // Set firstDescriptor’s buffer to ? TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
1552            first_descriptor.buffer = transfer_array_buffer(buffer)?;
1553        }
1554
1555        // Perform ? ReadableByteStreamControllerRespondInternal(controller, viewByteLength).
1556        Self::readable_byte_stream_controller_respond_internal(ctx, objects, byte_length)
1557    }
1558
1559    fn readable_byte_stream_controller_fill_head_pull_into_descriptor<'a>(
1560        &mut self,
1561        size: usize,
1562        pull_into_descriptor_ref: &mut PullIntoDescriptorRefMut<'js, 'a>,
1563    ) {
1564        let pull_into_descriptor = match pull_into_descriptor_ref {
1565            PullIntoDescriptorRefMut::Index(i) => &mut self.pending_pull_intos[*i],
1566            PullIntoDescriptorRefMut::Owned(r) => *r,
1567        };
1568
1569        // Set pullIntoDescriptor’s bytes filled to bytes filled + size.
1570        pull_into_descriptor.bytes_filled += size;
1571    }
1572
1573    fn start_algorithm<R: ReadableStreamReader<'js>>(
1574        ctx: Ctx<'js>,
1575        objects: ReadableByteStreamObjects<'js, R>,
1576        start_algorithm: StartAlgorithm<'js>,
1577    ) -> Result<(
1578        Value<'js>,
1579        ReadableStreamClassObjects<'js, OwnedBorrowMut<'js, Self>, R>,
1580    )> {
1581        let objects_class = objects.into_inner();
1582
1583        Ok((
1584            start_algorithm.call(
1585                ctx,
1586                ReadableStreamControllerClass::ReadableStreamByteController(
1587                    objects_class.controller.clone(),
1588                ),
1589            )?,
1590            objects_class,
1591        ))
1592    }
1593
1594    fn pull_algorithm<R: ReadableStreamReader<'js>>(
1595        ctx: Ctx<'js>,
1596        objects: ReadableByteStreamObjects<'js, R>,
1597    ) -> Result<(
1598        Promise<'js>,
1599        ReadableStreamClassObjects<'js, OwnedBorrowMut<'js, Self>, R>,
1600    )> {
1601        let pull_algorithm = objects
1602            .controller
1603            .pull_algorithm
1604            .clone()
1605            .expect("pull algorithm used after ReadableStreamDefaultControllerClearAlgorithms");
1606        let promise_primordials = objects.stream.promise_primordials.clone();
1607        let objects_class = objects.into_inner();
1608
1609        Ok((
1610            pull_algorithm.call(
1611                ctx,
1612                &promise_primordials,
1613                ReadableStreamControllerClass::ReadableStreamByteController(
1614                    objects_class.controller.clone(),
1615                ),
1616            )?,
1617            objects_class,
1618        ))
1619    }
1620
1621    fn cancel_algorithm<R: ReadableStreamReader<'js>>(
1622        ctx: Ctx<'js>,
1623        objects: ReadableByteStreamObjects<'js, R>,
1624        reason: Value<'js>,
1625    ) -> Result<(
1626        Promise<'js>,
1627        ReadableStreamClassObjects<'js, OwnedBorrowMut<'js, Self>, R>,
1628    )> {
1629        let cancel_algorithm =
1630            objects.controller.cancel_algorithm.clone().expect(
1631                "cancel algorithm used after ReadableStreamDefaultControllerClearAlgorithms",
1632            );
1633        let promise_primordials = objects.stream.promise_primordials.clone();
1634        let objects_class = objects.into_inner();
1635
1636        Ok((
1637            cancel_algorithm.call(ctx, &promise_primordials, reason)?,
1638            objects_class,
1639        ))
1640    }
1641}
1642
1643#[methods(rename_all = "camelCase")]
1644impl<'js> ReadableByteStreamController<'js> {
1645    #[qjs(constructor)]
1646    fn new(ctx: Ctx<'js>) -> Result<Class<'js, Self>> {
1647        Err(Exception::throw_type(&ctx, "Illegal constructor"))
1648    }
1649
1650    // readonly attribute ReadableStreamBYOBRequest? byobRequest;
1651    #[qjs(get, rename = "byobRequest")]
1652    fn byob_request_getter(
1653        ctx: Ctx<'js>,
1654        controller: This<Class<'js, Self>>,
1655    ) -> Result<Null<Class<'js, ReadableStreamBYOBRequest<'js>>>> {
1656        // Use `try_borrow_mut` so that reentrant access during enqueue
1657        // (e.g. via a patched `Object.prototype.then` getter, WPT
1658        // `readable-byte-streams/patched-global`) doesn't hard-error with
1659        // "can't borrow" when the outer enqueue already holds the mut
1660        // borrow. If the controller IS currently borrowed, we can still
1661        // answer correctly by reading the state via an immutable try_borrow;
1662        // materialization is only needed when state is consistent.
1663        if let Ok(owned) = rquickjs::class::OwnedBorrowMut::try_from_class(controller.0.clone()) {
1664            let (request, _) = Self::readable_byte_stream_controller_get_byob_request(ctx, owned)?;
1665            return Ok(request);
1666        }
1667        // Reentrant access mid-enqueue: can't acquire immutable borrow
1668        // either (because enqueue holds mut). Return null — the spec's
1669        // observable state during this transient window is that the
1670        // byob request has been invalidated (the enqueue path clears it
1671        // as pull-into descriptors are filled).
1672        Ok(Null(None))
1673    }
1674
1675    // readonly attribute unrestricted double? desiredSize;
1676    #[qjs(get)]
1677    fn desired_size(&self) -> Null<f64> {
1678        let stream = OwnedBorrow::from_class(self.stream.clone());
1679        self.readable_byte_stream_controller_get_desired_size(&stream)
1680    }
1681
1682    // undefined close();
1683    fn close(ctx: Ctx<'js>, controller: This<OwnedBorrowMut<'js, Self>>) -> Result<()> {
1684        // If this.[[closeRequested]] is true, throw a TypeError exception.
1685        if controller.close_requested {
1686            return Err(Exception::throw_type(&ctx, "close() called more than once"));
1687        }
1688
1689        let objects = ReadableStreamObjects::from_byte_controller(controller.0).refresh_reader();
1690
1691        if !matches!(objects.stream.state, ReadableStreamState::Readable) {
1692            return Err(Exception::throw_type(
1693                &ctx,
1694                "close() called when stream is not readable",
1695            ));
1696        };
1697
1698        // Perform ? ReadableByteStreamControllerClose(this).
1699        Self::readable_byte_stream_controller_close(ctx, objects)?;
1700        Ok(())
1701    }
1702
1703    // undefined enqueue(ArrayBufferView chunk);
1704    fn enqueue(
1705        this: This<OwnedBorrowMut<'js, Self>>,
1706        ctx: Ctx<'js>,
1707        chunk: Value<'js>,
1708    ) -> Result<()> {
1709        let chunk = ViewBytes::from_value(&ctx, &this.function_array_buffer_is_view, Some(&chunk))?;
1710
1711        let (array_buffer, byte_length, _) = chunk.get_array_buffer()?;
1712
1713        // If chunk.[[ByteLength]] is 0, throw a TypeError exception.
1714        if byte_length == 0 {
1715            return Err(Exception::throw_type(
1716                &ctx,
1717                "chunk must have non-zero byteLength",
1718            ));
1719        }
1720
1721        // If chunk.[[ViewedArrayBuffer]].[[ArrayBufferByteLength]] is 0, throw a TypeError exception.
1722        if array_buffer.is_empty() {
1723            return Err(Exception::throw_type(
1724                &ctx,
1725                "chunk must have non-zero buffer byteLength",
1726            ));
1727        }
1728
1729        // If this.[[closeRequested]] is true, throw a TypeError exception.
1730        if this.close_requested {
1731            return Err(Exception::throw_type(&ctx, "stream is closed or draining"));
1732        }
1733
1734        let objects = ReadableStreamObjects::from_byte_controller(this.0).refresh_reader();
1735
1736        // If this.[[stream]].[[state]] is not "readable", throw a TypeError exception.
1737        if !matches!(objects.stream.state, ReadableStreamState::Readable) {
1738            return Err(Exception::throw_type(
1739                &ctx,
1740                "The stream is not in the readable state and cannot be enqueued to",
1741            ));
1742        };
1743
1744        // Return ? ReadableByteStreamControllerEnqueue(this, chunk).
1745        Self::readable_byte_stream_controller_enqueue(&ctx, objects, chunk)?;
1746        Ok(())
1747    }
1748
1749    // undefined error(optional any e);
1750    fn error(
1751        ctx: Ctx<'js>,
1752        controller: This<OwnedBorrowMut<'js, Self>>,
1753        e: Opt<Value<'js>>,
1754    ) -> Result<()> {
1755        let objects = ReadableStreamObjects::from_byte_controller(controller.0).refresh_reader();
1756
1757        // Perform ! ReadableByteStreamControllerError(this, e).
1758        Self::readable_byte_stream_controller_error(objects, e.0.unwrap_or_undefined(&ctx))?;
1759        Ok(())
1760    }
1761}
1762
1763impl<'js> ReadableStreamController<'js> for ReadableByteStreamControllerOwned<'js> {
1764    type Class = ReadableByteStreamControllerClass<'js>;
1765
1766    fn with_controller<C, O>(
1767        self,
1768        ctx: C,
1769        _: impl FnOnce(
1770            C,
1771            ReadableStreamDefaultControllerOwned<'js>,
1772        ) -> Result<(O, ReadableStreamDefaultControllerOwned<'js>)>,
1773        byte: impl FnOnce(
1774            C,
1775            ReadableByteStreamControllerOwned<'js>,
1776        ) -> Result<(O, ReadableByteStreamControllerOwned<'js>)>,
1777    ) -> Result<(O, Self)> {
1778        let (ctx, reader) = byte(ctx, self)?;
1779        Ok((ctx, reader))
1780    }
1781
1782    fn into_inner(self) -> Self::Class {
1783        OwnedBorrowMut::into_inner(self)
1784    }
1785
1786    fn from_class(class: Self::Class) -> Self {
1787        OwnedBorrowMut::from_class(class)
1788    }
1789
1790    fn into_erased(self) -> ReadableStreamControllerOwned<'js> {
1791        ReadableStreamControllerOwned::ReadableStreamByteController(self)
1792    }
1793
1794    fn try_from_erased(erased: ReadableStreamControllerOwned<'js>) -> Option<Self> {
1795        match erased {
1796            ReadableStreamControllerOwned::ReadableStreamDefaultController(_) => None,
1797            ReadableStreamControllerOwned::ReadableStreamByteController(r) => Some(r),
1798        }
1799    }
1800
1801    fn pull_steps(
1802        ctx: &Ctx<'js>,
1803        mut objects: ReadableStreamDefaultReaderObjects<'js, Self>,
1804        read_request: impl ReadableStreamReadRequest<'js> + 'js,
1805    ) -> Result<ReadableStreamDefaultReaderObjects<'js, Self>> {
1806        // If this.[[queueTotalSize]] > 0,
1807        if objects.controller.queue_total_size > 0 {
1808            // Perform ! ReadableByteStreamControllerFillReadRequestFromQueue(this, readRequest).
1809            // Return.
1810            return ReadableByteStreamController::readable_byte_stream_controller_fill_read_request_from_queue(
1811                ctx,
1812                objects,
1813                read_request,
1814            );
1815        }
1816
1817        // Let autoAllocateChunkSize be this.[[autoAllocateChunkSize]].
1818        let auto_allocate_chunk_size = objects.controller.auto_allocate_chunk_size;
1819
1820        // If autoAllocateChunkSize is not undefined,
1821        if let Some(auto_allocate_chunk_size) = auto_allocate_chunk_size {
1822            // Let buffer be Construct(%ArrayBuffer%, « autoAllocateChunkSize »).
1823            let buffer: ArrayBuffer = match objects
1824                .controller
1825                .constructor_array_buffer
1826                .construct((auto_allocate_chunk_size,))
1827            {
1828                // If buffer is an abrupt completion,
1829                Err(Error::Exception) => {
1830                    // Perform readRequest’s error steps, given buffer.[[Value]].
1831                    return read_request.error_steps_typed(objects, ctx.catch());
1832                },
1833                Err(err) => return Err(err),
1834                Ok(buffer) => buffer,
1835            };
1836
1837            // Let pullIntoDescriptor be a new pull-into descriptor with...
1838            let pull_into_descriptor = PullIntoDescriptor {
1839                buffer,
1840                buffer_byte_length: auto_allocate_chunk_size,
1841                byte_offset: 0,
1842                byte_length: auto_allocate_chunk_size,
1843                bytes_filled: 0,
1844                minimum_fill: 1,
1845                element_size: 1,
1846                view_constructor: objects
1847                    .controller
1848                    .array_constructor_primordials
1849                    .constructor_uint8array
1850                    .clone(),
1851                reader_type: PullIntoDescriptorReaderType::Default,
1852            };
1853
1854            // Append pullIntoDescriptor to this.[[pendingPullIntos]].
1855            objects
1856                .controller
1857                .pending_pull_intos
1858                .push_back(pull_into_descriptor);
1859        }
1860
1861        // Perform ! ReadableStreamAddReadRequest(stream, readRequest).
1862        objects
1863            .stream
1864            .readable_stream_add_read_request(&mut objects.reader, read_request);
1865
1866        // Perform ! ReadableByteStreamControllerCallPullIfNeeded(this).
1867        ReadableByteStreamController::readable_byte_stream_controller_call_pull_if_needed(
1868            ctx.clone(),
1869            objects,
1870        )
1871    }
1872
1873    fn cancel_steps<R: ReadableStreamReader<'js>>(
1874        ctx: &Ctx<'js>,
1875        mut objects: ReadableStreamObjects<'js, Self, R>,
1876        reason: Value<'js>,
1877    ) -> Result<(Promise<'js>, ReadableStreamObjects<'js, Self, R>)> {
1878        // Perform ! ReadableByteStreamControllerClearPendingPullIntos(this).
1879        objects
1880            .controller
1881            .readable_byte_stream_controller_clear_pending_pull_intos();
1882
1883        // Perform ! ResetQueue(this).
1884        objects.controller.reset_queue();
1885
1886        // Let result be the result of performing this.[[cancelAlgorithm]], passing in reason.
1887        let (result, objects_class) =
1888            ReadableByteStreamController::cancel_algorithm(ctx.clone(), objects, reason)?;
1889
1890        objects = ReadableStreamObjects::from_class(objects_class);
1891
1892        // Perform ! ReadableByteStreamControllerClearAlgorithms(this).
1893        objects
1894            .controller
1895            .readable_byte_stream_controller_clear_algorithms();
1896
1897        // Return result.
1898        Ok((result, objects))
1899    }
1900
1901    fn release_steps(&mut self) {
1902        // If this.[[pendingPullIntos]] is not empty,
1903        if !self.pending_pull_intos.is_empty() {
1904            // Let firstPendingPullInto be this.[[pendingPullIntos]][0].
1905            let first_pending_pull_into = &mut self.pending_pull_intos[0];
1906
1907            // Set firstPendingPullInto’s reader type to "none".
1908            first_pending_pull_into.reader_type = PullIntoDescriptorReaderType::None;
1909
1910            // Set this.[[pendingPullIntos]] to the list « firstPendingPullInto ».
1911            _ = self.pending_pull_intos.split_off(1);
1912        }
1913    }
1914}
1915
1916#[derive(JsLifetime, Trace, Clone)]
1917#[rquickjs::class]
1918pub(crate) struct ReadableStreamBYOBRequest<'js> {
1919    pub(super) view: Option<ViewBytes<'js>>,
1920    controller: Option<ReadableByteStreamControllerClass<'js>>,
1921}
1922
1923#[methods(rename_all = "camelCase")]
1924impl<'js> ReadableStreamBYOBRequest<'js> {
1925    #[qjs(constructor)]
1926    fn new(ctx: Ctx<'js>) -> Result<Class<'js, Self>> {
1927        Err(Exception::throw_type(&ctx, "Illegal constructor"))
1928    }
1929
1930    #[qjs(get)]
1931    fn view(&self) -> Null<ViewBytes<'js>> {
1932        Null(self.view.clone())
1933    }
1934
1935    fn respond(
1936        ctx: Ctx<'js>,
1937        byob_request: This<OwnedBorrowMut<'js, Self>>,
1938        bytes_written: usize,
1939    ) -> Result<()> {
1940        // If this.[[controller]] is undefined, throw a TypeError exception.
1941        let (controller, view) = match (&byob_request.controller, &byob_request.view) {
1942            (Some(controller), Some(view)) => (controller.clone(), view),
1943            _ => {
1944                return Err(Exception::throw_type(
1945                    &ctx,
1946                    "This BYOB request has been invalidated",
1947                ));
1948            },
1949        };
1950        let (buffer, _, _) = view.get_array_buffer()?;
1951        drop(byob_request);
1952
1953        // If ! IsDetachedBuffer(this.[[view]].[[ArrayBuffer]]) is true, throw a TypeError exception.
1954        if buffer.as_bytes().is_none() {
1955            return Err(Exception::throw_type(
1956                &ctx,
1957                "The BYOB request's buffer has been detached and so cannot be used as a response",
1958            ));
1959        }
1960
1961        let objects =
1962            ReadableStreamObjects::from_byte_controller(OwnedBorrowMut::from_class(controller))
1963                .refresh_reader();
1964
1965        // Perform ? ReadableByteStreamControllerRespond(this.[[controller]], bytesWritten).
1966        ReadableByteStreamController::readable_byte_stream_controller_respond(
1967            ctx,
1968            objects,
1969            bytes_written,
1970        )
1971    }
1972
1973    fn respond_with_new_view(
1974        ctx: Ctx<'js>,
1975        byob_request: This<OwnedBorrowMut<'js, Self>>,
1976        view: Opt<Value<'js>>,
1977    ) -> Result<()> {
1978        // If this.[[controller]] is undefined, throw a TypeError exception.
1979        let controller = match &byob_request.controller {
1980            Some(controller) => controller.clone(),
1981            _ => {
1982                return Err(Exception::throw_type(
1983                    &ctx,
1984                    "This BYOB request has been invalidated",
1985                ));
1986            },
1987        };
1988        drop(byob_request);
1989
1990        let controller = OwnedBorrowMut::from_class(controller);
1991
1992        let view = ViewBytes::from_value(
1993            &ctx,
1994            &controller.function_array_buffer_is_view,
1995            view.0.as_ref(),
1996        )?;
1997
1998        let (buffer, _, _) = view.get_array_buffer()?;
1999
2000        // If ! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
2001        if buffer.as_bytes().is_none() {
2002            return Err(Exception::throw_type(
2003                &ctx,
2004                "The given view's buffer has been detached and so cannot be used as a response",
2005            ));
2006        }
2007
2008        let objects = ReadableStreamObjects::from_byte_controller(controller).refresh_reader();
2009
2010        // Return ? ReadableByteStreamControllerRespondWithNewView(this.[[controller]], view).
2011        ReadableByteStreamController::readable_byte_stream_controller_respond_with_new_view(
2012            ctx, objects, view,
2013        )
2014    }
2015}
2016
2017#[derive(JsLifetime)]
2018pub(super) struct PullIntoDescriptor<'js> {
2019    buffer: ArrayBuffer<'js>,
2020    buffer_byte_length: usize,
2021    byte_offset: usize,
2022    byte_length: usize,
2023    bytes_filled: usize,
2024    minimum_fill: usize,
2025    element_size: usize,
2026    view_constructor: Constructor<'js>,
2027    reader_type: PullIntoDescriptorReaderType,
2028}
2029
2030impl<'js> Trace<'js> for PullIntoDescriptor<'js> {
2031    fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
2032        self.buffer.trace(tracer);
2033        self.buffer_byte_length.trace(tracer);
2034        self.byte_offset.trace(tracer);
2035        self.byte_length.trace(tracer);
2036        self.bytes_filled.trace(tracer);
2037        self.minimum_fill.trace(tracer);
2038        self.element_size.trace(tracer);
2039        self.view_constructor.trace(tracer);
2040        self.reader_type.trace(tracer);
2041    }
2042}
2043
2044enum PullIntoDescriptorRefMut<'js, 'a> {
2045    Index(usize),
2046    Owned(&'a mut PullIntoDescriptor<'js>),
2047}
2048
2049#[derive(Trace, Clone, Copy)]
2050enum PullIntoDescriptorReaderType {
2051    Default,
2052    Byob,
2053    None,
2054}
2055
2056#[derive(JsLifetime)]
2057struct ReadableByteStreamQueueEntry<'js> {
2058    buffer: ArrayBuffer<'js>,
2059    byte_offset: usize,
2060    byte_length: usize,
2061}
2062
2063impl<'js> Trace<'js> for ReadableByteStreamQueueEntry<'js> {
2064    fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
2065        self.buffer.trace(tracer);
2066        self.byte_offset.trace(tracer);
2067        self.byte_length.trace(tracer)
2068    }
2069}
2070
2071fn transfer_array_buffer(buffer: ArrayBuffer<'_>) -> Result<ArrayBuffer<'_>> {
2072    buffer.get::<_, Function>("transfer")?.call((This(buffer),))
2073}
2074
2075fn copy_data_block_bytes(
2076    ctx: &Ctx<'_>,
2077    to_block: &ArrayBuffer,
2078    to_index: usize,
2079    from_block: &ArrayBuffer,
2080    from_index: usize,
2081    count: usize,
2082) -> Result<()> {
2083    let to_raw = to_block
2084        .as_raw()
2085        .ok_or(ERROR_MSG_ARRAY_BUFFER_DETACHED)
2086        .or_throw(ctx)?;
2087    let to_slice = unsafe { std::slice::from_raw_parts_mut(to_raw.ptr.as_ptr(), to_raw.len) };
2088    let from_raw = from_block
2089        .as_raw()
2090        .ok_or(ERROR_MSG_ARRAY_BUFFER_DETACHED)
2091        .or_throw(ctx)?;
2092    let from_slice = unsafe { std::slice::from_raw_parts(from_raw.ptr.as_ptr(), from_raw.len) };
2093
2094    to_slice[to_index..to_index + count]
2095        .copy_from_slice(&from_slice[from_index..from_index + count]);
2096    Ok(())
2097}
2098
2099/// Public API for enqueuing a `Uint8Array` (built from the caller-supplied
2100/// `ArrayBuffer`) into a byte stream controller from Rust code. Used by
2101/// byte-source streams created via `ReadableStream::from_byte_pull_algorithm`.
2102pub fn readable_byte_stream_controller_enqueue_bytes<'js>(
2103    ctx: Ctx<'js>,
2104    controller: ReadableByteStreamControllerClass<'js>,
2105    buffer: ArrayBuffer<'js>,
2106) -> Result<()> {
2107    readable_byte_stream_controller_enqueue_bytes_inner(ctx, controller, buffer, false)
2108}
2109
2110/// Zero-copy variant of [`readable_byte_stream_controller_enqueue_bytes`]:
2111/// the incoming `ArrayBuffer` is NOT transferred/detached before being
2112/// queued. The producer keeps the buffer alive through the stream's
2113/// `'js` queue entry, so consumers get a `Uint8Array` that views directly
2114/// into the producer's storage.
2115///
2116/// Only call this when the caller can guarantee the backing allocation
2117/// won't be mutated out from under readers (e.g. `Blob.stream()`, where
2118/// the blob's `ArrayBuffer` is never written after construction). For the
2119/// normal spec-compliant flow that detaches the source, use
2120/// [`readable_byte_stream_controller_enqueue_bytes`].
2121pub fn readable_byte_stream_controller_enqueue_bytes_borrowed<'js>(
2122    ctx: Ctx<'js>,
2123    controller: ReadableByteStreamControllerClass<'js>,
2124    buffer: ArrayBuffer<'js>,
2125) -> Result<()> {
2126    readable_byte_stream_controller_enqueue_bytes_inner(ctx, controller, buffer, true)
2127}
2128
2129fn readable_byte_stream_controller_enqueue_bytes_inner<'js>(
2130    ctx: Ctx<'js>,
2131    controller: ReadableByteStreamControllerClass<'js>,
2132    buffer: ArrayBuffer<'js>,
2133    skip_transfer: bool,
2134) -> Result<()> {
2135    let byte_length = buffer.len();
2136    if byte_length == 0 {
2137        return Ok(());
2138    }
2139    let view = rquickjs::TypedArray::<u8>::from_arraybuffer(buffer)?;
2140    let borrow = OwnedBorrowMut::from_class(controller);
2141    let chunk = ViewBytes::from_value(
2142        &ctx,
2143        &borrow.function_array_buffer_is_view,
2144        Some(&view.into_value()),
2145    )?;
2146    let objects = ReadableStreamObjects::from_byte_controller(borrow).refresh_reader();
2147    if skip_transfer {
2148        ReadableByteStreamController::readable_byte_stream_controller_enqueue_borrowed(
2149            &ctx, objects, chunk,
2150        )?;
2151    } else {
2152        ReadableByteStreamController::readable_byte_stream_controller_enqueue(
2153            &ctx, objects, chunk,
2154        )?;
2155    }
2156    Ok(())
2157}
2158
2159/// Public API for closing a byte stream controller from Rust code.
2160pub fn readable_byte_stream_controller_close_stream<'js>(
2161    ctx: Ctx<'js>,
2162    controller: ReadableByteStreamControllerClass<'js>,
2163) -> Result<()> {
2164    let borrow = OwnedBorrowMut::from_class(controller);
2165    let objects = ReadableStreamObjects::from_byte_controller(borrow).refresh_reader();
2166    ReadableByteStreamController::readable_byte_stream_controller_close(ctx, objects)?;
2167    Ok(())
2168}