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)
}