Skip to content

Commit 986b46a

Browse files
anonrigjuanarbol
authored andcommitted
fs: add a fast-path for readFileSync utf-8
PR-URL: #48658 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent 6406f50 commit 986b46a

File tree

6 files changed

+110
-5
lines changed

6 files changed

+110
-5
lines changed

benchmark/fs/readFileSync.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ const common = require('../common.js');
44
const fs = require('fs');
55

66
const bench = common.createBenchmark(main, {
7-
n: [60e4],
7+
encoding: ['undefined', 'utf8'],
8+
path: ['existing', 'non-existing'],
9+
n: [60e1],
810
});
911

10-
function main({ n }) {
12+
function main({ n, encoding, path }) {
13+
const enc = encoding === 'undefined' ? undefined : encoding;
14+
const file = path === 'existing' ? __filename : '/tmp/not-found';
1115
bench.start();
12-
for (let i = 0; i < n; ++i)
13-
fs.readFileSync(__filename);
16+
for (let i = 0; i < n; ++i) {
17+
try {
18+
fs.readFileSync(file, enc);
19+
} catch {
20+
// do nothing
21+
}
22+
}
1423
bench.end(n);
1524
}

lib/fs.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ const {
142142
validateObject,
143143
validateString,
144144
} = require('internal/validators');
145+
const { readFileSyncUtf8 } = require('internal/fs/read/utf8');
145146

146147
let truncateWarn = true;
147148
let fs;
@@ -380,7 +381,7 @@ function checkAborted(signal, callback) {
380381
function readFile(path, options, callback) {
381382
callback = maybeCallback(callback || options);
382383
options = getOptions(options, { flag: 'r' });
383-
const ReadFileContext = require('internal/fs/read_file_context');
384+
const ReadFileContext = require('internal/fs/read/context');
384385
const context = new ReadFileContext(callback, options.encoding);
385386
context.isUserFd = isFd(path); // File descriptor ownership
386387

@@ -457,7 +458,15 @@ function tryReadSync(fd, isUserFd, buffer, pos, len) {
457458
*/
458459
function readFileSync(path, options) {
459460
options = getOptions(options, { flag: 'r' });
461+
460462
const isUserFd = isFd(path); // File descriptor ownership
463+
464+
// TODO(@anonrig): Do not handle file descriptor ownership for now.
465+
if (!isUserFd && (options.encoding === 'utf8' || options.encoding === 'utf-8')) {
466+
path = getValidatedPath(path);
467+
return readFileSyncUtf8(pathModule.toNamespacedPath(path), stringToFlags(options.flag));
468+
}
469+
461470
const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666);
462471

463472
const stats = tryStatSync(fd, isUserFd);
File renamed without changes.

lib/internal/fs/read/utf8.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
const { handleErrorFromBinding } = require('internal/fs/utils');
4+
5+
const binding = internalBinding('fs');
6+
7+
/**
8+
* @param {string} path
9+
* @param {number} flag
10+
* @return {string}
11+
*/
12+
function readFileSyncUtf8(path, flag) {
13+
const response = binding.readFileSync(path, flag);
14+
15+
if (typeof response === 'string') {
16+
return response;
17+
}
18+
19+
handleErrorFromBinding({ errno: response, path });
20+
}
21+
22+
module.exports = {
23+
readFileSyncUtf8,
24+
};

src/node_file.cc

+62
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,66 @@ static inline Maybe<void> CheckOpenPermissions(Environment* env,
19941994
return JustVoid();
19951995
}
19961996

1997+
static void ReadFileSync(const FunctionCallbackInfo<Value>& args) {
1998+
Environment* env = Environment::GetCurrent(args);
1999+
2000+
CHECK_GE(args.Length(), 2);
2001+
2002+
BufferValue path(env->isolate(), args[0]);
2003+
CHECK_NOT_NULL(*path);
2004+
2005+
CHECK(args[1]->IsInt32());
2006+
const int flags = args[1].As<Int32>()->Value();
2007+
2008+
if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
2009+
2010+
uv_fs_t req;
2011+
auto defer_req_cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
2012+
2013+
FS_SYNC_TRACE_BEGIN(open);
2014+
uv_file file = uv_fs_open(nullptr, &req, *path, flags, 438, nullptr);
2015+
FS_SYNC_TRACE_END(open);
2016+
if (req.result < 0) {
2017+
// req will be cleaned up by scope leave.
2018+
return args.GetReturnValue().Set(
2019+
v8::Integer::New(env->isolate(), req.result));
2020+
}
2021+
uv_fs_req_cleanup(&req);
2022+
2023+
auto defer_close = OnScopeLeave([file]() {
2024+
uv_fs_t close_req;
2025+
CHECK_EQ(0, uv_fs_close(nullptr, &close_req, file, nullptr));
2026+
uv_fs_req_cleanup(&close_req);
2027+
});
2028+
2029+
std::string result{};
2030+
char buffer[8192];
2031+
uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
2032+
2033+
FS_SYNC_TRACE_BEGIN(read);
2034+
while (true) {
2035+
auto r = uv_fs_read(nullptr, &req, file, &buf, 1, -1, nullptr);
2036+
if (req.result < 0) {
2037+
FS_SYNC_TRACE_END(read);
2038+
// req will be cleaned up by scope leave.
2039+
return args.GetReturnValue().Set(
2040+
v8::Integer::New(env->isolate(), req.result));
2041+
}
2042+
uv_fs_req_cleanup(&req);
2043+
if (r <= 0) {
2044+
break;
2045+
}
2046+
result.append(buf.base, r);
2047+
}
2048+
FS_SYNC_TRACE_END(read);
2049+
2050+
args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(),
2051+
result.data(),
2052+
v8::NewStringType::kNormal,
2053+
result.size())
2054+
.ToLocalChecked());
2055+
}
2056+
19972057
static void Open(const FunctionCallbackInfo<Value>& args) {
19982058
Environment* env = Environment::GetCurrent(args);
19992059

@@ -3149,6 +3209,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
31493209
SetMethod(isolate, target, "stat", Stat);
31503210
SetMethod(isolate, target, "lstat", LStat);
31513211
SetMethod(isolate, target, "fstat", FStat);
3212+
SetMethodNoSideEffect(isolate, target, "readFileSync", ReadFileSync);
31523213
SetMethod(isolate, target, "statfs", StatFs);
31533214
SetMethod(isolate, target, "link", Link);
31543215
SetMethod(isolate, target, "symlink", Symlink);
@@ -3266,6 +3327,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
32663327
registry->Register(Stat);
32673328
registry->Register(LStat);
32683329
registry->Register(FStat);
3330+
registry->Register(ReadFileSync);
32693331
registry->Register(StatFs);
32703332
registry->Register(Link);
32713333
registry->Register(Symlink);

test/parallel/test-bootstrap-modules.js

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ const expectedModules = new Set([
7474
'NativeModule internal/webstreams/queuingstrategies',
7575
'NativeModule internal/blob',
7676
'NativeModule internal/fs/utils',
77+
'NativeModule internal/fs/read/utf8',
7778
'NativeModule fs',
7879
'Internal Binding options',
7980
'NativeModule internal/options',

0 commit comments

Comments
 (0)