mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
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.
This commit is contained in:
parent
ca5e5c7e9c
commit
7ad76fd453
8 changed files with 341 additions and 2 deletions
|
@ -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",
|
||||
|
|
130
ext/node/polyfills/_fs/_fs_readv.ts
Normal file
130
ext/node/polyfills/_fs/_fs_readv.ts
Normal file
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
100
tests/node_compat/test/parallel/test-fs-readv-sync.js
Normal file
100
tests/node_compat/test/parallel/test-fs-readv-sync.js
Normal file
|
@ -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'
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
102
tests/node_compat/test/parallel/test-fs-readv.js
Normal file
102
tests/node_compat/test/parallel/test-fs-readv.js
Normal file
|
@ -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'
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue