lzw-stream
Version:
Streaming LZW decoder and encoder
104 lines (82 loc) • 2.42 kB
JavaScript
var Transform = require('stream').Transform;
var util = require('util');
var MAX_DICT = 4096;
function LZWEncoder(dataBits) {
Transform.call(this);
this.dataBits = dataBits || 8;
this.clearCode = 1 << this.dataBits;
this.codeMask = this.clearCode - 1;
this.eoiCode = this.clearCode + 1;
this.nextCode = this.eoiCode + 1;
this.codeSize = this.dataBits + 1;
this.curShift = 0;
this.first = true;
this.curCode = 0;
this.codeTable = {};
this.cur = 0;
this.buf = new Buffer(255);
this.pos = 0;
}
util.inherits(LZWEncoder, Transform);
LZWEncoder.prototype._transform = function(data, encoding, done) {
var i = 0;
if (this.first) {
this.curCode = data[i++] & this.codeMask;
this.emitCode(this.clearCode);
this.first = false;
}
for (; i < data.length; i++) {
var k = data[i] & this.codeMask;
var curKey = this.curCode << 8 | k;
if (curKey in this.codeTable) {
this.curCode = this.codeTable[curKey];
} else {
this.emitCode(this.curCode);
// reset dictionary if we hit the size limit
if (this.nextCode === MAX_DICT) {
this.emitCode(this.clearCode);
this.nextCode = this.eoiCode + 1;
this.codeSize = this.dataBits + 1;
this.codeTable = {};
} else {
// increase code size if we used up all the
// available bits at the current size
if (this.nextCode >= (1 << this.codeSize))
this.codeSize++;
// add a new code to the dictionary
this.codeTable[curKey] = this.nextCode++;
}
this.curCode = k;
}
}
done();
};
LZWEncoder.prototype._flush = function(done) {
this.emitCode(this.curCode);
this.emitCode(this.eoiCode);
this.emitBits(1);
this.outputBuffer();
done();
};
LZWEncoder.prototype.emitCode = function(c) {
this.cur |= c << this.curShift;
this.curShift += this.codeSize;
if (this.curShift >= 8)
this.emitBits(8);
};
LZWEncoder.prototype.emitBits = function(bits) {
while (this.curShift >= bits) {
this.buf[this.pos++] = this.cur & 0xff;
this.cur >>>= 8;
this.curShift -= 8;
if (this.pos === this.buf.length)
this.outputBuffer();
}
};
LZWEncoder.prototype.outputBuffer = function() {
if (this.pos > 0)
this.push(this.buf.slice(0, this.pos));
this.buf = new Buffer(255);
this.pos = 0;
};
module.exports = LZWEncoder;