103 lines
2.7 KiB
JavaScript
103 lines
2.7 KiB
JavaScript
|
var util = require('util')
|
||
|
var Stream = require('stream')
|
||
|
var StringDecoder = require('string_decoder').StringDecoder
|
||
|
|
||
|
module.exports = StringStream
|
||
|
module.exports.AlignedStringDecoder = AlignedStringDecoder
|
||
|
|
||
|
function StringStream(from, to) {
|
||
|
if (!(this instanceof StringStream)) return new StringStream(from, to)
|
||
|
|
||
|
Stream.call(this)
|
||
|
|
||
|
if (from == null) from = 'utf8'
|
||
|
|
||
|
this.readable = this.writable = true
|
||
|
this.paused = false
|
||
|
this.toEncoding = (to == null ? from : to)
|
||
|
this.fromEncoding = (to == null ? '' : from)
|
||
|
this.decoder = new AlignedStringDecoder(this.toEncoding)
|
||
|
}
|
||
|
util.inherits(StringStream, Stream)
|
||
|
|
||
|
StringStream.prototype.write = function(data) {
|
||
|
if (!this.writable) {
|
||
|
var err = new Error('stream not writable')
|
||
|
err.code = 'EPIPE'
|
||
|
this.emit('error', err)
|
||
|
return false
|
||
|
}
|
||
|
if (this.fromEncoding) {
|
||
|
if (Buffer.isBuffer(data)) data = data.toString()
|
||
|
data = new Buffer(data, this.fromEncoding)
|
||
|
}
|
||
|
var string = this.decoder.write(data)
|
||
|
if (string.length) this.emit('data', string)
|
||
|
return !this.paused
|
||
|
}
|
||
|
|
||
|
StringStream.prototype.flush = function() {
|
||
|
if (this.decoder.flush) {
|
||
|
var string = this.decoder.flush()
|
||
|
if (string.length) this.emit('data', string)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
StringStream.prototype.end = function() {
|
||
|
if (!this.writable && !this.readable) return
|
||
|
this.flush()
|
||
|
this.emit('end')
|
||
|
this.writable = this.readable = false
|
||
|
this.destroy()
|
||
|
}
|
||
|
|
||
|
StringStream.prototype.destroy = function() {
|
||
|
this.decoder = null
|
||
|
this.writable = this.readable = false
|
||
|
this.emit('close')
|
||
|
}
|
||
|
|
||
|
StringStream.prototype.pause = function() {
|
||
|
this.paused = true
|
||
|
}
|
||
|
|
||
|
StringStream.prototype.resume = function () {
|
||
|
if (this.paused) this.emit('drain')
|
||
|
this.paused = false
|
||
|
}
|
||
|
|
||
|
function AlignedStringDecoder(encoding) {
|
||
|
StringDecoder.call(this, encoding)
|
||
|
|
||
|
switch (this.encoding) {
|
||
|
case 'base64':
|
||
|
this.write = alignedWrite
|
||
|
this.alignedBuffer = new Buffer(3)
|
||
|
this.alignedBytes = 0
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
util.inherits(AlignedStringDecoder, StringDecoder)
|
||
|
|
||
|
AlignedStringDecoder.prototype.flush = function() {
|
||
|
if (!this.alignedBuffer || !this.alignedBytes) return ''
|
||
|
var leftover = this.alignedBuffer.toString(this.encoding, 0, this.alignedBytes)
|
||
|
this.alignedBytes = 0
|
||
|
return leftover
|
||
|
}
|
||
|
|
||
|
function alignedWrite(buffer) {
|
||
|
var rem = (this.alignedBytes + buffer.length) % this.alignedBuffer.length
|
||
|
if (!rem && !this.alignedBytes) return buffer.toString(this.encoding)
|
||
|
|
||
|
var returnBuffer = new Buffer(this.alignedBytes + buffer.length - rem)
|
||
|
|
||
|
this.alignedBuffer.copy(returnBuffer, 0, 0, this.alignedBytes)
|
||
|
buffer.copy(returnBuffer, this.alignedBytes, 0, buffer.length - rem)
|
||
|
|
||
|
buffer.copy(this.alignedBuffer, 0, buffer.length - rem, buffer.length)
|
||
|
this.alignedBytes = rem
|
||
|
|
||
|
return returnBuffer.toString(this.encoding)
|
||
|
}
|