fs: add read(buffer[, options]) versions

This adds the following:
- `fs.read(fd, buffer[, options], callback)`.
- `filehandle.read(buffer[, options])`.

PR-URL: https://github.com/nodejs/node/pull/42768
Refs: https://github.com/nodejs/node/pull/42601
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
Livia Medeiros 2022-05-03 02:01:27 +08:00 committed by GitHub
parent 6be94c9443
commit 57678e5581
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 152 additions and 55 deletions

View File

@ -417,6 +417,33 @@ Reads data from the file and stores that in the given buffer.
If the file is not modified concurrently, the end-of-file is reached when the
number of bytes read is zero.
#### `filehandle.read(buffer[, options])`
<!-- YAML
added: REPLACEME
-->
* `buffer` {Buffer|TypedArray|DataView} A buffer that will be filled with the
file data read.
* `options` {Object}
* `offset` {integer} The location in the buffer at which to start filling.
**Default:** `0`
* `length` {integer} The number of bytes to read. **Default:**
`buffer.byteLength - offset`
* `position` {integer} The location where to begin reading data from the
file. If `null`, data will be read from the current file position, and
the position will be updated. If `position` is an integer, the current
file position will remain unchanged. **Default:**: `null`
* Returns: {Promise} Fulfills upon success with an object with two properties:
* `bytesRead` {integer} The number of bytes read
* `buffer` {Buffer|TypedArray|DataView} A reference to the passed in `buffer`
argument.
Reads data from the file and stores that in the given buffer.
If the file is not modified concurrently, the end-of-file is reached when the
number of bytes read is zero.
#### `filehandle.readableWebStream()`
<!-- YAML
@ -3247,6 +3274,28 @@ Similar to the [`fs.read()`][] function, this version takes an optional
`options` object. If no `options` object is specified, it will default with the
above values.
### `fs.read(fd, buffer[, options], callback)`
<!-- YAML
added: REPLACEME
-->
* `fd` {integer}
* `buffer` {Buffer|TypedArray|DataView} The buffer that the data will be
written to.
* `options` {Object}
* `offset` {integer} **Default:** `0`
* `length` {integer} **Default:** `buffer.byteLength - offset`
* `position` {integer|bigint} **Default:** `null`
* `callback` {Function}
* `err` {Error}
* `bytesRead` {integer}
* `buffer` {Buffer}
Similar to the [`fs.read()`][] function, this version takes an optional
`options` object. If no `options` object is specified, it will default with the
above values.
### `fs.readdir(path[, options], callback)`
<!-- YAML

View File

