Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
mcollina committed Dec 23, 2017
1 parent b01c2d7 commit 878a634
Show file tree
Hide file tree
Showing 9 changed files with 4,680 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
language: node_js
sudo: false
node_js:
- '6'
- '8'
- '9'
58 changes: 57 additions & 1 deletion README.md
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
30 changes: 30 additions & 0 deletions bench.js
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)
}
18 changes: 18 additions & 0 deletions check.js
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()
8 changes: 8 additions & 0 deletions example.js
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')
}
124 changes: 124 additions & 0 deletions index.js
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
Loading

0 comments on commit 878a634

Please sign in to comment.