Skip to content

Commit f7f3d9b

Browse files
committed
fs: improve cpSync performance
1 parent d335487 commit f7f3d9b

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

lib/internal/fs/cp/cp-sync.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,40 @@ const {
4646
resolve,
4747
} = require('path');
4848
const { isPromise } = require('util/types');
49+
const internalFsBinding = internalBinding('fs');
4950

51+
/**
52+
*
53+
* @param {string} src
54+
* @param {string} dest
55+
* @param {{
56+
* preserveTimestamps?: boolean,
57+
* filter?: (src: string, dest: string) => boolean,
58+
* dereference?: boolean,
59+
* errorOnExist?: boolean,
60+
* force?: boolean,
61+
* recursive?: boolean,
62+
* mode?: number
63+
* verbatimSymlinks?: boolean
64+
* }} opts
65+
*/
5066
function cpSyncFn(src, dest, opts) {
67+
// Calling a JavaScript function from C++ is costly. Therefore, we don't support it.
68+
// TODO(@anonrig): Support `mode` option.
69+
if (opts.filter == null && opts.mode == null) {
70+
return internalFsBinding.cpSync(
71+
src,
72+
dest,
73+
opts.preserveTimestamps,
74+
opts.dereference,
75+
opts.errorOnExist,
76+
opts.force,
77+
opts.recursive,
78+
opts.verbatimSymlinks,
79+
);
80+
}
81+
82+
5183
// Warn about using preserveTimestamps on 32-bit node
5284
if (opts.preserveTimestamps && process.arch === 'ia32') {
5385
const warning = 'Using the preserveTimestamps option in 32-bit ' +

src/node_file.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2106,6 +2106,47 @@ static void OpenFileHandle(const FunctionCallbackInfo<Value>& args) {
21062106
}
21072107
}
21082108

2109+
// TODO(@anonrig): Implement v8 fast APi calls for `cpSync`.
2110+
static void CpSync(const FunctionCallbackInfo<Value>& args) {
2111+
Environment* env = Environment::GetCurrent(args);
2112+
CHECK(args.Length() == 8); // src, dest, preserveTimestamps, dereference, errorOnExist, force, recursive, verbatimSymlinks
2113+
BufferValue path(env->isolate(), args[0]);
2114+
CHECK_NOT_NULL(*path);
2115+
ToNamespacedPath(env, &path);
2116+
2117+
BufferValue dest(env->isolate(), args[1]);
2118+
CHECK_NOT_NULL(*dest);
2119+
ToNamespacedPath(env, &dest);
2120+
2121+
bool preserveTimestamps = args[2]->IsTrue();
2122+
bool dereference = args[3]->IsTrue();
2123+
bool errorOnExist = args[4]->IsTrue();
2124+
bool force = args[5]->IsTrue();
2125+
bool recursive = args[6]->IsTrue();
2126+
bool verbatimSymlinks = args[7]->IsTrue();
2127+
2128+
if (errorOnExist) {
2129+
if (std::filesystem::exists(*path)) {
2130+
return env->ThrowUVException(UV_EEXIST, "EEXIST", "File already exists");
2131+
}
2132+
}
2133+
2134+
std::filesystem::copy_options options;
2135+
2136+
// When true timestamps from src will be preserved.
2137+
if (preserveTimestamps) options |= std::filesystem::copy_options::create_hard_links;
2138+
// Dereference symbolic links.
2139+
if (dereference) options |= std::filesystem::copy_options::copy_symlinks;
2140+
// Overwrite existing file or directory.
2141+
if (force) options |= std::filesystem::copy_options::overwrite_existing;
2142+
// Copy directories recursively.
2143+
if (recursive) options |= std::filesystem::copy_options::recursive;
2144+
// When true, path resolution for symlinks will be skipped.
2145+
if (verbatimSymlinks) options |= std::filesystem::copy_options::skip_symlinks;
2146+
2147+
std::filesystem::copy(*path, *dest, options);
2148+
}
2149+
21092150
static void CopyFile(const FunctionCallbackInfo<Value>& args) {
21102151
Environment* env = Environment::GetCurrent(args);
21112152
Isolate* isolate = env->isolate();
@@ -3344,6 +3385,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
33443385
SetMethod(isolate, target, "writeFileUtf8", WriteFileUtf8);
33453386
SetMethod(isolate, target, "realpath", RealPath);
33463387
SetMethod(isolate, target, "copyFile", CopyFile);
3388+
SetMethod(isolate, target, "cpSync", CpSync);
33473389

33483390
SetMethod(isolate, target, "chmod", Chmod);
33493391
SetMethod(isolate, target, "fchmod", FChmod);
@@ -3466,6 +3508,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
34663508
registry->Register(WriteFileUtf8);
34673509
registry->Register(RealPath);
34683510
registry->Register(CopyFile);
3511+
registry->Register(CpSync);
34693512

34703513
registry->Register(Chmod);
34713514
registry->Register(FChmod);

typings/internalBinding/fs.d.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,20 @@ declare namespace InternalFSBinding {
7373
function close(fd: number, req: undefined, ctx: FSSyncContext): void;
7474

7575
function copyFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number, req: FSReqCallback): void;
76-
function copyFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number, req: undefined, ctx: FSSyncContext): void;
76+
function copyFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number): void;
7777
function copyFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number, usePromises: typeof kUsePromises): Promise<void>;
7878

79+
function cpSync(
80+
src: StringOrBuffer,
81+
dest: StringOrBuffer,
82+
preserveTimestamps: boolean,
83+
dereference?: boolean,
84+
errorOnExist?: boolean,
85+
force?: boolean,
86+
recursive?: boolean,
87+
verbatimSymlinks?: boolean,
88+
): void;
89+
7990
function fchmod(fd: number, mode: number, req: FSReqCallback): void;
8091
function fchmod(fd: number, mode: number): void;
8192
function fchmod(fd: number, mode: number, usePromises: typeof kUsePromises): Promise<void>;
@@ -253,6 +264,7 @@ export interface FsBinding {
253264
chown: typeof InternalFSBinding.chown;
254265
close: typeof InternalFSBinding.close;
255266
copyFile: typeof InternalFSBinding.copyFile;
267+
cpSync: typeof InternalFSBinding.cpSync;
256268
fchmod: typeof InternalFSBinding.fchmod;
257269
fchown: typeof InternalFSBinding.fchown;
258270
fdatasync: typeof InternalFSBinding.fdatasync;

0 commit comments

Comments
 (0)