aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-07-07 00:33:43 -0700
committerGravatar GitHub <noreply@github.com> 2023-07-07 00:33:43 -0700
commit0ecdbf4793f19b3244a3e099bd47e3e81e993811 (patch)
treee495d272a780906f637ea3492a53194a221718b5
parent284aaec3cd41b527428c475c345d79fd67b6dce2 (diff)
downloadbun-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.zig5
-rw-r--r--src/bun.js/node/node_fs.zig50
-rw-r--r--test/js/node/fs/fs.test.ts52
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);