Skip to content

Commit 0afabd5

Browse files
committed
fs: add v8 fast api to existsSync
1 parent 783cc2f commit 0afabd5

File tree

7 files changed

+130
-46
lines changed

7 files changed

+130
-46
lines changed

benchmark/fs/bench-existsSync.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const fs = require('fs');
5+
const tmpdir = require('../../test/common/tmpdir');
6+
tmpdir.refresh();
7+
8+
const paths = [
9+
__filename,
10+
tmpdir.resolve(`.non-existing-file-${process.pid}`),
11+
];
12+
13+
const bench = common.createBenchmark(main, {
14+
n: [1e6],
15+
});
16+
17+
function main({ n }) {
18+
bench.start();
19+
for (let i = 0; i < n; i++) {
20+
for (let j = 0; j < paths.length; j++) {
21+
fs.existsSync(paths[j]);
22+
}
23+
}
24+
bench.end(n);
25+
}

lib/fs.js

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ const {
141141
validateObject,
142142
validateString,
143143
} = require('internal/validators');
144-
const { readFileSyncUtf8 } = require('internal/fs/read/utf8');
144+
const syncFs = require('internal/fs/sync');
145145

146146
let truncateWarn = true;
147147
let fs;
@@ -290,23 +290,7 @@ ObjectDefineProperty(exists, kCustomPromisifiedSymbol, {
290290
* @returns {boolean}
291291
*/
292292
function existsSync(path) {
293-
try {
294-
path = getValidatedPath(path);
295-
} catch {
296-
return false;
297-
}
298-
const ctx = { path };
299-
const nPath = pathModule.toNamespacedPath(path);
300-
binding.access(nPath, F_OK, undefined, ctx);
301-
302-
// In case of an invalid symlink, `binding.access()` on win32
303-
// will **not** return an error and is therefore not enough.
304-
// Double check with `binding.stat()`.
305-
if (isWindows && ctx.errno === undefined) {
306-
binding.stat(nPath, false, undefined, ctx);
307-
}
308-
309-
return ctx.errno === undefined;
293+
return syncFs.exists(path);
310294
}
311295

312296
function readFileAfterOpen(err, fd) {
@@ -462,8 +446,7 @@ function readFileSync(path, options) {
462446

463447
// TODO(@anonrig): Do not handle file descriptor ownership for now.
464448
if (!isUserFd && (options.encoding === 'utf8' || options.encoding === 'utf-8')) {
465-
path = getValidatedPath(path);
466-
return readFileSyncUtf8(pathModule.toNamespacedPath(path), stringToFlags(options.flag));
449+
return syncFs.readFileUtf8(path, options.flag);
467450
}
468451

469452
const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666);

lib/internal/fs/read/utf8.js

Lines changed: 0 additions & 25 deletions
This file was deleted.

lib/internal/fs/sync.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
const pathModule = require('path');
4+
const { handleErrorFromBinding, getValidatedPath, stringToFlags } = require('internal/fs/utils');
5+
6+
const syncBinding = internalBinding('fs_sync');
7+
8+
/**
9+
* @param {string} path
10+
* @param {number} flag
11+
* @return {string}
12+
*/
13+
function readFileUtf8(path, flag) {
14+
path = pathModule.toNamespacedPath(getValidatedPath(path));
15+
const response = syncBinding.readFileUtf8(path, stringToFlags(flag));
16+
17+
if (typeof response === 'string') {
18+
return response;
19+
}
20+
21+
const { 0: errno, 1: syscall } = response;
22+
handleErrorFromBinding({ errno, syscall, path });
23+
}
24+
25+
function exists(path) {
26+
try {
27+
path = getValidatedPath(path);
28+
} catch {
29+
return false;
30+
}
31+
32+
return syncBinding.exists(pathModule.toNamespacedPath(path));
33+
}
34+
35+
module.exports = {
36+
readFileUtf8,
37+
exists,
38+
};

src/node_file_sync.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "node_constants.h"
12
#include "node_file_sync.h"
23
#include "memory_tracker-inl.h"
34
#include "node_buffer.h"
@@ -16,16 +17,22 @@
1617

1718
#include <fcntl.h>
1819

20+
#if !defined(_MSC_VER)
21+
#include <unistd.h>
22+
#endif
23+
1924
namespace node {
2025
namespace fs_sync {
2126

2227
using v8::Array;
28+
using v8::CFunction;
2329
using v8::Context;
2430
using v8::FunctionCallbackInfo;
2531
using v8::HandleScope;
2632
using v8::Int32;
2733
using v8::Integer;
2834
using v8::Isolate;
35+
using v8::FastOneByteString;
2936
using v8::JustVoid;
3037
using v8::Local;
3138
using v8::Maybe;
@@ -115,6 +122,50 @@ void BindingData::Deserialize(v8::Local<v8::Context> context,
115122
CHECK_NOT_NULL(binding);
116123
}
117124

125+
bool BindingData::ExistsInternal(const std::string_view path) {
126+
uv_fs_t req;
127+
auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
128+
FS_SYNC_TRACE_BEGIN(access);
129+
int err = uv_fs_access(nullptr, &req, path.data(), F_OK, nullptr);
130+
FS_SYNC_TRACE_END(access);
131+
132+
#ifdef _WIN32
133+
// In case of an invalid symlink, `binding.access()` on win32
134+
// will **not** return an error and is therefore not enough.
135+
// Double check with `stat()`.
136+
if (err != 0) {
137+
FS_SYNC_TRACE_BEGIN(stat);
138+
err = uv_fs_stat(nullptr, &req, path.data, nullptr);
139+
FS_SYNC_TRACE_END(stat);
140+
}
141+
#endif // _WIN32
142+
143+
return err == 0;
144+
}
145+
146+
void BindingData::Exists(const FunctionCallbackInfo<Value>& args) {
147+
Environment* env = Environment::GetCurrent(args);
148+
Isolate* isolate = env->isolate();
149+
150+
const int argc = args.Length();
151+
CHECK_GE(argc, 1);
152+
153+
BufferValue path(isolate, args[0]);
154+
CHECK_NOT_NULL(*path);
155+
THROW_IF_INSUFFICIENT_PERMISSIONS(
156+
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
157+
158+
return ExistsInternal(path.ToStringView());
159+
}
160+
161+
bool BindingData::FastExists(Local<Value> receiver,
162+
const FastOneByteString& path) {
163+
// TODO(@anonrig): Add "THROW_IF_INSUFFICIENT_PERMISSIONS"
164+
return ExistsInternal(std::string_view(path.data, path.length));
165+
}
166+
167+
CFunction BindingData::fast_exists_(CFunction::Make(FastExists));
168+
118169
void BindingData::ReadFileUtf8(const FunctionCallbackInfo<Value>& args) {
119170
Environment* env = Environment::GetCurrent(args);
120171
auto isolate = env->isolate();
@@ -186,6 +237,7 @@ void BindingData::ReadFileUtf8(const FunctionCallbackInfo<Value>& args) {
186237
void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
187238
Local<ObjectTemplate> target) {
188239
Isolate* isolate = isolate_data->isolate();
240+
SetFastMethodNoSideEffect(isolate, target, "exists", Exists, &fast_exists_);
189241
SetMethodNoSideEffect(isolate, target, "readFileUtf8", ReadFileUtf8);
190242
}
191243

@@ -199,6 +251,9 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
199251

200252
void BindingData::RegisterExternalReferences(
201253
ExternalReferenceRegistry* registry) {
254+
registry->Register(Exists);
255+
registry->Register(FastExists);
256+
registry->Register(fast_exists_.GetTypeInfo());
202257
registry->Register(ReadFileUtf8);
203258
}
204259

src/node_file_sync.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class BindingData : public SnapshotableObject {
3131
SET_SELF_SIZE(BindingData)
3232
SET_MEMORY_INFO_NAME(BindingData)
3333

34+
static void Exists(const v8::FunctionCallbackInfo<v8::Value>& args);
35+
static bool FastExists(v8::Local<v8::Value> receiver, const v8::FastOneByteString& path);
36+
3437
static void ReadFileUtf8(const v8::FunctionCallbackInfo<v8::Value>& args);
3538

3639
static void CreatePerIsolateProperties(IsolateData* isolate_data,
@@ -40,6 +43,11 @@ class BindingData : public SnapshotableObject {
4043
v8::Local<v8::Context> context,
4144
void* priv);
4245
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
46+
47+
private:
48+
static v8::CFunction fast_exists_;
49+
50+
static bool BindingData::ExistsInternal(const std::string_view path);
4351
};
4452

4553
} // namespace fs_sync

test/parallel/test-bootstrap-modules.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const expectedModules = new Set([
7575
'NativeModule internal/webstreams/queuingstrategies',
7676
'NativeModule internal/blob',
7777
'NativeModule internal/fs/utils',
78-
'NativeModule internal/fs/read/utf8',
78+
'NativeModule internal/fs/sync',
7979
'NativeModule fs',
8080
'Internal Binding options',
8181
'NativeModule internal/options',

0 commit comments

Comments
 (0)