🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

qfputs

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

qfputs - npm Package Compare versions

Comparing version
1.8.0
to
1.8.2
+30
test/benchmark.js
'use strict';
if (process.argv.join(' ').indexOf('qnit ') >= 0) return;
var fs = require('fs');
var fputs = require('../');
var setImmediate = global.setImmediate || process.nextTick;
var filename = '/tmp/test.out';
var fp = fputs(fputs.FileWriter(filename, 'a'), { writeSize: 40960 });
var str = new Array(200).join('x') + '\n';
var nlines = 0;
console.time('100k fputs');
repeatFor(100000, function(cb) { fp.fputs(str); cb() }, function(err) {
fp.fflush(function(err) {
console.timeEnd('100k fputs');
fs.unlinkSync(filename);
});
})
// repeatFor adapted from minisql:
function repeatFor(n, proc, callback) {
var ix = 0, ncalls = 0;
(function _loop(err) {
if (err || n-- <= 0) return callback(err);
(ncalls++ > 100) ? setImmediate((++n, (ncalls = 0), _loop)) : proc(_loop, (ix++));
})();
}
// qfputs test
// Copyright (C) 2014 Andras Radics
// Licensed under the Apache License, Version 2.0
var fs = require('fs');
try {
var fse = require('fs-ext');
} catch (e) {
}
var Fputs = require('../');
var FileWriter = require('../lib/filewriter.js');
var fromBuf = eval('parseInt(process.versions.node) >= 7 ? Buffer.from : Buffer');
function uniqid( ) {
return Math.floor(Math.random() * 0x100000000).toString(16);
}
module.exports = {
setUp: function(cb) {
var tempfile = "/tmp/nodeunit-" + process.pid + ".tmp";
var tempfile2 = "/tmp/nodeunit-" + process.pid + "-2.tmp";
try { fs.unlinkSync(tempfile); } catch (e) {}
this.tempfile = tempfile;
this.tempfile2 = tempfile2;
this.mockWriter = {
written: [],
write: function(str, cb) { this.written.push("" + str); cb(); },
fflush: function(cb) { cb(); },
sync: function(cb) { cb(); },
getContents: function() { return this.written.join(''); },
};
this.fileWriter = new Fputs.FileWriter(tempfile, "a");
this.fileWriter.getContents = function() { return "" + fs.readFileSync(this.tempfile); }
this.writer = this.mockWriter;
this.fp = new Fputs(this.writer);
cb();
},
tearDown: function(cb) {
try { fs.unlinkSync(this.tempfile); } catch (e) {}
try { fs.unlinkSync(this.tempfile2); } catch (e) {}
cb();
},
'package should be valid json': function(t) {
json = require('../package.json');
t.done();
},
'should expose FileWriter class': function(t) {
t.equal(Fputs.FileWriter, FileWriter);
t.done();
},
'should expose FileWriter.renameFile class method': function(t) {
t.equal(Fputs.renameFile, FileWriter.renameFile);
t.done();
},
'should expose FileWriter.renameFile as an instance method': function(t) {
t.equal(this.fileWriter.renameFile, FileWriter.renameFile);
t.done();
},
'fputs should lazy create the logfile': function(t) {
var fp = new Fputs(this.tempfile);
t.expect(1);
try {
fs.statSync(this.tempfile);
t.ok(false, "logfile should not exist yet");
} catch (err) { t.ok(true); }
t.done();
},
'should create writable file with default write mode': function(t) {
var fp = new Fputs.FileWriter(this.tempfile);
var self = this;
fp.write("one\ntwo\n", function(err) {
t.ifError(err);
t.equals(fs.readFileSync(self.tempfile), "one\ntwo\n");
t.done();
});
},
'fputs should write contents soon': function(t) {
var line = "test line " + uniqid() + "\n";
this.fp.fputs(line);
var self = this;
setTimeout(function(err) {
t.ifError(err);
t.equals(self.writer.getContents(), line);
t.done();
}, 10);
},
'fputs should not clobber write-only files on reopen': function(t) {
var line1 = "test line " + uniqid() + "\n";
var line2 = "test line " + uniqid() + "\n";
var self = this;
fp = new Fputs(new Fputs.FileWriter(self.tempfile, "w"));
fp.fputs(line1);
setTimeout(function() {
fp.fputs(line2);
fp.fflush(function(err) {
t.ifError(err);
t.equal(fs.readFileSync(self.tempfile), line1 + line2);
t.done();
});
}, 25);
},
'fputs should get newline terminated': function(t) {
var line = "test line " + uniqid();
this.fp.fputs(line);
var self = this;
setTimeout(function(err) {
t.ifError(err);
t.equals(self.writer.getContents(), line + "\n");
t.done();
}, 10);
},
'fflush should write pending data': function(t) {
var line = "test line";
this.fp.fputs(line);
var self = this;
t.expect(2);
this.fp.fflush(function(err) {
t.ifError(err);
var contents = self.writer.getContents();
t.equals(contents, line + "\n");
t.done();
});
},
'fflush should return write errors': function(t) {
writer = new Fputs.FileWriter("/nonesuch", "a");
fp = new Fputs(writer);
fp.write("data");
fp.fflush(function(err) {
t.ok(err instanceof Error);
t.done();
});
},
'abort should discard unwritten data': function(t) {
t.expect(4);
this.fp.fputs("test 1\n");
t.ok(this.fp._syncing);
t.ok(this.fp.datachunks[0]);
var self = this;
this.fp.abort(function(err) {
t.ifError(err);
var contents = self.writer.getContents();
t.equals(contents, "");
t.done();
});
},
'setOnError handler should report errors before fflush callback runs': function(t) {
var writer = new Fputs.FileWriter("/nonesuch", "a");
var fp = new Fputs(writer);
var error = null;
fp.setOnError(function(err) { error = err });
fp.write("data");
fp.fflush(function(err) {
t.ok(!err, "fflush callback should not return error");
t.ok(error instanceof Error, "setOnError should have reported the error");
t.done();
});
},
'getUnwrittenLength': {
'should return unwritten chars for strings': function(t) {
var self = this;
this.fp.write("test\x81\n");
t.equal(this.fp.getUnwrittenLength(), 6);
this.fp.write("test\x82\n");
t.equal(this.fp.getUnwrittenLength(), 12);
this.fp.fflush(function(err) {
t.equal(self.fp.getUnwrittenLength(), 0);
t.done();
});
},
'should return unwritten bytes for string followed by buffer': function(t) {
var self = this;
this.fp.write("test\x81\n");
this.fp.write("test\x82\n");
this.fp.write(fromBuf("test3\n"));
t.equal(this.fp.getUnwrittenLength(), 20);
t.done();
},
},
'drain should write pending data': function(t) {
this.fp.fputs("test 1\n");
var self = this;
this.fp.drain(0, function(err) {
t.ifError(err);
t.equal(self.writer.getContents(), "test 1\n");
t.done();
});
},
'drain should return write errors': function(t) {
writer = new Fputs.FileWriter("/nonesuch", "a");
fp = new Fputs(writer);
fp.write("data");
fp.drain(0, function(err) {
t.ok(err instanceof Error);
t.done();
});
},
'constructor should accept a filename': function(t) {
var fp = new Fputs(this.tempfile);
fp.fputs("test");
var self = this;
fp.fflush(function(err) {
t.ifError(err);
t.equals(fs.readFileSync(self.tempfile), "test\n");
t.done();
});
},
'constructor should accept a writable object': function(t) {
t.expect(1);
var self = this;
writer = fs.createWriteStream(this.tempfile, {flags: "a"});
var fp = new Fputs(writer);
fp.write("data");
fp.fflush(function() {
var contents = "" + fs.readFileSync(self.tempfile);
t.equals(contents, "data");
t.done();
});
},
'write': {
'write should write contents, without newline': function(t) {
var line = "test line " + uniqid();
this.fp.write(line);
var self = this;
this.fp.fflush(function() {
var contents = self.writer.getContents();
t.equals(contents, line);
t.done();
});
},
'write should take an optional callback': function(t) {
t.expect(1);
var self = this;
this.fp.write("test callback", function(err) {
t.ifError(err);
// wait for the write to finish so tempfile can be removed.
// Nodejs is async, the pending write can land in the next tests tempfile.
self.fp.fflush(function() {
t.done();
});
});
},
'write should return true if buffer has room': function(t) {
var fp = this.fp;
var ok = fp.write("test");
t.equal(ok, true);
t.done();
},
'write should return false if buffer is full': function(t) {
var fp = this.fp;
var nleft = fp.highWaterMark;
while (nleft > 0) {
fp.write("xxxxxxxxxxxxxxxxxxxx");
nleft -= 20;
}
var ok = fp.write("test");
t.equal(ok, false);
t.done();
},
'should write many lines of various sizes': function(t) {
var i, line = "", expect = "";
for (i=0; i<2000; i++) {
line += "xxxx";
expect += line + "\n";
this.fp.fputs(line);
}
var tempfile = this.tempfile;
t.expect(2);
var self = this;
this.fp.fflush(function(err) {
t.ifError(err);
var contents = "" + self.writer.getContents(tempfile);
t.equals(contents, expect);
t.done();
});
},
'should write buffers': function(t) {
var written = [];
var writer = {
write: function(s, cb) { written.push(s.toString()); if (cb) cb() }
}
var fp = new Fputs(writer, {writesize: 20});
var expect = "";
for (var i=0; i<100; i++) {
var line = "test " + i + "\n";
fp.write(fromBuf(line));
expect += line;
}
fp.fflush(function(err){
t.ifError(err);
t.equal(written.join(''), expect);
t.ok(written.length > 25 && written.length < 50);
t.done();
})
},
'should write mix of strings and bufferse': function(t) {
var written = [];
var writer = {
write: function(s, cb) { written.push(s.toString()); if (cb) cb() }
}
var fp = new Fputs(writer, {writesize: 20});
var expect = "";
for (var i=0; i<100; i++) {
var line = "test " + i + "\n";
fp.write(i % 2 ? fromBuf(line) : line);
expect += line;
}
fp.fflush(function(err){
t.ifError(err);
t.equal(written.join(''), expect);
t.ok(written.length > 10 && written.length < 25);
t.done();
})
},
},
'FileWriter': {
'FileWriter.write should write entire string': function(t) {
var self = this;
this.fileWriter.write("test123", function(err) {
t.equal(fs.readFileSync(self.tempfile), "test123");
t.done();
});
},
'FileWriter.write should write buffers': function(t) {
var self = this;
this.fileWriter.write(fromBuf("test123"), function(err) {
t.equal(fs.readFileSync(self.tempfile), "test123");
t.done();
});
},
'FileWriter.renameFile should rename file': function(t) {
var self = this;
fs.writeFileSync(this.tempfile, "test");
Fputs.FileWriter.renameFile(self.tempfile, self.tempfile2, function(err) {
t.equals(fs.readFileSync(self.tempfile2).toString(), "test");
t.done();
});
},
'FileWriter.renameFile should pause N ms': function(t) {
var self = this;
fs.writeFileSync(this.tempfile, "test2");
var t1 = Date.now();
t.expect(1);
Fputs.FileWriter.renameFile(self.tempfile, self.tempfile2, 66, function(err) {
t.ok(Date.now() >= t1 + 66 - 1);
t.done();
});
},
'FileWriter.renameFile should return EEXIST error, not pause and not overwrite if target already exists': function(t) {
var self = this;
fs.writeFileSync(this.tempfile, "test3a");
fs.writeFileSync(this.tempfile2, "test3b");
var t1 = Date.now();
t.expect(4);
Fputs.FileWriter.renameFile(self.tempfile, self.tempfile2, 66, function(err, ret) {
t.ok(Date.now() < t1 + 5);
t.ok(err instanceof Error);
t.ok(err.message.indexOf('EEXIST') === 0);
t.equal(fs.readFileSync(self.tempfile2), "test3b");
t.done();
});
},
'should expose FileWriter.renameFile on fputs instances': function(t) {
var fp = new Fputs(this.fileWriter);
t.equals(Fputs.FileWriter.renameFile, fp.renameFile);
t.done();
},
'FileWriter.renameFile should wait for ongoing write to finish': function(t) {
if (!fse) t.skip();
fs.writeFileSync(this.tempfile, "test4");
var fd = fs.openSync(this.tempfile, 'r');
fse.flockSync(fd, 'ex') ;
var t1 = Date.now();
// protect against setTimeout off-by-one
setTimeout(function(){ fse.flockSync(fd, 'un'); fs.closeSync(fd) }, 125 + 1);
var self = this;
t.expect(3);
Fputs.FileWriter.renameFile(this.tempfile, this.tempfile2, function(err, ret) {
t.ifError(err);
t.ok(Date.now() >= t1 + 125);
t.equal(fs.readFileSync(self.tempfile2).toString(), "test4");
t.done();
});
},
'FileWriter.renameFile should time out after mutexTimeout': function(t) {
if (!fse) t.skip();
fs.writeFileSync(this.tempfile, "test4");
var fd = fs.openSync(this.tempfile, 'r');
fse.flockSync(fd, 'ex') ;
var t1 = Date.now();
setTimeout(function(){ fse.flockSync(fd, 'un'); fs.closeSync(fd) }, 200);
var self = this;
t.expect(3);
Fputs.FileWriter.renameFile(this.tempfile, this.tempfile2, {mutexTimeout: 125, waitMs: 10}, function(err, ret) {
t.ok(err);
t.ok(Date.now() >= t1 + 125);
t.ok(Date.now() < t1 + 150);
// note: node does not exit while fd is locked
fse.flockSync(fd, 'un');
t.done();
});
},
}
}
+16
-5

