Skip to content

Commit 3d7808e

Browse files
LiviaMedeirostargos
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 <[email protected]>
1 parent cc398a8 commit 3d7808e

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.readFile(options)`
421448
422449
<!-- YAML
@@ -3088,6 +3115,28 @@ Similar to the [`fs.read()`][] function, this version takes an optional
30883115
`options` object. If no `options` object is specified, it will default with the
30893116
above values.
30903117
3118+
### `fs.read(fd, buffer[, options], callback)`
3119+
3120+
<!-- YAML
3121+
added: REPLACEME
3122+
-->
3123+
3124+
* `fd` {integer}
3125+
* `buffer` {Buffer|TypedArray|DataView} The buffer that the data will be
3126+
written to.
3127+
* `options` {Object}
3128+
* `offset` {integer} **Default:** `0`
3129+
* `length` {integer} **Default:** `buffer.byteLength - offset`
3130+
* `position` {integer|bigint} **Default:** `null`
3131+
* `callback` {Function}
3132+
* `err` {Error}
3133+
* `bytesRead` {integer}
3134+
* `buffer` {Buffer}
3135+
3136+
Similar to the [`fs.read()`][] function, this version takes an optional
3137+
`options` object. If no `options` object is specified, it will default with the
3138+
above values.
3139+
30913140
### `fs.readdir(path[, options], callback)`
30923141
30933142
<!-- YAML

lib/fs.js

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

@@ -591,7 +592,7 @@ function openSync(path, flags, mode) {
591592
* Reads file from the specified `fd` (file descriptor).
592593
* @param {number} fd
593594
* @param {Buffer | TypedArray | DataView} buffer
594-
* @param {number} offset
595+
* @param {number} offsetOrOptions
595596
* @param {number} length
596597
* @param {number | bigint | null} position
597598
* @param {(
@@ -601,30 +602,36 @@ function openSync(path, flags, mode) {
601602
* ) => any} callback
602603
* @returns {void}
603604
*/
604-
function read(fd, buffer, offset, length, position, callback) {
605+
function read(fd, buffer, offsetOrOptions, length, position, callback) {
605606
fd = getValidatedFd(fd);
606607

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

622630
({
623-
buffer = Buffer.alloc(16384),
624631
offset = 0,
625632
length = buffer.byteLength - offset,
626633
position = null
627-
} = options);
634+
} = params ?? ObjectCreate(null));
628635
}
629636

630637
validateBuffer(buffer);

lib/internal/fs/promises.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -456,20 +456,29 @@ async function open(path, flags, mode) {
456456
flagsNumber, mode, kUsePromises));
457457
}
458458

459-
async function read(handle, bufferOrOptions, offset, length, position) {
460-
let buffer = bufferOrOptions;
459+
async function read(handle, bufferOrParams, offset, length, position) {
460+
let buffer = bufferOrParams;
461461
if (!isArrayBufferView(buffer)) {
462-
bufferOrOptions ??= ObjectCreate(null);
462+
// This is fh.read(params)
463463
({
464464
buffer = Buffer.alloc(16384),
465465
offset = 0,
466466
length = buffer.byteLength - offset,
467467
position = null
468-
} = bufferOrOptions);
468+
} = bufferOrParams ?? ObjectCreate(null));
469469

470470
validateBuffer(buffer);
471471
}
472472

473+
if (offset !== null && typeof offset === 'object') {
474+
// This is fh.read(buffer, options)
475+
({
476+
offset = 0,
477+
length = buffer.byteLength - offset,
478+
position = null
479+
} = offset ?? ObjectCreate(null));
480+
}
481+
473482
if (offset == null) {
474483
offset = 0;
475484
} 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)