Skip to content

Commit d747a6f

Browse files
committed
fs: make FileHandle.readableWebStream always create byte streams
The original implementation of the experimental `FileHandle.readableWebStream` API created non-`type: 'bytes'` streams, which prevented callers from creating `mode: 'byob'` readers from the returned stream, which means they could not achieve the associated "zero-copy" performance characteristics. Then, nodejs#46933 added a parameter allowing callers to pass the `type` parameter down to the ReadableStream constructor, exposing the same semantics to callers of `FileHandle.readableWebStream`. But there is no point to giving callers this choice: FileHandle-derived streams are by their very nature byte streams. We should not require callers to explicitly opt in to having byte stream semantics. Moreover, I do not see a situation in which callers would ever want to have a non-bytes stream: bytes-streams only do anything differently than normal ones if `mode: 'byob'` is passed to `getReader`. So, remove the `options` parameter and always create a ReadableStream with `type: 'bytes'`. Fixes nodejs#54041.
1 parent 5e5af29 commit d747a6f

File tree

3 files changed

+46
-95
lines changed

3 files changed

+46
-95
lines changed

doc/api/fs.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -480,11 +480,14 @@ Reads data from the file and stores that in the given buffer.
480480
If the file is not modified concurrently, the end-of-file is reached when the
481481
number of bytes read is zero.
482482
483-
#### `filehandle.readableWebStream([options])`
483+
#### `filehandle.readableWebStream()`
484484
485485
<!-- YAML
486486
added: v17.0.0
487487
changes:
488+
- version: REPLACEME
489+
pr-url: https://github.com/nodejs/node/pull/55461
490+
description: Removed option to create a 'bytes' stream. Streams are now always 'bytes' streams.
488491
- version:
489492
- v20.0.0
490493
- v18.17.0
@@ -494,13 +497,10 @@ changes:
494497
495498
> Stability: 1 - Experimental
496499
497-
* `options` {Object}
498-
* `type` {string|undefined} Whether to open a normal or a `'bytes'` stream.
499-
**Default:** `undefined`
500-
501500
* Returns: {ReadableStream}
502501
503-
Returns a `ReadableStream` that may be used to read the files data.
502+
Returns a byte-oriented `ReadableStream` that may be used to read the file's
503+
contents.
504504
505505
An error will be thrown if this method is called more than once or is called
506506
after the `FileHandle` is closed or closing.

lib/internal/fs/promises.js

