Skip to content

Commit 784d84c

Browse files
LiviaMedeirosRafaelGSS
authored andcommitted
fs: add read(buffer[, options]) versions
This adds the following: - `fs.read(fd, buffer[, options], callback)`. - `filehandle.read(buffer[, options])`. PR-URL: #42768 Refs: #42601 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 68ed3c8 commit 784d84c

6 files changed

+151
-54
lines changed

doc/api/fs.md

+49
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,33 @@ Reads data from the file and stores that in the given buffer.
417417
If the file is not modified concurrently, the end-of-file is reached when the
418418
number of bytes read is zero.
419419
420+
#### `filehandle.read(buffer[, options])`
421+
422+
<!-- YAML
423+
added: REPLACEME
424+
-->
425+
426+
* `buffer` {Buffer|TypedArray|DataView} A buffer that will be filled with the
427+
file data read.
428+
* `options` {Object}
429+
* `offset` {integer} The location in the buffer at which to start filling.
430+
**Default:** `0`
431+
* `length` {integer} The number of bytes to read. **Default:**
432+
`buffer.byteLength - offset`
433+
* `position` {integer} The location where to begin reading data from the
434+
file. If `null`, data will be read from the current file position, and
435+
the position will be updated. If `position` is an integer, the current
436+
file position will remain unchanged. **Default:**: `null`
437+
* Returns: {Promise} Fulfills upon success with an object with two properties:
438+
* `bytesRead` {integer} The number of bytes read
439+
* `buffer` {Buffer|TypedArray|DataView} A reference to the passed in `buffer`
440+
argument.
441+
442+
Reads data from the file and stores that in the given buffer.
443+
444+
If the file is not modified concurrently, the end-of-file is reached when the
445+
number of bytes read is zero.
446+
420447
#### `filehandle.readableWebStream()`
421448
422449
<!-- YAML
@@ -3243,6 +3270,28 @@ Similar to the [`fs.read()`][] function, this version takes an optional
32433270
`options` object. If no `options` object is specified, it will default with the
32443271
above values.
32453272
3273+
### `fs.read(fd, buffer[, options], callback)`
3274+
3275+
<!-- YAML
3276+
added: REPLACEME
3277+
-->
3278+
3279+
* `fd` {integer}
3280+
* `buffer` {Buffer|TypedArray|DataView} The buffer that the data will be
3281+
written to.
3282+
* `options` {Object}
3283+
* `offset` {integer} **Default:** `0`
3284+
* `length` {integer} **Default:** `buffer.byteLength - offset`
3285+
* `position` {integer|bigint} **Default:** `null`
3286+
* `callback` {Function}
3287+
* `err` {Error}
3288+
* `bytesRead` {integer}
3289+
* `buffer` {Buffer}
3290+
3291+
Similar to the [`fs.read()`][] function, this version takes an optional
3292+
`options` object. If no `options` object is specified, it will default with the
3293+
above values.
3294+
32463295
### `fs.readdir(path[, options], callback)`
32473296
32483297
<!-- YAML

lib/fs.js

+22-15
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ const {
136136
validateEncoding,
137137
validateFunction,
138138
validateInteger,
139+
validateObject,
139140
validateString,
140141
} = require('internal/validators');
141142

