diff options
-rw-r--r-- | bench/copyfile/node.mitata.mjs | 51 | ||||
m--------- | src/bun.js/WebKit | 0 | ||||
-rw-r--r-- | src/bun.js/node/node_fs.zig | 92 | ||||
-rw-r--r-- | src/bun.js/node/syscall.zig | 2 |
4 files changed, 127 insertions, 18 deletions
diff --git a/bench/copyfile/node.mitata.mjs b/bench/copyfile/node.mitata.mjs index 93833cfcf..aa77245e1 100644 --- a/bench/copyfile/node.mitata.mjs +++ b/bench/copyfile/node.mitata.mjs @@ -1,23 +1,40 @@ -import { copyFileSync, writeFileSync } from "node:fs"; +import { copyFileSync, writeFileSync, readFileSync, statSync } from "node:fs"; import { bench, run } from "mitata"; -const size = parseInt(process.env.FILE_SIZE, 10) || 1024 * 16; -const rand = new Float64Array(size); -for (let i = 0; i < size; i++) { - rand[i] = Math.random(); +function runner(ready) { + for (let size of [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]) { + const rand = new Int32Array(size); + for (let i = 0; i < size; i++) { + rand[i] = (Math.random() * 1024 * 1024) | 0; + } + const dest = `/tmp/fs-test-copy-file-${( + (Math.random() * 10000000 + 100) | + 0 + ).toString(32)}`; + const src = `/tmp/fs-test-copy-file-${( + (Math.random() * 10000000 + 100) | + 0 + ).toString(32)}`; + writeFileSync(src, Buffer.from(rand.buffer), { encoding: "buffer" }); + const { size: fileSize } = statSync(src); + if (fileSize !== rand.byteLength) { + throw new Error("size mismatch"); + } + ready(src, dest, new Uint8Array(rand.buffer)); + } } -const dest = `/tmp/fs-test-copy-file-${(Math.random() * 100000 + 100).toString( - 32 -)}`; -const src = `/tmp/fs-test-copy-file-${(Math.random() * 100000 + 100).toString( - 32 -)}`; -writeFileSync(src, new Buffer(rand.buffer)); +runner((src, dest, rand) => + bench(`copyFileSync(${rand.buffer.byteLength} bytes)`, () => { + copyFileSync(src, dest); + // const output = readFileSync(dest).buffer; -const srcBuf = new TextEncoder().encode(src); -const destBuf = new TextEncoder().encode(dest); -bench(`copyFileSync(${rand.buffer.byteLength} bytes)`, () => - copyFileSync(srcBuf, destBuf) + // for (let i = 0; i < output.length; i++) { + // if (output[i] !== rand[i]) { + // throw new Error( + // "Files are not equal" + " " + output[i] + " " + rand[i] + " " + i + // ); + // } + // } + }) ); - await run(); diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit -Subproject 4d6ee41032866e63aa43ba007616c632ed1c90a +Subproject 7d9e2ffa4365e676a4aa96a9941154a68e54f0f diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 440527e8f..af6e95d26 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -2419,6 +2419,98 @@ pub const NodeFS = struct { if (args.mode.isForceClone()) { // https://www.manpagez.com/man/2/clonefile/ return ret.errnoSysP(C.clonefile(src, dest, 0), .clonefile, src) orelse ret.success; + } else { + const stat_ = switch (Syscall.stat(src)) { + .result => |result| result, + .err => |err| return Maybe(Return.CopyFile){ .err = err.withPath(src) }, + }; + + if (!os.S.ISREG(stat_.mode)) { + return Maybe(Return.CopyFile){ .err = .{ .errno = @enumToInt(C.SystemErrno.ENOTSUP) } }; + } + + if (stat_.size > 128 * 1024) { + if (!args.mode.shouldntOverwrite()) { + // clonefile() will fail if it already exists + _ = Syscall.unlink(dest); + } + + if (ret.errnoSysP(C.clonefile(src, dest, 0), .clonefile, src) == null) { + _ = C.chmod(dest, stat_.mode); + return ret.success; + } + } else { + const src_fd = switch (Syscall.open(src, std.os.O.RDONLY, 0644)) { + .result => |result| result, + .err => |err| return .{ .err = err.withPath(args.src.slice()) }, + }; + defer { + _ = Syscall.close(src_fd); + } + + var flags: Mode = std.os.O.CREAT | std.os.O.WRONLY; + var wrote: usize = 0; + if (args.mode.shouldntOverwrite()) { + flags |= std.os.O.EXCL; + } + + const dest_fd = switch (Syscall.open(dest, flags, JSC.Node.default_permission)) { + .result => |result| result, + .err => |err| return Maybe(Return.CopyFile){ .err = err }, + }; + defer { + _ = std.c.ftruncate(dest_fd, @intCast(std.c.off_t, @truncate(u63, wrote))); + + _ = Syscall.close(dest_fd); + } + + var buf: [16384]u8 = undefined; + var remain = @intCast(u64, @maximum(stat_.size, 0)); + toplevel: while (remain > 0) { + const amt = switch (Syscall.read(src_fd, buf[0..@minimum(buf.len, remain)])) { + .result => |result| result, + .err => |err| return Maybe(Return.CopyFile){ .err = err.withPath(src) }, + }; + if (amt == 0) { + break :toplevel; + } + wrote += amt; + remain -|= amt; + + var slice = buf[0..amt]; + while (slice.len > 0) { + const written = switch (Syscall.write(dest_fd, slice)) { + .result => |result| result, + .err => |err| return Maybe(Return.CopyFile){ .err = err.withPath(dest) }, + }; + if (written == 0) break :toplevel; + slice = slice[written..]; + } + } else { + outer: while (true) { + const amt = switch (Syscall.read(src_fd, &buf)) { + .result => |result| result, + .err => |err| return Maybe(Return.CopyFile){ .err = err.withPath(src) }, + }; + if (amt == 0) { + break; + } + wrote += amt; + + var slice = buf[0..amt]; + while (slice.len > 0) { + const written = switch (Syscall.write(dest_fd, slice)) { + .result => |result| result, + .err => |err| return Maybe(Return.CopyFile){ .err = err.withPath(dest) }, + }; + slice = slice[written..]; + if (written == 0) break :outer; + } + } + } + _ = C.fchmod(dest_fd, stat_.mode); + return ret.success; + } } var mode: Mode = C.darwin.COPYFILE_ACL | C.darwin.COPYFILE_DATA; diff --git a/src/bun.js/node/syscall.zig b/src/bun.js/node/syscall.zig index 4378a4762..1f74753d2 100644 --- a/src/bun.js/node/syscall.zig +++ b/src/bun.js/node/syscall.zig @@ -419,7 +419,7 @@ pub fn fcopyfile(fd_in: std.os.fd_t, fd_out: std.os.fd_t, flags: u32) Maybe(void pub fn unlink(from: [:0]const u8) Maybe(void) { while (true) { - if (Maybe(void).errno(sys.unlink(from), .unlink)) |err| { + if (Maybe(void).errnoSys(sys.unlink(from), .unlink)) |err| { if (err.getErrno() == .INTR) continue; return err; } |