@ -136,6 +136,7 @@ const {
validateEncoding,
validateFunction,
validateInteger,
validateObject,
validateString,
} = require('internal/validators');
@ -595,7 +596,7 @@ function openSync(path, flags, mode) {
* Reads file from the specified `fd` (file descriptor).
* @param {number} fd
* @param {Buffer | TypedArray | DataView} buffer
* @param {number} offset
* @param {number} offsetOrOptions
* @param {number} length
* @param {number | bigint | null} position
* @param {(
@ -605,30 +606,36 @@ function openSync(path, flags, mode) {
* ) => any} callback
* @returns {void}
*/
function read(fd, buffer, offset, length, position, callback) {
function read(fd, buffer, offsetOrOptions, length, position, callback) {
fd = getValidatedFd(fd);
if (arguments.length <= 3) {
// Assume fs.read(fd, options, callback)
let options = ObjectCreate(null);
if (arguments.length < 3) {
// This is fs.read(fd, callback)
// buffer will be the callback
callback = buffer;
let offset = offsetOrOptions;
let params = null;
if (arguments.length <= 4) {
if (arguments.length === 4) {
// This is fs.read(fd, buffer, options, callback)
validateObject(offsetOrOptions, 'options', { nullable: true });
callback = length;
params = offsetOrOptions;
} else if (arguments.length === 3) {
// This is fs.read(fd, bufferOrParams, callback)
if (!isArrayBufferView(buffer)) {
// This is fs.read(fd, params, callback)
params = buffer;
({ buffer = Buffer.alloc(16384) } = params ?? ObjectCreate(null));
}
callback = offsetOrOptions;
} else {
// This is fs.read(fd, {}, callback)
// buffer will be the options object
// offset is the callback
options = buffer;
callback = offset;
// This is fs.read(fd, callback)
callback = buffer;
buffer = Buffer.alloc(16384);
}
({
buffer = Buffer.alloc(16384),
offset = 0,
length = buffer.byteLength - offset,
position = null
} = options);
} = params ?? ObjectCreate(null));
}
validateBuffer(buffer);

View File

@ -508,20 +508,29 @@ async function open(path, flags, mode) {
flagsNumber, mode, kUsePromises));
}
async function read(handle, bufferOrOptions, offset, length, position) {
let buffer = bufferOrOptions;
async function read(handle, bufferOrParams, offset, length, position) {
let buffer = bufferOrParams;
if (!isArrayBufferView(buffer)) {
bufferOrOptions ??= ObjectCreate(null);
// This is fh.read(params)
({
buffer = Buffer.alloc(16384),
offset = 0,
length = buffer.byteLength - offset,
position = null
} = bufferOrOptions);
} = bufferOrParams ?? ObjectCreate(null));
validateBuffer(buffer);
}
if (offset !== null && typeof offset === 'object') {
// This is fh.read(buffer, options)
({
offset = 0,
length = buffer.byteLength - offset,
position = null
} = offset ?? ObjectCreate(null));
}
if (offset == null) {
offset = 0;
} else {

View File

@ -14,13 +14,22 @@ const filepath = fixtures.path('x.txt');
const buf = Buffer.alloc(1);
// Reading only one character, hence buffer of one byte is enough.
// Test for callback API.
// Tests are done by making sure the first letter in buffer is
// same as first letter in file.
// 120 is the ascii code of letter x.
// Tests for callback API.
fs.open(filepath, 'r', common.mustSucceed((fd) => {
fs.read(fd, { offset: null, buffer: buf },
common.mustSucceed((bytesRead, buffer) => {
// Test is done by making sure the first letter in buffer is
// same as first letter in file.
// 120 is the hex for ascii code of letter x.
assert.strictEqual(buffer[0], 120);
fs.close(fd, common.mustSucceed(() => {}));
}));
}));
fs.open(filepath, 'r', common.mustSucceed((fd) => {
fs.read(fd, buf, { offset: null },
common.mustSucceed((bytesRead, buffer) => {
assert.strictEqual(buffer[0], 120);
fs.close(fd, common.mustSucceed(() => {}));
}));
@ -28,15 +37,28 @@ fs.open(filepath, 'r', common.mustSucceed((fd) => {
let filehandle = null;
// Test for promise api
// Tests for promises api
(async () => {
filehandle = await fsPromises.open(filepath, 'r');
const readObject = await filehandle.read(buf, { offset: null });
assert.strictEqual(readObject.buffer[0], 120);
})()
.finally(() => filehandle?.close())
.then(common.mustCall());
// Undocumented: omitted position works the same as position === null
(async () => {
filehandle = await fsPromises.open(filepath, 'r');
const readObject = await filehandle.read(buf, null, buf.length);
assert.strictEqual(readObject.buffer[0], 120);
})()
.then(common.mustCall())
.finally(async () => {
// Close the file handle if it is opened
if (filehandle)
await filehandle.close();
});
.finally(() => filehandle?.close())
.then(common.mustCall());
(async () => {
filehandle = await fsPromises.open(filepath, 'r');
const readObject = await filehandle.read(buf, null, buf.length, 0);
assert.strictEqual(readObject.buffer[0], 120);
})()
.finally(() => filehandle?.close())
.then(common.mustCall());

View File

@ -5,32 +5,34 @@ const fixtures = require('../common/fixtures');
const fs = require('fs');
const assert = require('assert');
const filepath = fixtures.path('x.txt');
const fd = fs.openSync(filepath, 'r');
const expected = Buffer.from('xyz\n');
const defaultBufferAsync = Buffer.alloc(16384);
const bufferAsOption = Buffer.allocUnsafe(expected.length);
const bufferAsOption = Buffer.allocUnsafe(expected.byteLength);
// Test not passing in any options object
fs.read(fd, common.mustCall((err, bytesRead, buffer) => {
assert.strictEqual(bytesRead, expected.length);
assert.deepStrictEqual(defaultBufferAsync.length, buffer.length);
}));
function testValid(message, ...options) {
const paramsMsg = `${message} (as params)`;
const paramsFilehandle = fs.openSync(filepath, 'r');
fs.read(paramsFilehandle, ...options, common.mustSucceed((bytesRead, buffer) => {
assert.strictEqual(bytesRead, expected.byteLength, paramsMsg);
assert.deepStrictEqual(defaultBufferAsync.byteLength, buffer.byteLength, paramsMsg);
fs.closeSync(paramsFilehandle);
}));
// Test passing in an empty options object
fs.read(fd, { position: 0 }, common.mustCall((err, bytesRead, buffer) => {
assert.strictEqual(bytesRead, expected.length);
assert.deepStrictEqual(defaultBufferAsync.length, buffer.length);
}));
const optionsMsg = `${message} (as options)`;
const optionsFilehandle = fs.openSync(filepath, 'r');
fs.read(optionsFilehandle, bufferAsOption, ...options, common.mustSucceed((bytesRead, buffer) => {
assert.strictEqual(bytesRead, expected.byteLength, optionsMsg);
assert.deepStrictEqual(bufferAsOption.byteLength, buffer.byteLength, optionsMsg);
fs.closeSync(optionsFilehandle);
}));
}
// Test passing in options
fs.read(fd, {
buffer: bufferAsOption,
testValid('Not passing in any object');
testValid('Passing in a null', null);
testValid('Passing in an empty object', {});
testValid('Passing in an object', {
offset: 0,
length: bufferAsOption.length,
position: 0
},
common.mustCall((err, bytesRead, buffer) => {
assert.strictEqual(bytesRead, expected.length);
assert.deepStrictEqual(bufferAsOption.length, buffer.length);
}));
length: bufferAsOption.byteLength,
position: 0,
});

View File

@ -10,10 +10,18 @@ const fd = fs.openSync(filepath, 'r');
const expected = Buffer.from('xyz\n');
const defaultBufferAsync = Buffer.alloc(16384);
const bufferAsOption = Buffer.allocUnsafe(expected.byteLength);
read(fd, {})
.then(function({ bytesRead, buffer }) {
assert.strictEqual(bytesRead, expected.length);
assert.deepStrictEqual(defaultBufferAsync.length, buffer.length);
assert.strictEqual(bytesRead, expected.byteLength);
assert.deepStrictEqual(defaultBufferAsync.byteLength, buffer.byteLength);
})
.then(common.mustCall());
read(fd, bufferAsOption, { position: 0 })
.then(function({ bytesRead, buffer }) {
assert.strictEqual(bytesRead, expected.byteLength);
assert.deepStrictEqual(bufferAsOption.byteLength, buffer.byteLength);
})
.then(common.mustCall());