Skip to content

Commit c4cdf1d

Browse files
RaisinTenruyadorno
authored andcommitted
fs: allow position parameter to be a BigInt in read and readSync
Fixes: #36185 PR-URL: #36190 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Zeyu Yang <himself65@outlook.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 02a8f52 commit c4cdf1d

File tree

4 files changed

+127
-10
lines changed

4 files changed

+127
-10
lines changed

doc/api/fs.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -2921,7 +2921,7 @@ changes:
29212921
* `buffer` {Buffer|TypedArray|DataView}
29222922
* `offset` {integer}
29232923
* `length` {integer}
2924-
* `position` {integer}
2924+
* `position` {integer|bigint}
29252925
* `callback` {Function}
29262926
* `err` {Error}
29272927
* `bytesRead` {integer}
@@ -2966,7 +2966,7 @@ changes:
29662966
* `buffer` {Buffer|TypedArray|DataView} **Default:** `Buffer.alloc(16384)`
29672967
* `offset` {integer} **Default:** `0`
29682968
* `length` {integer} **Default:** `buffer.length`
2969-
* `position` {integer} **Default:** `null`
2969+
* `position` {integer|bigint} **Default:** `null`
29702970
* `callback` {Function}
29712971
* `err` {Error}
29722972
* `bytesRead` {integer}
@@ -3263,7 +3263,7 @@ changes:
32633263
* `buffer` {Buffer|TypedArray|DataView}
32643264
* `offset` {integer}
32653265
* `length` {integer}
3266-
* `position` {integer}
3266+
* `position` {integer|bigint}
32673267
* Returns: {number}
32683268

32693269
Returns the number of `bytesRead`.
@@ -3290,7 +3290,7 @@ changes:
32903290
* `options` {Object}
32913291
* `offset` {integer} **Default:** `0`
32923292
* `length` {integer} **Default:** `buffer.length`
3293-
* `position` {integer} **Default:** `null`
3293+
* `position` {integer|bigint} **Default:** `null`
32943294
* Returns: {number}
32953295

32963296
Returns the number of `bytesRead`.

lib/fs.js

