Skip to content

Commit bb53bec

Browse files
authored
Allow other specifications to create readable byte streams
Closes #1121.
1 parent cada812 commit bb53bec

File tree

1 file changed

+153
-34
lines changed

1 file changed

+153
-34
lines changed

index.bs

Lines changed: 153 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ urlPrefix: https://tc39.es/ecma262/; spec: ECMASCRIPT
4242
text: the typed array constructors table; url: #table-49
4343
text: typed array; url: #sec-typedarray-objects
4444
text: Number type; url: #sec-ecmascript-language-types-number-type
45+
text: Data Block; url: #sec-data-blocks
4546
type: abstract-op
4647
text: CloneArrayBuffer; url: #sec-clonearraybuffer
4748
text: CopyDataBlockBytes; url: #sec-copydatablockbytes
@@ -165,9 +166,9 @@ readable stream tee">branches</dfn>, which can be consumed independently.
165166
For streams representing bytes, an extended version of the [=readable stream=] is provided to handle
166167
bytes efficiently, in particular by minimizing copies. The [=underlying source=] for such a readable
167168
stream is called an <dfn>underlying byte source</dfn>. A readable stream whose underlying source is
168-
an underlying byte source is sometimes called a <dfn>readable byte stream</dfn>. Consumers of a
169-
readable byte stream can acquire a [=BYOB reader=] using the stream's {{ReadableStream/getReader()}}
170-
method.
169+
an underlying byte source is sometimes called a <dfn export>readable byte stream</dfn>. Consumers of
170+
a readable byte stream can acquire a [=BYOB reader=] using the stream's
171+
{{ReadableStream/getReader()}} method.
171172

172173
<h3 id="ws-model">Writable streams</h3>
173174

@@ -309,11 +310,12 @@ object that allows direct reading of [=chunks=] from a [=readable stream=]. With
309310
stream|canceling=] the stream, or [=piping=] the readable stream to a writable stream. A reader is
310311
acquired via the stream's {{ReadableStream/getReader()}} method.
311312

312-
A [=readable byte stream=] has the ability to vend two types of readers: <dfn>default readers</dfn>
313-
and <dfn>BYOB readers</dfn>. BYOB ("bring your own buffer") readers allow reading into a
314-
developer-supplied buffer, thus minimizing copies. A non-byte readable stream can only vend default
315-
readers. Default readers are instances of the {{ReadableStreamDefaultReader}} class, while BYOB
316-
readers are instances of {{ReadableStreamBYOBReader}}.
313+
A [=readable byte stream=] has the ability to vend two types of readers: <dfn export lt="default
314+
reader">default readers</dfn> and <dfn export lt="BYOB reader">BYOB readers</dfn>. BYOB ("bring your
315+
own buffer") readers allow reading into a developer-supplied buffer, thus minimizing copies. A
316+
non-byte readable stream can only vend default readers. Default readers are instances of the
317+
{{ReadableStreamDefaultReader}} class, while BYOB readers are instances of
318+
{{ReadableStreamBYOBReader}}.
317319

318320
Similarly, a <dfn lt="writer|writable stream writer">writable stream writer</dfn>, or simply
319321
writer, is an object that allows direct writing of [=chunks=] to a [=writable stream=]. Without a
@@ -3244,8 +3246,9 @@ The following abstract operations support the implementation of the
32443246
</div>
32453247

32463248
<div algorithm>
3247-
<dfn abstract-op lt="ReadableByteStreamControllerGetBYOBRequest">ReadableByteStreamControllerGetBYOBRequest(|controller|)</dfn>
3248-
performs the following steps:
3249+
<dfn abstract-op
3250+
lt="ReadableByteStreamControllerGetBYOBRequest">ReadableByteStreamControllerGetBYOBRequest(|controller|)</dfn> performs
3251+
the following steps:
32493252

