Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSk Sajidul Kadir <sheikh.sajid522@gmail.com>2020-03-16 16:50:27 +0300
committerAnna Henningsen <anna@addaleax.net>2020-03-30 11:18:58 +0300
commit97ef4d76c96c070aad76bd3b6d5b89f0f03e458c (patch)
tree72370490b9cb49293258f93194c9cdc1307e07ee
parent89ae1f1b73451ed40fa78d379e66d5c061f04548 (diff)
fs: add fs.readv()
Fixes: https://github.com/nodejs/node/issues/2298 PR-URL: https://github.com/nodejs/node/pull/32356 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
-rw-r--r--doc/api/fs.md56
-rw-r--r--lib/fs.js35
-rw-r--r--lib/internal/fs/promises.js16
-rw-r--r--src/node_file.cc47
-rw-r--r--test/parallel/test-fs-readv-promises.js67
-rw-r--r--test/parallel/test-fs-readv-sync.js92
-rw-r--r--test/parallel/test-fs-readv.js97
7 files changed, 410 insertions, 0 deletions
diff --git a/doc/api/fs.md b/doc/api/fs.md
index 7c3f7d2204f..5e6cfb31786 100644
--- a/doc/api/fs.md
+++ b/doc/api/fs.md
@@ -3069,6 +3069,42 @@ Returns the number of `bytesRead`.
For detailed information, see the documentation of the asynchronous version of
this API: [`fs.read()`][].
+## `fs.readv(fd, buffers[, position], callback)`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `fd` {integer}
+* `buffers` {ArrayBufferView[]}
+* `position` {integer}
+* `callback` {Function}
+ * `err` {Error}
+ * `bytesRead` {integer}
+ * `buffers` {ArrayBufferView[]}
+
+Read from a file specified by `fd` and write to an array of `ArrayBufferView`s
+using `readv()`.
+
+`position` is the offset from the beginning of the file from where data
+should be read. If `typeof position !== 'number'`, the data will be read
+from the current position.
+
+The callback will be given three arguments: `err`, `bytesRead`, and
+`buffers`. `bytesRead` is how many bytes were read from the file.
+
+## `fs.readvSync(fd, buffers[, position])`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `fd` {integer}
+* `buffers` {ArrayBufferView[]}
+* `position` {integer}
+* Returns: {number} The number of bytes read.
+
+For detailed information, see the documentation of the asynchronous version of
+this API: [`fs.readv()`][].
+
## `fs.realpath(path[, options], callback)`
<!-- YAML
added: v0.1.31
@@ -4445,6 +4481,25 @@ If one or more `filehandle.read()` calls are made on a file handle and then a
position till the end of the file. It doesn't always read from the beginning
of the file.
+#### `filehandle.readv(buffers[, position])`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `buffers` {ArrayBufferView[]}
+* `position` {integer}
+* Returns: {Promise}
+
+Read from a file and write to an array of `ArrayBufferView`s
+
+The `Promise` is resolved with an object containing a `bytesRead` property
+identifying the number of bytes read, and a `buffers` property containing
+a reference to the `buffers` input.
+
+`position` is the offset from the beginning of the file where this data
+should be read from. If `typeof position !== 'number'`, the data will be read
+from the current position.
+
#### `filehandle.stat([options])`
<!-- YAML
added: v10.0.0
@@ -5655,6 +5710,7 @@ the file contents.
[`fs.readFileSync()`]: #fs_fs_readfilesync_path_options
[`fs.readdir()`]: #fs_fs_readdir_path_options_callback
[`fs.readdirSync()`]: #fs_fs_readdirsync_path_options
+[`fs.readv()`]: #fs_fs_readv_fd_buffers_position_callback
[`fs.realpath()`]: #fs_fs_realpath_path_options_callback
[`fs.rmdir()`]: #fs_fs_rmdir_path_options_callback
[`fs.stat()`]: #fs_fs_stat_path_options_callback
diff --git a/lib/fs.js b/lib/fs.js
index 81bfaee1fd5..29d0e1344b5 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -564,6 +564,39 @@ function readSync(fd, buffer, offset, length, position) {
return result;
}
+function readv(fd, buffers, position, callback) {
+ function wrapper(err, read) {
+ callback(err, read || 0, buffers);
+ }
+
+ validateInt32(fd, 'fd', /* min */ 0);
+ validateBufferArray(buffers);
+
+ const req = new FSReqCallback();
+ req.oncomplete = wrapper;
+
+ callback = maybeCallback(callback || position);
+
+ if (typeof position !== 'number')
+ position = null;
+
+ return binding.readBuffers(fd, buffers, position, req);
+}
+
+function readvSync(fd, buffers, position) {
+ validateInt32(fd, 'fd', 0);
+ validateBufferArray(buffers);
+
+ const ctx = {};
+
+ if (typeof position !== 'number')
+ position = null;
+
+ const result = binding.readBuffers(fd, buffers, position, undefined, ctx);
+ handleErrorFromBinding(ctx);
+ return result;
+}
+
// usage:
// fs.write(fd, buffer[, offset[, length[, position]]], callback);
// OR
@@ -1928,6 +1961,8 @@ module.exports = fs = {
readdirSync,
read,
readSync,
+ readv,
+ readvSync,
readFile,
readFileSync,
readlink,
diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js
index e75a7bc1911..d04e84aa9aa 100644
--- a/lib/internal/fs/promises.js
+++ b/lib/internal/fs/promises.js
@@ -101,6 +101,10 @@ class FileHandle {
return read(this, buffer, offset, length, position);
}
+ readv(buffers, position) {
+ return readv(this, buffers, position);
+ }
+
readFile(options) {
return readFile(this, options);
}
@@ -253,6 +257,18 @@ async function read(handle, buffer, offset, length, position) {
return { bytesRead, buffer };
}
+async function readv(handle, buffers, position) {
+ validateFileHandle(handle);
+ validateBufferArray(buffers);
+
+ if (typeof position !== 'number')
+ position = null;
+
+ const bytesRead = (await binding.readBuffers(handle.fd, buffers, position,
+ kUsePromises)) || 0;
+ return { bytesRead, buffers };
+}
+
async function write(handle, buffer, offset, length, position) {
validateFileHandle(handle);
diff --git a/src/node_file.cc b/src/node_file.cc
index 5fc8dba7d8d..d00595d1ca9 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -1976,6 +1976,52 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
}
+// Wrapper for readv(2).
+//
+// bytesRead = fs.readv(fd, buffers[, position], callback)
+// 0 fd integer. file descriptor
+// 1 buffers array of buffers to read
+// 2 position if integer, position to read at in the file.
+// if null, read from the current position
+static void ReadBuffers(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+
+ const int argc = args.Length();
+ CHECK_GE(argc, 3);
+
+ CHECK(args[0]->IsInt32());
+ const int fd = args[0].As<Int32>()->Value();
+
+ CHECK(args[1]->IsArray());
+ Local<Array> buffers = args[1].As<Array>();
+
+ int64_t pos = GetOffset(args[2]); // -1 if not a valid JS int
+
+ MaybeStackBuffer<uv_buf_t> iovs(buffers->Length());
+
+ // Init uv buffers from ArrayBufferViews
+ for (uint32_t i = 0; i < iovs.length(); i++) {
+ Local<Value> buffer = buffers->Get(env->context(), i).ToLocalChecked();
+ CHECK(Buffer::HasInstance(buffer));
+ iovs[i] = uv_buf_init(Buffer::Data(buffer), Buffer::Length(buffer));
+ }
+
+ FSReqBase* req_wrap_async = GetReqWrap(env, args[3]);
+ if (req_wrap_async != nullptr) { // readBuffers(fd, buffers, pos, req)
+ AsyncCall(env, req_wrap_async, args, "read", UTF8, AfterInteger,
+ uv_fs_read, fd, *iovs, iovs.length(), pos);
+ } else { // readBuffers(fd, buffers, undefined, ctx)
+ CHECK_EQ(argc, 5);
+ FSReqWrapSync req_wrap_sync;
+ FS_SYNC_TRACE_BEGIN(read);
+ int bytesRead = SyncCall(env, /* ctx */ args[4], &req_wrap_sync, "read",
+ uv_fs_read, fd, *iovs, iovs.length(), pos);
+ FS_SYNC_TRACE_END(read, "bytesRead", bytesRead);
+ args.GetReturnValue().Set(bytesRead);
+ }
+}
+
+
/* fs.chmod(path, mode);
* Wrapper for chmod(1) / EIO_CHMOD
*/
@@ -2239,6 +2285,7 @@ void Initialize(Local<Object> target,
env->SetMethod(target, "open", Open);
env->SetMethod(target, "openFileHandle", OpenFileHandle);
env->SetMethod(target, "read", Read);
+ env->SetMethod(target, "readBuffers", ReadBuffers);
env->SetMethod(target, "fdatasync", Fdatasync);
env->SetMethod(target, "fsync", Fsync);
env->SetMethod(target, "rename", Rename);
diff --git a/test/parallel/test-fs-readv-promises.js b/test/parallel/test-fs-readv-promises.js
new file mode 100644
index 00000000000..e0536505c91
--- /dev/null
+++ b/test/parallel/test-fs-readv-promises.js
@@ -0,0 +1,67 @@
+'use strict';
+
+require('../common');
+const assert = require('assert');
+const path = require('path');
+const fs = require('fs').promises;
+const tmpdir = require('../common/tmpdir');
+
+tmpdir.refresh();
+
+const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف';
+const exptectedBuff = Buffer.from(expected);
+
+let cnt = 0;
+function getFileName() {
+ return path.join(tmpdir.path, `readv_promises_${++cnt}.txt`);
+}
+
+const allocateEmptyBuffers = (combinedLength) => {
+ const bufferArr = [];
+ // Allocate two buffers, each half the size of exptectedBuff
+ bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)),
+ bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length);
+
+ return bufferArr;
+};
+
+(async () => {
+ {
+ const filename = getFileName();
+ await fs.writeFile(filename, exptectedBuff);
+ const handle = await fs.open(filename, 'r');
+ // const buffer = Buffer.from(expected);
+ const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
+ const expectedLength = exptectedBuff.length;
+
+ let { bytesRead, buffers } = await handle.readv([Buffer.from('')],
+ null);
+ assert.deepStrictEqual(bytesRead, 0);
+ assert.deepStrictEqual(buffers, [Buffer.from('')]);
+
+ ({ bytesRead, buffers } = await handle.readv(bufferArr, null));
+ assert.deepStrictEqual(bytesRead, expectedLength);
+ assert.deepStrictEqual(buffers, bufferArr);
+ assert(Buffer.concat(bufferArr).equals(await fs.readFile(filename)));
+ handle.close();
+ }
+
+ {
+ const filename = getFileName();
+ await fs.writeFile(filename, exptectedBuff);
+ const handle = await fs.open(filename, 'r');
+ // const buffer = Buffer.from(expected);
+ const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
+ const expectedLength = exptectedBuff.length;
+
+ let { bytesRead, buffers } = await handle.readv([Buffer.from('')]);
+ assert.deepStrictEqual(bytesRead, 0);
+ assert.deepStrictEqual(buffers, [Buffer.from('')]);
+
+ ({ bytesRead, buffers } = await handle.readv(bufferArr));
+ assert.deepStrictEqual(bytesRead, expectedLength);
+ assert.deepStrictEqual(buffers, bufferArr);
+ assert(Buffer.concat(bufferArr).equals(await fs.readFile(filename)));
+ handle.close();
+ }
+})();
diff --git a/test/parallel/test-fs-readv-sync.js b/test/parallel/test-fs-readv-sync.js
new file mode 100644
index 00000000000..f5bb448f920
--- /dev/null
+++ b/test/parallel/test-fs-readv-sync.js
@@ -0,0 +1,92 @@
+'use strict';
+
+require('../common');
+const assert = require('assert');
+const fs = require('fs');
+const tmpdir = require('../common/tmpdir');
+
+tmpdir.refresh();
+
+const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف';
+
+const exptectedBuff = Buffer.from(expected);
+const expectedLength = exptectedBuff.length;
+
+const filename = 'readv_sync.txt';
+fs.writeFileSync(filename, exptectedBuff);
+
+const allocateEmptyBuffers = (combinedLength) => {
+ const bufferArr = [];
+ // Allocate two buffers, each half the size of exptectedBuff
+ bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)),
+ bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length);
+
+ return bufferArr;
+};
+
+// fs.readvSync with array of buffers with all parameters
+{
+ const fd = fs.openSync(filename, 'r');
+
+ const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
+
+ let read = fs.readvSync(fd, [Buffer.from('')], 0);
+ assert.deepStrictEqual(read, 0);
+
+ read = fs.readvSync(fd, bufferArr, 0);
+ assert.deepStrictEqual(read, expectedLength);
+
+ fs.closeSync(fd);
+
+ assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename)));
+}
+
+// fs.readvSync with array of buffers without position
+{
+ const fd = fs.openSync(filename, 'r');
+
+ const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
+
+ let read = fs.readvSync(fd, [Buffer.from('')]);
+ assert.deepStrictEqual(read, 0);
+
+ read = fs.readvSync(fd, bufferArr);
+ assert.deepStrictEqual(read, expectedLength);
+
+ fs.closeSync(fd);
+
+ assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename)));
+}
+
+/**
+ * Testing with incorrect arguments
+ */
+const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined];
+
+{
+ const fd = fs.openSync(filename, 'r');
+
+ wrongInputs.forEach((wrongInput) => {
+ assert.throws(
+ () => fs.readvSync(fd, wrongInput, null), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError'
+ }
+ );
+ });
+
+ fs.closeSync(fd);
+}
+
+{
+ // fs.readv with wrong fd argument
+ wrongInputs.forEach((wrongInput) => {
+ assert.throws(
+ () => fs.readvSync(wrongInput),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError'
+ }
+ );
+ });
+}
diff --git a/test/parallel/test-fs-readv.js b/test/parallel/test-fs-readv.js
new file mode 100644
index 00000000000..1fa9d80f9f2
--- /dev/null
+++ b/test/parallel/test-fs-readv.js
@@ -0,0 +1,97 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+const path = require('path');
+const fs = require('fs');
+const tmpdir = require('../common/tmpdir');
+
+tmpdir.refresh();
+
+const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف';
+
+let cnt = 0;
+const getFileName = () => path.join(tmpdir.path, `readv_${++cnt}.txt`);
+const exptectedBuff = Buffer.from(expected);
+
+const allocateEmptyBuffers = (combinedLength) => {
+ const bufferArr = [];
+ // Allocate two buffers, each half the size of exptectedBuff
+ bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)),
+ bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length);
+
+ return bufferArr;
+};
+
+const getCallback = (fd, bufferArr) => {
+ return common.mustCall((err, bytesRead, buffers) => {
+ assert.ifError(err);
+
+ assert.deepStrictEqual(bufferArr, buffers);
+ const expectedLength = exptectedBuff.length;
+ assert.deepStrictEqual(bytesRead, expectedLength);
+ fs.closeSync(fd);
+
+ assert(Buffer.concat(bufferArr).equals(exptectedBuff));
+ });
+};
+
+// fs.readv with array of buffers with all parameters
+{
+ const filename = getFileName();
+ const fd = fs.openSync(filename, 'w+');
+ fs.writeSync(fd, exptectedBuff);
+
+ const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
+ const callback = getCallback(fd, bufferArr);
+
+ fs.readv(fd, bufferArr, 0, callback);
+}
+
+// fs.readv with array of buffers without position
+{
+ const filename = getFileName();
+ fs.writeFileSync(filename, exptectedBuff);
+ const fd = fs.openSync(filename, 'r');
+
+ const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
+ const callback = getCallback(fd, bufferArr);
+
+ fs.readv(fd, bufferArr, callback);
+}
+
+/**
+ * Testing with incorrect arguments
+ */
+const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined];
+
+{
+ const filename = getFileName(2);
+ fs.writeFileSync(filename, exptectedBuff);
+ const fd = fs.openSync(filename, 'r');
+
+
+ wrongInputs.forEach((wrongInput) => {
+ assert.throws(
+ () => fs.readv(fd, wrongInput, null, common.mustNotCall()), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError'
+ }
+ );
+ });
+
+ fs.closeSync(fd);
+}
+
+{
+ // fs.readv with wrong fd argument
+ wrongInputs.forEach((wrongInput) => {
+ assert.throws(
+ () => fs.readv(wrongInput, common.mustNotCall()),
+ {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError'
+ }
+ );
+ });
+}