GeezerVison is showing github.com — fs: add fs.readv() · nodejs/node@30d55a3
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/fs.md +56Lines changed: 56 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -3080,6 +3080,42 @@ Returns the number of `bytesRead`. | 3080 | 3080 | For detailed information, see the documentation of the asynchronous version of | 3081 | 3081 | this API: [`fs.read()`][]. | 3082 | 3082 |
| | 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 | + | 3083 | 3119 | ## `fs.realpath(path[, options], callback)` | 3084 | 3120 | <!-- YAML | 3085 | 3121 | added: v0.1.31 | @@ -4430,6 +4466,25 @@ If one or more `filehandle.read()` calls are made on a file handle and then a | 4430 | 4466 | position till the end of the file. It doesn't always read from the beginning | 4431 | 4467 | of the file. | 4432 | 4468 |
| | 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 | + | 4433 | 4488 | #### `filehandle.stat([options])` | 4434 | 4489 | <!-- YAML | 4435 | 4490 | added: v10.0.0 | @@ -5623,6 +5678,7 @@ the file contents. | 5623 | 5678 | [`fs.readFileSync()`]: #fs_fs_readfilesync_path_options | 5624 | 5679 | [`fs.readdir()`]: #fs_fs_readdir_path_options_callback | 5625 | 5680 | [`fs.readdirSync()`]: #fs_fs_readdirsync_path_options | | 5681 | +[`fs.readv()`]: #fs_fs_readv_fd_buffers_position_callback | 5626 | 5682 | [`fs.realpath()`]: #fs_fs_realpath_path_options_callback | 5627 | 5683 | [`fs.rmdir()`]: #fs_fs_rmdir_path_options_callback | 5628 | 5684 | [`fs.stat()`]: #fs_fs_stat_path_options_callback | |
Collapse file Copy file name to clipboardExpand all lines: lib/fs.js +35Lines changed: 35 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -565,6 +565,39 @@ function readSync(fd, buffer, offset, length, position) { | 565 | 565 | return result; | 566 | 566 | } | 567 | 567 |
| | 568 | +function readv(fd, buffers, position, callback) { | | 569 | + function wrapper(err, read) { | | 570 | + callback(err, read || 0, buffers); | | 571 | + } | | 572 | + | | 573 | + validateInt32(fd, 'fd', /* min */ 0); | | 574 | + validateBufferArray(buffers); | | 575 | + | | 576 | + const req = new FSReqCallback(); | | 577 | + req.oncomplete = wrapper; | | 578 | + | | 579 | + callback = maybeCallback(callback || position); | | 580 | + | | 581 | + if (typeof position !== 'number') | | 582 | + position = null; | | 583 | + | | 584 | + return binding.readBuffers(fd, buffers, position, req); | | 585 | +} | | 586 | + | | 587 | +function readvSync(fd, buffers, position) { | | 588 | + validateInt32(fd, 'fd', 0); | | 589 | + validateBufferArray(buffers); | | 590 | + | | 591 | + const ctx = {}; | | 592 | + | | 593 | + if (typeof position !== 'number') | | 594 | + position = null; | | 595 | + | | 596 | + const result = binding.readBuffers(fd, buffers, position, undefined, ctx); | | 597 | + handleErrorFromBinding(ctx); | | 598 | + return result; | | 599 | +} | | 600 | + | 568 | 601 | // usage: | 569 | 602 | // fs.write(fd, buffer[, offset[, length[, position]]], callback); | 570 | 603 | // OR | @@ -1917,6 +1950,8 @@ module.exports = fs = { | 1917 | 1950 | readdirSync, | 1918 | 1951 | read, | 1919 | 1952 | readSync, | | 1953 | + readv, | | 1954 | + readvSync, | 1920 | 1955 | readFile, | 1921 | 1956 | readFileSync, | 1922 | 1957 | readlink, | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/fs/promises.js +16Lines changed: 16 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -99,6 +99,10 @@ class FileHandle { | 99 | 99 | return read(this, buffer, offset, length, position); | 100 | 100 | } | 101 | 101 |
| | 102 | + readv(buffers, position) { | | 103 | + return readv(this, buffers, position); | | 104 | + } | | 105 | + | 102 | 106 | readFile(options) { | 103 | 107 | return readFile(this, options); | 104 | 108 | } | @@ -253,6 +257,18 @@ async function read(handle, buffer, offset, length, position) { | 253 | 257 | return { bytesRead, buffer }; | 254 | 258 | } | 255 | 259 |
| | 260 | +async function readv(handle, buffers, position) { | | 261 | + validateFileHandle(handle); | | 262 | + validateBufferArray(buffers); | | 263 | + | | 264 | + if (typeof position !== 'number') | | 265 | + position = null; | | 266 | + | | 267 | + const bytesRead = (await binding.readBuffers(handle.fd, buffers, position, | | 268 | + kUsePromises)) || 0; | | 269 | + return { bytesRead, buffers }; | | 270 | +} | | 271 | + | 256 | 272 | async function write(handle, buffer, offset, length, position) { | 257 | 273 | validateFileHandle(handle); | 258 | 274 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/node_file.cc +47Lines changed: 47 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1965,6 +1965,52 @@ static void Read(const FunctionCallbackInfo<Value>& args) { | 1965 | 1965 | } | 1966 | 1966 |
| 1967 | 1967 |
| | 1968 | +// Wrapper for readv(2). | | 1969 | +// | | 1970 | +// bytesRead = fs.readv(fd, buffers[, position], callback) | | 1971 | +// 0 fd integer. file descriptor | | 1972 | +// 1 buffers array of buffers to read | | 1973 | +// 2 position if integer, position to read at in the file. | | 1974 | +// if null, read from the current position | | 1975 | +static void ReadBuffers(const FunctionCallbackInfo<Value>& args) { | | 1976 | + Environment* env = Environment::GetCurrent(args); | | 1977 | + | | 1978 | + const int argc = args.Length(); | | 1979 | + CHECK_GE(argc, 3); | | 1980 | + | | 1981 | + CHECK(args[0]->IsInt32()); | | 1982 | + const int fd = args[0].As<Int32>()->Value(); | | 1983 | + | | 1984 | + CHECK(args[1]->IsArray()); | | 1985 | + Local<Array> buffers = args[1].As<Array>(); | | 1986 | + | | 1987 | + int64_t pos = GetOffset(args[2]); // -1 if not a valid JS int | | 1988 | + | | 1989 | + MaybeStackBuffer<uv_buf_t> iovs(buffers->Length()); | | 1990 | + | | 1991 | + // Init uv buffers from ArrayBufferViews | | 1992 | + for (uint32_t i = 0; i < iovs.length(); i++) { | | 1993 | + Local<Value> buffer = buffers->Get(env->context(), i).ToLocalChecked(); | | 1994 | + CHECK(Buffer::HasInstance(buffer)); | | 1995 | + iovs[i] = uv_buf_init(Buffer::Data(buffer), Buffer::Length(buffer)); | | 1996 | + } | | 1997 | + | | 1998 | + FSReqBase* req_wrap_async = GetReqWrap(env, args[3]); | | 1999 | + if (req_wrap_async != nullptr) { // readBuffers(fd, buffers, pos, req) | | 2000 | + AsyncCall(env, req_wrap_async, args, "read", UTF8, AfterInteger, | | 2001 | + uv_fs_read, fd, *iovs, iovs.length(), pos); | | 2002 | + } else { // readBuffers(fd, buffers, undefined, ctx) | | 2003 | + CHECK_EQ(argc, 5); | | 2004 | + FSReqWrapSync req_wrap_sync; | | 2005 | + FS_SYNC_TRACE_BEGIN(read); | | 2006 | + int bytesRead = SyncCall(env, /* ctx */ args[4], &req_wrap_sync, "read", | | 2007 | + uv_fs_read, fd, *iovs, iovs.length(), pos); | | 2008 | + FS_SYNC_TRACE_END(read, "bytesRead", bytesRead); | | 2009 | + args.GetReturnValue().Set(bytesRead); | | 2010 | + } | | 2011 | +} | | 2012 | + | | 2013 | + | 1968 | 2014 | /* fs.chmod(path, mode); | 1969 | 2015 | * Wrapper for chmod(1) / EIO_CHMOD | 1970 | 2016 | */ | @@ -2228,6 +2274,7 @@ void Initialize(Local<Object> target, | 2228 | 2274 | env->SetMethod(target, "open", Open); | 2229 | 2275 | env->SetMethod(target, "openFileHandle", OpenFileHandle); | 2230 | 2276 | env->SetMethod(target, "read", Read); | | 2277 | + env->SetMethod(target, "readBuffers", ReadBuffers); | 2231 | 2278 | env->SetMethod(target, "fdatasync", Fdatasync); | 2232 | 2279 | env->SetMethod(target, "fsync", Fsync); | 2233 | 2280 | env->SetMethod(target, "rename", Rename); | |
Collapse file Copy file name to clipboard +67Lines changed: 67 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 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 | +})(); |
Collapse file Copy file name to clipboard +92Lines changed: 92 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 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 | +} |
|