From b79c54192048968162cf842c8714c4f96be6be6d Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 19 Jan 2022 09:39:33 +0900 Subject: [PATCH] fix(node): fix fs.write/fs.writeAll (#1832) --- node/_fs/_fs_write.js | 25 ++- node/_tools/config.json | 1 + .../suites/parallel/test-fs-write-buffer.js | 172 ++++++++++++++++++ 3 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 node/_tools/suites/parallel/test-fs-write-buffer.js diff --git a/node/_fs/_fs_write.js b/node/_fs/_fs_write.js index cd833ec2aaee..d33b6b7fd8f5 100644 --- a/node/_fs/_fs_write.js +++ b/node/_fs/_fs_write.js @@ -1,5 +1,5 @@ import { Buffer } from "../buffer.ts"; -import { writeAll, writeAllSync } from "../../streams/conversion.ts"; +import { validateInteger } from "../internal/validators.js"; export function writeSync(fd, bufferLike, ...args) { const [buffer, pos] = bufferAndPos(bufferLike, args); @@ -26,12 +26,31 @@ export function write(fd, bufferLike, ...args) { ); } +async function writeAll(fd, buf) { + let nwritten = 0; + while (nwritten < buf.length) { + nwritten += await Deno.write(fd, buf.subarray(nwritten)); + } + return nwritten; +} + +function writeAllSync(fd, buf) { + let nwritten = 0; + while (nwritten < buf.length) { + nwritten += Deno.writeSync(fd, buf.subarray(nwritten)); + } + return nwritten; +} + function bufferAndPos(bufferLike, args) { if (typeof bufferLike === "string") { const [position, encoding] = args; - return [Buffer.from(bufferLike, encoding), position]; + return [Buffer.from(bufferLike.buffer, encoding), position]; } const [offset, length, position] = args; - return [Buffer.from(bufferLike, offset, length), position]; + if (typeof offset === "number") { + validateInteger(offset, "offset", 0); + } + return [Buffer.from(bufferLike.buffer, offset, length), position]; } diff --git a/node/_tools/config.json b/node/_tools/config.json index fd751addb612..2e63890f496a 100644 --- a/node/_tools/config.json +++ b/node/_tools/config.json @@ -168,6 +168,7 @@ "test-fs-rmdir-recursive-warns-on-file.js", "test-fs-rmdir-recursive.js", "test-fs-rmdir-type-check.js", + "test-fs-write-buffer.js", "test-fs-write-file-buffer.js", "test-fs-write-file-invalid-path.js", "test-fs-write-file-sync.js", diff --git a/node/_tools/suites/parallel/test-fs-write-buffer.js b/node/_tools/suites/parallel/test-fs-write-buffer.js new file mode 100644 index 000000000000..a71181edba82 --- /dev/null +++ b/node/_tools/suites/parallel/test-fs-write-buffer.js @@ -0,0 +1,172 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 16.13.0 +// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const expected = Buffer.from('hello'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// fs.write with all parameters provided: +{ + const filename = path.join(tmpdir.path, 'write1.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, expected.toString()); + }); + + fs.write(fd, expected, 0, expected.length, null, cb); + })); +} + +// fs.write with a buffer, without the length parameter: +{ + const filename = path.join(tmpdir.path, 'write2.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, 2); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, 'lo'); + }); + + fs.write(fd, Buffer.from('hello'), 3, cb); + })); +} + +// fs.write with a buffer, without the offset and length parameters: +{ + const filename = path.join(tmpdir.path, 'write3.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.deepStrictEqual(expected.toString(), found); + }); + + fs.write(fd, expected, cb); + })); +} + +// fs.write with the offset passed as undefined followed by the callback: +{ + const filename = path.join(tmpdir.path, 'write4.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.deepStrictEqual(expected.toString(), found); + }); + + fs.write(fd, expected, undefined, cb); + })); +} + +// fs.write with offset and length passed as undefined followed by the callback: +{ + const filename = path.join(tmpdir.path, 'write5.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, expected.toString()); + }); + + fs.write(fd, expected, undefined, undefined, cb); + })); +} + +// fs.write with a Uint8Array, without the offset and length parameters: +{ + const filename = path.join(tmpdir.path, 'write6.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, expected.toString()); + }); + + fs.write(fd, Uint8Array.from(expected), cb); + })); +} + +// fs.write with invalid offset type +{ + const filename = path.join(tmpdir.path, 'write7.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + assert.throws(() => { + fs.write(fd, + Buffer.from('abcd'), + NaN, + expected.length, + 0, + common.mustNotCall()); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + 'It must be an integer. Received NaN' + }); + + fs.closeSync(fd); + })); +} + +// fs.write with a DataView, without the offset and length parameters: +{ + const filename = path.join(tmpdir.path, 'write8.txt'); + fs.open(filename, 'w', 0o644, common.mustSucceed((fd) => { + const cb = common.mustSucceed((written) => { + assert.strictEqual(written, expected.length); + fs.closeSync(fd); + + const found = fs.readFileSync(filename, 'utf8'); + assert.strictEqual(found, expected.toString()); + }); + + const uint8 = Uint8Array.from(expected); + fs.write(fd, new DataView(uint8.buffer), cb); + })); +}