Skip to content

Commit 5d67aa1

Browse files
committed
fs: add c++ fast path for writeFileSync utf8
1 parent c829c03 commit 5d67aa1

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const fs = require('fs');
5+
const tmpdir = require('../../test/common/tmpdir');
6+
tmpdir.refresh();
7+
8+
const bench = common.createBenchmark(main, {
9+
encoding: ['utf8'],
10+
hasFileDescriptor: ['true', 'false'],
11+
stringLength: [1024, 4096, 65535],//, 1024 * 1024],
12+
n: [1e3],
13+
});
14+
15+
function main({ n, encoding, stringLength, hasFileDescriptor }) {
16+
const enc = encoding === 'undefined' ? undefined : encoding;
17+
const path = tmpdir.resolve(`.writefilesync-file-${process.pid}`);
18+
19+
const useFd = hasFileDescriptor === 'true';
20+
const file = useFd ? fs.openSync(path, 'w') : path;
21+
22+
const data = 'a'.repeat(stringLength);
23+
24+
bench.start();
25+
for (let i = 0; i < n; ++i) {
26+
try {
27+
fs.writeFileSync(file, data, enc);
28+
} catch {
29+
// do nothing
30+
}
31+
}
32+
bench.end(n);
33+
34+
fs.unlinkSync(path);
35+
if (useFd) fs.closeSync(file);
36+
}

lib/fs.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,6 +2280,10 @@ function writeFile(path, data, options, callback) {
22802280
function writeFileSync(path, data, options) {
22812281
options = getOptions(options, { encoding: 'utf8', mode: 0o666, flag: 'w' });
22822282

2283+
if (options.encoding === 'utf8' || options.encoding === 'utf-8') {
2284+
return syncFs.writeFileUtf8(path, data, options.flag, options.mode);
2285+
}
2286+
22832287
if (!isArrayBufferView(data)) {
22842288
validateStringAfterArrayBufferView(data, 'data');
22852289
data = Buffer.from(data, options.encoding || 'utf8');

lib/internal/fs/sync.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ function readFileUtf8(path, flag) {
2525
return binding.readFileUtf8(path, stringToFlags(flag));
2626
}
2727

28+
function writeFileUtf8(path, data, flag, mode) {
29+
if (!isInt32(path)) {
30+
path = pathModule.toNamespacedPath(getValidatedPath(path));
31+
}
32+
return binding.writeFileUtf8(path, data, stringToFlags(flag), mode);
33+
}
34+
2835
function exists(path) {
2936
try {
3037
path = getValidatedPath(path);
@@ -90,6 +97,7 @@ function close(fd) {
9097

9198
module.exports = {
9299
readFileUtf8,
100+
writeFileUtf8,
93101
exists,
94102
access,
95103
copyFile,

src/node_file.cc

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2480,6 +2480,63 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {
24802480
}
24812481
}
24822482

2483+
static void WriteFileUtf8(const FunctionCallbackInfo<Value>& args) {
2484+
// Fast C++ path for fs.writeFileSync(path, data) with utf8 encoding
2485+
// (file, data, options.flag, options.mode)
2486+
2487+
Environment* env = Environment::GetCurrent(args);
2488+
auto isolate = env->isolate();
2489+
2490+
CHECK_GE(args.Length(), 4);
2491+
2492+
CHECK(args[2]->IsInt32());
2493+
const int flags = args[2].As<Int32>()->Value();
2494+
2495+
CHECK(args[3]->IsInt32());
2496+
const int mode = args[3].As<Int32>()->Value();
2497+
2498+
uv_file file;
2499+
uv_fs_t req;
2500+
2501+
bool is_fd = args[0]->IsInt32();
2502+
2503+
// Check for file descriptor
2504+
if (is_fd) {
2505+
file = args[0].As<Int32>()->Value();
2506+
} else {
2507+
BufferValue path(env->isolate(), args[0]);
2508+
CHECK_NOT_NULL(*path);
2509+
if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
2510+
2511+
FS_SYNC_TRACE_BEGIN(open);
2512+
file = uv_fs_open(nullptr, &req, *path, flags, mode, nullptr);
2513+
FS_SYNC_TRACE_END(open);
2514+
if (req.result < 0) {
2515+
uv_fs_req_cleanup(&req);
2516+
// req will be cleaned up by scope leave.
2517+
return env->ThrowUVException(req.result, "open", nullptr, path.out());
2518+
}
2519+
}
2520+
2521+
auto defer_close = OnScopeLeave([file, is_fd, &req]() {
2522+
if (!is_fd) {
2523+
FS_SYNC_TRACE_BEGIN(close);
2524+
CHECK_EQ(0, uv_fs_close(nullptr, &req, file, nullptr));
2525+
FS_SYNC_TRACE_END(close);
2526+
}
2527+
uv_fs_req_cleanup(&req);
2528+
});
2529+
2530+
CHECK(args[1]->IsString());
2531+
node::Utf8Value value(isolate, args[1]);
2532+
2533+
uv_buf_t uvbuf = uv_buf_init(*value, value.length());
2534+
2535+
FS_SYNC_TRACE_BEGIN(write);
2536+
uv_fs_write(nullptr, &req, file, &uvbuf, 1, 0, nullptr);
2537+
FS_SYNC_TRACE_END(write);
2538+
}
2539+
24832540

24842541
/*
24852542
* Wrapper for read(2).
@@ -3393,6 +3450,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
33933450
SetMethod(isolate, target, "writeBuffer", WriteBuffer);
33943451
SetMethod(isolate, target, "writeBuffers", WriteBuffers);
33953452
SetMethod(isolate, target, "writeString", WriteString);
3453+
SetMethod(isolate, target, "writeFileUtf8", WriteFileUtf8);
33963454
SetMethod(isolate, target, "realpath", RealPath);
33973455
SetMethod(isolate, target, "copyFile", CopyFile);
33983456
SetMethodNoSideEffect(isolate, target, "copyFileSync", CopyFileSync);
@@ -3518,6 +3576,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
35183576
registry->Register(WriteBuffer);
35193577
registry->Register(WriteBuffers);
35203578
registry->Register(WriteString);
3579+
registry->Register(WriteFileUtf8);
35213580
registry->Register(RealPath);
35223581
registry->Register(CopyFile);
35233582
registry->Register(CopyFileSync);

0 commit comments

Comments
 (0)