Skip to content

Commit

Permalink
Move BYOBRequest after BYOBController and add FS example
Browse files Browse the repository at this point in the history
  • Loading branch information
domenic committed Mar 3, 2016
1 parent beb60c1 commit b430f33
Showing 1 changed file with 133 additions and 86 deletions.
219 changes: 133 additions & 86 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -1613,91 +1613,6 @@ Specifications should <em>not</em> use this on streams they did not create.
1. Return _controller_@[[strategyHWM]] − _queueSize_.
</emu-alg>

<h3 id="rs-byob-request-class" lt="ReadableStreamBYOBRequest">Class <code>ReadableStreamBYOBRequest</code></h3>

The <code>ReadableStreamBYOBRequest</code> class represents a pull into request in a
<code>ReadableStreamBYOBController</code>.

<h4 id="rs-byob-request-class-definition">Class Definition</h4>

<em>This section is non-normative.</em>

If one were to write the <code>ReadableStreamBYOBController</code> class in something close to the syntax of
[[!ECMASCRIPT]], it would look like

<pre><code class="lang-javascript">
class ReadableStreamBYOBController {
constructor(controller, descriptor)

get view()

respond(bytesWritten)
respondWithNewView(view)

_invalidate()
}
</code></pre>

<h4 id="rs-byob-request-internal-slots">Internal Slots</h4>

Instances of <code>ReadableStreamBYOBRequest</code> are created with the internal slots described in the following
table:

<table>
<thead>
<tr>
<th>Internal Slot</th>
<th>Description (<em>non-normative</em>)</th>
</tr>
</thead>
<tr>
<td>\[[associatedReadableStreamBYOBController]]
<td>The parent ReadableStreamBYOBController instance
</tr>
<tr>
<td>\[[view]]
<td>A Typed Array representing the destination region to which the controller may write generated data
</tr>
</table>

<h4 id="rs-byob-request-constructor">new ReadableStreamBYOBRequest(controller, descriptor)</h4>

<emu-alg>
1. Set *this*@[[associatedReadableStreamBYOBController]] to _controller_.
1. Set *this*@[[view]] to Construct(`Uint8Array`, «_descriptor_.[[buffer]], _descriptor_.[[byteOffset]] + _descriptor_.[[bytesFilled]], _descriptor_.[[byteLength]] - _descriptor_.[[bytesFilled]]»).
</emu-alg>

<h4 id="rs-byob-request-prototype">Properties of the <code>ReadableStreamBYOBRequest</code> Prototype</h4>

<h5 id="rs-byob-request-view">get view</h5>

<emu-alg>
1. Return *this*@[[view]].
</emu-alg>

<h5 id="rs-byob-request-respond">respond(bytesWritten)</h5>

<emu-alg>
1. If IsReadableStreamBYOBRequest(*this*) is *false*, throw a *TypeError* exception.
1. If *this*@[[associatedReadableStreamBYOBController]] is *undefined*, throw a *TypeError* exception.
1. Return ReadableStreamBYOBControllerRespond(*this*@[[associatedReadableStreamBYOBController]], _bytesWritten_).
</emu-alg>

<h5 id="rs-byob-request-respond-with-new-view">respondWithNewView(view)</h5>

<emu-alg>
1. If IsReadableStreamBYOBRequest(*this*) is *false*, throw a *TypeError* exception.
1. If *this*@[[associatedReadableStreamBYOBController]] is *undefined*, throw a *TypeError* exception.
1. Return ReadableStreamBYOBControllerRespondWithNewView(*this*@[[associatedReadableStreamBYOBController]], _view_).
</emu-alg>

<h5 id="rs-byob-request-respond-private-invalidate">_invalidate()</h5>

<emu-alg>
1. Set *this*@[[associatedReadableStreamBYOBController]] to *undefined*.
1. Set *this*@[[view]] to *undefined*.
</emu-alg>

<h3 id="rs-byob-controller-class" lt="ReadableStreamBYOBController">Class <code>ReadableStreamBYOBController</code></h3>

