Skip to content

Commit 195d45a

Browse files
committed
Allow other specifications to create readable byte streams
Closes #1121.
1 parent cada812 commit 195d45a

File tree

1 file changed

+142
-31
lines changed

1 file changed

+142
-31
lines changed

Diff for: index.bs

+142-31
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,149 @@ 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):
6536+
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:
6540+
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>
6548+
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.
65046552

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.
6553+
<div algorithm>
6554+
To <dfn export for="ReadableStream">close</dfn> a {{ReadableStream}} |stream|:
6555+
6556+
1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}},
6557+
then perform !
6558+
[$ReadableByteStreamControllerClose$](|stream|.[=ReadableStream/[[controller]]=]).
6559+
1. Perform ! [$ReadableStreamDefaultControllerClose$](|stream|.[=ReadableStream/[[controller]]=]).
6560+
</div>
6561+
6562+
<div algorithm>
6563+
To <dfn export for="ReadableStream">error</dfn> a {{ReadableStream}} |stream| given a JavaScript
6564+
value |e|:
6565+
6566+
1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}},
6567+
then perform ! [$ReadableByteStreamControllerError$](|stream|.[=ReadableStream/[[controller]]=],
6568+
|e|).
6569+
1. Perform ! [$ReadableStreamDefaultControllerError$](|stream|.[=ReadableStream/[[controller]]=],
6570+
|e|).
6571+
</div>
6572+
6573+
<hr>
6574+
6575+
The following algorithm must only be used on {{ReadableStream}} instances initialized via the above
6576+
[=ReadableStream/set up=] algorithm:
65096577

65106578
<p algorithm>To <dfn export for="ReadableStream">enqueue</dfn> the JavaScript value |chunk| into a
65116579
{{ReadableStream}} |stream|, perform !
65126580
[$ReadableStreamDefaultControllerEnqueue$](|stream|.[=ReadableStream/[[controller]]=], |chunk|).
65136581

6514-
<p algorithm>To <dfn export for="ReadableStream">close</dfn> a {{ReadableStream}} |stream|, perform
6515-
! [$ReadableStreamDefaultControllerClose$](|stream|.[=ReadableStream/[[controller]]=]).
6582+
<hr>
6583+
6584+
The following algorithms must only be used on {{ReadableStream}} instances initialized via the above
6585+
[=ReadableStream/set up with byte reading support=] algorithm:
6586+
6587+
<div algorithm>
6588+
The <dfn export for="ReadableStream">current BYOB request view</dfn> for a
6589+
{{ReadableStream}} |stream| is either an {{ArrayBufferView}} or null, determined by the following
6590+
steps:
6591+
6592+
1. Assert: |stream|.[=ReadableStream/[[controller]]=] [=implements=]
6593+
{{ReadableByteStreamController}}.
6594+
1. Let |byobRequest| be !
6595+
[$ReadableByteStreamControllerGetBYOBRequest$](|stream|.[=ReadableStream/[[controller]]=]).
6596+
1. If |byobRequest| is null, then return null.
6597+
1. Return |byobRequest|.[=ReadableStreamBYOBRequest/[[view]]=].
6598+
</div>
6599+
6600+
<div algorithm>
6601+
To <dfn export for="ReadableStream">enqueue bytes</dfn> into a {{ReadableStream}} |stream| given an
6602+
{{ArrayBufferView}} |view|:
6603+
6604+
1. Assert: |stream|.[=ReadableStream/[[controller]]=] [=implements=]
6605+
{{ReadableByteStreamController}}.
6606+
1. Let |byobRequest| be !
6607+
[$ReadableByteStreamControllerGetBYOBRequest$](|stream|.[=ReadableStream/[[controller]]=]).
6608+
1. If |byobRequest| is non-null, and |view|.\[[ViewedArrayBuffer]].\[[ArrayBufferData]] is the same
6609+
[=Data Block=] as |byobRequest|.\[[view]].\[[ViewedArrayBuffer]].\[[ArrayBufferData]], then:
6610+
1. Assert: |view|.\[[ByteOffset]] is |byobRequest|.\[[view]].\[[ByteOffset]].
6611+
1. Assert: |view|.\[[ByteLength]] ≤ |byobRequest|.\[[view]].\[[ByteLength]].
6612+
<p class="note">These asserts ensure that the caller does not write outside the requested
6613+
range in the [=ReadableStream/current BYOB request view=].
6614+
1. Perform ?
6615+
[$ReadableByteStreamControllerRespondWithNewView$](|stream|.[=ReadableStream/[[controller]]=],
6616+
|view|).
6617+
<p class="note">This works fine even in the case where |view|.\[[ViewedArrayBuffer]] equals
6618+
|byobRequest|.\[[view]].\[[ViewedArrayBuffer]].
6619+
1. Otherwise, perform ?
6620+
[$ReadableByteStreamControllerEnqueue$](|stream|.[=ReadableStream/[[controller]]=], |view|).
6621+
</div>
6622+
6623+
Specifications should, when possible, [=ArrayBufferView/write=] into the [=ReadableStream/current
6624+
BYOB request view=] when it is non-null, and then call [=enqueue bytes=] with that view. They should
6625+
only [=ArrayBufferView/create=] a new {{ArrayBufferView}} to pass to [=enqueue bytes=] when the
6626+
[=ReadableStream/current BYOB request view=] is null, or when they have more bytes on hand than the
6627+
[=ReadableStream/current BYOB request view=]'s [=BufferSource/byte length=]. This avoids unnecessary
6628+
copies and better respects the wishes of the stream's [=consumer=].
65166629

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|).
65206630

65216631
<h4 id="other-specs-rs-reading">Reading</h4>
65226632

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

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

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

0 commit comments

Comments
 (0)