+30-36
Original file line numberDiff line numberDiff line change
@@ -276,12 +276,9 @@ class FileHandle extends EventEmitter {
276276
/**
277277
* @typedef {import('../webstreams/readablestream').ReadableStream
278278
* } ReadableStream
279-
* @param {{
280-
* type?: string;
281-
* }} [options]
282279
* @returns {ReadableStream}
283280
*/
284-
readableWebStream(options = kEmptyObject) {
281+
readableWebStream(options = { type: 'bytes' }) {
285282
if (this[kFd] === -1)
286283
throw new ERR_INVALID_STATE('The FileHandle is closed');
287284
if (this[kClosePromise])
@@ -293,46 +290,43 @@ class FileHandle extends EventEmitter {
293290
if (options.type !== undefined) {
294291
validateString(options.type, 'options.type');
295292
}
293+
if (options.type !== 'bytes') {
294+
process.emitWarning(
295+
'A non-"bytes" type option to readableWebStream() no longer does ' +
296+
'anything. readableWebStream() always creates a byte-oriented stream ' +
297+
'now.',
298+
'ExperimentalWarning',
299+
);
300+
}
296301

297-
let readable;
302+
const {
303+
ReadableStream,
304+
} = require('internal/webstreams/readablestream');
298305

299-
if (options.type !== 'bytes') {
300-
const {
301-
newReadableStreamFromStreamBase,
302-
} = require('internal/webstreams/adapters');
303-
readable = newReadableStreamFromStreamBase(
304-
this[kHandle],
305-
undefined,
306-
{ ondone: () => this[kUnref]() });
307-
} else {
308-
const {
309-
ReadableStream,
310-
} = require('internal/webstreams/readablestream');
306+
const readFn = FunctionPrototypeBind(this.read, this);
307+
const ondone = FunctionPrototypeBind(this[kUnref], this);
311308

312-
const readFn = FunctionPrototypeBind(this.read, this);
313-
const ondone = FunctionPrototypeBind(this[kUnref], this);
309+
const readable = new ReadableStream({
310+
type: 'bytes',
311+
autoAllocateChunkSize: 16384,
314312

315-
readable = new ReadableStream({
316-
type: 'bytes',
317-
autoAllocateChunkSize: 16384,
313+
async pull(controller) {
314+
const view = controller.byobRequest.view;
315+
const { bytesRead } = await readFn(view, view.byteOffset, view.byteLength);
318316

319-
async pull(controller) {
320-
const view = controller.byobRequest.view;
321-
const { bytesRead } = await readFn(view, view.byteOffset, view.byteLength);
317+
if (bytesRead === 0) {
318+
ondone();
319+
controller.close();
320+
}
322321

323-
if (bytesRead === 0) {
324-
ondone();
325-
controller.close();
326-
}
322+
controller.byobRequest.respond(bytesRead);
323+
},
327324

328-
controller.byobRequest.respond(bytesRead);
329-
},
325+
cancel() {
326+
ondone();
327+
},
328+
});
330329

331-
cancel() {
332-
ondone();
333-
},
334-
});
335-
}
336330

337331
const {
338332
readableStreamCancel,

test/parallel/test-filehandle-readablestream.js

+10-53
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ const check = readFileSync(__filename, { encoding: 'utf8' });
8787
await file.close();
8888
})().then(common.mustCall());
8989

90-
// Make sure 'bytes' stream works
90+
// Make sure 'byob' reader works
9191
(async () => {
9292
const file = await open(__filename);
9393
const dec = new TextDecoder();
94-
const readable = file.readableWebStream({ type: 'bytes' });
94+
const readable = file.readableWebStream();
9595
const reader = readable.getReader({ mode: 'byob' });
9696

9797
let data = '';
@@ -114,59 +114,16 @@ const check = readFileSync(__filename, { encoding: 'utf8' });
114114
await file.close();
115115
})().then(common.mustCall());
116116

117-
// Make sure that acquiring a ReadableStream 'bytes' stream
118-
// fails if the FileHandle is already closed.
119-
(async () => {
120-
const file = await open(__filename);
121-
await file.close();
122-
123-
assert.throws(() => file.readableWebStream({ type: 'bytes' }), {
124-
code: 'ERR_INVALID_STATE',
125-
});
126-
})().then(common.mustCall());
127-
128-
// Make sure that acquiring a ReadableStream 'bytes' stream
129-
// fails if the FileHandle is already closing.
130-
(async () => {
131-
const file = await open(__filename);
132-
file.close();
133-
134-
assert.throws(() => file.readableWebStream({ type: 'bytes' }), {
135-
code: 'ERR_INVALID_STATE',
136-
});
137-
})().then(common.mustCall());
138-
139-
// Make sure the 'bytes' ReadableStream is closed when the underlying
140-
// FileHandle is closed.
141-
(async () => {
142-
const file = await open(__filename);
143-
const readable = file.readableWebStream({ type: 'bytes' });
144-
const reader = readable.getReader({ mode: 'byob' });
145-
file.close();
146-
await reader.closed;
147-
})().then(common.mustCall());
148-
149-
// Make sure the 'bytes' ReadableStream is closed when the underlying
150-
// FileHandle is closed.
151-
(async () => {
152-
const file = await open(__filename);
153-
const readable = file.readableWebStream({ type: 'bytes' });
154-
file.close();
155-
const reader = readable.getReader({ mode: 'byob' });
156-
await reader.closed;
157-
})().then(common.mustCall());
158-
159-
// Make sure that the FileHandle is properly marked "in use"
160-
// when a 'bytes' ReadableStream has been acquired for it.
117+
// Make sure a warning is logged if a non-'bytes' type is passed.
161118
(async () => {
162119
const file = await open(__filename);
163-
file.readableWebStream({ type: 'bytes' });
164-
const mc = new MessageChannel();
165-
mc.port1.onmessage = common.mustNotCall();
166-
assert.throws(() => mc.port2.postMessage(file, [file]), {
167-
code: 25,
168-
name: 'DataCloneError',
120+
common.expectWarning({
121+
ExperimentalWarning: [
122+
'A non-"bytes" type option to readableWebStream() no longer does ' +
123+
'anything. readableWebStream() always creates a byte-oriented stream ' +
124+
'now.',
125+
],
169126
});
170-
mc.port1.close();
127+
file.readableWebStream({ type: 'foobar' });
171128
await file.close();
172129
})().then(common.mustCall());

0 commit comments

Comments
 (0)