summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Whitaker <17734409+nathanwhit@users.noreply.github.com>2024-04-01 20:42:49 -0700
committerGitHub <noreply@github.com>2024-04-01 20:42:49 -0700
commit7ad76fd453972e9262985c61840c77b8b8a6dbb7 (patch)
treea97eb0a22d7931ff9ac161f9be97e1faf1358547
parentca5e5c7e9c8bb68beb751c25f2f08f18a59cd76e (diff)
fix(ext/node): Add fs.readv, fs.readvSync (#23166)
Part of #18218. Implements `fs.readv` and `fs.readvSync` and enables the corresponding `node_compat` tests.
-rw-r--r--ext/node/lib.rs1
-rw-r--r--ext/node/polyfills/_fs/_fs_readv.ts130
-rw-r--r--ext/node/polyfills/fs.ts5
-rw-r--r--tests/node_compat/config.jsonc2
-rw-r--r--tests/node_compat/test/parallel/test-fs-readv-sync.js100
-rw-r--r--tests/node_compat/test/parallel/test-fs-readv.js102
-rw-r--r--tools/core_import_map.json1
-rw-r--r--tools/node_compat/TODO.md2
8 files changed, 341 insertions, 2 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index cf63a5785..85abe49e4 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -371,6 +371,7 @@ deno_core::extension!(deno_node,
"_fs/_fs_readdir.ts",
"_fs/_fs_readFile.ts",
"_fs/_fs_readlink.ts",
+ "_fs/_fs_readv.ts",
"_fs/_fs_realpath.ts",
"_fs/_fs_rename.ts",
"_fs/_fs_rm.ts",
diff --git a/ext/node/polyfills/_fs/_fs_readv.ts b/ext/node/polyfills/_fs/_fs_readv.ts
new file mode 100644
index 000000000..7d87c51f7
--- /dev/null
+++ b/ext/node/polyfills/_fs/_fs_readv.ts
@@ -0,0 +1,130 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+// TODO(petamoriken): enable prefer-primordials for node polyfills
+// deno-lint-ignore-file prefer-primordials
+
+import {
+ ERR_INVALID_ARG_TYPE,
+ type ErrnoException,
+} from "ext:deno_node/internal/errors.ts";
+import {
+ getValidatedFd,
+ validateBufferArray,
+} from "ext:deno_node/internal/fs/utils.mjs";
+import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts";
+import { validateInteger } from "ext:deno_node/internal/validators.mjs";
+import * as io from "ext:deno_io/12_io.js";
+import * as fs from "ext:deno_fs/30_fs.js";
+
+type Callback = (
+ err: ErrnoException | null,
+ bytesRead: number,
+ buffers: readonly ArrayBufferView[],
+) => void;
+
+export function readv(
+ fd: number,
+ buffers: readonly ArrayBufferView[],
+ callback: Callback,
+): void;
+export function readv(
+ fd: number,
+ buffers: readonly ArrayBufferView[],
+ position: number | Callback,
+ callback?: Callback,
+): void {
+ if (typeof fd !== "number") {
+ throw new ERR_INVALID_ARG_TYPE("fd", "number", fd);
+ }
+ fd = getValidatedFd(fd);
+ validateBufferArray(buffers);
+ const cb = maybeCallback(callback || position) as Callback;
+ let pos: number | null = null;
+ if (typeof position === "number") {
+ validateInteger(position, "position", 0);
+ pos = position;
+ }
+
+ if (buffers.length === 0) {
+ process.nextTick(cb, null, 0, buffers);
+ return;
+ }
+
+ const innerReadv = async (
+ fd: number,
+ buffers: readonly ArrayBufferView[],
+ position: number | null,
+ ) => {
+ if (typeof position === "number") {
+ await fs.seek(fd, position, io.SeekMode.Start);
+ }
+
+ let readTotal = 0;
+ let readInBuf = 0;
+ let bufIdx = 0;
+ let buf = buffers[bufIdx];
+ while (bufIdx < buffers.length) {
+ const nread = await io.read(fd, buf);
+ if (nread === null) {
+ break;
+ }
+ readInBuf += nread;
+ if (readInBuf === buf.byteLength) {
+ readTotal += readInBuf;
+ readInBuf = 0;
+ bufIdx += 1;
+ buf = buffers[bufIdx];
+ }
+ }
+ readTotal += readInBuf;
+
+ return readTotal;
+ };
+
+ innerReadv(fd, buffers, pos).then(
+ (numRead) => {
+ cb(null, numRead, buffers);
+ },
+ (err) => cb(err, -1, buffers),
+ );
+}
+
+export function readvSync(
+ fd: number,
+ buffers: readonly ArrayBufferView[],
+ position: number | null = null,
+): number {
+ if (typeof fd !== "number") {
+ throw new ERR_INVALID_ARG_TYPE("fd", "number", fd);
+ }
+ fd = getValidatedFd(fd);
+ validateBufferArray(buffers);
+ if (buffers.length === 0) {
+ return 0;
+ }
+ if (typeof position === "number") {
+ validateInteger(position, "position", 0);
+ fs.seekSync(fd, position, io.SeekMode.Start);
+ }
+
+ let readTotal = 0;
+ let readInBuf = 0;
+ let bufIdx = 0;
+ let buf = buffers[bufIdx];
+ while (bufIdx < buffers.length) {
+ const nread = io.readSync(fd, buf);
+ if (nread === null) {
+ break;
+ }
+ readInBuf += nread;
+ if (readInBuf === buf.byteLength) {
+ readTotal += readInBuf;
+ readInBuf = 0;
+ bufIdx += 1;
+ buf = buffers[bufIdx];
+ }
+ }
+ readTotal += readInBuf;
+
+ return readTotal;
+}
diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts
index bdf7e4aa6..f788f72b5 100644
--- a/ext/node/polyfills/fs.ts
+++ b/ext/node/polyfills/fs.ts
@@ -110,6 +110,7 @@ import {
import { write, writeSync } from "ext:deno_node/_fs/_fs_write.mjs";
// @deno-types="./_fs/_fs_writev.d.ts"
import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.mjs";
+import { readv, readvSync } from "ext:deno_node/_fs/_fs_readv.ts";
import {
writeFile,
writeFilePromise,
@@ -250,6 +251,8 @@ export default {
ReadStream,
realpath,
realpathSync,
+ readv,
+ readvSync,
rename,
renameSync,
rmdir,
@@ -353,6 +356,8 @@ export {
readlinkSync,
ReadStream,
readSync,
+ readv,
+ readvSync,
realpath,
realpathSync,
rename,
diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc
index bf254faf6..af45cc663 100644
--- a/tests/node_compat/config.jsonc
+++ b/tests/node_compat/config.jsonc
@@ -334,6 +334,8 @@
"test-fs-readdir-stack-overflow.js",
"test-fs-readdir.js",
"test-fs-readfile-empty.js",
+ "test-fs-readv-sync.js",
+ "test-fs-readv.js",
"test-fs-realpath-native.js",
"test-fs-rmdir-recursive-sync-warns-not-found.js",
"test-fs-rmdir-recursive-sync-warns-on-file.js",
diff --git a/tests/node_compat/test/parallel/test-fs-readv-sync.js b/tests/node_compat/test/parallel/test-fs-readv-sync.js
new file mode 100644
index 000000000..c09362848
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-fs-readv-sync.js
@@ -0,0 +1,100 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tools/node_compat/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const tmpdir = require('../common/tmpdir');
+
+tmpdir.refresh();
+
+const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف';
+
+const exptectedBuff = Buffer.from(expected);
+const expectedLength = exptectedBuff.length;
+
+const filename = path.join(tmpdir.path, '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.strictEqual(read, 0);
+
+ read = fs.readvSync(fd, bufferArr, 0);
+ assert.strictEqual(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.strictEqual(read, 0);
+
+ read = fs.readvSync(fd, bufferArr);
+ assert.strictEqual(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/tests/node_compat/test/parallel/test-fs-readv.js b/tests/node_compat/test/parallel/test-fs-readv.js
new file mode 100644
index 000000000..b385d5f6f
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-fs-readv.js
@@ -0,0 +1,102 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tools/node_compat/setup.ts`. Do not modify this file manually.
+
+'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.mustSucceed((bytesRead, buffers) => {
+ 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'
+ }
+ );
+ });
+}
diff --git a/tools/core_import_map.json b/tools/core_import_map.json
index 9e70f52f7..078808aa0 100644
--- a/tools/core_import_map.json
+++ b/tools/core_import_map.json
@@ -32,6 +32,7 @@
"ext:deno_node/_fs/_fs_mkdir.ts": "../ext/node/polyfills/_fs/_fs_mkdir.ts",
"ext:deno_node/_fs/_fs_open.ts": "../ext/node/polyfills/_fs/_fs_open.ts",
"ext:deno_node/_fs/_fs_read.ts": "../ext/node/polyfills/_fs/_fs_read.ts",
+ "ext:deno_node/_fs/_fs_readv.ts": "../ext/node/polyfills/_fs/_fs_readv.ts",
"ext:deno_node/_fs/_fs_stat.ts": "../ext/node/polyfills/_fs/_fs_stat.ts",
"ext:deno_node/_fs/_fs_watch.ts": "../ext/node/polyfills/_fs/_fs_watch.ts",
"ext:deno_node/_fs/_fs_write.mjs": "../ext/node/polyfills/_fs/_fs_write.mjs",
diff --git a/tools/node_compat/TODO.md b/tools/node_compat/TODO.md
index 3d8306988..ae3e184be 100644
--- a/tools/node_compat/TODO.md
+++ b/tools/node_compat/TODO.md
@@ -813,8 +813,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
- [parallel/test-fs-readlink-type-check.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-readlink-type-check.js)
- [parallel/test-fs-readv-promises.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-readv-promises.js)
- [parallel/test-fs-readv-promisify.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-readv-promisify.js)
-- [parallel/test-fs-readv-sync.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-readv-sync.js)
-- [parallel/test-fs-readv.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-readv.js)
- [parallel/test-fs-ready-event-stream.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-ready-event-stream.js)
- [parallel/test-fs-realpath-buffer-encoding.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-realpath-buffer-encoding.js)
- [parallel/test-fs-realpath-on-substed-drive.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-realpath-on-substed-drive.js)