32503253
1. If |controller|.[=ReadableByteStreamController/[[byobRequest]]=] is null and
32513254
|controller|.[=ReadableByteStreamController/[[pendingPullIntos]]=] is not [=list/is empty|empty=],
@@ -6463,7 +6466,7 @@ to grow organically as needed.
64636466
{{ReadableStream}} object |stream|, given an optional algorithm <dfn export for="ReadableStream/set
64646467
up"><var>pullAlgorithm</var></dfn>, an optional algorithm <dfn export for="ReadableStream/set
64656468
up"><var>cancelAlgorithm</var></dfn>, an optional number <dfn export for="ReadableStream/set
6466-
up"><var>highWaterMark</var></dfn> (default 1), an optional algorithm <dfn export
6469+
up"><var>highWaterMark</var></dfn> (default 1), and an optional algorithm <dfn export
64676470
for="ReadableStream/set up"><var>sizeAlgorithm</var></dfn>, perform the following steps. If
64686471
given, |pullAlgorithm| and |cancelAlgorithm| may return a promise. If given, |sizeAlgorithm| must
64696472
be an algorithm accepting [=chunk=] objects and returning a number; and if given, |highWaterMark|
@@ -6481,42 +6484,157 @@ to grow organically as needed.
64816484
1. If |result| is a {{Promise}}, then return |result|.
64826485
1. Return [=a promise resolved with=] undefined.
64836486
1. If |sizeAlgorithm| was not given, then set it to an algorithm that returns 1.
6484-
1. Perform [$InitializeReadableStream$](|stream|).
6487+
1. Perform ! [$InitializeReadableStream$](|stream|).
64856488
1. Let |controller| be a [=new=] {{ReadableStreamDefaultController}}.
64866489
1. Perform ! [$SetUpReadableStreamDefaultController$](|stream|, |controller|, |startAlgorithm|,
64876490
|pullAlgorithmWrapper|, |cancelAlgorithmWrapper|, |highWaterMark|, |sizeAlgorithm|).
6491+
</div>
64886492

6489-
<div class="example" id="example-set-up-rs">
6490-
Creating a {{ReadableStream}} from other specifications is thus a two-step process, like so:
6493+
<div algorithm="set up a byte-source ReadableStream">
6494+
To <dfn export for="ReadableStream">set up with byte reading support</dfn> a
6495+
newly-[=new|created-via-Web IDL=] {{ReadableStream}} object |stream|, given an optional algorithm
6496+
<dfn export for="ReadableStream/set up with byte reading support"><var>pullAlgorithm</var></dfn>,
6497+
an optional algorithm <dfn export for="ReadableStream/set up with byte reading
6498+
support"><var>cancelAlgorithm</var></dfn>, and an optional number <dfn export
6499+
for="ReadableStream/set up with byte reading support"><var>highWaterMark</var></dfn> (default 0),
6500+
perform the following steps. If given, |pullAlgorithm| and |cancelAlgorithm| may return a promise.
6501+
If given, |highWaterMark| must be a non-negative, non-NaN number.
64916502

6492-
1. Let |readableStream| be a [=new=] {{ReadableStream}}.
6493-
1. [=ReadableStream/Set up=] |readableStream| given….
6494-
</div>
6503+
1. Let |startAlgorithm| be an algorithm that returns undefined.
6504+
1. Let |pullAlgorithmWrapper| be an algorithm that runs these steps:
6505+
1. Let |result| be the result of running |pullAlgorithm|, if |pullAlgorithm| was given, or null
6506+
otherwise. If this throws an exception |e|, return [=a promise rejected with=] |e|.
6507+
1. If |result| is a {{Promise}}, then return |result|.
6508+
1. Return [=a promise resolved with=] undefined.
6509+
1. Let |cancelAlgorithmWrapper| be an algorithm that runs these steps:
6510+
1. Let |result| be the result of running |cancelAlgorithm|, if |cancelAlgorithm| was given, or
6511+
null otherwise. If this throws an exception |e|, return [=a promise rejected with=] |e|.
6512+
1. If |result| is a {{Promise}}, then return |result|.
6513+
1. Return [=a promise resolved with=] undefined.
6514+
1. Perform ! [$InitializeReadableStream$](|stream|).
6515+
1. Let |controller| be a [=new=] {{ReadableByteStreamController}}.
6516+
1. Perform ! [$SetUpReadableByteStreamController$](|stream|, |controller|, |startAlgorithm|,
6517+
|pullAlgorithmWrapper|, |cancelAlgorithmWrapper|, |highWaterMark|, undefined).
6518+
</div>
64956519

6496-
<p class="note">Subclasses of {{ReadableStream}} will use the [=ReadableStream/set up=] operation
6497-
directly on the [=this=] value inside their constructor steps.</p>
6520+
<div class="example" id="example-set-up-rs">
6521+
Creating a {{ReadableStream}} from other specifications is thus a two-step process, like so:
6522+
6523+
1. Let |readableStream| be a [=new=] {{ReadableStream}}.
6524+
1. [=ReadableStream/Set up=] |readableStream| given….
64986525
</div>
64996526

6527+
<p class="note">Subclasses of {{ReadableStream}} will use the [=ReadableStream/set up=] or
6528+
[=ReadableStream/set up with byte reading support=] operations directly on the [=this=] value inside
6529+
their constructor steps.
6530+
65006531
<hr>
65016532

65026533
The following algorithms must only be used on {{ReadableStream}} instances initialized via the above
6503-
[=ReadableStream/set up=] algorithm:
6534+
[=ReadableStream/set up=] or [=ReadableStream/set up with byte reading support=] algorithms (not,
6535+
e.g., on web-developer-created instances):
65046536

