diff options
author | 2023-07-07 00:33:43 -0700 | |
---|---|---|
committer | 2023-07-07 00:33:43 -0700 | |
commit | 0ecdbf4793f19b3244a3e099bd47e3e81e993811 (patch) | |
tree | e495d272a780906f637ea3492a53194a221718b5 | |
parent | 284aaec3cd41b527428c475c345d79fd67b6dce2 (diff) | |
download | bun-0ecdbf4793f19b3244a3e099bd47e3e81e993811.tar.gz bun-0ecdbf4793f19b3244a3e099bd47e3e81e993811.tar.zst bun-0ecdbf4793f19b3244a3e099bd47e3e81e993811.zip |
[node:fs] `read`, `write` - support large numbers and BigInt (#3556)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 5 | ||||
-rw-r--r-- | src/bun.js/node/node_fs.zig | 50 | ||||
-rw-r--r-- | test/js/node/fs/fs.test.ts | 52 |
3 files changed, 80 insertions, 27 deletions
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 07882d857..c8d0515e9 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -3445,6 +3445,7 @@ pub const JSValue = enum(JSValueReprInt) { c_int => @intCast(c_int, toInt32(this)), ?AnyPromise => asAnyPromise(this), u52 => @truncate(u52, @intCast(u64, @max(this.toInt64(), 0))), + i52 => @truncate(i52, @intCast(i52, this.toInt64())), u64 => toUInt64NoTruncate(this), u8 => @truncate(u8, toU32(this)), i16 => @truncate(i16, toInt32(this)), @@ -4620,11 +4621,11 @@ pub const JSValue = enum(JSValueReprInt) { } pub inline fn toU16(this: JSValue) u16 { - return @truncate(u16, this.toU32()); + return @truncate(u16, @max(this.toInt32(), 0)); } pub inline fn toU32(this: JSValue) u32 { - return @intCast(u32, @max(this.toInt32(), 0)); + return @intCast(u32, @min(@max(this.toInt64(), 0), std.math.maxInt(u32))); } /// This function supports: diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index fa33a575b..74a41c5bd 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -35,7 +35,7 @@ const Mode = JSC.Node.Mode; const uid_t = std.os.uid_t; const gid_t = std.os.gid_t; /// u63 to allow one null bit -const ReadPosition = u63; +const ReadPosition = i64; const Stats = JSC.Node.Stats; const Dirent = JSC.Node.Dirent; @@ -1524,7 +1524,7 @@ pub const Arguments = struct { // fs.write(fd, string[, position[, encoding]], callback) .string => { if (current.isNumber()) { - args.position = current.toU32(); + args.position = current.to(i52); arguments.eat(); current = arguments.next() orelse break :parse; } @@ -1540,18 +1540,18 @@ pub const Arguments = struct { break :parse; } - if (!current.isNumber()) break :parse; - args.offset = current.toU32(); + if (!(current.isNumber() or current.isBigInt())) break :parse; + args.offset = current.to(u52); arguments.eat(); current = arguments.next() orelse break :parse; - if (!current.isNumber()) break :parse; - args.length = current.toU32(); + if (!(current.isNumber() or current.isBigInt())) break :parse; + args.length = current.to(u52); arguments.eat(); current = arguments.next() orelse break :parse; - if (!current.isNumber()) break :parse; - args.position = current.toU32(); + if (!(current.isNumber() or current.isBigInt())) break :parse; + args.position = current.to(i52); arguments.eat(); }, } @@ -1631,8 +1631,8 @@ pub const Arguments = struct { if (arguments.next()) |current| { arguments.eat(); - if (current.isNumber()) { - args.offset = current.toU32(); + if (current.isNumber() or current.isBigInt()) { + args.offset = current.to(u52); if (arguments.remaining.len < 2) { JSC.throwInvalidArguments( @@ -1644,8 +1644,8 @@ pub const Arguments = struct { return null; } - - args.length = arguments.remaining[0].toU32(); + if (arguments.remaining[0].isNumber() or arguments.remaining[0].isBigInt()) + args.length = arguments.remaining[0].to(u52); if (args.length == 0) { JSC.throwInvalidArguments( @@ -1658,26 +1658,26 @@ pub const Arguments = struct { return null; } - const position: i32 = if (arguments.remaining[1].isNumber()) - arguments.remaining[1].toInt32() - else - -1; + if (arguments.remaining[1].isNumber() or arguments.remaining[1].isBigInt()) + args.position = @intCast(ReadPosition, arguments.remaining[1].to(i52)); - args.position = if (position > -1) @intCast(ReadPosition, position) else null; arguments.remaining = arguments.remaining[2..]; } else if (current.isObject()) { - if (current.getIfPropertyExists(ctx.ptr(), "offset")) |num| { - args.offset = num.toU32(); + if (current.getTruthy(ctx.ptr(), "offset")) |num| { + if (num.isNumber() or num.isBigInt()) { + args.offset = num.to(u52); + } } - if (current.getIfPropertyExists(ctx.ptr(), "length")) |num| { - args.length = num.toU32(); + if (current.getTruthy(ctx.ptr(), "length")) |num| { + if (num.isNumber() or num.isBigInt()) { + args.length = num.to(u52); + } } - if (current.getIfPropertyExists(ctx.ptr(), "position")) |num| { - const position: i32 = if (num.isEmptyOrUndefinedOrNull()) -1 else num.coerce(i32, ctx); - if (position > -1) { - args.position = @intCast(ReadPosition, position); + if (current.getTruthy(ctx.ptr(), "position")) |num| { + if (num.isNumber() or num.isBigInt()) { + args.position = num.to(i52); } } } diff --git a/test/js/node/fs/fs.test.ts b/test/js/node/fs/fs.test.ts index 272522fc0..3b1688c15 100644 --- a/test/js/node/fs/fs.test.ts +++ b/test/js/node/fs/fs.test.ts @@ -278,6 +278,41 @@ it("readdirSync throws when given a file path with trailing slash", () => { describe("readSync", () => { const firstFourBytes = new Uint32Array(new TextEncoder().encode("File").buffer)[0]; + + it("works on large files", () => { + const dest = join(tmpdir(), "readSync-large-file.txt"); + rmSync(dest, { force: true }); + + const writefd = openSync(dest, "w"); + writeSync(writefd, Buffer.from([0x10]), 0, 1, 4_900_000_000); + closeSync(writefd); + + const fd = openSync(dest, "r"); + const out = Buffer.alloc(1); + const bytes = readSync(fd, out, 0, 1, 4_900_000_000); + expect(bytes).toBe(1); + expect(out[0]).toBe(0x10); + closeSync(fd); + rmSync(dest, { force: true }); + }); + + it("works with bigint on read", () => { + const dest = join(tmpdir(), "readSync-large-file-bigint.txt"); + rmSync(dest, { force: true }); + + const writefd = openSync(dest, "w"); + writeSync(writefd, Buffer.from([0x10]), 0, 1, 400); + closeSync(writefd); + + const fd = openSync(dest, "r"); + const out = Buffer.alloc(1); + const bytes = readSync(fd, out, 0, 1, 400n as any); + expect(bytes).toBe(1); + expect(out[0]).toBe(0x10); + closeSync(fd); + rmSync(dest, { force: true }); + }); + it("works with a position set to 0", () => { const fd = openSync(import.meta.dir + "/readFileSync.txt", "r"); const four = new Uint8Array(4); @@ -367,6 +402,23 @@ it("preadv", () => { }); describe("writeSync", () => { + it("works with bigint", () => { + const dest = join(tmpdir(), "writeSync-large-file-bigint.txt"); + rmSync(dest, { force: true }); + + const writefd = openSync(dest, "w"); + writeSync(writefd, Buffer.from([0x10]), 0, 1, 400n as any); + closeSync(writefd); + + const fd = openSync(dest, "r"); + const out = Buffer.alloc(1); + const bytes = readSync(fd, out, 0, 1, 400 as any); + expect(bytes).toBe(1); + expect(out[0]).toBe(0x10); + closeSync(fd); + rmSync(dest, { force: true }); + }); + it("works with a position set to 0", () => { const fd = openSync(import.meta.dir + "/writeFileSync.txt", "w+"); const four = new Uint8Array(4); |