@@ -595,7 +596,7 @@ function openSync(path, flags, mode) {
595596
* Reads file from the specified `fd` (file descriptor).
596597
* @param {number} fd
597598
* @param {Buffer | TypedArray | DataView} buffer
598-
* @param {number} offset
599+
* @param {number} offsetOrOptions
599600
* @param {number} length
600601
* @param {number | bigint | null} position
601602
* @param {(
@@ -605,30 +606,36 @@ function openSync(path, flags, mode) {
605606
* ) => any} callback
606607
* @returns {void}
607608
*/
608-
function read(fd, buffer, offset, length, position, callback) {
609+
function read(fd, buffer, offsetOrOptions, length, position, callback) {
609610
fd = getValidatedFd(fd);
610611

611-
if (arguments.length <= 3) {
612-
// Assume fs.read(fd, options, callback)
613-
let options = ObjectCreate(null);
614-
if (arguments.length < 3) {
612+
let offset = offsetOrOptions;
613+
let params = null;
614+
if (arguments.length <= 4) {
615+
if (arguments.length === 4) {
616+
// This is fs.read(fd, buffer, options, callback)
617+
validateObject(offsetOrOptions, 'options', { nullable: true });
618+
callback = length;
619+
params = offsetOrOptions;
620+
} else if (arguments.length === 3) {
621+
// This is fs.read(fd, bufferOrParams, callback)
622+
if (!isArrayBufferView(buffer)) {
623+
// This is fs.read(fd, params, callback)
624+
params = buffer;
625+
({ buffer = Buffer.alloc(16384) } = params ?? ObjectCreate(null));
626+
}
627+
callback = offsetOrOptions;
628+
} else {
615629
// This is fs.read(fd, callback)
616-
// buffer will be the callback
617630
callback = buffer;
618-
} else {
619-
// This is fs.read(fd, {}, callback)
620-
// buffer will be the options object
621-
// offset is the callback
622-
options = buffer;
623-
callback = offset;
631+
buffer = Buffer.alloc(16384);
624632
}
625633

626634
({
627-
buffer = Buffer.alloc(16384),
628635
offset = 0,
629636
length = buffer.byteLength - offset,
630637
position = null
631-
} = options);
638+
} = params ?? ObjectCreate(null));
632639
}
633640

634641
validateBuffer(buffer);

lib/internal/fs/promises.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -508,20 +508,29 @@ async function open(path, flags, mode) {
508508
flagsNumber, mode, kUsePromises));
509509
}
510510

511-
async function read(handle, bufferOrOptions, offset, length, position) {
512-
let buffer = bufferOrOptions;
511+
async function read(handle, bufferOrParams, offset, length, position) {
512+
let buffer = bufferOrParams;
513513
if (!isArrayBufferView(buffer)) {
514-
bufferOrOptions ??= ObjectCreate(null);
514+
// This is fh.read(params)
515515
({
516516
buffer = Buffer.alloc(16384),
517517
offset = 0,
518518
length = buffer.byteLength - offset,
519519
position = null
520-
} = bufferOrOptions);
520+
} = bufferOrParams ?? ObjectCreate(null));
521521

522522
validateBuffer(buffer);
523523
}
524524

525+
if (offset !== null && typeof offset === 'object') {
526+
// This is fh.read(buffer, options)
527+
({
528+
offset = 0,
529+
length = buffer.byteLength - offset,
530+
position = null
531+
} = offset ?? ObjectCreate(null));
532+
}
533+
525534
if (offset == null) {
526535
offset = 0;
527536
} else {

test/parallel/test-fs-read-offset-null.js

+33-11
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,51 @@ const filepath = fixtures.path('x.txt');
1414
const buf = Buffer.alloc(1);
1515
// Reading only one character, hence buffer of one byte is enough.
1616

17-
// Test for callback API.
17+
// Tests are done by making sure the first letter in buffer is
18+
// same as first letter in file.
19+
// 120 is the ascii code of letter x.
20+
21+
// Tests for callback API.
1822
fs.open(filepath, 'r', common.mustSucceed((fd) => {
1923
fs.read(fd, { offset: null, buffer: buf },
2024
common.mustSucceed((bytesRead, buffer) => {
21-
// Test is done by making sure the first letter in buffer is
22-
// same as first letter in file.
23-
// 120 is the hex for ascii code of letter x.
25+
assert.strictEqual(buffer[0], 120);
26+
fs.close(fd, common.mustSucceed(() => {}));
27+
}));
28+
}));
29+
30+
fs.open(filepath, 'r', common.mustSucceed((fd) => {
31+
fs.read(fd, buf, { offset: null },
32+
common.mustSucceed((bytesRead, buffer) => {
2433
assert.strictEqual(buffer[0], 120);
2534
fs.close(fd, common.mustSucceed(() => {}));
2635
}));
2736
}));
2837

2938
let filehandle = null;
3039

31-
// Test for promise api
40+
// Tests for promises api
41+
(async () => {
42+
filehandle = await fsPromises.open(filepath, 'r');
43+
const readObject = await filehandle.read(buf, { offset: null });
44+
assert.strictEqual(readObject.buffer[0], 120);
45+
})()
46+
.finally(() => filehandle?.close())
47+
.then(common.mustCall());
48+
49+
// Undocumented: omitted position works the same as position === null
3250
(async () => {
3351
filehandle = await fsPromises.open(filepath, 'r');
3452
const readObject = await filehandle.read(buf, null, buf.length);
3553
assert.strictEqual(readObject.buffer[0], 120);
3654
})()
37-
.then(common.mustCall())
38-
.finally(async () => {
39-
// Close the file handle if it is opened
40-
if (filehandle)
41-
await filehandle.close();
42-
});
55+
.finally(() => filehandle?.close())
56+
.then(common.mustCall());
57+
58+
(async () => {
59+
filehandle = await fsPromises.open(filepath, 'r');
60+
const readObject = await filehandle.read(buf, null, buf.length, 0);
61+
assert.strictEqual(readObject.buffer[0], 120);
62+
})()
63+
.finally(() => filehandle?.close())
64+
.then(common.mustCall());

test/parallel/test-fs-read-optional-params.js

+24-22
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,34 @@ const fixtures = require('../common/fixtures');
55
const fs = require('fs');
66
const assert = require('assert');
77
const filepath = fixtures.path('x.txt');
8-
const fd = fs.openSync(filepath, 'r');
98

109
const expected = Buffer.from('xyz\n');
1110
const defaultBufferAsync = Buffer.alloc(16384);
12-
const bufferAsOption = Buffer.allocUnsafe(expected.length);
11+
const bufferAsOption = Buffer.allocUnsafe(expected.byteLength);
1312

14-
// Test not passing in any options object
15-
fs.read(fd, common.mustCall((err, bytesRead, buffer) => {
16-
assert.strictEqual(bytesRead, expected.length);
17-
assert.deepStrictEqual(defaultBufferAsync.length, buffer.length);
18-
}));
13+
function testValid(message, ...options) {
14+
const paramsMsg = `${message} (as params)`;
15+
const paramsFilehandle = fs.openSync(filepath, 'r');
16+
fs.read(paramsFilehandle, ...options, common.mustSucceed((bytesRead, buffer) => {
17+
assert.strictEqual(bytesRead, expected.byteLength, paramsMsg);
18+
assert.deepStrictEqual(defaultBufferAsync.byteLength, buffer.byteLength, paramsMsg);
19+
fs.closeSync(paramsFilehandle);
20+
}));
1921

20-
// Test passing in an empty options object
21-
fs.read(fd, { position: 0 }, common.mustCall((err, bytesRead, buffer) => {
22-
assert.strictEqual(bytesRead, expected.length);
23-
assert.deepStrictEqual(defaultBufferAsync.length, buffer.length);
24-
}));
22+
const optionsMsg = `${message} (as options)`;
23+
const optionsFilehandle = fs.openSync(filepath, 'r');
24+
fs.read(optionsFilehandle, bufferAsOption, ...options, common.mustSucceed((bytesRead, buffer) => {
25+
assert.strictEqual(bytesRead, expected.byteLength, optionsMsg);
26+
assert.deepStrictEqual(bufferAsOption.byteLength, buffer.byteLength, optionsMsg);
27+
fs.closeSync(optionsFilehandle);
28+
}));
29+
}
2530

26-
// Test passing in options
27-
fs.read(fd, {
28-
buffer: bufferAsOption,
31+
testValid('Not passing in any object');
32+
testValid('Passing in a null', null);
33+
testValid('Passing in an empty object', {});
34+
testValid('Passing in an object', {
2935
offset: 0,
30-
length: bufferAsOption.length,
31-
position: 0
32-
},
33-
common.mustCall((err, bytesRead, buffer) => {
34-
assert.strictEqual(bytesRead, expected.length);
35-
assert.deepStrictEqual(bufferAsOption.length, buffer.length);
36-
}));
36+
length: bufferAsOption.byteLength,
37+
position: 0,
38+
});

test/parallel/test-fs-read-promises-optional-params.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@ const fd = fs.openSync(filepath, 'r');
1010

1111
const expected = Buffer.from('xyz\n');
1212
const defaultBufferAsync = Buffer.alloc(16384);
13+
const bufferAsOption = Buffer.allocUnsafe(expected.byteLength);
1314

1415
read(fd, {})
1516
.then(function({ bytesRead, buffer }) {
16-
assert.strictEqual(bytesRead, expected.length);
17-
assert.deepStrictEqual(defaultBufferAsync.length, buffer.length);
17+
assert.strictEqual(bytesRead, expected.byteLength);
18+
assert.deepStrictEqual(defaultBufferAsync.byteLength, buffer.byteLength);
19+
})
20+
.then(common.mustCall());
21+
22+
read(fd, bufferAsOption, { position: 0 })
23+
.then(function({ bytesRead, buffer }) {
24+
assert.strictEqual(bytesRead, expected.byteLength);
25+
assert.deepStrictEqual(bufferAsOption.byteLength, buffer.byteLength);
1826
})
1927
.then(common.mustCall());

0 commit comments

Comments
 (0)