+40
-17
@@ -77,2 +77,6 @@ /** | ||
| Fputs.prototype.getUnwrittenLength = function getUnwrittenLength( ) { | ||
| return this.unwrittenLength - this.writtenLength; | ||
| }, | ||
| /** | ||
@@ -89,6 +93,6 @@ * Append a newline terminated string to the fifo. | ||
| */ | ||
| Fputs.prototype.write = function write( str, callback ) { | ||
| var type = typeof str; | ||
| if (type === 'string' || !Buffer.isBuffer(str)) { | ||
| if (type !== 'string') str = "" + str; | ||
| Fputs.prototype.write = function write( dataItem, callback ) { | ||
| var type = typeof dataItem; | ||
| if (type === 'string' || !Buffer.isBuffer(dataItem)) { | ||
| if (type !== 'string') dataItem = "" + dataItem; | ||
@@ -98,9 +102,9 @@ // merge writes into this.writesize sized data chunks | ||
| var nchunks = this.datachunks.length; | ||
| if (nchunks && typeof this.datachunks[nchunks-1] === 'string' && this.datachunks[nchunks-1].length + str.length <= this.writesize) { | ||
| if (nchunks && typeof this.datachunks[nchunks-1] === 'string' && this.datachunks[nchunks-1].length + dataItem.length <= this.writesize) { | ||
| // the last chunk has space for more | ||
| this.datachunks[nchunks-1] += str; | ||
| this.datachunks[nchunks-1] += dataItem; | ||
| } | ||
| else { | ||
| // else start a new chunk | ||
| this.datachunks.push(str); | ||
| this.datachunks.push(dataItem); | ||
| } | ||
@@ -114,20 +118,23 @@ } | ||
| // if possible, much more efficient to combine buffer-string-buffer into buffer-buffer-buffer | ||
| // be sure to adjust the buffered length to keep drain and flush synced up | ||
| var chunk = new Buffer(lastChunk); | ||
| this.unwrittenLength += chunk.length - lastChunk.length; | ||
| chunks[nchunks-2].chunks.push(chunk); | ||
| chunks[nchunks-2].chunks.push(str); | ||
| chunks[nchunks-2].length += chunk.length + str.length; | ||
| chunks[nchunks-2].chunks.push(dataItem); | ||
| chunks[nchunks-2].length += chunk.length + dataItem.length; | ||
| chunks.pop(); | ||
| } | ||
| else { | ||
| lastChunk = { length: lastChunk.length + str.length, chunks: [new Buffer(lastChunk), str] }; | ||
| chunks.pop(); | ||
| chunks.push(lastChunk); | ||
| // if buffer follows string, combine them and swap the string for a list of buffers | ||
| var stringBuffer = new Buffer(lastChunk); | ||
| chunks[chunks.length-1] = { length: stringBuffer.length + dataItem.length, chunks: [stringBuffer, dataItem] }; | ||
| this.unwrittenLength += stringBuffer.length - lastChunk.length; | ||
| } | ||
| } | ||
| else if (typeof lastChunk === 'object' && lastChunk.length < this.writesize) { | ||
| lastChunk.chunks.push(str); | ||
| lastChunk.length += str.length; | ||
| lastChunk.chunks.push(dataItem); | ||
| lastChunk.length += dataItem.length; | ||
| } | ||
| else { | ||
| chunks.push({ length: str.length, chunks: [str] }); | ||
| chunks.push({ length: dataItem.length, chunks: [dataItem] }); | ||
| } | ||
@@ -141,3 +148,3 @@ } | ||
| } | ||
| this.unwrittenLength += str.length; | ||
| this.unwrittenLength += dataItem.length; | ||
@@ -151,3 +158,3 @@ if (!this._syncing) { | ||
| if (callback) callback(null, str.length); | ||
| if (callback) callback(null, dataItem.length); | ||
| return this.unwrittenLength - this.writtenLength <= this.highWaterMark; | ||
@@ -196,2 +203,17 @@ } | ||
| /** | ||
| * discard any unwritten data and wait for any write in progress to finish | ||
| */ | ||
| Fputs.prototype.abort = function abort( callback ) { | ||
| var self = this; | ||
| this.datachunks = []; | ||
| if (!this._syncing) return callback(self.returnError()); | ||
| // TODO: this._fflushCallbacks.push({n: this.unwrittenLength, f: callback}) | ||
| (function waitloop() { | ||
| if (self._syncing) setTimeout(waitloop, 2); | ||
| else return callback(self.returnError()); | ||
| })(); | ||
| } | ||
| // the sync thread runs whenever there is data waiting, | ||
@@ -214,2 +236,3 @@ // and tries to write chunks ending on line boundaries | ||
| else self._syncing = false; | ||
| // TODO: invoke fflush callbacks from here, to not have to poll | ||
| }); | ||
@@ -216,0 +239,0 @@ |
+4
-4
| { | ||
| "name": "qfputs", | ||
| "version": "1.5.0", | ||
| "version": "1.7.0", | ||
| "description": "very fast write-combining bufferd output", | ||
@@ -38,8 +38,8 @@ "license": "Apache-2.0", | ||
| "dependencies": { | ||
| "aflow": "0.9.3", | ||
| "fs-ext": "0.3.2" | ||
| "aflow": "0.10.0", | ||
| "fs-ext": "0.5.0" | ||
| }, | ||
| "devDependencies": { | ||
| "nodeunit": "0.9.0" | ||
| "qnit": "0.11.0" | ||
| } | ||
| } |
+27
-0
@@ -99,2 +99,16 @@ qfputs | ||
| ### abort( callback(error) ) | ||
| Wait for the current write to finish but discard all other unwritten data. | ||
| Any unreported write error will be returned via the callback. | ||
| ### getUnwrittenLength( ) | ||
| Return the estimated length of data remaining to be written. | ||
| Strings are estimated in characters, Buffers in bytes. Strings converted to | ||
| Buffers for write-combining also adjust the expected length to keep drain and | ||
| fflush in sync with writes. | ||
| ### setOnError( errorHandler(err) ) | ||
@@ -178,2 +192,3 @@ | ||
| - maybe FileWriter.getLockedFd should use mutexTimeout? | ||
| - use a stack for fflush callbacks | ||
@@ -184,2 +199,14 @@ | ||
| ### 1.7.0 | ||
| - upgrade to the faster aflow 0.10.0 | ||
| - upgrade to fs-ext 0.5.0 to work with node-v4 and node-v5 | ||
| - use qnit for unit tests | ||
| ### 1.6.0 | ||
| - abort() method to discard unwritten data and return when last write finishes | ||
| - getUnwrittenLength() method | ||
| - speed up interleaved string-buffer-string writes | ||
| ### 1.5.0 | ||
@@ -186,0 +213,0 @@ |
+37
-0
@@ -146,2 +146,16 @@ // qfputs test | ||
| '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) { | ||
@@ -160,2 +174,25 @@ var writer = new Fputs.FileWriter("/nonesuch", "a"); | ||
| '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(new Buffer("test3\n")); | ||
| t.equal(this.fp.getUnwrittenLength(), 20); | ||
| t.done(); | ||
| }, | ||
| }, | ||
| 'drain should write pending data': function(t) { | ||
@@ -162,0 +199,0 @@ this.fp.fputs("test 1\n"); |
42969
7.98%832
6.94%281
10.63%+ Added
+ Added
+ Added
- Removed
- Removed
Updated
Updated