-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
4,680 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
language: node_js | ||
sudo: false | ||
node_js: | ||
- '6' | ||
- '8' | ||
- '9' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,58 @@ | ||
# sonic-boom | ||
Extremely fast utf8 only stream implementation | ||
|
||
Extremely fast utf8-only stream implementation to write to files and | ||
file descriptors. | ||
|
||
This implementation is partial, but support for being piped in is there. | ||
|
||
## Install | ||
|
||
``` | ||
npm i sonic-boom | ||
``` | ||
|
||
## Example | ||
|
||
```js | ||
'use strict' | ||
|
||
const SonicBoom = require('sonic-boom') | ||
const sonic = new SonicBoom(process.stdout.fd) // or '/path/to/destination' | ||
|
||
for (var i = 0; i < 10; i++) { | ||
sonic.write('hello sonic\n') | ||
} | ||
``` | ||
|
||
## API | ||
|
||
### SonicBoom(String|Number) | ||
|
||
Creates a new instance of SonicBoom. | ||
|
||
The first argument can be: | ||
|
||
1. a string that is a path to a file to be written to (mode `'a'`) | ||
2. a file descriptor, something that is returned by `fs.open` or | ||
`fs.openSync`. | ||
|
||
### SonicBoom#write(string) | ||
|
||
Writes the string to the file. | ||
It will return false to signal the producer to slow down. | ||
|
||
### SonicBoom#end() | ||
|
||
Closes the stream, the data will be flushed down asynchronously | ||
|
||
### SonicBook#destroy() | ||
|
||
Closes the stream immediately, the data is not flushed. | ||
|
||
### SonicBoom#flushSync() | ||
|
||
Flushes the buffered data synchronously | ||
|
||
## License | ||
|
||
MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
'use strict' | ||
|
||
var bench = require('fastbench') | ||
var SonicBoom = require('./') | ||
var fs = require('fs') | ||
|
||
var core = fs.createWriteStream('/dev/null') | ||
var fd = fs.openSync('/dev/null', 'w') | ||
var sonic = new SonicBoom(fd) | ||
|
||
setTimeout(doBench, 100) | ||
|
||
var run = bench([ | ||
function benchSonic (cb) { | ||
sonic.once('drain', cb) | ||
for (var i = 0; i < 10000; i++) { | ||
sonic.write('hello world') | ||
} | ||
}, | ||
function benchCore (cb) { | ||
core.once('drain', cb) | ||
for (var i = 0; i < 10000; i++) { | ||
core.write('hello world') | ||
} | ||
} | ||
], 1000) | ||
|
||
function doBench () { | ||
run(run) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
'use strict' | ||
|
||
const SonicBoom = require('.') | ||
const sonic = new SonicBoom(process.stdout.fd) | ||
|
||
let count = 0 | ||
function scheduleWrites () { | ||
for (var i = 0; i < 1000; i++) { | ||
sonic.write('hello sonic\n') | ||
console.log('hello console') | ||
} | ||
|
||
if (++count < 10) { | ||
setTimeout(scheduleWrites, 100) | ||
} | ||
} | ||
|
||
scheduleWrites() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
'use strict' | ||
|
||
const SonicBoom = require('.') | ||
const sonic = new SonicBoom(process.stdout.fd) // or 'destination' | ||
|
||
for (var i = 0; i < 10; i++) { | ||
sonic.write('hello sonic\n') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
'use strict' | ||
|
||
const fs = require('fs') | ||
const EventEmitter = require('events') | ||
const reusify = require('reusify') | ||
const flatstr = require('flatstr') | ||
|
||
class Holder { | ||
constructor () { | ||
this.sonic = null | ||
var that = this | ||
this.release = function (err) { | ||
var sonic = that.sonic | ||
if (err) { | ||
sonic.emit('error', err) | ||
return | ||
} | ||
|
||
pool.release(that) | ||
|
||
if (sonic._buf.length > 0) { | ||
actualWrite(sonic) | ||
} else if (sonic._ending === true) { | ||
actualClose(sonic) | ||
} else { | ||
sonic._writing = false | ||
sonic.emit('drain') | ||
} | ||
} | ||
} | ||
} | ||
|
||
const pool = reusify(Holder) | ||
|
||
class SonicBoom extends EventEmitter { | ||
constructor (fd) { | ||
super() | ||
this._buf = '' | ||
this.fd = -1 | ||
this._writing = false | ||
this._ending = false | ||
this.destroyed = false | ||
|
||
if (typeof fd === 'number') { | ||
this.fd = fd | ||
} else if (typeof fd === 'string') { | ||
this._writing = true | ||
fs.open(fd, 'a', (err, fd) => { | ||
if (err) { | ||
this.emit('error', err) | ||
return | ||
} | ||
|
||
this.fd = fd | ||
this._writing = false | ||
// start | ||
if (this._buf.length > 0) { | ||
actualWrite(this) | ||
} | ||
}) | ||
} else { | ||
throw new Error('SonicBoom supports only file descriptors and files') | ||
} | ||
} | ||
|
||
write (data) { | ||
if (this.destroyed) { | ||
throw new Error('SonicBoom destroyed') | ||
} | ||
this._buf += data | ||
if (this._writing === false) { | ||
actualWrite(this) | ||
} | ||
return this._buf.length < 16384 | ||
} | ||
|
||
end () { | ||
if (this._writing === true) { | ||
this._ending = true | ||
return | ||
} | ||
|
||
actualClose(this) | ||
} | ||
|
||
flushSync () { | ||
if (this.fd < 0) { | ||
throw new Error('sonic boom is not ready yet') | ||
} | ||
|
||
if (this._buf.length > 0) { | ||
fs.writeSync(this.fd, this._buf, 'utf8') | ||
} | ||
} | ||
|
||
destroy () { | ||
actualClose(this) | ||
} | ||
} | ||
|
||
function actualWrite (sonic) { | ||
const holder = pool.get() | ||
holder.sonic = sonic | ||
sonic._writing = true | ||
flatstr(sonic._buf) | ||
fs.write(sonic.fd, sonic._buf, 'utf8', holder.release) | ||
sonic._buf = '' | ||
} | ||
|
||
function actualClose (sonic) { | ||
// TODO write a test to check if we are not leaking fds | ||
fs.close(sonic.fd, (err) => { | ||
if (err) { | ||
sonic.emit('error', err) | ||
return | ||
} | ||
|
||
sonic.emit('finish') | ||
}) | ||
sonic.destroyed = true | ||
sonic._buf = '' | ||
} | ||
|
||
module.exports = SonicBoom |
Oops, something went wrong.