Skip to content

Commit 6601fac

Browse files
SheikhSajidtargos
authored andcommitted
fs: add fs.readv()
Fixes: #2298 PR-URL: #32356 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent cdf4f48 commit 6601fac

File tree

7 files changed

+410
-0
lines changed

7 files changed

+410
-0
lines changed

doc/api/fs.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3080,6 +3080,42 @@ Returns the number of `bytesRead`.
30803080
For detailed information, see the documentation of the asynchronous version of
30813081
this API: [`fs.read()`][].
30823082

3083+
## `fs.readv(fd, buffers[, position], callback)`
3084+
<!-- YAML
3085+
added: REPLACEME
3086+
-->
3087+
3088+
* `fd` {integer}
3089+
* `buffers` {ArrayBufferView[]}
3090+
* `position` {integer}
3091+
* `callback` {Function}
3092+
* `err` {Error}
3093+
* `bytesRead` {integer}
3094+
* `buffers` {ArrayBufferView[]}
3095+
3096+
Read from a file specified by `fd` and write to an array of `ArrayBufferView`s
3097+
using `readv()`.
3098+
3099+
`position` is the offset from the beginning of the file from where data
3100+
should be read. If `typeof position !== 'number'`, the data will be read
3101+
from the current position.
3102+
3103+
The callback will be given three arguments: `err`, `bytesRead`, and
3104+
`buffers`. `bytesRead` is how many bytes were read from the file.
3105+
3106+
## `fs.readvSync(fd, buffers[, position])`
3107+
<!-- YAML
3108+
added: REPLACEME
3109+
-->
3110+
3111+
* `fd` {integer}
3112+
* `buffers` {ArrayBufferView[]}
3113+
* `position` {integer}
3114+
* Returns: {number} The number of bytes read.
3115+
3116+
For detailed information, see the documentation of the asynchronous version of
3117+
this API: [`fs.readv()`][].
3118+
30833119
## `fs.realpath(path[, options], callback)`
30843120
<!-- YAML
30853121
added: v0.1.31
@@ -4430,6 +4466,25 @@ If one or more `filehandle.read()` calls are made on a file handle and then a
44304466
position till the end of the file. It doesn't always read from the beginning
44314467
of the file.
44324468

4469+
#### `filehandle.readv(buffers[, position])`
4470+
<!-- YAML
4471+
added: REPLACEME
4472+
-->
4473+
4474+
* `buffers` {ArrayBufferView[]}
4475+
* `position` {integer}
4476+
* Returns: {Promise}
4477+
4478+
Read from a file and write to an array of `ArrayBufferView`s
4479+
4480+
The `Promise` is resolved with an object containing a `bytesRead` property
4481+
identifying the number of bytes read, and a `buffers` property containing
4482+
a reference to the `buffers` input.
4483+
4484+
`position` is the offset from the beginning of the file where this data
4485+
should be read from. If `typeof position !== 'number'`, the data will be read
4486+
from the current position.
4487+
44334488
#### `filehandle.stat([options])`
44344489
<!-- YAML
44354490
added: v10.0.0
@@ -5623,6 +5678,7 @@ the file contents.
56235678
[`fs.readFileSync()`]: #fs_fs_readfilesync_path_options
56245679
[`fs.readdir()`]: #fs_fs_readdir_path_options_callback
56255680
[`fs.readdirSync()`]: #fs_fs_readdirsync_path_options
5681+
[`fs.readv()`]: #fs_fs_readv_fd_buffers_position_callback
56265682
[`fs.realpath()`]: #fs_fs_realpath_path_options_callback
56275683
[`fs.rmdir()`]: #fs_fs_rmdir_path_options_callback
56285684
[`fs.stat()`]: #fs_fs_stat_path_options_callback

lib/fs.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,39 @@ function readSync(fd, buffer, offset, length, position) {
551551
return result;
552552
}
553553