The <code>ReadableStreamBYOBController</code> class has methods that allow control of a <code>ReadableStream</code>'s
Expand Down Expand Up @@ -1929,6 +1844,91 @@ controllers.
1. Return _promise_.
</emu-alg>

<h3 id="rs-byob-request-class" lt="ReadableStreamBYOBRequest">Class <code>ReadableStreamBYOBRequest</code></h3>

The <code>ReadableStreamBYOBRequest</code> class represents a pull into request in a
<code>ReadableStreamBYOBController</code>.

<h4 id="rs-byob-request-class-definition">Class Definition</h4>

<em>This section is non-normative.</em>

If one were to write the <code>ReadableStreamBYOBController</code> class in something close to the syntax of
[[!ECMASCRIPT]], it would look like

<pre><code class="lang-javascript">
class ReadableStreamBYOBController {
constructor(controller, descriptor)

get view()

respond(bytesWritten)
respondWithNewView(view)

_invalidate()
}
</code></pre>

<h4 id="rs-byob-request-internal-slots">Internal Slots</h4>

Instances of <code>ReadableStreamBYOBRequest</code> are created with the internal slots described in the following
table:

<table>
<thead>
<tr>
<th>Internal Slot</th>
<th>Description (<em>non-normative</em>)</th>
</tr>
</thead>
<tr>
<td>\[[associatedReadableStreamBYOBController]]
<td>The parent ReadableStreamBYOBController instance
</tr>
<tr>
<td>\[[view]]
<td>A Typed Array representing the destination region to which the controller may write generated data
</tr>
</table>

<h4 id="rs-byob-request-constructor">new ReadableStreamBYOBRequest(controller, descriptor)</h4>

<emu-alg>
1. Set *this*@[[associatedReadableStreamBYOBController]] to _controller_.
1. Set *this*@[[view]] to Construct(`Uint8Array`, «_descriptor_.[[buffer]], _descriptor_.[[byteOffset]] + _descriptor_.[[bytesFilled]], _descriptor_.[[byteLength]] - _descriptor_.[[bytesFilled]]»).
</emu-alg>

<h4 id="rs-byob-request-prototype">Properties of the <code>ReadableStreamBYOBRequest</code> Prototype</h4>

<h5 id="rs-byob-request-view">get view</h5>

<emu-alg>
1. Return *this*@[[view]].
</emu-alg>

<h5 id="rs-byob-request-respond">respond(bytesWritten)</h5>

<emu-alg>
1. If IsReadableStreamBYOBRequest(*this*) is *false*, throw a *TypeError* exception.
1. If *this*@[[associatedReadableStreamBYOBController]] is *undefined*, throw a *TypeError* exception.
1. Return ReadableStreamBYOBControllerRespond(*this*@[[associatedReadableStreamBYOBController]], _bytesWritten_).
</emu-alg>

<h5 id="rs-byob-request-respond-with-new-view">respondWithNewView(view)</h5>

<emu-alg>
1. If IsReadableStreamBYOBRequest(*this*) is *false*, throw a *TypeError* exception.
1. If *this*@[[associatedReadableStreamBYOBController]] is *undefined*, throw a *TypeError* exception.
1. Return ReadableStreamBYOBControllerRespondWithNewView(*this*@[[associatedReadableStreamBYOBController]], _view_).
</emu-alg>

<h5 id="rs-byob-request-respond-private-invalidate">_invalidate()</h5>

<emu-alg>
1. Set *this*@[[associatedReadableStreamBYOBController]] to *undefined*.
1. Set *this*@[[view]] to *undefined*.
</emu-alg>

<h3 id="rs-byob-controller-abstract-ops">Readable Stream BYOB Controller Abstract Operations</h3>

<h4 id="is-readable-stream-byob-controller" aoid="IsReadableStreamBYOBController" nothrow>IsReadableStreamBYOBController ( x )</h4>
Expand Down Expand Up @@ -3133,7 +3133,7 @@ socket.
<h3 id="example-rs-pull">A readable stream with an underlying pull source</h3>

