Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
RaisinTen authored and MylesBorins committed Aug 31, 2021
1 parent 779310a commit 2459c11
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 10 deletions.
8 changes: 4 additions & 4 deletions doc/api/fs.md
Expand Up @@ -2904,7 +2904,7 @@ changes:
* `buffer` {Buffer|TypedArray|DataView}
* `offset` {integer}
* `length` {integer}
* `position` {integer}
* `position` {integer|bigint}
* `callback` {Function}
* `err` {Error}
* `bytesRead` {integer}
Expand Down Expand Up @@ -2945,7 +2945,7 @@ changes:
* `buffer` {Buffer|TypedArray|DataView} **Default:** `Buffer.alloc(16384)`
* `offset` {integer} **Default:** `0`
* `length` {integer} **Default:** `buffer.length`
* `position` {integer} **Default:** `null`
* `position` {integer|bigint} **Default:** `null`
* `callback` {Function}
* `err` {Error}
* `bytesRead` {integer}
Expand Down Expand Up @@ -3264,7 +3264,7 @@ changes:
* `buffer` {Buffer|TypedArray|DataView}
* `offset` {integer}
* `length` {integer}
* `position` {integer}
* `position` {integer|bigint}
* Returns: {number}

Returns the number of `bytesRead`.
Expand All @@ -3287,7 +3287,7 @@ changes:
* `options` {Object}
* `offset` {integer} **Default:** `0`
* `length` {integer} **Default:** `buffer.length`
* `position` {integer} **Default:** `null`
* `position` {integer|bigint} **Default:** `null`
* Returns: {number}

Returns the number of `bytesRead`.
Expand Down
36 changes: 32 additions & 4 deletions lib/fs.js
Expand Up @@ -36,7 +36,6 @@ const {
Map,
MathMax,
Number,
NumberIsSafeInteger,
ObjectCreate,
ObjectDefineProperties,
ObjectDefineProperty,
Expand Down Expand Up @@ -69,7 +68,8 @@ const {
ERR_INVALID_ARG_VALUE,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_CALLBACK,
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM,
ERR_OUT_OF_RANGE,
},
hideStackFrames,
uvErrmapGet,
Expand Down Expand Up @@ -553,9 +553,23 @@ function read(fd, buffer, offset, length, position, callback) {

validateOffsetLengthRead(offset, length, buffer.byteLength);

if (!NumberIsSafeInteger(position))
if (position == null)
position = -1;

if (typeof position === 'number') {
validateInteger(position, 'position');
} else if (typeof position === 'bigint') {
if (!(position >= -(2n ** 63n) && position <= 2n ** 63n - 1n)) {
throw new ERR_OUT_OF_RANGE('position',
`>= ${-(2n ** 63n)} && <= ${2n ** 63n - 1n}`,
position);
}
} else {
throw new ERR_INVALID_ARG_TYPE('position',
['integer', 'bigint'],
position);
}

function wrapper(err, bytesRead) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback(err, bytesRead || 0, buffer);
Expand Down Expand Up @@ -605,9 +619,23 @@ function readSync(fd, buffer, offset, length, position) {

validateOffsetLengthRead(offset, length, buffer.byteLength);

if (!NumberIsSafeInteger(position))
if (position == null)
position = -1;

if (typeof position === 'number') {
validateInteger(position, 'position');
} else if (typeof position === 'bigint') {
if (!(position >= -(2n ** 63n) && position <= 2n ** 63n - 1n)) {
throw new ERR_OUT_OF_RANGE('position',
`>= ${-(2n ** 63n)} && <= ${2n ** 63n - 1n}`,
position);
}
} else {
throw new ERR_INVALID_ARG_TYPE('position',
['integer', 'bigint'],
position);
}

const ctx = {};
const result = binding.read(fd, buffer, offset, length, position,
undefined, ctx);
Expand Down
7 changes: 5 additions & 2 deletions src/node_file.cc
Expand Up @@ -51,6 +51,7 @@ namespace node {
namespace fs {

using v8::Array;
using v8::BigInt;
using v8::Boolean;
using v8::Context;
using v8::EscapableHandleScope;
Expand Down Expand Up @@ -2037,8 +2038,10 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value());
CHECK(Buffer::IsWithinBounds(off, len, buffer_length));

CHECK(IsSafeJsInt(args[4]));
const int64_t pos = args[4].As<Integer>()->Value();
CHECK(IsSafeJsInt(args[4]) || args[4]->IsBigInt());
const int64_t pos = args[4]->IsNumber() ?
args[4].As<Integer>()->Value() :
args[4].As<BigInt>()->Int64Value();

char* buf = buffer_data + off;
uv_buf_t uvbuf = uv_buf_init(buf, len);
Expand Down
86 changes: 86 additions & 0 deletions test/parallel/test-fs-read-type.js
Expand Up @@ -74,6 +74,47 @@ assert.throws(() => {
'It must be >= 0. Received -1'
});

[true, () => {}, {}, ''].forEach((value) => {
assert.throws(() => {
fs.read(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
value,
common.mustNotCall());
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
});

[0.5, 2 ** 53, 2n ** 63n].forEach((value) => {
assert.throws(() => {
fs.read(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
value,
common.mustNotCall());
}, {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError'
});
});

fs.read(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
0n,
common.mustCall());

fs.read(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
2n ** 53n - 1n,
common.mustCall());

assert.throws(
() => fs.readSync(fd, expected.length, 0, 'utf-8'),
Expand Down Expand Up @@ -147,3 +188,48 @@ assert.throws(() => {
message: 'The value of "length" is out of range. ' +
'It must be <= 4. Received 5'
});

[true, () => {}, {}, ''].forEach((value) => {
assert.throws(() => {
fs.readSync(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
value);
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
});

[0.5, 2 ** 53, 2n ** 63n].forEach((value) => {
assert.throws(() => {
fs.readSync(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
value);
}, {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError'
});
});

fs.readSync(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
0n);

try {
fs.readSync(fd,
Buffer.allocUnsafe(expected.length),
0,
expected.length,
2n ** 53n - 1n);
} catch (err) {
// On systems where max file size is below 2^53-1, we'd expect a EFBIG error.
// This is not using `assert.throws` because the above call should not raise
// any error on systems that allows file of that size.
if (err.code !== 'EFBIG') throw err;
}

0 comments on commit 2459c11

Please sign in to comment.