6505-
<p algorithm>A {{ReadableStream}} |stream| <dfn export for="ReadableStream" lt="need more
6506-
data|needs more data">needs more data</dfn> if |stream| is [=ReadableStream/readable=] and !
6507-
[$ReadableStreamDefaultControllerGetDesiredSize$](|stream|.[=ReadableStream/[[controller]]=])
6508-
returns a positive number.
6537+
<div algorithm>
6538+
A {{ReadableStream}} |stream|'s <dfn export for="ReadableStream">desired size to fill up to the
6539+
high water mark</dfn> is the result of running the following steps:
65096540

6510-
<p algorithm>To <dfn export for="ReadableStream">enqueue</dfn> the JavaScript value |chunk| into a
6511-
{{ReadableStream}} |stream|, perform !
6512-
[$ReadableStreamDefaultControllerEnqueue$](|stream|.[=ReadableStream/[[controller]]=], |chunk|).
6541+
1. If |stream| is not [=ReadableStream/readable=], then return 0.
6542+
1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}},
6543+
then return !
6544+
[$ReadableByteStreamControllerGetDesiredSize$](|stream|.[=ReadableStream/[[controller]]=]).
6545+
1. Return !
6546+
[$ReadableStreamDefaultControllerGetDesiredSize$](|stream|.[=ReadableStream/[[controller]]=]).
6547+
</div>
65136548

6514-
<p algorithm>To <dfn export for="ReadableStream">close</dfn> a {{ReadableStream}} |stream|, perform
6515-
! [$ReadableStreamDefaultControllerClose$](|stream|.[=ReadableStream/[[controller]]=]).
6549+
<p algorithm>A {{ReadableStream}} <dfn export for="ReadableStream" lt="need more data|needs
6550+
more data">needs more data</dfn> if its [=ReadableStream/desired size to fill up to the high water
6551+
mark=] is greater than zero.
65166552