The following function returns <a>readable streams</a> that wrap portions of the
<a href="https://iojs.org/api/fs.html">io.js file system API</a> (which themselves map fairly directly to C's
<a href="https://nodejs.org/api/fs.html">Node.js file system API</a> (which themselves map fairly directly to C's
<code>fopen</code>, <code>fread</code>, and <code>fclose</code> trio). Files are a typical example of <a>pull
sources</a>. Note how in contrast to the examples with push sources, most of the work here happens on-demand in the
<code>pull</code> function, and not at startup time in the <code>start</code> function.

This comment has been minimized.

Copy link
@tyoshino

tyoshino Mar 9, 2016

Member

BTW, the example below also need to handle bytesRead as well as the issue @yutakahirano pointed out at #430 (comment) in the newly added example.

buffer can be filled partially (even if the API blocks until CHUNK_SIZE bytes are filled, we may eventually need to return a partially filled buffer at the end of the file), and there's no way to know that for the user of the stream.

This comment has been minimized.

Copy link
@tyoshino

tyoshino Mar 10, 2016

Member

Fixed: 390b8f4

Expand Down Expand Up @@ -3175,6 +3175,53 @@ sources</a>. Note how in contrast to the examples with push sources, most of the

We can then create and use readable streams for files just as we could before for sockets.

<h3 id="example-rbs-pull">A readable byte stream with an underlying pull source</h3>

The following function returns <a>readable byte streams</a> that allow efficient zero-copy reading of files, again
using the <a href="https://nodejs.org/api/fs.html">Node.js file system API</a>. Instead of using a predetermined chunk
size of 1024, it attempts to fill the developer-supplied buffer, allowing full control.

<pre><code class="lang-javascript">
const fs = require("pr/fs"); // https://github.com/jden/pr

function makeReadableByteFileStream(filename) {
let fd;
let position = 0;

return new ReadableStream({
byob: true,

start() {
return fs.open(filename, "r").then(result => {
fd = result;
});
},

pull(controller) {
const v = controller.byobRequest.view;

This comment has been minimized.

Copy link
@tyoshino

tyoshino Mar 9, 2016

Member

The example should check whether or not controller.byobRequest is null.

This comment has been minimized.

Copy link
@tyoshino

tyoshino Mar 10, 2016

Member

Fixed: da06f3d


return fs.read(fd, v, v.byteOffset, v.byteLength, position).then(bytesRead => {
if (bytesRead === 0) {
return fs.close(fd).then(() => controller.close());
} else {
position += bytesRead;
controller.byobRequest.respond(bytesRead);
}
});
},

cancel() {
return fs.close(fd);
}
});
}
</code></pre>

With this in hand, we can create and use <a>BYOB readers</a> for the returned <code>ReadableStream</code>. But we can
also create <a>default readers</a>, using them in the same simple and generic manner as usual. The adaptation between
the low-level byte tracking of the <a>underlying byte source</a> shown here, and the higher-level chunk-based
consumption of a <a>default reader</a>, is all taken care of automatically by the streams implementation.

This comment has been minimized.

Copy link
@tyoshino

tyoshino Mar 9, 2016

Member

We need to provide a concrete example of how to implement such a controller representing a passive data source and reacting to both BYOB and default read adaptively.

The controller can know that there's a BYOB reader and it's receiving a BYOB read request by looking at controller.byobRequest, but there's no such API for checking read requests from a default reader.

I think HWM needs to be set to 0 (we've changed the default from 1 to use 0 for BYOB controller at some point. Maybe for the same reason? I don't remember...) to expose the default-read signal (pull is invoked only when there's at least one default-read request).

This comment has been minimized.

Copy link
@tyoshino

tyoshino Mar 10, 2016

Member

Fixed: da06f3d


<h3 id="example-ws-no-backpressure">A writable stream with no backpressure or success signals</h3>

The following function returns a <a>writable stream</a> that wraps a web socket [[HTML]]. Web sockets do not provide
Expand Down

3 comments on commit b430f33

@domenic
Copy link
Member Author

@domenic domenic commented on b430f33 Mar 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be able to take over making the examples better?

@tyoshino
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I'll try to today.

@tyoshino
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Please sign in to comment.