lzma-purejs-requirejs
Version:
pure JavaScript LZMA de/compression, for node.js, volo, and the browser.
162 lines (147 loc) • 4.76 kB
JavaScript
'use strict';
const freeze = require('../freeze');
// largest 32/24/16/8-bit numbers.
var MAX32 = 0xFFFFFFFF;
var MAX24 = 0x00FFFFFF;
var MAX16 = 0x0000FFFF;
var MAX8 = 0x000000FF;
var MASK24= 0xFF000000; // 32-bit inverse of MAX24
var kNumBitModelTotalBits = 11;
var kBitModelTotal = (1 << kNumBitModelTotalBits);
var kNumMoveBits = 5;
var kNumMoveReducingBits = 2;
var kNumBitPriceShiftBits = 6;
var Encoder = function(stream){
this.init();
if (stream) { this.setStream(stream); }
};
Encoder.prototype.setStream = function(stream) {
this._stream = stream;
};
Encoder.prototype.releaseStream = function() {
this._stream = null;
};
Encoder.prototype.init = function() {
// The cache/cache_size variables are used to properly handle
// carries, and represent a number defined by a big-endian sequence
// starting with the cache value, and followed by cache_size 0xff
// bytes, which has been shifted out of the low register, but hasn't
// been written yet, because it could be incremented by one due to a
// carry.
// Note that the first byte output will always be 0 due to the fact
// that cache and low are initialized to 0, and the encoder
// implementation; the decoder ignores this byte.
this._position = 0;
this.low = 0; // unsigned 33-bit integer
this.range = MAX32; // unsigned 32-bit integer
// cacheSize needs to be large enough to store the full uncompressed
// size; javascript's 2^53 limit should be enough
this._cacheSize = 1;
this._cache = 0; // unsigned 8-bit
};
Encoder.prototype.flushData = function() {
var i;
for (i=0; i<5; i++) {
this.shiftLow();
}
};
Encoder.prototype.flushStream = function() {
if (this._stream.flush) {
this._stream.flush();
}
};
Encoder.prototype.shiftLow = function() {
// "normalization"
var overflow = (this.low > MAX32) ? 1 : 0;
if (this.low < MASK24 || overflow) {
this._position += this._cacheSize;
var temp = this._cache;
do {
this._stream.writeByte((temp + overflow) & MAX8);
temp = MAX8;
} while (--this._cacheSize !== 0);
// set cache to bits 24-31 of 'low'
this._cache = this.low >>> 24; // this truncates correctly
// set cache_size to 0 (do/while loop did this)
}
this._cacheSize++;
// 'lowest 24 bits of low, shifted left by 8'
// careful, '<< 8' can flip sign of 'low'
this.low = (this.low & MAX24) * 256;
};
Encoder.prototype.encodeDirectBits = function(v, numTotalBits) {
var i, mask;
mask = 1 << (numTotalBits-1);
for (i = numTotalBits - 1; i >= 0; i--, mask>>>=1) {
this.range >>>= 1; // range is unsigned 32-bit int
if (v & mask) {
this.low += this.range;
}
if (this.range <= MAX24) {
this.range *= 256; // careful not to flip sign
this.shiftLow();
}
}
};
Encoder.prototype.getProcessedSizeAdd = function() {
return this._cacheSize + this._position + 4;
};
Encoder.initBitModels = function(probs, len) {
var i;
if (len && !probs) {
if (typeof(Uint16Array)!=='undefined') {
probs = new Uint16Array(len);
} else {
probs = [];
probs.length = len;
}
}
for (i=0; i < probs.length; i++)
probs[i] = (kBitModelTotal >>> 1); // 0.5 probability
return probs;
};
Encoder.prototype.encode = function(probs, index, symbol) {
var prob = probs[index];
var newBound = (this.range >>> kNumBitModelTotalBits) * prob;
if (symbol === 0) {
this.range = newBound;
probs[index] = prob + ((kBitModelTotal - prob) >>> kNumMoveBits);
} else {
this.low += newBound;
this.range -= newBound;
probs[index] = prob - (prob >>> kNumMoveBits);
}
if (this.range <= MAX24) {
this.range *= 256; // careful not to flip sign
this.shiftLow();
}
};
var ProbPrices = [];
if (typeof(Uint32Array)!=='undefined') {
ProbPrices = new Uint32Array(kBitModelTotal >>> kNumMoveReducingBits);
}
(function() {
var kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits);
var i, j;
for (i = kNumBits - 1; i >= 0; i--) {
var start = 1 << (kNumBits - i - 1);
var end = 1 << (kNumBits - i);
for (j = start; j < end; j++) {
ProbPrices[j] = (i << kNumBitPriceShiftBits) +
(((end - j) << kNumBitPriceShiftBits) >>> (kNumBits - i - 1));
}
}
})();
Encoder.getPrice = function(prob, symbol) {
return ProbPrices[(((prob - symbol) ^ ((-symbol))) & (kBitModelTotal - 1)) >>> kNumMoveReducingBits];
};
Encoder.getPrice0 = function(prob) {
return ProbPrices[prob >>> kNumMoveReducingBits];
};
Encoder.getPrice1 = function(prob) {
return ProbPrices[(kBitModelTotal - prob) >>> kNumMoveReducingBits];
};
// export constants for use in Encoder.
Encoder.kNumBitPriceShiftBits = kNumBitPriceShiftBits;
freeze(Encoder.prototype);
module.exports = freeze(Encoder);