+32-4
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ const {
3737
BigIntPrototypeToString,
3838
MathMax,
3939
Number,
40-
NumberIsSafeInteger,
4140
ObjectCreate,
4241
ObjectDefineProperties,
4342
ObjectDefineProperty,
@@ -75,7 +74,8 @@ const {
7574
ERR_FS_FILE_TOO_LARGE,
7675
ERR_INVALID_ARG_VALUE,
7776
ERR_INVALID_ARG_TYPE,
78-
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM
77+
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM,
78+
ERR_OUT_OF_RANGE,
7979
},
8080
hideStackFrames,
8181
uvErrmapGet,
@@ -545,9 +545,23 @@ function read(fd, buffer, offset, length, position, callback) {
545545

546546
validateOffsetLengthRead(offset, length, buffer.byteLength);
547547

548-
if (!NumberIsSafeInteger(position))
548+
if (position == null)
549549
position = -1;
550550

551+
if (typeof position === 'number') {
552+
validateInteger(position, 'position');
553+
} else if (typeof position === 'bigint') {
554+
if (!(position >= -(2n ** 63n) && position <= 2n ** 63n - 1n)) {
555+
throw new ERR_OUT_OF_RANGE('position',
556+
`>= ${-(2n ** 63n)} && <= ${2n ** 63n - 1n}`,
557+
position);
558+
}
559+
} else {
560+
throw new ERR_INVALID_ARG_TYPE('position',
561+
['integer', 'bigint'],
562+
position);
563+
}
564+
551565
function wrapper(err, bytesRead) {
552566
// Retain a reference to buffer so that it can't be GC'ed too soon.
553567
callback(err, bytesRead || 0, buffer);
@@ -597,9 +611,23 @@ function readSync(fd, buffer, offset, length, position) {
597611

598612
validateOffsetLengthRead(offset, length, buffer.byteLength);
599613

600-
if (!NumberIsSafeInteger(position))
614+
if (position == null)
601615
position = -1;
602616

617+
if (typeof position === 'number') {
618+
validateInteger(position, 'position');
619+
} else if (typeof position === 'bigint') {
620+
if (!(position >= -(2n ** 63n) && position <= 2n ** 63n - 1n)) {
621+
throw new ERR_OUT_OF_RANGE('position',
622+
`>= ${-(2n ** 63n)} && <= ${2n ** 63n - 1n}`,
623+
position);
624+
}
625+
} else {
626+
throw new ERR_INVALID_ARG_TYPE('position',
627+
['integer', 'bigint'],
628+
position);
629+
}
630+
603631
const ctx = {};
604632
const result = binding.read(fd, buffer, offset, length, position,
605633
undefined, ctx);

src/node_file.cc

+5-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ namespace node {
5151
namespace fs {
5252

5353
using v8::Array;
54+
using v8::BigInt;
5455
using v8::Boolean;
5556
using v8::Context;
5657
using v8::EscapableHandleScope;
@@ -2038,8 +2039,10 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
20382039
const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value());
20392040
CHECK(Buffer::IsWithinBounds(off, len, buffer_length));
20402041

2041-
CHECK(IsSafeJsInt(args[4]));
2042-
const int64_t pos = args[4].As<Integer>()->Value();
2042+
CHECK(IsSafeJsInt(args[4]) || args[4]->IsBigInt());
2043+
const int64_t pos = args[4]->IsNumber() ?
2044+
args[4].As<Integer>()->Value() :
2045+
args[4].As<BigInt>()->Int64Value();
20432046

20442047
char* buf = buffer_data + off;
20452048
uv_buf_t uvbuf = uv_buf_init(buf, len);

test/parallel/test-fs-read-type.js

+86
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,47 @@ assert.throws(() => {
7676
'It must be >= 0. Received -1'
7777
});
7878

79+
[true, () => {}, {}, ''].forEach((value) => {
80+
assert.throws(() => {
81+
fs.read(fd,
82+
Buffer.allocUnsafe(expected.length),
83+
0,
84+
expected.length,
85+
value,
86+
common.mustNotCall());
87+
}, {
88+
code: 'ERR_INVALID_ARG_TYPE',
89+
name: 'TypeError'
90+
});
91+
});
92+
93+
[0.5, 2 ** 53, 2n ** 63n].forEach((value) => {
94+
assert.throws(() => {
95+
fs.read(fd,
96+
Buffer.allocUnsafe(expected.length),
97+
0,
98+
expected.length,
99+
value,
100+
common.mustNotCall());
101+
}, {
102+
code: 'ERR_OUT_OF_RANGE',
103+
name: 'RangeError'
104+
});
105+
});
106+
107+
fs.read(fd,
108+
Buffer.allocUnsafe(expected.length),
109+
0,
110+
expected.length,
111+
0n,
112+
common.mustCall());
113+
114+
fs.read(fd,
115+
Buffer.allocUnsafe(expected.length),
116+
0,
117+
expected.length,
118+
2n ** 53n - 1n,
119+
common.mustCall());
79120

80121
assert.throws(
81122
() => fs.readSync(fd, expected.length, 0, 'utf-8'),
@@ -151,3 +192,48 @@ assert.throws(() => {
151192
message: 'The value of "length" is out of range. ' +
152193
'It must be <= 4. Received 5'
153194
});
195+
196+
[true, () => {}, {}, ''].forEach((value) => {
197+
assert.throws(() => {
198+
fs.readSync(fd,
199+
Buffer.allocUnsafe(expected.length),
200+
0,
201+
expected.length,
202+
value);
203+
}, {
204+
code: 'ERR_INVALID_ARG_TYPE',
205+
name: 'TypeError'
206+
});
207+
});
208+
209+
[0.5, 2 ** 53, 2n ** 63n].forEach((value) => {
210+
assert.throws(() => {
211+
fs.readSync(fd,
212+
Buffer.allocUnsafe(expected.length),
213+
0,
214+
expected.length,
215+
value);
216+
}, {
217+
code: 'ERR_OUT_OF_RANGE',
218+
name: 'RangeError'
219+
});
220+
});
221+
222+
fs.readSync(fd,
223+
Buffer.allocUnsafe(expected.length),
224+
0,
225+
expected.length,
226+
0n);
227+
228+
try {
229+
fs.readSync(fd,
230+
Buffer.allocUnsafe(expected.length),
231+
0,
232+
expected.length,
233+
2n ** 53n - 1n);
234+
} catch (err) {
235+
// On systems where max file size is below 2^53-1, we'd expect a EFBIG error.
236+
// This is not using `assert.throws` because the above call should not raise
237+
// any error on systems that allows file of that size.
238+
if (err.code !== 'EFBIG') throw err;
239+
}

0 commit comments

Comments
 (0)