@2003scape/rsc-client
Version:
runescape classic web client
1,424 lines (1,336 loc) • 2.08 MB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
const mudclient = require('./src/mudclient');
if (typeof window === 'undefined') {
throw new Error('rsc-client needs to run in a browser');
}
(async () => {
const mcContainer = document.createElement('div');
const args = window.location.hash.slice(1).split(',');
const mc = new mudclient(mcContainer);
window.mcOptions = mc.options;
Object.assign(mc.options, {
middleClickCamera: true,
mouseWheel: true,
resetCompass: true,
zoomCamera: true,
accountManagement: true,
mobile: false
});
mc.members = args[0] === 'members';
mc.server = args[1] ? args[1] : '127.0.0.1';
mc.port = args[2] && !isNaN(+args[2]) ? +args[2] : 43595;
mc.threadSleep = 10;
document.body.appendChild(mcContainer);
const fullscreen = document.createElement('button');
fullscreen.innerText = 'Fullscreen';
fullscreen.onclick = () => {
mcContainer.requestFullscreen();
};
document.body.appendChild(fullscreen);
await mc.startApplication(512, 346, 'Runescape by Andrew Gower');
})();
},{"./src/mudclient":243}],2:[function(require,module,exports){
/** Burrows-Wheeler transform, computed with the Induced Sorting Suffix Array
* construction mechanism (sais). Code is a port of:
* https://sites.google.com/site/yuta256/sais
* which is:
* Copyright (c) 2008-2010 Yuta Mori All Rights Reserved.
* and licensed under an MIT/X11 license. I generally looked at both
* the C and the Java implementations to guide my work.
*
* This JavaScript port is:
* Copyright (c) 2013 C. Scott Ananian
* and licensed under GPLv2; see the README at the top level of this package.
*/
const freeze = require('./freeze');
const Util = require('./Util');
var ASSERT = console.assert.bind(console);
// we're dispensing with the "arbitrary alphabet" stuff of the source
// and just using Uint8Arrays.
/** Find the start or end of each bucket. */
var getCounts = function(T, C, n, k) {
var i;
for (i = 0; i < k; i++) { C[i] = 0; }
for (i = 0; i < n; i++) { C[T[i]]++; }
};
var getBuckets = function(C, B, k, end) {
var i, sum = 0;
if (end) {
for (i = 0; i < k; i++) { sum += C[i]; B[i] = sum; }
} else {
for (i = 0; i < k; i++) { sum += C[i]; B[i] = sum - C[i]; }
}
};
/** Sort all type LMS suffixes */
var LMSsort = function(T, SA, C, B, n, k) {
var b, i, j;
var c0, c1;
/* compute SAl */
if (C === B) { getCounts(T, C, n, k); }
getBuckets(C, B, k, false); /* find starts of buckets */
j = n - 1;
b = B[c1 = T[j]];
j--;
SA[b++] = (T[j] < c1) ? ~j : j;
for (i = 0; i < n; i++) {
if ((j = SA[i]) > 0) {
ASSERT(T[j] >= T[j+1]);
if ((c0 = T[j]) !== c1) { B[c1] = b; b = B[c1 = c0]; }
ASSERT(i < b);
j--;
SA[b++] = (T[j] < c1) ? ~j : j;
SA[i] = 0;
} else if (j < 0) {
SA[i] = ~j;
}
}
/* compute SAs */
if (C === B) { getCounts(T, C, n, k); }
getBuckets(C, B, k, 1); /* find ends of buckets */
for (i = n-1, b = B[c1 = 0]; i >= 0; i--) {
if ((j = SA[i]) > 0) {
ASSERT(T[j] <= T[j+1]);
if ((c0 = T[j]) !== c1) { B[c1] = b; b = B[c1 = c0]; }
ASSERT(b <= i);
j--;
SA[--b] = (T[j] > c1) ? ~(j+1) : j;
SA[i] = 0;
}
}
};
var LMSpostproc = function(T, SA, n, m) {
var i, j, p, q, plen, qlen, name;
var c0, c1;
var diff;
/* compact all the sorted substrings into the first m items of SA
* 2*m must not be larger than n (provable) */
ASSERT(n > 0);
for (i = 0; (p = SA[i]) < 0; i++) { SA[i] = ~p; ASSERT((i+1) < n); }
if (i < m) {
for (j = i, i++; ; i++) {
ASSERT(i < n);
if ((p = SA[i]) < 0) {
SA[j++] = ~p; SA[i] = 0;
if (j === m) { break; }
}
}
}
/* store the length of all substrings */
c0 = T[i = j = n - 1];
do { c1 = c0; } while ( ((--i) >= 0 ) && ((c0=T[i]) >= c1) );
for (; i >= 0; ) {
do { c1 = c0; } while ( ((--i) >= 0 ) && ((c0=T[i]) <= c1) );
if (i >= 0) {
SA[m + ((i + 1) >>> 1)] = j - i; j = i + 1;
do { c1 = c0; } while ( ((--i) >= 0 ) && ((c0=T[i]) >= c1) );
}
}
/* find the lexicographic names of all substrings */
for (i = 0, name = 0, q = n, qlen = 0; i < m; i++) {
p = SA[i]; plen = SA[m + (p >>> 1)]; diff = true;
if ((plen === qlen) && ((q + plen) < n)) {
for (j = 0; (j < plen) && (T[p + j] === T[q + j]); ) { j++; }
if (j === plen) { diff = false; }
}
if (diff) { name++; q = p; qlen = plen; }
SA[m + (p >>> 1)] = name;
}
return name;
};
/* compute SA and BWT */
var induceSA = function(T, SA, C, B, n, k) {
var b, i, j;
var c0, c1;
/* compute SAl */
if (C === B) { getCounts(T, C, n, k); }
getBuckets(C, B, k, false); /* find starts of buckets */
j = n - 1;
b = B[c1 = T[j]];
SA[b++] = ((j > 0) && (T[j-1] < c1)) ? ~j : j;
for (i = 0; i < n; i++) {
j = SA[i]; SA[i] = ~j;
if (j > 0) {
j--;
ASSERT( T[j] >= T[j + 1] );
if ((c0 = T[j]) !== c1) { B[c1] = b; b = B[c1=c0]; }
ASSERT( i < b );
SA[b++] = ((j > 0) && (T[j-1] < c1)) ? ~j : j;
}
}
/* compute SAs */
if (C === B) { getCounts(T, C, n, k); }
getBuckets(C, B, k, true); /* find ends of buckets */
for (i = n-1, b = B[c1 = 0]; i >= 0; i--) {
if ((j = SA[i]) > 0) {
j--;
ASSERT( T[j] <= T[j + 1] );
if ((c0 = T[j]) !== c1) { B[c1] = b; b = B[c1 = c0]; }
ASSERT( b <= i );
SA[--b] = ((j === 0) || (T[j - 1] > c1)) ? ~j : j;
} else {
SA[i] = ~j;
}
}
};
var computeBWT = function(T, SA, C, B, n, k) {
var b, i, j, pidx = -1;
var c0, c1;
/* compute SAl */
if (C === B) { getCounts(T, C, n, k); }
getBuckets(C, B, k, false); /* find starts of buckets */
j = n - 1;
b = B[c1 = T[j]];
SA[b++] = ((j > 0) && (T[j - 1] < c1)) ? ~j : j;
for (i = 0; i < n; i++) {
if ((j=SA[i]) > 0) {
j--;
ASSERT( T[j] >= T[j+1] );
SA[i] = ~(c0 = T[j]);
if (c0 !== c1) { B[c1] = b; b = B[c1 = c0]; }
ASSERT( i < b );
SA[b++] = ((j > 0) && (T[j - 1] < c1)) ? ~j : j;
} else if (j !== 0) {
SA[i] = ~j;
}
}
/* compute SAs */
if (C === B) { getCounts(T, C, n, k); }
getBuckets(C, B, k, true); /* find ends of buckets */
for (i = n-1, b = B[c1 = 0]; i >= 0; i--) {
if ((j = SA[i]) > 0) {
j--;
ASSERT( T[j] <= T[j+1] );
SA[i] = c0 = T[j];
if (c0 !== c1) { B[c1] = b; b = B[c1 = c0]; }
ASSERT( b <= i );
SA[--b] = ((j > 0) && (T[j-1] > c1)) ? (~T[j-1]) : j;
} else if (j !== 0) {
SA[i] = ~j;
} else {
pidx = i;
}
}
return pidx;
};
/* find the suffix array SA of T[0..n-1] in {0..k-1}^n
use a working space (excluding T and SA) of at most 2n+O(1) for a
constant alphabet */
var SA_IS = function(T, SA, fs, n, k, isbwt) {
var C, B, RA;
var i, j, b, c, m, p, q, name, pidx = 0, newfs;
var c0, c1;
var flags = 0;
// allocate temporary storage [CSA]
if (k <= 256) {
C = Util.makeS32Buffer(k);
if (k <= fs) { B = SA.subarray(n + fs - k); flags = 1; }
else { B = Util.makeS32Buffer(k); flags = 3; }
} else if (k <= fs) {
C = SA.subarray(n + fs - k);
if (k <= (fs - k)) { B = SA.subarray(n + fs - k * 2); flags = 0; }
else if (k <= 1024) { B = Util.makeS32Buffer(k); flags = 2; }
else { B = C; flags = 8; }
} else {
C = B = Util.makeS32Buffer(k);
flags = 4 | 8;
}
/* stage 1: reduce the problem by at least 1/2
sort all the LMS-substrings */
getCounts(T, C, n, k);
getBuckets(C, B, k, true); /* find ends of buckets */
for (i = 0; i < n; i++) { SA[i] = 0; }
b = -1; i = n - 1; j = n; m = 0; c0 = T[n - 1];
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) >= c1));
for (; i >= 0 ;) {
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) <= c1));
if ( i >= 0 ) {
if ( b >= 0 ) { SA[b] = j; }
b = --B[c1];
j = i;
++m;
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) >= c1));
}
}
if (m > 1) {
LMSsort(T, SA, C, B, n, k);
name = LMSpostproc(T, SA, n, m);
} else if (m === 1) {
SA[b] = j + 1;
name = 1;
} else {
name = 0;
}
/* stage 2: solve the reduced problem
recurse if names are not yet unique */
if(name < m) {
if((flags & 4) !== 0) { C = null; B = null; }
if((flags & 2) !== 0) { B = null; }
newfs = (n + fs) - (m * 2);
if((flags & (1 | 4 | 8)) === 0) {
if((k + name) <= newfs) { newfs -= k; }
else { flags |= 8; }
}
ASSERT( (n >>> 1) <= (newfs + m) );
for (i = m + (n >>> 1) - 1, j = m * 2 + newfs - 1; m <= i; i--) {
if(SA[i] !== 0) { SA[j--] = SA[i] - 1; }
}
RA = SA.subarray(m + newfs);
SA_IS(RA, SA, newfs, m, name, false);
RA = null;
i = n - 1; j = m * 2 - 1; c0 = T[n - 1];
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) >= c1));
for (; i >= 0 ;) {
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) <= c1));
if ( i >= 0 ) {
SA[j--] = i + 1;
do { c1 = c0; } while ((--i >= 0) && ((c0 = T[i]) >= c1));
}
}
for (i = 0; i < m; i++) { SA[i] = SA[m + SA[i]]; }
if((flags & 4) !== 0) { C = B = Util.makeS32Buffer(k); }
if((flags & 2) !== 0) { B = Util.makeS32Buffer(k); }
}
/* stage 3: induce the result for the original problem */
if((flags & 8) !== 0) { getCounts(T, C, n, k); }
/* put all left-most S characters into their buckets */
if (m > 1) {
getBuckets(C, B, k, true); /* find ends of buckets */
i = m - 1; j = n; p = SA[m - 1]; c1 = T[p];
do {
q = B[c0 = c1];
while (q < j) { SA[--j] = 0; }
do {
SA[--j] = p;
if(--i < 0) { break; }
p = SA[i];
} while((c1 = T[p]) === c0);
} while (i >= 0 );
while ( j > 0 ) { SA[--j] = 0; }
}
if (!isbwt) { induceSA(T, SA, C, B, n, k); }
else { pidx = computeBWT(T, SA, C, B, n, k); }
C = null; B = null;
return pidx;
};
var BWT = Object.create(null);
/** SA should be a Int32Array (signed!); T can be any typed array.
* alphabetSize is optional if T is an Uint8Array or Uint16Array. */
BWT.suffixsort = function(T, SA, n, alphabetSize) {
ASSERT( T && SA && T.length >= n && SA.length >= n );
if (n <= 1) {
if (n === 1) { SA[0] = 0; }
return 0;
}
if (!alphabetSize) {
if (T.BYTES_PER_ELEMENT === 1) { alphabetSize = 256; }
else if (T.BYTES_PER_ELEMENT === 2) { alphabetSize = 65536; }
else throw new Error('Need to specify alphabetSize');
}
ASSERT( alphabetSize > 0 );
if (T.BYTES_PER_ELEMENT) {
ASSERT( alphabetSize <= (1 << (T.BYTES_PER_ELEMENT*8) ) );
}
return SA_IS(T, SA, 0, n, alphabetSize, false);
};
/** Burrows-Wheeler Transform.
A should be Int32Array (signed!); T can be any typed array.
U is the same type as T (it is used for output).
alphabetSize is optional if T is an Uint8Array or Uint16Array.
ASSUMES STRING IS TERMINATED WITH AN EOF CHARACTER.
*/
BWT.bwtransform = function(T, U, A, n, alphabetSize) {
var i, pidx;
ASSERT( T && U && A );
ASSERT( T.length >= n && U.length >= n && A.length >= n );
if (n <= 1) {
if (n === 1) { U[0] = T[0]; }
return n;
}
if (!alphabetSize) {
if (T.BYTES_PER_ELEMENT === 1) { alphabetSize = 256; }
else if (T.BYTES_PER_ELEMENT === 2) { alphabetSize = 65536; }
else throw new Error('Need to specify alphabetSize');
}
ASSERT( alphabetSize > 0 );
if (T.BYTES_PER_ELEMENT) {
ASSERT( alphabetSize <= (1 << (T.BYTES_PER_ELEMENT*8) ) );
}
pidx = SA_IS(T, A, 0, n, alphabetSize, true);
U[0] = T[n - 1];
for (i = 0; i < pidx ; i++) { U[i + 1] = A[i]; }
for (i += 1; i < n; i++) { U[i] = A[i]; }
return pidx + 1;
};
/** Reverses transform above. (ASSUMED STRING IS TERMINATED WITH EOF.) */
BWT.unbwtransform = function(T, U, LF, n, pidx) {
var C = Util.makeU32Buffer(256);
var i, t;
for (i=0; i<256; i++) { C[i] = 0; }
for (i=0; i<n; i++) { LF[i] = C[T[i]]++; }
for (i=0, t=0; i<256; i++) { t += C[i]; C[i] = t - C[i]; }
for (i=n-1, t=0; i>=0; i--) {
t = LF[t] + C[U[i]=T[t]];
t += (t<pidx) ? 1 : 0;
}
C = null;
};
/** Burrows-Wheeler Transform.
A should be Int32Array (signed!); T can be any typed array.
U is the same type as T (it is used for output).
alphabetSize is optional if T is an Uint8Array or Uint16Array.
ASSUMES STRING IS CYCLIC.
(XXX: this is twice as inefficient as I'd like! [CSA])
*/
BWT.bwtransform2 = function(T, U, n, alphabetSize) {
var i, j, pidx = 0;
ASSERT( T && U );
ASSERT( T.length >= n && U.length >= n );
if (n <= 1) {
if (n === 1) { U[0] = T[0]; }
return 0;
}
if (!alphabetSize) {
if (T.BYTES_PER_ELEMENT === 1) { alphabetSize = 256; }
else if (T.BYTES_PER_ELEMENT === 2) { alphabetSize = 65536; }
else throw new Error('Need to specify alphabetSize');
}
ASSERT( alphabetSize > 0 );
if (T.BYTES_PER_ELEMENT) {
ASSERT( alphabetSize <= (1 << (T.BYTES_PER_ELEMENT*8) ) );
}
// double length of T
var TT;
if (T.length >= n*2) {
TT = T; // do it in place if possible
} else if (alphabetSize <= 256) {
TT = Util.makeU8Buffer(n*2);
} else if (alphabetSize <= 65536) {
TT = Util.makeU16Buffer(n*2);
} else {
TT = Util.makeU32Buffer(n*2);
}
if (TT!==T) {
for (i=0; i<n; i++) { TT[i] = T[i]; }
}
for (i=0; i<n; i++) { TT[n+i] = TT[i]; }
// sort doubled string
var A = Util.makeS32Buffer(n*2);
SA_IS(TT, A, 0, n*2, alphabetSize, false);
for (i=0, j=0; i<2*n; i++) {
var s = A[i];
if (s < n) {
if (s === 0) { pidx = j; }
if (--s < 0) { s = n-1; }
U[j++] = T[s];
}
}
ASSERT(j===n);
return pidx;
};
module.exports = freeze(BWT);
},{"./Util":23,"./freeze":24}],3:[function(require,module,exports){
const freeze = require('./freeze');
const Stream = require('./Stream');
const BWT = require('./BWT');
const DefSumModel = require('./DefSumModel');
const FenwickModel = require('./FenwickModel');
const LogDistanceModel = require('./LogDistanceModel');
const NoModel = require('./NoModel');
const RangeCoder = require('./RangeCoder');
const Util = require('./Util');
/* A simple bzip-like BWT compressor with a range encoder; written as a
* self-test of the BWT package. */
var EOF = Stream.EOF;
var F_PROB_MAX = 0xFF00;
var F_PROB_INCR = 0x0100;
BWTC = Object.create(null);
BWTC.MAGIC = "bwtc";
BWTC.compressFile = Util.compressFileHelper(BWTC.MAGIC, function(input, output, size, props, finalByte) {
var encoder = new RangeCoder(output);
encoder.encodeStart(finalByte, 1);
var blockSize = 9;
if (typeof(props)==='number' && props >= 1 && props <= 9) {
blockSize = props;
}
encoder.encodeByte(blockSize);
var fast = (blockSize <= 5);
blockSize *= 100000;
var block = Util.makeU8Buffer(blockSize);
var readBlock = function() {
var pos;
for (pos=0; pos < blockSize; ) {
var ch = input.readByte();
if (ch < 0) { break; }
block[pos++] = ch;
}
return pos;
};
var U = Util.makeU8Buffer(blockSize);
var A = Util.makeS32Buffer(blockSize);
var M = Util.makeU8Buffer(256); // move to front array
var bitModelFactory = NoModel.factory(encoder);
var lenModel = new LogDistanceModel(blockSize, 0,
bitModelFactory,
bitModelFactory);
var length, b, c, pidx, i, j;
do {
length = readBlock();
if (length === 0) { break; }
// indicate that there's another block comin'
// and encode the length of the block if necessary
if (length === block.length) {
encoder.encodeFreq(1, 0, 3); // "full size block"
b = block;
} else {
encoder.encodeFreq(1, 1, 3); // "short block"
lenModel.encode(length);
b = block.subarray(0, length);
}
pidx = BWT.bwtransform(b, U, A, length, 256);
lenModel.encode(pidx); // starting index
// encode the alphabet subset used
var useTree = Util.makeU16Buffer(512);
for (i=0; i<length; i++) {
c = U[i];
useTree[256+c] = 1;
}
for (i=255; i>0; i--) { // sum all the way up the tree
useTree[i] = useTree[2*i] + useTree[2*i + 1];
}
useTree[0] = 1; // sentinel
for (i=1; i<512; i++) {
var parent = i>>>1;
var full = 1 << (9-Util.fls(i));
if (useTree[parent] === 0 || useTree[parent] === (full*2)) {
/* already known full/empty */
} else if (i >= 256) {
encoder.encodeBit(useTree[i]); // leaf node
} else {
var v = useTree[i];
v = (v===0) ? 0 : (v===full) ? 2 : 1;
encoder.encodeFreq(1, v, 3);
}
}
// remap symbols to this subset
var alphabetSize = 0;
for (i=0; i<256; i++) {
if (useTree[256+i]) { // symbol in use
M[alphabetSize++] = i;
}
}
useTree = null;
// MTF encoding of U
for (i=0; i<length; i++) {
c = U[i];
for (j=0; j<alphabetSize; j++) {
if (M[j] === c) {
break;
}
}
console.assert(j<alphabetSize);
U[i] = j;
// move to front
for (; j>0; j--) {
M[j] = M[j-1];
}
M[0] = c;
}
// RLE/range encoding
var model = new FenwickModel(encoder, alphabetSize+1,
F_PROB_MAX, F_PROB_INCR);
if (fast) { model = new DefSumModel(encoder, alphabetSize+1); }
var runLength = 0;
var emitLastRun = function() {
// binary encode runs of zeros
while (runLength !== 0) {
if (runLength&1) {
model.encode(0); // RUNA
runLength-=1;
} else {
model.encode(1); // RUNB
runLength-=2;
}
runLength >>>= 1;
}
};
for (i=0; i<length; i++) {
c = U[i];
if (c === 0) {
runLength++;
} else {
emitLastRun();
model.encode(c+1);
// reset for next
runLength = 0;
}
}
emitLastRun();
// done with this block!
} while (length === block.length);
encoder.encodeFreq(1, 2, 3); // "no more blocks"
encoder.encodeFinish();
}, true);
BWTC.decompressFile = Util.decompressFileHelper(BWTC.MAGIC, function(input, output, size) {
var decoder = new RangeCoder(input);
decoder.decodeStart(true/* already read the extra byte */);
var blockSize = decoder.decodeByte();
console.assert(blockSize >= 1 && blockSize <= 9);
var fast = (blockSize <= 5);
blockSize *= 100000;
var block = Util.makeU8Buffer(blockSize);
var U = Util.makeU8Buffer(blockSize);
var A = Util.makeS32Buffer(blockSize);
var M = Util.makeU8Buffer(256); // move to front array
var bitModelFactory = NoModel.factory(decoder);
var lenModel = new LogDistanceModel(blockSize, 0,
bitModelFactory,
bitModelFactory);
var b, length, i, j, c;
while (true) {
var blockIndicator = decoder.decodeCulFreq(3);
decoder.decodeUpdate(1, blockIndicator, 3);
if (blockIndicator === 0) { // full-length block
length = blockSize;
b = block;
} else if (blockIndicator === 1) { // short block
length = lenModel.decode();
b = block.subarray(0, length);
} else if (blockIndicator === 2) { // all done, no more blocks
break;
}
// read starting index for unBWT
var pidx = lenModel.decode();
// decode the alphabet subset used
var useTree = Util.makeU16Buffer(512);
useTree[0] = 1; // sentinel
for (i=1; i<512; i++) {
var parent = i>>>1;
var full = 1 << (9-Util.fls(i));
if (useTree[parent] === 0 || useTree[parent] === (full*2)) {
/* already known full/empty */
useTree[i] = useTree[parent] >>> 1;
} else if (i >= 256) {
useTree[i] = decoder.decodeBit(); // leaf node
} else {
var v = decoder.decodeCulFreq(3);
decoder.decodeUpdate(1, v, 3);
useTree[i] = (v===2) ? full : v;
}
}
// remap symbols to this subset
var alphabetSize = 0;
for (i=0; i<256; i++) {
if (useTree[256+i]) { // symbol in use
M[alphabetSize++] = i;
}
}
useTree = null;
// RLE/range decoding
var model = new FenwickModel(decoder, alphabetSize+1,
F_PROB_MAX, F_PROB_INCR);
if (fast) { model = new DefSumModel(decoder, alphabetSize+1, true);}
var val = 1; // repeat count
for (i=0; i<length; ) {
c = model.decode();
if (c===0) {
for (j=0; j<val; j++) { b[i++] = 0; }
val *= 2;
} else if (c===1) {
for (j=0; j<val; j++) { b[i++] = 0; b[i++] = 0; }
val *= 2;
} else {
val = 1;
b[i++] = c-1;
}
}
// MTF decode
for (i=0; i<length; i++) {
j = b[i];
b[i] = c = M[j];
// move to front
for (; j>0; j--) {
M[j] = M[j-1];
}
M[0] = c;
}
// unBWT
BWT.unbwtransform(block, U, A, length, pidx);
// emit!
output.write(U, 0, length);
}
decoder.decodeFinish();
});
module.exports = BWTC;
},{"./BWT":2,"./DefSumModel":8,"./FenwickModel":10,"./LogDistanceModel":13,"./NoModel":18,"./RangeCoder":20,"./Stream":22,"./Util":23,"./freeze":24}],4:[function(require,module,exports){
/** Big-Endian Bit Stream, implemented on top of a (normal byte) stream. */
const Stream = require('./Stream');
var BitStream = function(stream) {
(function() {
var bufferByte = 0x100; // private var for readers
this.readBit = function() {
if ((bufferByte & 0xFF) === 0) {
var ch = stream.readByte();
if (ch === Stream.EOF) {
this._eof = true;
return ch; /* !!! */
}
bufferByte = (ch << 1) | 1;
}
var bit = (bufferByte & 0x100) ? 1 : 0;
bufferByte <<= 1;
return bit;
};
// seekable iff the provided stream is
this.seekBit = function(pos) {
var n_byte = pos >>> 3;
var n_bit = pos - (n_byte*8);
this.seek(n_byte);
this._eof = false;
this.readBits(n_bit);
};
this.tellBit = function() {
var pos = stream.tell() * 8;
var b = bufferByte;
while ((b & 0xFF) !== 0) {
pos--;
b <<= 1;
}
return pos;
};
// implement byte stream interface as well.
this.readByte = function() {
if ((bufferByte & 0xFF) === 0) {
return stream.readByte();
}
return this.readBits(8);
};
this.seek = function(pos) {
stream.seek(pos);
bufferByte = 0x100;
};
}).call(this);
(function() {
var bufferByte = 1; // private var for writers
this.writeBit = function(b) {
bufferByte <<= 1;
if (b) { bufferByte |= 1; }
if (bufferByte & 0x100) {
stream.writeByte(bufferByte & 0xFF);
bufferByte = 1;
}
};
// implement byte stream interface as well
this.writeByte = function(_byte) {
if (bufferByte===1) {
stream.writeByte(_byte);
} else {
stream.writeBits(8, _byte);
}
};
this.flush = function() {
while (bufferByte !== 1) {
this.writeBit(0);
}
if (stream.flush) { stream.flush(); }
};
}).call(this);
};
// inherit read/write methods from Stream.
BitStream.EOF = Stream.EOF;
BitStream.prototype = Object.create(Stream.prototype);
// bit chunk read/write
BitStream.prototype.readBits = function(n) {
var i, r = 0, b;
if (n > 31) {
r = this.readBits(n-16)*0x10000; // fp multiply, not shift
return r + this.readBits(16);
}
for (i = 0; i < n; i++) {
r <<= 1; // this could make a negative value if n>31
// bits read past EOF are all zeros!
if (this.readBit() > 0) { r++; }
}
return r;
};
BitStream.prototype.writeBits = function(n, value) {
if (n > 32) {
var low = (value & 0xFFFF);
var high = (value - low) / (0x10000); // fp division, not shift
this.writeBits(n-16, high);
this.writeBits(16, low);
return;
}
var i;
for (i = n-1; i >= 0; i--) {
this.writeBit( (value >>> i) & 1 );
}
};
module.exports = BitStream;
},{"./Stream":22}],5:[function(require,module,exports){
/*
An implementation of Bzip2 de/compression, including the ability to
seek within bzip2 data.
Copyright (C) 2013 C. Scott Ananian
Copyright (C) 2012 Eli Skeggs
Copyright (C) 2011 Kevin Kwok
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see
http://www.gnu.org/licenses/lgpl-2.1.html
Adapted from node-bzip, copyright 2012 Eli Skeggs.
Adapted from bzip2.js, copyright 2011 Kevin Kwok (antimatter15@gmail.com).
Based on micro-bunzip by Rob Landley (rob@landley.net).
Based on bzip2 decompression code by Julian R Seward (jseward@acm.org),
which also acknowledges contributions by Mike Burrows, David Wheeler,
Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten,
Robert Sedgewick, and Jon L. Bentley.
BWT implementation based on work by Yuta Mori; see BWT.js for details.
bzip2 compression code inspired by https://code.google.com/p/jbzip2
*/
const freeze = require('./freeze');
const BitStream = require('./BitStream');
const BWT = require('./BWT');
const CRC32 = require('./CRC32');
const HuffmanAllocator = require('./HuffmanAllocator');
const Stream = require('./Stream');
const Util = require('./Util');
var MAX_HUFCODE_BITS = 20;
var MAX_SYMBOLS = 258;
var SYMBOL_RUNA = 0;
var SYMBOL_RUNB = 1;
var MIN_GROUPS = 2;
var MAX_GROUPS = 6;
var GROUP_SIZE = 50;
var WHOLEPI = 0x314159265359; // 48-bit integer
var SQRTPI = 0x177245385090; // 48-bit integer
var EOF = Stream.EOF;
var mtf = function(array, index) {
var src = array[index], i;
for (i = index; i > 0; i--) {
array[i] = array[i-1];
}
array[0] = src;
return src;
};
var Err = {
OK: 0,
LAST_BLOCK: -1,
NOT_BZIP_DATA: -2,
UNEXPECTED_INPUT_EOF: -3,
UNEXPECTED_OUTPUT_EOF: -4,
DATA_ERROR: -5,
OUT_OF_MEMORY: -6,
OBSOLETE_INPUT: -7,
END_OF_BLOCK: -8
};
var ErrorMessages = {};
ErrorMessages[Err.LAST_BLOCK] = "Bad file checksum";
ErrorMessages[Err.NOT_BZIP_DATA] = "Not bzip data";
ErrorMessages[Err.UNEXPECTED_INPUT_EOF] = "Unexpected input EOF";
ErrorMessages[Err.UNEXPECTED_OUTPUT_EOF] = "Unexpected output EOF";
ErrorMessages[Err.DATA_ERROR] = "Data error";
ErrorMessages[Err.OUT_OF_MEMORY] = "Out of memory";
ErrorMessages[Err.OBSOLETE_INPUT] = "Obsolete (pre 0.9.5) bzip format not supported.";
var _throw = function(status, optDetail) {
var msg = ErrorMessages[status] || 'unknown error';
if (optDetail) { msg += ': '+optDetail; }
var e = new TypeError(msg);
e.errorCode = status;
throw e;
};
var Bunzip = function(inputStream, outputStream) {
this.writePos = this.writeCurrent = this.writeCount = 0;
this._start_bunzip(inputStream, outputStream);
};
Bunzip.prototype._init_block = function() {
var moreBlocks = this._get_next_block();
if ( !moreBlocks ) {
this.writeCount = -1;
return false; /* no more blocks */
}
this.blockCRC = new CRC32();
return true;
};
/* XXX micro-bunzip uses (inputStream, inputBuffer, len) as arguments */
Bunzip.prototype._start_bunzip = function(inputStream, outputStream) {
/* Ensure that file starts with "BZh['1'-'9']." */
var buf = Util.makeU8Buffer(4);
if (inputStream.read(buf, 0, 4) !== 4 ||
String.fromCharCode(buf[0], buf[1], buf[2]) !== 'BZh')
_throw(Err.NOT_BZIP_DATA, 'bad magic');
var level = buf[3] - 0x30;
if (level < 1 || level > 9)
_throw(Err.NOT_BZIP_DATA, 'level out of range');
this.reader = new BitStream(inputStream);
/* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of
uncompressed data. Allocate intermediate buffer for block. */
this.dbufSize = 100000 * level;
this.nextoutput = 0;
this.outputStream = outputStream;
this.streamCRC = 0;
};
Bunzip.prototype._get_next_block = function() {
var i, j, k;
var reader = this.reader;
// this is get_next_block() function from micro-bunzip:
/* Read in header signature and CRC, then validate signature.
(last block signature means CRC is for whole file, return now) */
var h = reader.readBits(48);
if (h === SQRTPI) { // last block
return false; /* no more blocks */
}
if (h !== WHOLEPI)
_throw(Err.NOT_BZIP_DATA);
this.targetBlockCRC = reader.readBits(32);
this.streamCRC = (this.targetBlockCRC ^
((this.streamCRC << 1) | (this.streamCRC>>>31))) >>> 0;
/* We can add support for blockRandomised if anybody complains. There was
some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
it didn't actually work. */
if (reader.readBits(1))
_throw(Err.OBSOLETE_INPUT);
var origPointer = reader.readBits(24);
if (origPointer > this.dbufSize)
_throw(Err.DATA_ERROR, 'initial position out of bounds');
/* mapping table: if some byte values are never used (encoding things
like ASCII text), the compression code removes the gaps to have fewer
symbols to deal with, and writes a sparse bitfield indicating which
values were present. We make a translation table to convert the symbols
back to the corresponding bytes. */
var t = reader.readBits(16);
var symToByte = Util.makeU8Buffer(256), symTotal = 0;
for (i = 0; i < 16; i++) {
if (t & (1 << (0xF - i))) {
var o = i * 16;
k = reader.readBits(16);
for (j = 0; j < 16; j++)
if (k & (1 << (0xF - j)))
symToByte[symTotal++] = o + j;
}
}
/* How many different Huffman coding groups does this block use? */
var groupCount = reader.readBits(3);
if (groupCount < MIN_GROUPS || groupCount > MAX_GROUPS)
_throw(Err.DATA_ERROR);
/* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding
group. Read in the group selector list, which is stored as MTF encoded
bit runs. (MTF=Move To Front, as each value is used it's moved to the
start of the list.) */
var nSelectors = reader.readBits(15);
if (nSelectors === 0)
_throw(Err.DATA_ERROR);
var mtfSymbol = Util.makeU8Buffer(256);
for (i = 0; i < groupCount; i++)
mtfSymbol[i] = i;
var selectors = Util.makeU8Buffer(nSelectors); // was 32768...
for (i = 0; i < nSelectors; i++) {
/* Get next value */
for (j = 0; reader.readBits(1); j++)
if (j >= groupCount) _throw(Err.DATA_ERROR);
/* Decode MTF to get the next selector */
selectors[i] = mtf(mtfSymbol, j);
}
/* Read the Huffman coding tables for each group, which code for symTotal
literal symbols, plus two run symbols (RUNA, RUNB) */
var symCount = symTotal + 2;
var groups = [], hufGroup;
for (j = 0; j < groupCount; j++) {
var length = Util.makeU8Buffer(symCount), temp = Util.makeU16Buffer(MAX_HUFCODE_BITS + 1);
/* Read Huffman code lengths for each symbol. They're stored in
a way similar to MTF; record a starting value for the first symbol,
and an offset from the previous value for every symbol after that. */
t = reader.readBits(5); // lengths
for (i = 0; i < symCount; i++) {
for (;;) {
if (t < 1 || t > MAX_HUFCODE_BITS) _throw(Err.DATA_ERROR);
/* If first bit is 0, stop. Else second bit indicates whether
to increment or decrement the value. */
if(!reader.readBits(1))
break;
if(!reader.readBits(1))
t++;
else
t--;
}
length[i] = t;
}
/* Find largest and smallest lengths in this group */
var minLen, maxLen;
minLen = maxLen = length[0];
for (i = 1; i < symCount; i++) {
if (length[i] > maxLen)
maxLen = length[i];
else if (length[i] < minLen)
minLen = length[i];
}
/* Calculate permute[], base[], and limit[] tables from length[].
*
* permute[] is the lookup table for converting Huffman coded symbols
* into decoded symbols. base[] is the amount to subtract from the
* value of a Huffman symbol of a given length when using permute[].
*
* limit[] indicates the largest numerical value a symbol with a given
* number of bits can have. This is how the Huffman codes can vary in
* length: each code with a value>limit[length] needs another bit.
*/
hufGroup = {};
groups.push(hufGroup);
hufGroup.permute = Util.makeU16Buffer(MAX_SYMBOLS);
hufGroup.limit = Util.makeU32Buffer(MAX_HUFCODE_BITS + 2);
hufGroup.base = Util.makeU32Buffer(MAX_HUFCODE_BITS + 1);
hufGroup.minLen = minLen;
hufGroup.maxLen = maxLen;
/* Calculate permute[]. Concurrently, initialize temp[] and limit[]. */
var pp = 0;
for (i = minLen; i <= maxLen; i++) {
temp[i] = hufGroup.limit[i] = 0;
for (t = 0; t < symCount; t++)
if (length[t] === i)
hufGroup.permute[pp++] = t;
}
/* Count symbols coded for at each bit length */
for (i = 0; i < symCount; i++)
temp[length[i]]++;
/* Calculate limit[] (the largest symbol-coding value at each bit
* length, which is (previous limit<<1)+symbols at this level), and
* base[] (number of symbols to ignore at each bit length, which is
* limit minus the cumulative count of symbols coded for already). */
pp = t = 0;
for (i = minLen; i < maxLen; i++) {
pp += temp[i];
/* We read the largest possible symbol size and then unget bits
after determining how many we need, and those extra bits could
be set to anything. (They're noise from future symbols.) At
each level we're really only interested in the first few bits,
so here we set all the trailing to-be-ignored bits to 1 so they
don't affect the value>limit[length] comparison. */
hufGroup.limit[i] = pp - 1;
pp <<= 1;
t += temp[i];
hufGroup.base[i + 1] = pp - t;
}
hufGroup.limit[maxLen + 1] = Number.MAX_VALUE; /* Sentinel value for reading next sym. */
hufGroup.limit[maxLen] = pp + temp[maxLen] - 1;
hufGroup.base[minLen] = 0;
}
/* We've finished reading and digesting the block header. Now read this
block's Huffman coded symbols from the file and undo the Huffman coding
and run length encoding, saving the result into dbuf[dbufCount++]=uc */
/* Initialize symbol occurrence counters and symbol Move To Front table */
var byteCount = Util.makeU32Buffer(256);
for (i = 0; i < 256; i++)
mtfSymbol[i] = i;
/* Loop through compressed symbols. */
var runPos = 0, dbufCount = 0, selector = 0, uc;
var dbuf = this.dbuf = Util.makeU32Buffer(this.dbufSize);
symCount = 0;
for (;;) {
/* Determine which Huffman coding group to use. */
if (!(symCount--)) {
symCount = GROUP_SIZE - 1;
if (selector >= nSelectors) { _throw(Err.DATA_ERROR); }
hufGroup = groups[selectors[selector++]];
}
/* Read next Huffman-coded symbol. */
i = hufGroup.minLen;
j = reader.readBits(i);
for (;;i++) {
if (i > hufGroup.maxLen) { _throw(Err.DATA_ERROR); }
if (j <= hufGroup.limit[i])
break;
j = (j << 1) | reader.readBits(1);
}
/* Huffman decode value to get nextSym (with bounds checking) */
j -= hufGroup.base[i];
if (j < 0 || j >= MAX_SYMBOLS) { _throw(Err.DATA_ERROR); }
var nextSym = hufGroup.permute[j];
/* We have now decoded the symbol, which indicates either a new literal
byte, or a repeated run of the most recent literal byte. First,
check if nextSym indicates a repeated run, and if so loop collecting
how many times to repeat the last literal. */
if (nextSym === SYMBOL_RUNA || nextSym === SYMBOL_RUNB) {
/* If this is the start of a new run, zero out counter */
if (!runPos){
runPos = 1;
t = 0;
}
/* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
each bit position, add 1 or 2 instead. For example,
1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
You can make any bit pattern that way using 1 less symbol than
the basic or 0/1 method (except all bits 0, which would use no
symbols, but a run of length 0 doesn't mean anything in this
context). Thus space is saved. */
if (nextSym === SYMBOL_RUNA)
t += runPos;
else
t += 2 * runPos;
runPos <<= 1;
continue;
}
/* When we hit the first non-run symbol after a run, we now know
how many times to repeat the last literal, so append that many
copies to our buffer of decoded symbols (dbuf) now. (The last
literal used is the one at the head of the mtfSymbol array.) */
if (runPos){
runPos = 0;
if (dbufCount + t > this.dbufSize) { _throw(Err.DATA_ERROR); }
uc = symToByte[mtfSymbol[0]];
byteCount[uc] += t;
while (t--)
dbuf[dbufCount++] = uc;
}
/* Is this the terminating symbol? */
if (nextSym > symTotal)
break;
/* At this point, nextSym indicates a new literal character. Subtract
one to get the position in the MTF array at which this literal is
currently to be found. (Note that the result can't be -1 or 0,
because 0 and 1 are RUNA and RUNB. But another instance of the
first symbol in the MTF array, position 0, would have been handled
as part of a run above. Therefore 1 unused MTF position minus
2 non-literal nextSym values equals -1.) */
if (dbufCount >= this.dbufSize) { _throw(Err.DATA_ERROR); }
i = nextSym - 1;
uc = mtf(mtfSymbol, i);
uc = symToByte[uc];
/* We have our literal byte. Save it into dbuf. */
byteCount[uc]++;
dbuf[dbufCount++] = uc;
}
/* At this point, we've read all the Huffman-coded symbols (and repeated
runs) for this block from the input stream, and decoded them into the
intermediate buffer. There are dbufCount many decoded bytes in dbuf[].
Now undo the Burrows-Wheeler transform on dbuf.
See http://dogma.net/markn/articles/bwt/bwt.htm
*/
if (origPointer < 0 || origPointer >= dbufCount) { _throw(Err.DATA_ERROR); }
/* Turn byteCount into cumulative occurrence counts of 0 to n-1. */
j = 0;
for (i = 0; i < 256; i++) {
k = j + byteCount[i];
byteCount[i] = j;
j = k;
}
/* Figure out what order dbuf would be in if we sorted it. */
for (i = 0; i < dbufCount; i++) {
uc = dbuf[i] & 0xff;
dbuf[byteCount[uc]] |= (i << 8);
byteCount[uc]++;
}
/* Decode first byte by hand to initialize "previous" byte. Note that it
doesn't get output, and if the first three characters are identical
it doesn't qualify as a run (hence writeRunCountdown=5). */
var pos = 0, current = 0, run = 0;
if (dbufCount) {
pos = dbuf[origPointer];
current = (pos & 0xff);
pos >>= 8;
run = -1;
}
this.writePos = pos;
this.writeCurrent = current;
this.writeCount = dbufCount;
this.writeRun = run;
return true; /* more blocks to come */
};
/* Undo burrows-wheeler transform on intermediate buffer to produce output.
If start_bunzip was initialized with out_fd=-1, then up to len bytes of
data are written to outbuf. Return value is number of bytes written or
error (all errors are negative numbers). If out_fd!=-1, outbuf and len
are ignored, data is written to out_fd and return is RETVAL_OK or error.
*/
Bunzip.prototype._read_bunzip = function(outputBuffer, len) {
var copies, previous, outbyte;
/* james@jamestaylor.org: writeCount goes to -1 when the buffer is fully
decoded, which results in this returning RETVAL_LAST_BLOCK, also
equal to -1... Confusing, I'm returning 0 here to indicate no
bytes written into the buffer */
if (this.writeCount < 0) { return 0; }
var gotcount = 0;
var dbuf = this.dbuf, pos = this.writePos, current = this.writeCurrent;
var dbufCount = this.writeCount, outputsize = this.outputsize;
var run = this.writeRun;
while (dbufCount) {
dbufCount--;
previous = current;
pos = dbuf[pos];
current = pos & 0xff;
pos >>= 8;
if (run++ === 3){
copies = current;
outbyte = previous;
current = -1;
} else {
copies = 1;
outbyte = current;
}
this.blockCRC.updateCRCRun(outbyte, copies);
while (copies--) {
this.outputStream.writeByte(outbyte);
this.nextoutput++;
}
if (current != previous)
run = 0;
}
this.writeCount = dbufCount;
// check CRC
if (this.blockCRC.getCRC() !== this.targetBlockCRC) {
_throw(Err.DATA_ERROR, "Bad block CRC "+
"(got "+this.blockCRC.getCRC().toString(16)+
" expected "+this.targetBlockCRC.toString(16)+")");
}
return this.nextoutput;
};
/* Static helper functions */
Bunzip.Err = Err;
// 'input' can be a stream or a buffer
// 'output' can be a stream or a buffer or a number (buffer size)
Bunzip.decode = function(input, output, multistream) {
// make a stream from a buffer, if necessary
var inputStream = Util.coerceInputStream(input);
var o = Util.coerceOutputStream(output, output);
var outputStream = o.stream;
var bz = new Bunzip(inputStream, outputStream);
while (true) {
if ('eof' in inputStream && inputStream.eof()) break;
if (bz._init_block()) {
bz._read_bunzip();
} else {
var targetStreamCRC = bz.reader.readBits(32);
if (targetStreamCRC !== bz.streamCRC) {
_throw(Err.DATA_ERROR, "Bad stream CRC "+
"(got "+bz.streamCRC.toString(16)+
" expected "+targetStreamCRC.toString(16)+")");
}
if (multistream &&
'eof' in inputStream &&
!inputStream.eof()) {
// note that start_bunzip will also resync the bit reader to next byte
bz._start_bunzip(inputStream, outputStream);
} else break;
}
}
return o.retval;
};
Bunzip.decodeBlock = function(input, pos, output) {
// make a stream from a buffer, if necessary
var inputStream = Util.coerceInputStream(input);
var o = Util.coerceOutputStream(output, output);
var outputStream = o.stream;
var bz = new Bunzip(inputStream, outputStream);
bz.reader.seekBit(pos);
/* Fill the decode buffer for the block */
var moreBlocks = bz._get_next_block();
if (moreBlocks) {
/* Init the CRC for writing */
bz.blockCRC = new CRC32();
/* Zero this so the current byte from before the seek is not written */
bz.writeCopies = 0;
/* Decompress the block and write to stdout */
bz._read_bunzip();
// XXX keep writing?
}
return o.retval;
};
/* Reads bzip2 file from stream or buffer `input`, and invoke
* `callback(position, size)` once for each bzip2 block,
* where position gives the starting position (in *bits*)
* and size gives uncompressed size of the block (in *bytes*). */
Bunzip.table = function(input, callback, multistream) {
// make a stream from a buffer, if necessary
var inputStream = new Stream();
inputStream.delegate = Util.coerceInputStream(input);
inputStream.pos = 0;
inputStream.readByte = function() {
this.pos++;
return this.delegate.readByte();
};
inputStream.tell = function() { return this.pos; };
if (inputStream.delegate.eof) {
inputStream.eof = inputStream.delegate.eof.bind(inputStream.delegate);
}
var outputStream = new Stream();
outputStream.pos = 0;
outputStream.writeByte = function() { this.pos++; };
var bz = new Bunzip(inputStream, outputStream);
var blockSize = bz.dbufSize;
while (true) {
if ('eof' in inputStream && inputStream.eof()) break;
var position = bz.reader.tellBit();
if (bz._init_block()) {
var start = outputStream.pos;
bz._read_bunzip();
callback(position, outputStream.pos - start);
} else {
var crc = bz.reader.readBits(32); // (but we ignore the crc)
if (multistream &&
'eof' in inputStream &&
!inputStream.eof()) {
// note that start_bunzip will also resync the bit reader to next byte
bz._start_bunzip(inputStream, outputStream);
console.assert(bz.dbufSize === blockSize,
"shouldn't change block size within multistream file");
} else break;
}
}
};
// create a Huffman tree from the table of frequencies
var StaticHuffman = function(freq, alphabetSize) {
// As in BZip2HuffmanStageEncoder.java (from jbzip2):
// The Huffman allocator needs its input symbol frequencies to be
// sorted, but we need to return code lengths in the same order as
// the corresponding frequencies are passed in.
// The symbol frequency and index are merged into a single array of
// integers - frequency in the high 23 bits, index in the low 9
// bits.
// 2^23 = 8,388,608 which is higher than the maximum possible
// frequency for one symbol in a block
// 2^9 = 512 which is higher than the maximum possible
// alphabet size (== 258)
// Sorting this array simultaneously sorts the frequencies and
// leaves a lookup that can be used to cheaply invert the sort
var i, mergedFreq = [];
for (i=0; i<alphabetSize; i++) {
mergedFreq[i] = (freq[i] << 9) | i;
}
mergedFreq.sort(function(a,b) { return a-b; });
var sortedFreq = mergedFreq.map(function(v) { return v>>>9; });
// allocate code lengths in place. (result in sortedFreq array)
HuffmanAllocator.allocateHuffmanCodeLengths(sortedFreq, MAX_HUFCODE_BITS);
// reverse the sort to put codes & code lengths in order of input symbols
this.codeLengths = Util.makeU8Buffer(alphabetSize);
for (i=0; i<alphabetSize; i++) {
var sym = mergedFreq[i] & 0x1FF;
this.codeLengths[sym] = sortedFreq[i];
}
};
// compute canonical Huffman codes, given code lengths
StaticHuffman.prototype.computeCanonical = function() {
var alphabetSize = this.codeLengths.length;
// merge arrays; sort first by length then by symbol.
var i, merged = [];
for (i=0; i<alphabetSize; i++) {
merged[i] = (this.codeLengths[i] << 9) | i;
}
merged.sort(function(a,b) { return a-b; });
// use sorted lengths to assign codes
this.code = Util.makeU32Buffer(alphabetSize);
var code = 0, prevLen = 0;
for (i=0; i<alphabetSize; i++) {
var curLen = merged[i] >>> 9;
var sym = merged[i] & 0x1FF;
console.assert(prevLen <= curLen);
code <<= (curLen - prevLen);
this.code[sym] = code++;
prevLen = curLen;
}
};
// compute the cost of encoding