554+
function readv(fd, buffers, position, callback) {
555+
function wrapper(err, read) {
556+
callback(err, read || 0, buffers);
557+
}
558+
559+
validateInt32(fd, 'fd', /* min */ 0);
560+
validateBufferArray(buffers);
561+
562+
const req = new FSReqCallback();
563+
req.oncomplete = wrapper;
564+
565+
callback = maybeCallback(callback || position);
566+
567+
if (typeof position !== 'number')
568+
position = null;
569+
570+
return binding.readBuffers(fd, buffers, position, req);
571+
}
572+
573+
function readvSync(fd, buffers, position) {
574+
validateInt32(fd, 'fd', 0);
575+
validateBufferArray(buffers);
576+
577+
const ctx = {};
578+
579+
if (typeof position !== 'number')
580+
position = null;
581+
582+
const result = binding.readBuffers(fd, buffers, position, undefined, ctx);
583+
handleErrorFromBinding(ctx);
584+
return result;
585+
}
586+
554587
// usage:
555588
// fs.write(fd, buffer[, offset[, length[, position]]], callback);
556589
// OR
@@ -1897,6 +1930,8 @@ module.exports = fs = {
18971930
readdirSync,
18981931
read,
18991932
readSync,
1933+
readv,
1934+
readvSync,
19001935
readFile,
19011936
readFileSync,
19021937
readlink,

lib/internal/fs/promises.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ class FileHandle {
9595
return read(this, buffer, offset, length, position);
9696
}
9797

98+
readv(buffers, position) {
99+
return readv(this, buffers, position);
100+
}
101+
98102
readFile(options) {
99103
return readFile(this, options);
100104
}
@@ -244,6 +248,18 @@ async function read(handle, buffer, offset, length, position) {
244248
return { bytesRead, buffer };
245249
}
246250

251+
async function readv(handle, buffers, position) {
252+
validateFileHandle(handle);
253+
validateBufferArray(buffers);
254+
255+
if (typeof position !== 'number')
256+
position = null;
257+
258+
const bytesRead = (await binding.readBuffers(handle.fd, buffers, position,
259+
kUsePromises)) || 0;
260+
return { bytesRead, buffers };
261+
}
262+
247263
async function write(handle, buffer, offset, length, position) {
248264
validateFileHandle(handle);
249265

src/node_file.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,6 +1959,52 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
19591959
}
19601960

19611961

1962+
// Wrapper for readv(2).
1963+
//
1964+
// bytesRead = fs.readv(fd, buffers[, position], callback)
1965+
// 0 fd integer. file descriptor
1966+
// 1 buffers array of buffers to read
1967+
// 2 position if integer, position to read at in the file.
1968+
// if null, read from the current position
1969+
static void ReadBuffers(const FunctionCallbackInfo<Value>& args) {
1970+
Environment* env = Environment::GetCurrent(args);
1971+
1972+
const int argc = args.Length();
1973+
CHECK_GE(argc, 3);
1974+
1975+
CHECK(args[0]->IsInt32());
1976+
const int fd = args[0].As<Int32>()->Value();
1977+
1978+
CHECK(args[1]->IsArray());
1979+
Local<Array> buffers = args[1].As<Array>();
1980+
1981+
int64_t pos = GetOffset(args[2]); // -1 if not a valid JS int
1982+
1983+
MaybeStackBuffer<uv_buf_t> iovs(buffers->Length());
1984+
1985+
// Init uv buffers from ArrayBufferViews
1986+
for (uint32_t i = 0; i < iovs.length(); i++) {
1987+
Local<Value> buffer = buffers->Get(env->context(), i).ToLocalChecked();
1988+
CHECK(Buffer::HasInstance(buffer));
1989+
iovs[i] = uv_buf_init(Buffer::Data(buffer), Buffer::Length(buffer));
1990+
}
1991+
1992+
FSReqBase* req_wrap_async = GetReqWrap(env, args[3]);
1993+
if (req_wrap_async != nullptr) { // readBuffers(fd, buffers, pos, req)
1994+
AsyncCall(env, req_wrap_async, args, "read", UTF8, AfterInteger,
1995+
uv_fs_read, fd, *iovs, iovs.length(), pos);
1996+
} else { // readBuffers(fd, buffers, undefined, ctx)
1997+
CHECK_EQ(argc, 5);
1998+
FSReqWrapSync req_wrap_sync;
1999+
FS_SYNC_TRACE_BEGIN(read);
2000+
int bytesRead = SyncCall(env, /* ctx */ args[4], &req_wrap_sync, "read",
2001+
uv_fs_read, fd, *iovs, iovs.length(), pos);
2002+
FS_SYNC_TRACE_END(read, "bytesRead", bytesRead);
2003+
args.GetReturnValue().Set(bytesRead);
2004+
}
2005+
}
2006+
2007+
19622008
/* fs.chmod(path, mode);
19632009
* Wrapper for chmod(1) / EIO_CHMOD
19642010
*/
@@ -2222,6 +2268,7 @@ void Initialize(Local<Object> target,
22222268
env->SetMethod(target, "open", Open);
22232269
env->SetMethod(target, "openFileHandle", OpenFileHandle);
22242270
env->SetMethod(target, "read", Read);
2271+
env->SetMethod(target, "readBuffers", ReadBuffers);
22252272
env->SetMethod(target, "fdatasync", Fdatasync);
22262273
env->SetMethod(target, "fsync", Fsync);
22272274
env->SetMethod(target, "rename", Rename);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
const path = require('path');
6+
const fs = require('fs').promises;
7+
const tmpdir = require('../common/tmpdir');
8+
9+
tmpdir.refresh();
10+
11+
const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف';
12+
const exptectedBuff = Buffer.from(expected);
13+
14+
let cnt = 0;
15+
function getFileName() {
16+
return path.join(tmpdir.path, `readv_promises_${++cnt}.txt`);
17+
}
18+
19+
const allocateEmptyBuffers = (combinedLength) => {
20+
const bufferArr = [];
21+
// Allocate two buffers, each half the size of exptectedBuff
22+
bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)),
23+
bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length);
24+
25+
return bufferArr;
26+
};
27+
28+
(async () => {
29+
{
30+
const filename = getFileName();
31+
await fs.writeFile(filename, exptectedBuff);
32+
const handle = await fs.open(filename, 'r');
33+
// const buffer = Buffer.from(expected);
34+
const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
35+
const expectedLength = exptectedBuff.length;
36+
37+
let { bytesRead, buffers } = await handle.readv([Buffer.from('')],
38+
null);
39+
assert.deepStrictEqual(bytesRead, 0);
40+
assert.deepStrictEqual(buffers, [Buffer.from('')]);
41+
42+
({ bytesRead, buffers } = await handle.readv(bufferArr, null));
43+
assert.deepStrictEqual(bytesRead, expectedLength);
44+
assert.deepStrictEqual(buffers, bufferArr);
45+
assert(Buffer.concat(bufferArr).equals(await fs.readFile(filename)));
46+
handle.close();
47+
}
48+
49+
{
50+
const filename = getFileName();
51+
await fs.writeFile(filename, exptectedBuff);
52+
const handle = await fs.open(filename, 'r');
53+
// const buffer = Buffer.from(expected);
54+
const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
55+
const expectedLength = exptectedBuff.length;
56+
57+
let { bytesRead, buffers } = await handle.readv([Buffer.from('')]);
58+
assert.deepStrictEqual(bytesRead, 0);
59+
assert.deepStrictEqual(buffers, [Buffer.from('')]);
60+
61+
({ bytesRead, buffers } = await handle.readv(bufferArr));
62+
assert.deepStrictEqual(bytesRead, expectedLength);
63+
assert.deepStrictEqual(buffers, bufferArr);
64+
assert(Buffer.concat(bufferArr).equals(await fs.readFile(filename)));
65+
handle.close();
66+
}
67+
})();

