1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 04:52:26 -05:00

fix(ext/node): add writev method to FileHandle (#27563)

Part of #25554
This commit is contained in:
Aaron Ang 2025-01-14 04:01:14 -05:00 committed by GitHub
parent 7616429436
commit 9cb089f6db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 118 additions and 80 deletions

View file

@ -487,7 +487,7 @@ deno_core::extension!(deno_node,
"_fs/_fs_watch.ts", "_fs/_fs_watch.ts",
"_fs/_fs_write.mjs", "_fs/_fs_write.mjs",
"_fs/_fs_writeFile.ts", "_fs/_fs_writeFile.ts",
"_fs/_fs_writev.mjs", "_fs/_fs_writev.ts",
"_next_tick.ts", "_next_tick.ts",
"_process/exiting.ts", "_process/exiting.ts",
"_process/process.ts", "_process/process.ts",

View file

@ -1,65 +0,0 @@
// Copyright 2018-2025 the Deno authors. MIT license.
// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts
import { ErrnoException } from "ext:deno_node/_global.d.ts";
/**
* Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`.
*
* `position` is the offset from the beginning of the file where this data
* should be written. If `typeof position !== 'number'`, the data will be written
* at the current position.
*
* The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`.
*
* If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties.
*
* It is unsafe to use `fs.writev()` multiple times on the same file without
* waiting for the callback. For this scenario, use {@link createWriteStream}.
*
* On Linux, positional writes don't work when the file is opened in append mode.
* The kernel ignores the position argument and always appends the data to
* the end of the file.
* @since v12.9.0
*/
export function writev(
fd: number,
buffers: ReadonlyArray<ArrayBufferView>,
cb: (
err: ErrnoException | null,
bytesWritten: number,
buffers: ArrayBufferView[],
) => void,
): void;
export function writev(
fd: number,
buffers: ReadonlyArray<ArrayBufferView>,
position: number | null,
cb: (
err: ErrnoException | null,
bytesWritten: number,
buffers: ArrayBufferView[],
) => void,
): void;
export interface WriteVResult {
bytesWritten: number;
buffers: ArrayBufferView[];
}
export namespace writev {
function __promisify__(
fd: number,
buffers: ReadonlyArray<ArrayBufferView>,
position?: number,
): Promise<WriteVResult>;
}
/**
* For detailed information, see the documentation of the asynchronous version of
* this API: {@link writev}.
* @since v12.9.0
* @return The number of bytes written.
*/
export function writevSync(
fd: number,
buffers: ReadonlyArray<ArrayBufferView>,
position?: number,
): number;

View file

@ -5,15 +5,53 @@
// deno-lint-ignore-file prefer-primordials // deno-lint-ignore-file prefer-primordials
import { Buffer } from "node:buffer"; import { Buffer } from "node:buffer";
import process from "node:process";
import { ErrnoException } from "ext:deno_node/_global.d.ts";
import { validateBufferArray } from "ext:deno_node/internal/fs/utils.mjs"; import { validateBufferArray } from "ext:deno_node/internal/fs/utils.mjs";
import { getValidatedFd } from "ext:deno_node/internal/fs/utils.mjs"; import { getValidatedFd } from "ext:deno_node/internal/fs/utils.mjs";
import { WriteVResult } from "ext:deno_node/internal/fs/handle.ts";
import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts"; import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts";
import * as io from "ext:deno_io/12_io.js"; import * as io from "ext:deno_io/12_io.js";
import { op_fs_seek_async, op_fs_seek_sync } from "ext:core/ops"; import { op_fs_seek_async, op_fs_seek_sync } from "ext:core/ops";
export function writev(fd, buffers, position, callback) { export interface WriteVResult {
bytesWritten: number;
buffers: ReadonlyArray<ArrayBufferView>;
}
type writeVCallback = (
err: ErrnoException | null,
bytesWritten: number,
buffers: ReadonlyArray<ArrayBufferView>,
) => void;
/**
* Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`.
*
* `position` is the offset from the beginning of the file where this data
* should be written. If `typeof position !== 'number'`, the data will be written
* at the current position.
*
* The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`.
*
* If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties.
*
* It is unsafe to use `fs.writev()` multiple times on the same file without
* waiting for the callback. For this scenario, use {@link createWriteStream}.
*
* On Linux, positional writes don't work when the file is opened in append mode.
* The kernel ignores the position argument and always appends the data to
* the end of the file.
* @since v12.9.0
*/
export function writev(
fd: number,
buffers: ReadonlyArray<ArrayBufferView>,
position?: number | null,
callback?: writeVCallback,
): void {
const innerWritev = async (fd, buffers, position) => { const innerWritev = async (fd, buffers, position) => {
const chunks = []; const chunks: Buffer[] = [];
const offset = 0; const offset = 0;
for (let i = 0; i < buffers.length; i++) { for (let i = 0; i < buffers.length; i++) {
if (Buffer.isBuffer(buffers[i])) { if (Buffer.isBuffer(buffers[i])) {
@ -45,16 +83,24 @@ export function writev(fd, buffers, position, callback) {
if (typeof position !== "number") position = null; if (typeof position !== "number") position = null;
innerWritev(fd, buffers, position).then( innerWritev(fd, buffers, position).then(
(nwritten) => { (nwritten) => callback(null, nwritten, buffers),
callback(null, nwritten, buffers);
},
(err) => callback(err), (err) => callback(err),
); );
} }
export function writevSync(fd, buffers, position) { /**
* For detailed information, see the documentation of the asynchronous version of
* this API: {@link writev}.
* @since v12.9.0
* @return The number of bytes written.
*/
export function writevSync(
fd: number,
buffers: ArrayBufferView[],
position?: number | null,
): number {
const innerWritev = (fd, buffers, position) => { const innerWritev = (fd, buffers, position) => {
const chunks = []; const chunks: Buffer[] = [];
const offset = 0; const offset = 0;
for (let i = 0; i < buffers.length; i++) { for (let i = 0; i < buffers.length; i++) {
if (Buffer.isBuffer(buffers[i])) { if (Buffer.isBuffer(buffers[i])) {
@ -85,3 +131,16 @@ export function writevSync(fd, buffers, position) {
return innerWritev(fd, buffers, position); return innerWritev(fd, buffers, position);
} }
export function writevPromise(
fd: number,
buffers: ArrayBufferView[],
position?: number,
): Promise<WriteVResult> {
return new Promise((resolve, reject) => {
writev(fd, buffers, position, (err, bytesWritten, buffers) => {
if (err) reject(err);
else resolve({ bytesWritten, buffers });
});
});
}

View file

@ -119,7 +119,7 @@ import {
// @deno-types="./_fs/_fs_write.d.ts" // @deno-types="./_fs/_fs_write.d.ts"
import { write, writeSync } from "ext:deno_node/_fs/_fs_write.mjs"; import { write, writeSync } from "ext:deno_node/_fs/_fs_write.mjs";
// @deno-types="./_fs/_fs_writev.d.ts" // @deno-types="./_fs/_fs_writev.d.ts"
import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.mjs"; import { writev, writevSync } from "ext:deno_node/_fs/_fs_writev.ts";
import { readv, readvSync } from "ext:deno_node/_fs/_fs_readv.ts"; import { readv, readvSync } from "ext:deno_node/_fs/_fs_readv.ts";
import { import {
writeFile, writeFile,

View file

@ -6,7 +6,7 @@
import { EventEmitter } from "node:events"; import { EventEmitter } from "node:events";
import { Buffer } from "node:buffer"; import { Buffer } from "node:buffer";
import { Mode, promises, read, write } from "node:fs"; import { Mode, promises, read, write } from "node:fs";
export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts"; import { core } from "ext:core/mod.js";
import { import {
BinaryOptionsArgument, BinaryOptionsArgument,
FileOptionsArgument, FileOptionsArgument,
@ -14,7 +14,8 @@ import {
TextOptionsArgument, TextOptionsArgument,
} from "ext:deno_node/_fs/_fs_common.ts"; } from "ext:deno_node/_fs/_fs_common.ts";
import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts"; import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts";
import { core } from "ext:core/mod.js"; export type { BigIntStats, Stats } from "ext:deno_node/_fs/_fs_stat.ts";
import { writevPromise, WriteVResult } from "ext:deno_node/_fs/_fs_writev.ts";
interface WriteResult { interface WriteResult {
bytesWritten: number; bytesWritten: number;
@ -64,7 +65,7 @@ export class FileHandle extends EventEmitter {
position, position,
(err, bytesRead, buffer) => { (err, bytesRead, buffer) => {
if (err) reject(err); if (err) reject(err);
else resolve({ buffer: buffer, bytesRead: bytesRead }); else resolve({ buffer, bytesRead });
}, },
); );
}); });
@ -72,7 +73,7 @@ export class FileHandle extends EventEmitter {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
read(this.fd, bufferOrOpt, (err, bytesRead, buffer) => { read(this.fd, bufferOrOpt, (err, bytesRead, buffer) => {
if (err) reject(err); if (err) reject(err);
else resolve({ buffer: buffer, bytesRead: bytesRead }); else resolve({ buffer, bytesRead });
}); });
}); });
} }
@ -137,6 +138,10 @@ export class FileHandle extends EventEmitter {
return fsCall(promises.writeFile, this, data, options); return fsCall(promises.writeFile, this, data, options);
} }
writev(buffers: ArrayBufferView[], position?: number): Promise<WriteVResult> {
return fsCall(writevPromise, this, buffers, position);
}
close(): Promise<void> { close(): Promise<void> {
// Note that Deno.close is not async // Note that Deno.close is not async
return Promise.resolve(core.close(this.fd)); return Promise.resolve(core.close(this.fd));

View file

@ -18,7 +18,7 @@ import { errorOrDestroy } from "ext:deno_node/internal/streams/destroy.mjs";
import { open as fsOpen } from "ext:deno_node/_fs/_fs_open.ts"; import { open as fsOpen } from "ext:deno_node/_fs/_fs_open.ts";
import { read as fsRead } from "ext:deno_node/_fs/_fs_read.ts"; import { read as fsRead } from "ext:deno_node/_fs/_fs_read.ts";
import { write as fsWrite } from "ext:deno_node/_fs/_fs_write.mjs"; import { write as fsWrite } from "ext:deno_node/_fs/_fs_write.mjs";
import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.mjs"; import { writev as fsWritev } from "ext:deno_node/_fs/_fs_writev.ts";
import { close as fsClose } from "ext:deno_node/_fs/_fs_close.ts"; import { close as fsClose } from "ext:deno_node/_fs/_fs_close.ts";
import { Buffer } from "node:buffer"; import { Buffer } from "node:buffer";
import { import {

View file

@ -118,6 +118,45 @@ Deno.test("[node/fs filehandle.writeFile] Write to file", async function () {
assertEquals(decoder.decode(data), "hello world"); assertEquals(decoder.decode(data), "hello world");
}); });
Deno.test(
"[node/fs filehandle.writev] Write array of buffers to file",
async function () {
const tempFile: string = await Deno.makeTempFile();
const fileHandle = await fs.open(tempFile, "w");
const buffer1 = Buffer.from("hello ");
const buffer2 = Buffer.from("world");
const res = await fileHandle.writev([buffer1, buffer2]);
const data = Deno.readFileSync(tempFile);
await Deno.remove(tempFile);
await fileHandle.close();
assertEquals(res.bytesWritten, 11);
assertEquals(decoder.decode(data), "hello world");
},
);
Deno.test(
"[node/fs filehandle.writev] Write array of buffers to file with position",
async function () {
const tempFile: string = await Deno.makeTempFile();
const fileHandle = await fs.open(tempFile, "w");
const buffer1 = Buffer.from("hello ");
const buffer2 = Buffer.from("world");
await fileHandle.writev([buffer1, buffer2], 0);
const buffer3 = Buffer.from("lorem ipsum");
await fileHandle.writev([buffer3], 6);
const data = Deno.readFileSync(tempFile);
await Deno.remove(tempFile);
await fileHandle.close();
assertEquals(decoder.decode(data), "hello lorem ipsum");
},
);
Deno.test( Deno.test(
"[node/fs filehandle.truncate] Truncate file with length", "[node/fs filehandle.truncate] Truncate file with length",
async function () { async function () {

View file

@ -38,7 +38,7 @@
"ext:deno_node/_fs/_fs_stat.ts": "../ext/node/polyfills/_fs/_fs_stat.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_watch.ts": "../ext/node/polyfills/_fs/_fs_watch.ts",
"ext:deno_node/_fs/_fs_write.mjs": "../ext/node/polyfills/_fs/_fs_write.mjs", "ext:deno_node/_fs/_fs_write.mjs": "../ext/node/polyfills/_fs/_fs_write.mjs",
"ext:deno_node/_fs/_fs_writev.mjs": "../ext/node/polyfills/_fs/_fs_writev.mjs", "ext:deno_node/_fs/_fs_writev.ts": "../ext/node/polyfills/_fs/_fs_writev.ts",
"ext:deno_node/_global.d.ts": "../ext/node/polyfills/_global.d.ts", "ext:deno_node/_global.d.ts": "../ext/node/polyfills/_global.d.ts",
"node:_http_agent": "../ext/node/polyfills/_http_agent.mjs", "node:_http_agent": "../ext/node/polyfills/_http_agent.mjs",
"node:_http_common": "../ext/node/polyfills/_http_common.ts", "node:_http_common": "../ext/node/polyfills/_http_common.ts",