Signed-off-by: Matteo Collina <matteo.collina@gmail.com> PR-URL: https://github.com/nodejs/node/pull/63634 Fixes: https://github.com/nodejs/node/issues/63600 Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: LiviaMedeiros <livia@cirno.name>
197 lines
4.5 KiB
JavaScript
197 lines
4.5 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ArrayPrototypePush,
|
|
FunctionPrototypeCall,
|
|
MathMin,
|
|
} = primordials;
|
|
|
|
const {
|
|
constants: {
|
|
kReadFileBufferLength,
|
|
kReadFileUnknownBufferLength,
|
|
},
|
|
createReadFileBufferTooSmallError,
|
|
getReadFileBuffer,
|
|
getReadFileBufferByteLengthName,
|
|
} = require('internal/fs/utils');
|
|
|
|
const { Buffer } = require('buffer');
|
|
|
|
const { FSReqCallback, close, read } = internalBinding('fs');
|
|
|
|
const {
|
|
AbortError,
|
|
aggregateTwoErrors,
|
|
} = require('internal/errors');
|
|
|
|
function readFileAfterRead(err, bytesRead) {
|
|
const context = this.context;
|
|
|
|
if (err)
|
|
return context.close(err);
|
|
|
|
if (context.checkOverflow) {
|
|
context.checkOverflow = false;
|
|
|
|
if (bytesRead !== 0) {
|
|
return context.close(createReadFileBufferTooSmallError(
|
|
context.bufferByteLengthName,
|
|
context.buffer.byteLength,
|
|
));
|
|
}
|
|
|
|
return context.close();
|
|
}
|
|
|
|
context.pos += bytesRead;
|
|
|
|
if (context.userBuffer) {
|
|
if (context.pos === context.size || bytesRead === 0) {
|
|
context.close();
|
|
} else {
|
|
context.read();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (context.pos === context.size || bytesRead === 0) {
|
|
context.close();
|
|
} else {
|
|
if (context.size === 0) {
|
|
// Unknown size, just read until we don't get bytes.
|
|
const buffer = bytesRead === kReadFileUnknownBufferLength ?
|
|
context.buffer : context.buffer.slice(0, bytesRead);
|
|
ArrayPrototypePush(context.buffers, buffer);
|
|
}
|
|
context.read();
|
|
}
|
|
}
|
|
|
|
function readFileAfterClose(err) {
|
|
const context = this.context;
|
|
const callback = context.callback;
|
|
let buffer = null;
|
|
|
|
if (context.err || err)
|
|
return callback(aggregateTwoErrors(err, context.err));
|
|
|
|
try {
|
|
if (context.userBuffer) {
|
|
buffer = context.buffer.subarray(0, context.pos);
|
|
} else if (context.size === 0) {
|
|
buffer = Buffer.concat(context.buffers, context.pos);
|
|
} else if (context.pos < context.size) {
|
|
buffer = context.buffer.slice(0, context.pos);
|
|
} else {
|
|
buffer = context.buffer;
|
|
}
|
|
|
|
if (context.encoding)
|
|
buffer = buffer.toString(context.encoding);
|
|
} catch (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
callback(null, buffer);
|
|
}
|
|
|
|
class ReadFileContext {
|
|
constructor(callback, options) {
|
|
this.fd = undefined;
|
|
this.isUserFd = undefined;
|
|
this.size = 0;
|
|
this.callback = callback;
|
|
this.buffers = null;
|
|
this.buffer = null;
|
|
this.pos = 0;
|
|
this.encoding = options.encoding;
|
|
this.options = options;
|
|
this.err = null;
|
|
this.signal = undefined;
|
|
this.userBuffer = false;
|
|
this.bufferByteLengthName = null;
|
|
this.overflowBuffer = null;
|
|
this.checkOverflow = false;
|
|
}
|
|
|
|
prepare() {
|
|
const buffer = getReadFileBuffer(this.options, this.size);
|
|
|
|
if (buffer !== undefined) {
|
|
this.buffer = buffer;
|
|
this.userBuffer = true;
|
|
this.bufferByteLengthName = getReadFileBufferByteLengthName(this.options);
|
|
return;
|
|
}
|
|
|
|
if (this.size === 0) {
|
|
this.buffers = [];
|
|
} else {
|
|
this.buffer = Buffer.allocUnsafeSlow(this.size);
|
|
}
|
|
}
|
|
|
|
read() {
|
|
let buffer;
|
|
let offset;
|
|
let length;
|
|
|
|
if (this.signal?.aborted) {
|
|
return this.close(
|
|
new AbortError(undefined, { cause: this.signal.reason }));
|
|
}
|
|
|
|
if (this.userBuffer) {
|
|
if (this.size === 0) {
|
|
buffer = this.buffer;
|
|
offset = this.pos;
|
|
length = this.buffer.byteLength - this.pos;
|
|
|
|
if (length === 0) {
|
|
buffer = this.overflowBuffer ??= Buffer.allocUnsafeSlow(1);
|
|
offset = 0;
|
|
length = 1;
|
|
this.checkOverflow = true;
|
|
}
|
|
} else {
|
|
buffer = this.buffer;
|
|
offset = this.pos;
|
|
length = MathMin(kReadFileBufferLength, this.size - this.pos);
|
|
}
|
|
} else if (this.size === 0) {
|
|
buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength);
|
|
offset = 0;
|
|
length = kReadFileUnknownBufferLength;
|
|
this.buffer = buffer;
|
|
} else {
|
|
buffer = this.buffer;
|
|
offset = this.pos;
|
|
length = MathMin(kReadFileBufferLength, this.size - this.pos);
|
|
}
|
|
|
|
const req = new FSReqCallback();
|
|
req.oncomplete = readFileAfterRead;
|
|
req.context = this;
|
|
|
|
read(this.fd, buffer, offset, length, -1, req);
|
|
}
|
|
|
|
close(err) {
|
|
if (this.isUserFd) {
|
|
process.nextTick(function tick(context) {
|
|
FunctionPrototypeCall(readFileAfterClose, { context }, null);
|
|
}, this);
|
|
return;
|
|
}
|
|
|
|
const req = new FSReqCallback();
|
|
req.oncomplete = readFileAfterClose;
|
|
req.context = this;
|
|
this.err = err;
|
|
|
|
close(this.fd, req);
|
|
}
|
|
}
|
|
|
|
module.exports = ReadFileContext;
|