Skip to content

Commit ccaf7fe

Browse files
iskertargos
authored andcommitted
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, #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#54041. PR-URL: #55461 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 0c3ae25 commit ccaf7fe

3 files changed

Lines changed: 47 additions & 95 deletions

File tree

‎doc/api/fs.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -477,11 +477,14 @@ Reads data from the file and stores that in the given buffer.
477477
If the file is not modified concurrently, the end-of-file is reached when the
478478
number of bytes read is zero.
479479
480-
#### `filehandle.readableWebStream([options])`
480+
#### `filehandle.readableWebStream()`
481481
482482
<!-- YAML
483483
added: v17.0.0
484484
changes:
485+
- version: REPLACEME
486+
pr-url: https://github.com/nodejs/node/pull/55461
487+
description: Removed option to create a 'bytes' stream. Streams are now always 'bytes' streams.
485488
- version:
486489
- v20.0.0
487490
- v18.17.0
@@ -491,13 +494,10 @@ changes:
491494
492495
> Stability: 1 - Experimental
493496
494-
* `options` {Object}
495-
* `type` {string|undefined} Whether to open a normal or a `'bytes'` stream.
496-
**Default:** `undefined`
497-
498497
* Returns: {ReadableStream}
499498
500-
Returns a `ReadableStream` that may be used to read the files data.
499+
Returns a byte-oriented `ReadableStream` that may be used to read the file's
500+
contents.
501501
502502
An error will be thrown if this method is called more than once or is called
503503
after the `FileHandle` is closed or closing.

‎lib/internal/fs/promises.js‎

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ function lazyFsStreams() {
142142

143143
constlazyRimRaf=getLazy(()=>require('internal/fs/rimraf').rimrafPromises);
144144

145+
constlazyReadableStream=getLazy(()=>
146+
require('internal/webstreams/readablestream').ReadableStream,
147+
);
148+
145149
// By the time the C++ land creates an error for a promise rejection (likely from a
146150
// libuv callback), there is already no JS frames on the stack. So we need to
147151
// wait until V8 resumes execution back to JS land before we have enough information
@@ -276,12 +280,9 @@ class FileHandle extends EventEmitter {
276280
/**
277281
* @typedef {import('../webstreams/readablestream').ReadableStream
278282
* } ReadableStream
279-
* @param {{
280-
* type?: string;
281-
* }} [options]
282283
* @returns {ReadableStream}
283284
*/
284-
readableWebStream(options=kEmptyObject){
285+
readableWebStream(options={__proto__: null,type: 'bytes'}){
285286
if(this[kFd]===-1)
286287
thrownewERR_INVALID_STATE('The FileHandle is closed');
287288
if(this[kClosePromise])
@@ -293,46 +294,40 @@ class FileHandle extends EventEmitter {
293294
if(options.type!==undefined){
294295
validateString(options.type,'options.type');
295296
}
297+
if(options.type!=='bytes'){
298+
process.emitWarning(
299+
'A non-"bytes" options.type has no effect. A byte-oriented steam is '+
300+
'always created.',
301+
'ExperimentalWarning',
302+
);
303+
}
296304

297-
letreadable;
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+
constreadFn=FunctionPrototypeBind(this.read,this);
307+
constondone=FunctionPrototypeBind(this[kUnref],this);
311308

312-
constreadFn=FunctionPrototypeBind(this.read,this);
313-
constondone=FunctionPrototypeBind(this[kUnref],this);
309+
constReadableStream=lazyReadableStream();
310+
constreadable=newReadableStream({
311+
type: 'bytes',
312+
autoAllocateChunkSize: 16384,
314313

315-
readable=newReadableStream({
316-
type: 'bytes',
317-
autoAllocateChunkSize: 16384,
314+
asyncpull(controller){
315+
constview=controller.byobRequest.view;
316+
const{ bytesRead }=awaitreadFn(view,view.byteOffset,view.byteLength);
318317

319-
asyncpull(controller){
320-
constview=controller.byobRequest.view;
321-
const{ bytesRead }=awaitreadFn(view,view.byteOffset,view.byteLength);
318+
if(bytesRead===0){
319+
ondone();
320+
controller.close();
321+
}
322322

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

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

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

337332
const{
338333
readableStreamCancel,

‎test/parallel/test-filehandle-readablestream.js‎

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

90-
// Make sure 'bytes' stream works
90+
// Make sure 'byob' reader works
9191
(async()=>{
9292
constfile=awaitopen(__filename);
9393
constdec=newTextDecoder();
94-
constreadable=file.readableWebStream({type: 'bytes'});
94+
constreadable=file.readableWebStream();
9595
constreader=readable.getReader({mode: 'byob'});
9696

9797
letdata='';
@@ -114,59 +114,16 @@ const check = readFileSync(__filename, { encoding: 'utf8' });
114114
awaitfile.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-
constfile=awaitopen(__filename);
121-
awaitfile.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-
constfile=awaitopen(__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-
constfile=awaitopen(__filename);
143-
constreadable=file.readableWebStream({type: 'bytes'});
144-
constreader=readable.getReader({mode: 'byob'});
145-
file.close();
146-
awaitreader.closed;
147-
})().then(common.mustCall());
148-
149-
// Make sure the 'bytes' ReadableStream is closed when the underlying
150-
// FileHandle is closed.
151-
(async()=>{
152-
constfile=awaitopen(__filename);
153-
constreadable=file.readableWebStream({type: 'bytes'});
154-
file.close();
155-
constreader=readable.getReader({mode: 'byob'});
156-
awaitreader.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
constfile=awaitopen(__filename);
163-
file.readableWebStream({type: 'bytes'});
164-
constmc=newMessageChannel();
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" options.type has no effect. A byte-oriented steam is '+
123+
'always created.',
124+
],
169125
});
170-
mc.port1.close();
126+
conststream=file.readableWebStream({type: 'foobar'});
127+
awaitstream.cancel();
171128
awaitfile.close();
172129
})().then(common.mustCall());

0 commit comments

Comments
 (0)