6517-
<p algorithm>To <dfn export for="ReadableStream">error</dfn> a {{ReadableStream}} |stream| given a
6518-
JavaScript value |e|, perform !
6519-
[$ReadableStreamDefaultControllerError$](|stream|.[=ReadableStream/[[controller]]=], |e|).
6553+
<div algorithm>
6554+
To <dfn export for="ReadableStream">close</dfn> a {{ReadableStream}} |stream|:
6555+
6556+
1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}},
6557+
1. Perform !
6558+
[$ReadableByteStreamControllerClose$](|stream|.[=ReadableStream/[[controller]]=]).
6559+
1. If |stream|.[=ReadableStream/[[controller]]=].[=ReadableByteStreamController/[[pendingPullIntos]]=]
6560+
is not [=list/is empty|empty=], perform !
6561+
[$ReadableByteStreamControllerRespond$](|stream|.[=ReadableStream/[[controller]]=], 0).
6562+
1. Otherwise, perform ! [$ReadableStreamDefaultControllerClose$](|stream|.[=ReadableStream/[[controller]]=]).
6563+
</div>
6564+
6565+
<div algorithm>
6566+
To <dfn export for="ReadableStream">error</dfn> a {{ReadableStream}} |stream| given a JavaScript
6567+
value |e|:
6568+
6569+
1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}},
6570+
then perform ! [$ReadableByteStreamControllerError$](|stream|.[=ReadableStream/[[controller]]=],
6571+
|e|).
6572+
1. Otherwise, perform ! [$ReadableStreamDefaultControllerError$](|stream|.[=ReadableStream/[[controller]]=],
6573+
|e|).
6574+
</div>
6575+
6576+
<div algorithm>
6577+
To <dfn export for="ReadableStream">enqueue</dfn> the JavaScript value |chunk| into a
6578+
{{ReadableStream}} |stream|:
6579+
6580+
1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=]
6581+
{{ReadableStreamDefaultController}},
6582+
1. Perform ! [$ReadableStreamDefaultControllerEnqueue$](|stream|.[=ReadableStream/[[controller]]=],
6583+
|chunk|).
6584+
1. Otherwise,
6585+
1. Assert: |stream|.[=ReadableStream/[[controller]]=] [=implements=]
6586+
{{ReadableByteStreamController}}.
6587+
1. Assert: |chunk| is an {{ArrayBufferView}}.
6588+
1. Let |byobView| be the [=current BYOB request view=] for |stream|.
6589+
1. If |byobView| is non-null, and |chunk|.\[[ViewedArrayBuffer]] is
6590+
|byobView|.\[[ViewedArrayBuffer]], then:
6591+
1. Assert: |chunk|.\[[ByteOffset]] is |byobView|.\[[ByteOffset]].
6592+
1. Assert: |chunk|.\[[ByteLength]] ≤ |byobView|.\[[ByteLength]].
6593+
<p class="note">These asserts ensure that the caller does not write outside the requested
6594+
range in the [=ReadableStream/current BYOB request view=].
6595+
1. Perform ?
6596+
[$ReadableByteStreamControllerRespond$](|stream|.[=ReadableStream/[[controller]]=],
6597+
|chunk|.\[[ByteLength]]).
6598+
1. Otherwise, perform ?
6599+
[$ReadableByteStreamControllerEnqueue$](|stream|.[=ReadableStream/[[controller]]=], |chunk|).
6600+
</div>
6601+
6602+
<hr>
6603+
6604+
The following algorithm must only be used on {{ReadableStream}} instances initialized via the above
6605+
[=ReadableStream/set up with byte reading support=] algorithm:
6606+
6607+
<div algorithm>
6608+
The <dfn export for="ReadableStream">current BYOB request view</dfn> for a
6609+
{{ReadableStream}} |stream| is either an {{ArrayBufferView}} or null, determined by the following
6610+
steps:
6611+
6612+
1. Assert: |stream|.[=ReadableStream/[[controller]]=] [=implements=]
6613+
{{ReadableByteStreamController}}.
6614+
1. Let |byobRequest| be !
6615+
[$ReadableByteStreamControllerGetBYOBRequest$](|stream|.[=ReadableStream/[[controller]]=]).
6616+
1. If |byobRequest| is null, then return null.
6617+
1. Return |byobRequest|.[=ReadableStreamBYOBRequest/[[view]]=].
6618+
</div>
6619+
6620+
Specifications must not [=ArrayBuffer/transfer=] or [=ArrayBuffer/detach=] the
6621+
[=BufferSource/underlying buffer=] of the [=ReadableStream/current BYOB request view=].
6622+
6623+
<p class="note">Implementations could do something equivalent to transferring, e.g. if they want to
6624+
write into the memory from another thread. But they would need to make a few adjustments to how they
6625+
implement the [=ReadableStream/enqueue=] and [=ReadableStream/close=] algorithms to keep the same
6626+
observable consequences. In specification-land, transferring and detaching is just disallowed.
6627+
6628+
Specifications should, when possible, [=ArrayBufferView/write=] into the [=ReadableStream/current
6629+
BYOB request view=] when it is non-null, and then call [=ReadableStream/enqueue=] with that view.
6630+
They should only [=ArrayBufferView/create=] a new {{ArrayBufferView}} to pass to
6631+
[=ReadableStream/enqueue=] when the [=ReadableStream/current BYOB request view=] is null, or when
6632+
they have more bytes on hand than the [=ReadableStream/current BYOB request view=]'s
6633+
[=BufferSource/byte length=]. This avoids unnecessary copies and better respects the wishes of the
6634+
stream's [=consumer=].
6635+
6636+
Specifications must not [=ArrayBuffer/write=] into the [=ReadableStream/current BYOB request view=]
6637+
after [=ReadableStream/closing=] the corresponding {{ReadableStream}}.
65206638

65216639
<h4 id="other-specs-rs-reading">Reading</h4>
65226640

@@ -6573,7 +6691,8 @@ a chunk</dfn> from a {{ReadableStreamDefaultReader}} |reader|, given a [=read re
65736691

65746692
<p class="note">Because |reader| grants exclusive access to its corresponding {{ReadableStream}},
65756693
the actual mechanism of how to read cannot be observed. Implementations could use a more direct
6576-
mechanism if convenient.
6694+
mechanism if convenient, such as acquiring and using a {{ReadableStreamBYOBReader}} instead of a
6695+
{{ReadableStreamDefaultReader}}, or accessing the chunks directly.
65776696
</div>
65786697

65796698
<p algorithm>To <dfn export for="ReadableStreamDefaultReader">release</dfn> a
@@ -6659,7 +6778,7 @@ for="ReadableStream">locked</dfn> if ! [$IsReadableStreamLocked$](|stream|) retu
66596778
1. If |result| is a {{Promise}}, then return |result|.
66606779
1. Return [=a promise resolved with=] undefined.
66616780
1. If |sizeAlgorithm| was not given, then set it to an algorithm that returns 1.
6662-
1. Perform [$InitializeWritableStream$](|stream|).
6781+
1. Perform ! [$InitializeWritableStream$](|stream|).
66636782
1. Let |controller| be a [=new=] {{WritableStreamDefaultController}}.
66646783
1. Perform ! [$SetUpWritableStreamDefaultController$](|stream|, |controller|, |startAlgorithm|,
66656784
|writeAlgorithm|, |closeAlgorithmWrapper|, |abortAlgorithmWrapper|, |highWaterMark|,

0 commit comments

Comments
 (0)