Skip to content

Commit

Permalink
fs: fix fs.promises.writeFile with typed arrays
Browse files Browse the repository at this point in the history
Before this change, only the first part of typed arrays which have more
than 1 byte per element (e.g. Uint16Array) would be written.
This also removes the use of the `slice` method to avoid unnecessary
copying the data.

Fixes: #35343

PR-URL: #35376
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Zeyu Yang <himself65@outlook.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
targos authored and MylesBorins committed Sep 29, 2020
1 parent 70ad69b commit 99a79e3
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 3 deletions.
13 changes: 10 additions & 3 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ const kReadFileMaxChunkSize = 2 ** 14;
const kWriteFileMaxChunkSize = 2 ** 14;

const {
Error,
MathMax,
MathMin,
NumberIsSafeInteger,
Symbol,
Error,
Promise,
Symbol,
Uint8Array,
} = primordials;

const {
Expand Down Expand Up @@ -237,14 +238,20 @@ async function fsCall(fn, handle, ...args) {
}

async function writeFileHandle(filehandle, data) {
// `data` could be any kind of typed array.
data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
let remaining = data.length;
if (remaining === 0) return;
do {
const { bytesWritten } =
await write(filehandle, data, 0,
MathMin(kWriteFileMaxChunkSize, data.length));
remaining -= bytesWritten;
data = data.slice(bytesWritten);
data = new Uint8Array(
data.buffer,
data.byteOffset + bytesWritten,
data.byteLength - bytesWritten
);
} while (remaining > 0);
}

Expand Down
24 changes: 24 additions & 0 deletions test/parallel/test-fs-promises-writefile-typedarray.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

const common = require('../common');
const fs = require('fs');
const fsPromises = fs.promises;
const path = require('path');
const tmpdir = require('../common/tmpdir');
const assert = require('assert');
const tmpDir = tmpdir.path;

tmpdir.refresh();

const dest = path.resolve(tmpDir, 'tmp.txt');
// Use a file size larger than `kReadFileMaxChunkSize`.
const buffer = Buffer.from('012'.repeat(2 ** 14));

(async () => {
for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) {
const array = new Constructor(buffer.buffer);
await fsPromises.writeFile(dest, array);
const data = await fsPromises.readFile(dest);
assert.deepStrictEqual(data, buffer);
}
})().then(common.mustCall());

0 comments on commit 99a79e3

Please sign in to comment.