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