test/parallel/test-fs-readv-sync.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
const fs = require('fs');
6+
const tmpdir = require('../common/tmpdir');
7+
8+
tmpdir.refresh();
9+
10+
const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف';
11+
12+
const exptectedBuff = Buffer.from(expected);
13+
const expectedLength = exptectedBuff.length;
14+
15+
const filename = 'readv_sync.txt';
16+
fs.writeFileSync(filename, exptectedBuff);
17+
18+
const allocateEmptyBuffers = (combinedLength) => {
19+
const bufferArr = [];
20+
// Allocate two buffers, each half the size of exptectedBuff
21+
bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)),
22+
bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length);
23+
24+
return bufferArr;
25+
};
26+
27+
// fs.readvSync with array of buffers with all parameters
28+
{
29+
const fd = fs.openSync(filename, 'r');
30+
31+
const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
32+
33+
let read = fs.readvSync(fd, [Buffer.from('')], 0);
34+
assert.deepStrictEqual(read, 0);
35+
36+
read = fs.readvSync(fd, bufferArr, 0);
37+
assert.deepStrictEqual(read, expectedLength);
38+
39+
fs.closeSync(fd);
40+
41+
assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename)));
42+
}
43+
44+
// fs.readvSync with array of buffers without position
45+
{
46+
const fd = fs.openSync(filename, 'r');
47+
48+
const bufferArr = allocateEmptyBuffers(exptectedBuff.length);
49+
50+
let read = fs.readvSync(fd, [Buffer.from('')]);
51+
assert.deepStrictEqual(read, 0);
52+
53+
read = fs.readvSync(fd, bufferArr);
54+
assert.deepStrictEqual(read, expectedLength);
55+
56+
fs.closeSync(fd);
57+
58+
assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename)));
59+
}
60+
61+
/**
62+
* Testing with incorrect arguments
63+
*/
64+
const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined];
65+
66+
{
67+
const fd = fs.openSync(filename, 'r');
68+
69+
wrongInputs.forEach((wrongInput) => {
70+
assert.throws(
71+
() => fs.readvSync(fd, wrongInput, null), {
72+
code: 'ERR_INVALID_ARG_TYPE',
73+
name: 'TypeError'
74+
}
75+
);
76+
});
77+
78+
fs.closeSync(fd);
79+
}
80+
81+
{
82+
// fs.readv with wrong fd argument
83+
wrongInputs.forEach((wrongInput) => {
84+
assert.throws(
85+
() => fs.readvSync(wrongInput),
86+
{
87+
code: 'ERR_INVALID_ARG_TYPE',
88+
name: 'TypeError'
89+
}
90+
);
91+
});
92+
}

0 commit comments

Comments
 (0)