@@ -7,3 +7,3 @@ /**

*
* Copyright (C) 2014-2015,2019 Andras Radics
* Copyright (C) 2014-2015,2019,2021 Andras Radics
* Licensed under the Apache License, Version 2.0

@@ -16,3 +16,2 @@ */

var fse = _tryRequire('fs-ext');
var aflow = require('aflow');
if (!fse) {

@@ -40,2 +39,5 @@ console.warn("qfputs: fs-ext not installed, appending lines without flock");

var allocBuf = eval('parseInt(process.versions.node) >= 7 ? Buffer.allocUnsafe : Buffer');
var fromBuf = eval('parseInt(process.versions.node) >= 7 ? Buffer.from : Buffer');
function FileWriter( filename, openmode ) {

@@ -60,3 +62,3 @@ if (!(this instanceof FileWriter)) return new FileWriter(filename, openmode);

this.fd = undefined;
this._writebuf = new Buffer(writesize * 1.25);
this._writebuf = allocBuf(writesize * 1.25);
}

@@ -138,3 +140,3 @@

if (nbytes > buf.length - 4) {
buf = new Buffer(str);
buf = fromBuf(str);
nbytes = buf.length;

@@ -173,3 +175,3 @@ }

aflow.series([
runSteps([
function(cb) {

@@ -224,4 +226,13 @@ // only rename if the source exists, otherwise an empty newName would be created "wx" below

// runSteps from mysqule < miniq < qrepeat and aflow
function runSteps(steps, callback) {
var ix = 0;
(function _loop(err, a1, a2) {
if (err || ix >= steps.length) return callback(err, a1, a2);
try { steps[ix++](_loop, a1, a2) } catch (err) { _loop(err) }
})();
}
// expose renameFile as class method also
FileWriter.mutexTimeout = FileWriter.prototype.mutexTimeout;
FileWriter.renameFile = FileWriter.prototype.renameFile;

@@ -16,2 +16,3 @@ /**

var setImmediate = global.setImmediate || process.nextTick;
var fromBuf = eval('parseInt(process.versions.node) >= 7 ? Buffer.from : Buffer');
var FileWriter = require('./filewriter');

@@ -117,3 +118,3 @@

// be sure to adjust the buffered length to keep drain and flush synced up
var chunk = new Buffer(lastChunk);
var chunk = fromBuf(lastChunk);
this.unwrittenLength += chunk.length - lastChunk.length;

@@ -127,3 +128,3 @@ chunks[nchunks-2].chunks.push(chunk);

// if buffer follows string, combine them and swap the string for a list of buffers
var stringBuffer = new Buffer(lastChunk);
var stringBuffer = fromBuf(lastChunk);
chunks[chunks.length-1] = { length: stringBuffer.length + dataItem.length, chunks: [stringBuffer, dataItem] };

@@ -241,3 +242,3 @@ this.unwrittenLength += stringBuffer.length - lastChunk.length;

// ncalls = 0;
// for (i=0; i<100; i++) fs.write(fd, new Buffer("test\n"), 0, 5, null, function(err){ ncalls += 1; });
// for (i=0; i<100; i++) fs.write(fd, fromBuf("test\n"), 0, 5, null, function(err){ ncalls += 1; });
// setTimeout(function waitdone() {

@@ -244,0 +245,0 @@ // (ncalls < 100) ? setTimeout(waitdone, 10) : console.log("done", ncalls);

@@ -1,2 +0,2 @@

Copyright 2014-2015,2019 Andras Radics
Copyright 2014-2015,2019,2021 Andras Radics
andras at andrasq dot com

@@ -3,0 +3,0 @@

{
"name": "qfputs",
"version": "1.8.0",
"version": "1.8.2",
"description": "very fast write-combining bufferd output",

@@ -27,3 +27,2 @@ "license": "Apache-2.0",

"dependencies": {
"aflow": "0.10.0"
},

@@ -30,0 +29,0 @@ "optionalDependencies": {

@@ -197,2 +197,3 @@ qfputs

### 1.8.2 - fix Buffer deprecation warnings, remove `aflow` dependency
### 1.8.0 - deprecate index.js, make fs-ext an optional dependency

@@ -199,0 +200,0 @@ ### 1.7.2 - upgrade fs-ext to 1.2.1 for node-v10, upgrade qnit