fs: improve readFile
performance
This commit improves `readFile` performance by reducing number of closure allocations and using `FSReqWrap` directly. PR-URL: https://github.com/iojs/io.js/pull/718 Reviewed-By: Trevor Norris <trev.norris@gmail.com>
This commit is contained in:
parent
9681fcacf0
commit
e65308053c
228
lib/fs.js
228
lib/fs.js
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const SlowBuffer = require('buffer').SlowBuffer;
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const pathModule = require('path');
|
const pathModule = require('path');
|
||||||
|
|
||||||
@ -218,102 +219,163 @@ fs.existsSync = function(path) {
|
|||||||
fs.readFile = function(path, options, callback_) {
|
fs.readFile = function(path, options, callback_) {
|
||||||
var callback = maybeCallback(arguments[arguments.length - 1]);
|
var callback = maybeCallback(arguments[arguments.length - 1]);
|
||||||
|
|
||||||
if (!options || typeof options === 'function') {
|
if (!options || typeof options === 'function')
|
||||||
options = { encoding: null, flag: 'r' };
|
options = { encoding: null, flag: 'r' };
|
||||||
} else if (typeof options === 'string') {
|
else if (typeof options === 'string')
|
||||||
options = { encoding: options, flag: 'r' };
|
options = { encoding: options, flag: 'r' };
|
||||||
} else if (typeof options !== 'object') {
|
else if (typeof options !== 'object')
|
||||||
throw new TypeError('Bad arguments');
|
throw new TypeError('Bad arguments');
|
||||||
}
|
|
||||||
|
|
||||||
var encoding = options.encoding;
|
var encoding = options.encoding;
|
||||||
assertEncoding(encoding);
|
assertEncoding(encoding);
|
||||||
|
|
||||||
// first, stat the file, so we know the size.
|
|
||||||
var size;
|
|
||||||
var buffer; // single buffer with file data
|
|
||||||
var buffers; // list for when size is unknown
|
|
||||||
var pos = 0;
|
|
||||||
var fd;
|
|
||||||
|
|
||||||
var flag = options.flag || 'r';
|
var flag = options.flag || 'r';
|
||||||
fs.open(path, flag, 0o666, function(er, fd_) {
|
|
||||||
if (er) return callback(er);
|
|
||||||
fd = fd_;
|
|
||||||
|
|
||||||
fs.fstat(fd, function(er, st) {
|
if (!nullCheck(path, callback))
|
||||||
if (er) {
|
return;
|
||||||
return fs.close(fd, function() {
|
|
||||||
callback(er);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
size = st.size;
|
var context = new ReadFileContext(callback, encoding);
|
||||||
if (size === 0) {
|
var req = new FSReqWrap();
|
||||||
// the kernel lies about many files.
|
req.context = context;
|
||||||
// Go ahead and try to read some bytes.
|
req.oncomplete = readFileAfterOpen;
|
||||||
buffers = [];
|
|
||||||
return read();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size > kMaxLength) {
|
binding.open(pathModule._makeLong(path),
|
||||||
var err = new RangeError('File size is greater than possible Buffer: ' +
|
stringToFlags(flag),
|
||||||
'0x3FFFFFFF bytes');
|
0o666,
|
||||||
return fs.close(fd, function() {
|
req);
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
buffer = new Buffer(size);
|
|
||||||
read();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function read() {
|
|
||||||
if (size === 0) {
|
|
||||||
buffer = new Buffer(8192);
|
|
||||||
fs.read(fd, buffer, 0, 8192, -1, afterRead);
|
|
||||||
} else {
|
|
||||||
fs.read(fd, buffer, pos, size - pos, -1, afterRead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function afterRead(er, bytesRead) {
|
|
||||||
if (er) {
|
|
||||||
return fs.close(fd, function(er2) {
|
|
||||||
return callback(er);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytesRead === 0) {
|
|
||||||
return close();
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += bytesRead;
|
|
||||||
if (size !== 0) {
|
|
||||||
if (pos === size) close();
|
|
||||||
else read();
|
|
||||||
} else {
|
|
||||||
// unknown size, just read until we don't get bytes.
|
|
||||||
buffers.push(buffer.slice(0, bytesRead));
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function close() {
|
|
||||||
fs.close(fd, function(er) {
|
|
||||||
if (size === 0) {
|
|
||||||
// collected the data into the buffers list.
|
|
||||||
buffer = Buffer.concat(buffers, pos);
|
|
||||||
} else if (pos < size) {
|
|
||||||
buffer = buffer.slice(0, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encoding) buffer = buffer.toString(encoding);
|
|
||||||
return callback(er, buffer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const kReadFileBufferLength = 8 * 1024;
|
||||||
|
|
||||||
|
function ReadFileContext(callback, encoding) {
|
||||||
|
this.fd = undefined;
|
||||||
|
this.size = undefined;
|
||||||
|
this.callback = callback;
|
||||||
|
this.buffers = null;
|
||||||
|
this.buffer = null;
|
||||||
|
this.pos = 0;
|
||||||
|
this.encoding = encoding;
|
||||||
|
this.err = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadFileContext.prototype.read = function() {
|
||||||
|
var fd = this.fd;
|
||||||
|
var size = this.size;
|
||||||
|
var buffer;
|
||||||
|
var offset;
|
||||||
|
var length;
|
||||||
|
|
||||||
|
if (size === 0) {
|
||||||
|
buffer = this.buffer = new SlowBuffer(kReadFileBufferLength);
|
||||||
|
offset = 0;
|
||||||
|
length = kReadFileBufferLength;
|
||||||
|
} else {
|
||||||
|
buffer = this.buffer;
|
||||||
|
offset = this.pos;
|
||||||
|
length = size - this.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
var req = new FSReqWrap();
|
||||||
|
req.oncomplete = readFileAfterRead;
|
||||||
|
req.context = this;
|
||||||
|
|
||||||
|
binding.read(fd, buffer, offset, length, -1, req);
|
||||||
|
};
|
||||||
|
|
||||||
|
ReadFileContext.prototype.close = function(err) {
|
||||||
|
var req = new FSReqWrap();
|
||||||
|
req.oncomplete = readFileAfterClose;
|
||||||
|
req.context = this;
|
||||||
|
this.err = err;
|
||||||
|
binding.close(this.fd, req);
|
||||||
|
};
|
||||||
|
|
||||||
|
function readFileAfterOpen(err, fd) {
|
||||||
|
var context = this.context;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
var callback = context.callback;
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.fd = fd;
|
||||||
|
|
||||||
|
var req = new FSReqWrap();
|
||||||
|
req.oncomplete = readFileAfterStat;
|
||||||
|
req.context = context;
|
||||||
|
binding.fstat(fd, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFileAfterStat(err, st) {
|
||||||
|
var context = this.context;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return context.close(err);
|
||||||
|
|
||||||
|
var size = context.size = st.size;
|
||||||
|
|
||||||
|
if (size === 0) {
|
||||||
|
context.buffers = [];
|
||||||
|
context.read();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > kMaxLength) {
|
||||||
|
err = new RangeError('File size is greater than possible Buffer: ' +
|
||||||
|
`0x${kMaxLength.toString(16)} bytes`);
|
||||||
|
return context.close(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.buffer = new SlowBuffer(size);
|
||||||
|
context.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFileAfterRead(err, bytesRead) {
|
||||||
|
var context = this.context;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return context.close(err);
|
||||||
|
|
||||||
|
if (bytesRead === 0)
|
||||||
|
return context.close();
|
||||||
|
|
||||||
|
context.pos += bytesRead;
|
||||||
|
|
||||||
|
if (context.size !== 0) {
|
||||||
|
if (context.pos === context.size)
|
||||||
|
context.close();
|
||||||
|
else
|
||||||
|
context.read();
|
||||||
|
} else {
|
||||||
|
// unknown size, just read until we don't get bytes.
|
||||||
|
context.buffers.push(context.buffer.slice(0, bytesRead));
|
||||||
|
context.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFileAfterClose(err) {
|
||||||
|
var context = this.context;
|
||||||
|
var buffer = null;
|
||||||
|
var callback = context.callback;
|
||||||
|
|
||||||
|
if (context.err)
|
||||||
|
return callback(context.err);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
callback(err, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fs.readFileSync = function(path, options) {
|
fs.readFileSync = function(path, options) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = { encoding: null, flag: 'r' };
|
options = { encoding: null, flag: 'r' };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user