Merge pull request #3033 from tikD1337/fix/web-ui-lib-assets

fix(web-ui): ship the bundled consoles' lib/ files (VNC and xpra)
This commit is contained in:
Jeremy Grossmann 2026-10-26 15:34:29 +01:00 committed by GitHub
commit 16c5d028dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 77339 additions and 2 deletions

4
.gitignore vendored
View File

@ -25,8 +25,8 @@ var
sdist
develop-eggs
.installed.cfg
lib
lib64
/lib
/lib64
# Installer logs
pip-log.txt

View File

@ -0,0 +1,48 @@
// reduce buffer size, avoiding mem copy
export function shrinkBuf(buf, size) {
if (buf.length === size) {
return buf;
}
if (buf.subarray) {
return buf.subarray(0, size);
}
buf.length = size;
return buf;
}
export function arraySet(dest, src, src_offs, len, dest_offs) {
if (src.subarray && dest.subarray) {
dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
return;
}
// Fallback to ordinary array
for (var i = 0; i < len; i++) {
dest[dest_offs + i] = src[src_offs + i];
}
}
// Join array of chunks to single array.
export function flattenChunks(chunks) {
var i, l, len, pos, chunk, result;
// calculate data length
len = 0;
for (i = 0, l = chunks.length; i < l; i++) {
len += chunks[i].length;
}
// join chunks
result = new Uint8Array(len);
pos = 0;
for (i = 0, l = chunks.length; i < l; i++) {
chunk = chunks[i];
result.set(chunk, pos);
pos += chunk.length;
}
return result;
}
export var Buf8 = Uint8Array;
export var Buf16 = Uint16Array;
export var Buf32 = Int32Array;

View File

@ -0,0 +1,27 @@
// Note: adler32 takes 12% for level 0 and 2% for level 6.
// It doesn't worth to make additional optimizationa as in original.
// Small size is preferable.
export default function adler32(adler, buf, len, pos) {
var s1 = (adler & 0xffff) | 0,
s2 = ((adler >>> 16) & 0xffff) | 0,
n = 0;
while (len !== 0) {
// Set limit ~ twice less than 5552, to keep
// s2 in 31-bits, because we force signed ints.
// in other case %= will fail.
n = len > 2000 ? 2000 : len;
len -= n;
do {
s1 = (s1 + buf[pos++]) | 0;
s2 = (s2 + s1) | 0;
} while (--n);
s1 %= 65521;
s2 %= 65521;
}
return s1 | (s2 << 16) | 0;
}

View File

@ -0,0 +1,45 @@
export default {
/* Allowed flush values; see deflate() and inflate() below for details */
Z_NO_FLUSH: 0,
Z_PARTIAL_FLUSH: 1,
Z_SYNC_FLUSH: 2,
Z_FULL_FLUSH: 3,
Z_FINISH: 4,
Z_BLOCK: 5,
Z_TREES: 6,
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
Z_OK: 0,
Z_STREAM_END: 1,
Z_NEED_DICT: 2,
Z_ERRNO: -1,
Z_STREAM_ERROR: -2,
Z_DATA_ERROR: -3,
//Z_MEM_ERROR: -4,
Z_BUF_ERROR: -5,
//Z_VERSION_ERROR: -6,
/* compression levels */
Z_NO_COMPRESSION: 0,
Z_BEST_SPEED: 1,
Z_BEST_COMPRESSION: 9,
Z_DEFAULT_COMPRESSION: -1,
Z_FILTERED: 1,
Z_HUFFMAN_ONLY: 2,
Z_RLE: 3,
Z_FIXED: 4,
Z_DEFAULT_STRATEGY: 0,
/* Possible values of the data_type field (though see inflate()) */
Z_BINARY: 0,
Z_TEXT: 1,
//Z_ASCII: 1, // = Z_TEXT (deprecated)
Z_UNKNOWN: 2,
/* The deflate compression method */
Z_DEFLATED: 8,
//Z_NULL: null // Use -1 or null inline, depending on var type
};

View File

@ -0,0 +1,35 @@
// Note: we can't get significant speed boost here.
// So write code to minimize size - no pregenerated tables
// and array tools dependencies.
// Use ordinary array, since untyped makes no boost here
export default function makeTable() {
var c,
table = [];
for (var n = 0; n < 256; n++) {
c = n;
for (var k = 0; k < 8; k++) {
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
}
table[n] = c;
}
return table;
}
// Create table on load. Just 255 signed longs. Not a problem.
var crcTable = makeTable();
function crc32(crc, buf, len, pos) {
var t = crcTable,
end = pos + len;
crc ^= -1;
for (var i = pos; i < end; i++) {
crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xff];
}
return crc ^ -1; // >>> 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
export default function GZheader() {
/* true if compressed data believed to be text */
this.text = 0;
/* modification time */
this.time = 0;
/* extra flags (not used when writing a gzip file) */
this.xflags = 0;
/* operating system */
this.os = 0;
/* pointer to extra field or Z_NULL if none */
this.extra = null;
/* extra field length (valid if extra != Z_NULL) */
this.extra_len = 0; // Actually, we don't need it in JS,
// but leave for few code modifications
//
// Setup limits is not necessary because in js we should not preallocate memory
// for inflate use constant limit in 65536 bytes
//
/* space at extra (only when reading header) */
// this.extra_max = 0;
/* pointer to zero-terminated file name or Z_NULL */
this.name = '';
/* space at name (only when reading header) */
// this.name_max = 0;
/* pointer to zero-terminated comment or Z_NULL */
this.comment = '';
/* space at comment (only when reading header) */
// this.comm_max = 0;
/* true if there was or will be a header crc */
this.hcrc = 0;
/* true when done reading gzip header (not used when writing a gzip file) */
this.done = false;
}

View File

@ -0,0 +1,327 @@
// See state defs from inflate.js
var BAD = 30; /* got a data error -- remain here until reset */
var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
/*
Decode literal, length, and distance codes and write out the resulting
literal and match bytes until either not enough input or output is
available, an end-of-block is encountered, or a data error is encountered.
When large enough input and output buffers are supplied to inflate(), for
example, a 16K input buffer and a 64K output buffer, more than 95% of the
inflate execution time is spent in this routine.
Entry assumptions:
state.mode === LEN
strm.avail_in >= 6
strm.avail_out >= 258
start >= strm.avail_out
state.bits < 8
On return, state.mode is one of:
LEN -- ran out of enough output space or enough available input
TYPE -- reached end of block code, inflate() to interpret next block
BAD -- error in block data
Notes:
- The maximum input bits used by a length/distance pair is 15 bits for the
length code, 5 bits for the length extra, 15 bits for the distance code,
and 13 bits for the distance extra. This totals 48 bits, or six bytes.
Therefore if strm.avail_in >= 6, then there is enough input to avoid
checking for available input while decoding.
- The maximum bytes that a single length/distance pair can output is 258
bytes, which is the maximum length that can be coded. inflate_fast()
requires strm.avail_out >= 258 for each loop to avoid checking for
output space.
*/
export default function inflate_fast(strm, start) {
var state;
var _in; /* local strm.input */
var last; /* have enough input while in < last */
var _out; /* local strm.output */
var beg; /* inflate()'s initial strm.output */
var end; /* while out < end, enough space available */
//#ifdef INFLATE_STRICT
var dmax; /* maximum distance from zlib header */
//#endif
var wsize; /* window size or zero if not using window */
var whave; /* valid bytes in the window */
var wnext; /* window write index */
// Use `s_window` instead `window`, avoid conflict with instrumentation tools
var s_window; /* allocated sliding window, if wsize != 0 */
var hold; /* local strm.hold */
var bits; /* local strm.bits */
var lcode; /* local strm.lencode */
var dcode; /* local strm.distcode */
var lmask; /* mask for first level of length codes */
var dmask; /* mask for first level of distance codes */
var here; /* retrieved table entry */
var op; /* code bits, operation, extra bits, or */
/* window position, window bytes to copy */
var len; /* match length, unused bytes */
var dist; /* match distance */
var from; /* where to copy match from */
var from_source;
var input, output; // JS specific, because we have no pointers
/* copy state to local variables */
state = strm.state;
//here = state.here;
_in = strm.next_in;
input = strm.input;
last = _in + (strm.avail_in - 5);
_out = strm.next_out;
output = strm.output;
beg = _out - (start - strm.avail_out);
end = _out + (strm.avail_out - 257);
//#ifdef INFLATE_STRICT
dmax = state.dmax;
//#endif
wsize = state.wsize;
whave = state.whave;
wnext = state.wnext;
s_window = state.window;
hold = state.hold;
bits = state.bits;
lcode = state.lencode;
dcode = state.distcode;
lmask = (1 << state.lenbits) - 1;
dmask = (1 << state.distbits) - 1;
/* decode literals and length/distances until end-of-block or not enough
input data or output space */
top: do {
if (bits < 15) {
hold += input[_in++] << bits;
bits += 8;
hold += input[_in++] << bits;
bits += 8;
}
here = lcode[hold & lmask];
dolen: for (;;) {
// Goto emulation
op = here >>> 24 /*here.bits*/;
hold >>>= op;
bits -= op;
op = (here >>> 16) & 0xff /*here.op*/;
if (op === 0) {
/* literal */
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
// "inflate: literal '%c'\n" :
// "inflate: literal 0x%02x\n", here.val));
output[_out++] = here & 0xffff /*here.val*/;
} else if (op & 16) {
/* length base */
len = here & 0xffff /*here.val*/;
op &= 15; /* number of extra bits */
if (op) {
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
}
len += hold & ((1 << op) - 1);
hold >>>= op;
bits -= op;
}
//Tracevv((stderr, "inflate: length %u\n", len));
if (bits < 15) {
hold += input[_in++] << bits;
bits += 8;
hold += input[_in++] << bits;
bits += 8;
}
here = dcode[hold & dmask];
dodist: for (;;) {
// goto emulation
op = here >>> 24 /*here.bits*/;
hold >>>= op;
bits -= op;
op = (here >>> 16) & 0xff /*here.op*/;
if (op & 16) {
/* distance base */
dist = here & 0xffff /*here.val*/;
op &= 15; /* number of extra bits */
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
}
}
dist += hold & ((1 << op) - 1);
//#ifdef INFLATE_STRICT
if (dist > dmax) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break top;
}
//#endif
hold >>>= op;
bits -= op;
//Tracevv((stderr, "inflate: distance %u\n", dist));
op = _out - beg; /* max distance in output */
if (dist > op) {
/* see if copy from window */
op = dist - op; /* distance back in window */
if (op > whave) {
if (state.sane) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break top;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
// if (len <= op - whave) {
// do {
// output[_out++] = 0;
// } while (--len);
// continue top;
// }
// len -= op - whave;
// do {
// output[_out++] = 0;
// } while (--op > whave);
// if (op === 0) {
// from = _out - dist;
// do {
// output[_out++] = output[from++];
// } while (--len);
// continue top;
// }
//#endif
}
from = 0; // window index
from_source = s_window;
if (wnext === 0) {
/* very common case */
from += wsize - op;
if (op < len) {
/* some from window */
len -= op;
do {
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
} else if (wnext < op) {
/* wrap around window */
from += wsize + wnext - op;
op -= wnext;
if (op < len) {
/* some from end of window */
len -= op;
do {
output[_out++] = s_window[from++];
} while (--op);
from = 0;
if (wnext < len) {
/* some from start of window */
op = wnext;
len -= op;
do {
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
} else {
/* contiguous in window */
from += wnext - op;
if (op < len) {
/* some from window */
len -= op;
do {
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
while (len > 2) {
output[_out++] = from_source[from++];
output[_out++] = from_source[from++];
output[_out++] = from_source[from++];
len -= 3;
}
if (len) {
output[_out++] = from_source[from++];
if (len > 1) {
output[_out++] = from_source[from++];
}
}
} else {
from = _out - dist; /* copy direct from output */
do {
/* minimum length is three */
output[_out++] = output[from++];
output[_out++] = output[from++];
output[_out++] = output[from++];
len -= 3;
} while (len > 2);
if (len) {
output[_out++] = output[from++];
if (len > 1) {
output[_out++] = output[from++];
}
}
}
} else if ((op & 64) === 0) {
/* 2nd level distance code */
here = dcode[(here & 0xffff) /*here.val*/ + (hold & ((1 << op) - 1))];
continue dodist;
} else {
strm.msg = 'invalid distance code';
state.mode = BAD;
break top;
}
break; // need to emulate goto via "continue"
}
} else if ((op & 64) === 0) {
/* 2nd level length code */
here = lcode[(here & 0xffff) /*here.val*/ + (hold & ((1 << op) - 1))];
continue dolen;
} else if (op & 32) {
/* end-of-block */
//Tracevv((stderr, "inflate: end of block\n"));
state.mode = TYPE;
break top;
} else {
strm.msg = 'invalid literal/length code';
state.mode = BAD;
break top;
}
break; // need to emulate goto via "continue"
}
} while (_in < last && _out < end);
/* return unused bytes (on entry, bits < 8, so in won't go too far back) */
len = bits >> 3;
_in -= len;
bits -= len << 3;
hold &= (1 << bits) - 1;
/* update state and return */
strm.next_in = _in;
strm.next_out = _out;
strm.avail_in = _in < last ? 5 + (last - _in) : 5 - (_in - last);
strm.avail_out = _out < end ? 257 + (end - _out) : 257 - (_out - end);
state.hold = hold;
state.bits = bits;
return;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,323 @@
import * as utils from '../utils/common.js';
var MAXBITS = 15;
var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
var CODES = 0;
var LENS = 1;
var DISTS = 2;
var lbase = [
/* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99,
115, 131, 163, 195, 227, 258, 0, 0,
];
var lext = [
/* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20,
20, 20, 20, 21, 21, 21, 21, 16, 72, 78,
];
var dbase = [
/* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025,
1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0,
];
var dext = [
/* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25,
25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64,
];
export default function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) {
var bits = opts.bits;
//here = opts.here; /* table entry for duplication */
var len = 0; /* a code's length in bits */
var sym = 0; /* index of code symbols */
var min = 0,
max = 0; /* minimum and maximum code lengths */
var root = 0; /* number of index bits for root table */
var curr = 0; /* number of index bits for current table */
var drop = 0; /* code bits to drop for sub-table */
var left = 0; /* number of prefix codes available */
var used = 0; /* code entries in table used */
var huff = 0; /* Huffman code */
var incr; /* for incrementing code, index */
var fill; /* index for replicating entries */
var low; /* low bits for current root entry */
var mask; /* mask for low root bits */
var next; /* next available space in table */
var base = null; /* base value table to use */
var base_index = 0;
// var shoextra; /* extra bits table to use */
var end; /* use base and extra for symbol > end */
var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */
var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */
var extra = null;
var extra_index = 0;
var here_bits, here_op, here_val;
/*
Process a set of code lengths to create a canonical Huffman code. The
code lengths are lens[0..codes-1]. Each length corresponds to the
symbols 0..codes-1. The Huffman code is generated by first sorting the
symbols by length from short to long, and retaining the symbol order
for codes with equal lengths. Then the code starts with all zero bits
for the first code of the shortest length, and the codes are integer
increments for the same length, and zeros are appended as the length
increases. For the deflate format, these bits are stored backwards
from their more natural integer increment ordering, and so when the
decoding tables are built in the large loop below, the integer codes
are incremented backwards.
This routine assumes, but does not check, that all of the entries in
lens[] are in the range 0..MAXBITS. The caller must assure this.
1..MAXBITS is interpreted as that code length. zero means that that
symbol does not occur in this code.
The codes are sorted by computing a count of codes for each length,
creating from that a table of starting indices for each length in the
sorted table, and then entering the symbols in order in the sorted
table. The sorted table is work[], with that space being provided by
the caller.
The length counts are used for other purposes as well, i.e. finding
the minimum and maximum length codes, determining if there are any
codes at all, checking for a valid set of lengths, and looking ahead
at length counts to determine sub-table sizes when building the
decoding tables.
*/
/* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
for (len = 0; len <= MAXBITS; len++) {
count[len] = 0;
}
for (sym = 0; sym < codes; sym++) {
count[lens[lens_index + sym]]++;
}
/* bound code lengths, force root to be within code lengths */
root = bits;
for (max = MAXBITS; max >= 1; max--) {
if (count[max] !== 0) {
break;
}
}
if (root > max) {
root = max;
}
if (max === 0) {
/* no symbols to code at all */
//table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */
//table.bits[opts.table_index] = 1; //here.bits = (var char)1;
//table.val[opts.table_index++] = 0; //here.val = (var short)0;
table[table_index++] = (1 << 24) | (64 << 16) | 0;
//table.op[opts.table_index] = 64;
//table.bits[opts.table_index] = 1;
//table.val[opts.table_index++] = 0;
table[table_index++] = (1 << 24) | (64 << 16) | 0;
opts.bits = 1;
return 0; /* no symbols, but wait for decoding to report error */
}
for (min = 1; min < max; min++) {
if (count[min] !== 0) {
break;
}
}
if (root < min) {
root = min;
}
/* check for an over-subscribed or incomplete set of lengths */
left = 1;
for (len = 1; len <= MAXBITS; len++) {
left <<= 1;
left -= count[len];
if (left < 0) {
return -1;
} /* over-subscribed */
}
if (left > 0 && (type === CODES || max !== 1)) {
return -1; /* incomplete set */
}
/* generate offsets into symbol table for each length for sorting */
offs[1] = 0;
for (len = 1; len < MAXBITS; len++) {
offs[len + 1] = offs[len] + count[len];
}
/* sort symbols by length, by symbol order within each length */
for (sym = 0; sym < codes; sym++) {
if (lens[lens_index + sym] !== 0) {
work[offs[lens[lens_index + sym]]++] = sym;
}
}
/*
Create and fill in decoding tables. In this loop, the table being
filled is at next and has curr index bits. The code being used is huff
with length len. That code is converted to an index by dropping drop
bits off of the bottom. For codes where len is less than drop + curr,
those top drop + curr - len bits are incremented through all values to
fill the table with replicated entries.
root is the number of index bits for the root table. When len exceeds
root, sub-tables are created pointed to by the root entry with an index
of the low root bits of huff. This is saved in low to check for when a
new sub-table should be started. drop is zero when the root table is
being filled, and drop is root when sub-tables are being filled.
When a new sub-table is needed, it is necessary to look ahead in the
code lengths to determine what size sub-table is needed. The length
counts are used for this, and so count[] is decremented as codes are
entered in the tables.
used keeps track of how many table entries have been allocated from the
provided *table space. It is checked for LENS and DIST tables against
the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
the initial root table size constants. See the comments in inftrees.h
for more information.
sym increments through all symbols, and the loop terminates when
all codes of length max, i.e. all codes, have been processed. This
routine permits incomplete codes, so another loop after this one fills
in the rest of the decoding tables with invalid code markers.
*/
/* set up for code type */
// poor man optimization - use if-else instead of switch,
// to avoid deopts in old v8
if (type === CODES) {
base = extra = work; /* dummy value--not used */
end = 19;
} else if (type === LENS) {
base = lbase;
base_index -= 257;
extra = lext;
extra_index -= 257;
end = 256;
} else {
/* DISTS */
base = dbase;
extra = dext;
end = -1;
}
/* initialize opts for loop */
huff = 0; /* starting code */
sym = 0; /* starting code symbol */
len = min; /* starting code length */
next = table_index; /* current table to fill in */
curr = root; /* current table index bits */
drop = 0; /* current bits to drop from code for index */
low = -1; /* trigger new sub-table when len > root */
used = 1 << root; /* use root table entries */
mask = used - 1; /* mask for comparing low */
/* check available table space */
if ((type === LENS && used > ENOUGH_LENS) || (type === DISTS && used > ENOUGH_DISTS)) {
return 1;
}
/* process all codes and make table entries */
for (;;) {
/* create table entry */
here_bits = len - drop;
if (work[sym] < end) {
here_op = 0;
here_val = work[sym];
} else if (work[sym] > end) {
here_op = extra[extra_index + work[sym]];
here_val = base[base_index + work[sym]];
} else {
here_op = 32 + 64; /* end of block */
here_val = 0;
}
/* replicate for those indices with low len bits equal to huff */
incr = 1 << (len - drop);
fill = 1 << curr;
min = fill; /* save offset to next table */
do {
fill -= incr;
table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val | 0;
} while (fill !== 0);
/* backwards increment the len-bit code huff */
incr = 1 << (len - 1);
while (huff & incr) {
incr >>= 1;
}
if (incr !== 0) {
huff &= incr - 1;
huff += incr;
} else {
huff = 0;
}
/* go to next symbol, update count, len */
sym++;
if (--count[len] === 0) {
if (len === max) {
break;
}
len = lens[lens_index + work[sym]];
}
/* create new sub-table if needed */
if (len > root && (huff & mask) !== low) {
/* if first time, transition to sub-tables */
if (drop === 0) {
drop = root;
}
/* increment past last table */
next += min; /* here min is 1 << curr */
/* determine length of next table */
curr = len - drop;
left = 1 << curr;
while (curr + drop < max) {
left -= count[curr + drop];
if (left <= 0) {
break;
}
curr++;
left <<= 1;
}
/* check for enough space */
used += 1 << curr;
if ((type === LENS && used > ENOUGH_LENS) || (type === DISTS && used > ENOUGH_DISTS)) {
return 1;
}
/* point entry in root table to sub-table */
low = huff & mask;
/*table.op[low] = curr;
table.bits[low] = root;
table.val[low] = next - opts.table_index;*/
table[low] = (root << 24) | (curr << 16) | (next - table_index) | 0;
}
}
/* fill in remaining table entry if code is incomplete (guaranteed to have
at most one remaining entry, since if the code is incomplete, the
maximum code length that was allowed to get this far is one bit) */
if (huff !== 0) {
//table.op[next + huff] = 64; /* invalid code marker */
//table.bits[next + huff] = len - drop;
//table.val[next + huff] = 0;
table[next + huff] = ((len - drop) << 24) | (64 << 16) | 0;
}
/* set return parameters */
//opts.table_index += used;
opts.bits = root;
return 0;
}

View File

@ -0,0 +1,11 @@
export default {
2: 'need dictionary' /* Z_NEED_DICT 2 */,
1: 'stream end' /* Z_STREAM_END 1 */,
0: '' /* Z_OK 0 */,
'-1': 'file error' /* Z_ERRNO (-1) */,
'-2': 'stream error' /* Z_STREAM_ERROR (-2) */,
'-3': 'data error' /* Z_DATA_ERROR (-3) */,
'-4': 'insufficient memory' /* Z_MEM_ERROR (-4) */,
'-5': 'buffer error' /* Z_BUF_ERROR (-5) */,
'-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
export default function ZStream() {
/* next input byte */
this.input = null; // JS specific, because we have no pointers
this.next_in = 0;
/* number of bytes available at input */
this.avail_in = 0;
/* total number of input bytes read so far */
this.total_in = 0;
/* next output byte should be put there */
this.output = null; // JS specific, because we have no pointers
this.next_out = 0;
/* remaining free space at output */
this.avail_out = 0;
/* total number of bytes output so far */
this.total_out = 0;
/* last error message, NULL if no error */
this.msg = '' /*Z_NULL*/;
/* not visible by applications */
this.state = null;
/* best guess about the data type: binary or text */
this.data_type = 2 /*Z_UNKNOWN*/;
/* adler32 value of the uncompressed data */
this.adler = 0;
}

View File

@ -0,0 +1,171 @@
/*
* FileSaver.js
* A saveAs() FileSaver implementation.
*
* By Eli Grey, http://eligrey.com
*
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
* source : http://purl.eligrey.com/github/FileSaver.js
*/
// The one and only way of getting global scope in all environments
// https://stackoverflow.com/q/3277182/1008999
var _global = typeof window === 'object' && window.window === window
? window : typeof self === 'object' && self.self === self
? self : typeof global === 'object' && global.global === global
? global
: this
function bom (blob, opts) {
if (typeof opts === 'undefined') opts = { autoBom: false }
else if (typeof opts !== 'object') {
console.warn('Deprecated: Expected third argument to be a object')
opts = { autoBom: !opts }
}
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type })
}
return blob
}
function download (url, name, opts) {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'blob'
xhr.onload = function () {
saveAs(xhr.response, name, opts)
}
xhr.onerror = function () {
console.error('could not download file')
}
xhr.send()
}
function corsEnabled (url) {
var xhr = new XMLHttpRequest()
// use sync to avoid popup blocker
xhr.open('HEAD', url, false)
try {
xhr.send()
} catch (e) {}
return xhr.status >= 200 && xhr.status <= 299
}
// `a.click()` doesn't work for all browsers (#465)
function click (node) {
try {
node.dispatchEvent(new MouseEvent('click'))
} catch (e) {
var evt = document.createEvent('MouseEvents')
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
20, false, false, false, false, 0, null)
node.dispatchEvent(evt)
}
}
// Detect WebView inside a native macOS app by ruling out all browsers
// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent)
var saveAs = _global.saveAs || (
// probably in some web worker
(typeof window !== 'object' || window !== _global)
? function saveAs () { /* noop */ }
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
: ('download' in HTMLAnchorElement.prototype && !isMacOSWebView)
? function saveAs (blob, name, opts) {
var URL = _global.URL || _global.webkitURL
var a = document.createElement('a')
name = name || blob.name || 'download'
a.download = name
a.rel = 'noopener' // tabnabbing
// TODO: detect chrome extensions & packaged apps
// a.target = '_blank'
if (typeof blob === 'string') {
// Support regular links
a.href = blob
if (a.origin !== location.origin) {
corsEnabled(a.href)
? download(blob, name, opts)
: click(a, a.target = '_blank')
} else {
click(a)
}
} else {
// Support blobs
a.href = URL.createObjectURL(blob)
setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s
setTimeout(function () { click(a) }, 0)
}
}
// Use msSaveOrOpenBlob as a second approach
: 'msSaveOrOpenBlob' in navigator
? function saveAs (blob, name, opts) {
name = name || blob.name || 'download'
if (typeof blob === 'string') {
if (corsEnabled(blob)) {
download(blob, name, opts)
} else {
var a = document.createElement('a')
a.href = blob
a.target = '_blank'
setTimeout(function () { click(a) })
}
} else {
navigator.msSaveOrOpenBlob(bom(blob, opts), name)
}
}
// Fallback to using FileReader and a popup
: function saveAs (blob, name, opts, popup) {
// Open a popup immediately do go around popup blocker
// Mostly only available on user interaction and the fileReader is async so...
popup = popup || open('', '_blank')
if (popup) {
popup.document.title =
popup.document.body.innerText = 'downloading...'
}
if (typeof blob === 'string') return download(blob, name, opts)
var force = blob.type === 'application/octet-stream'
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent)
if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') {
// Safari doesn't allow downloading of blob URLs
var reader = new FileReader()
reader.onloadend = function () {
var url = reader.result
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;')
if (popup) popup.location.href = url
else location = url
popup = null // reverse-tabnabbing #460
}
reader.readAsDataURL(blob)
} else {
var URL = _global.URL || _global.webkitURL
var url = URL.createObjectURL(blob)
if (popup) popup.location = url
else location.href = url
popup = null // reverse-tabnabbing #460
setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s
}
}
)
_global.saveAs = saveAs.saveAs = saveAs
if (typeof module !== 'undefined') {
module.exports = saveAs;
}

View File

@ -0,0 +1,317 @@
/*! streamsaver. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
/* global chrome location ReadableStream define MessageChannel TransformStream */
;((name, definition) => {
typeof module !== 'undefined'
? module.exports = definition()
: typeof define === 'function' && typeof define.amd === 'object'
? define(definition)
: this[name] = definition()
})('streamSaver', () => {
'use strict'
const global = typeof window === 'object' ? window : this
if (!global.HTMLElement) console.warn('streamsaver is meant to run on browsers main thread')
let mitmTransporter = null
let supportsTransferable = false
const test = fn => { try { fn() } catch (e) {} }
const ponyfill = global.WebStreamsPolyfill || {}
const isSecureContext = global.isSecureContext
// TODO: Must come up with a real detection test (#69)
let useBlobFallback = /constructor/i.test(global.HTMLElement) || !!global.safari || !!global.WebKitPoint
const downloadStrategy = isSecureContext || 'MozAppearance' in document.documentElement.style
? 'iframe'
: 'navigate'
const streamSaver = {
createWriteStream,
WritableStream: global.WritableStream || ponyfill.WritableStream,
supported: true,
version: { full: '2.0.5', major: 2, minor: 0, dot: 5 },
mitm: './mitm.html'
}
/**
* create a hidden iframe and append it to the DOM (body)
*
* @param {string} src page to load
* @return {HTMLIFrameElement} page to load
*/
function makeIframe (src) {
if (!src) throw new Error('meh')
const iframe = document.createElement('iframe')
iframe.hidden = true
iframe.src = src
iframe.loaded = false
iframe.name = 'iframe'
iframe.isIframe = true
iframe.postMessage = (...args) => iframe.contentWindow.postMessage(...args)
iframe.addEventListener('load', () => {
iframe.loaded = true
}, { once: true })
document.body.appendChild(iframe)
return iframe
}
/**
* create a popup that simulates the basic things
* of what a iframe can do
*
* @param {string} src page to load
* @return {object} iframe like object
*/
function makePopup (src) {
const options = 'width=200,height=100'
const delegate = document.createDocumentFragment()
const popup = {
frame: global.open(src, 'popup', options),
loaded: false,
isIframe: false,
isPopup: true,
remove () { popup.frame.close() },
addEventListener (...args) { delegate.addEventListener(...args) },
dispatchEvent (...args) { delegate.dispatchEvent(...args) },
removeEventListener (...args) { delegate.removeEventListener(...args) },
postMessage (...args) { popup.frame.postMessage(...args) }
}
const onReady = evt => {
if (evt.source === popup.frame) {
popup.loaded = true
global.removeEventListener('message', onReady)
popup.dispatchEvent(new Event('load'))
}
}
global.addEventListener('message', onReady)
return popup
}
try {
// We can't look for service worker since it may still work on http
new Response(new ReadableStream())
if (isSecureContext && !('serviceWorker' in navigator)) {
useBlobFallback = true
}
} catch (err) {
useBlobFallback = true
}
test(() => {
// Transferable stream was first enabled in chrome v73 behind a flag
const { readable } = new TransformStream()
const mc = new MessageChannel()
mc.port1.postMessage(readable, [readable])
mc.port1.close()
mc.port2.close()
supportsTransferable = true
// Freeze TransformStream object (can only work with native)
Object.defineProperty(streamSaver, 'TransformStream', {
configurable: false,
writable: false,
value: TransformStream
})
})
function loadTransporter () {
if (!mitmTransporter) {
mitmTransporter = isSecureContext
? makeIframe(streamSaver.mitm)
: makePopup(streamSaver.mitm)
}
}
/**
* @param {string} filename filename that should be used
* @param {object} options [description]
* @param {number} size deprecated
* @return {WritableStream<Uint8Array>}
*/
function createWriteStream (filename, options, size) {
let opts = {
size: null,
pathname: null,
writableStrategy: undefined,
readableStrategy: undefined
}
let bytesWritten = 0 // by StreamSaver.js (not the service worker)
let downloadUrl = null
let channel = null
let ts = null
// normalize arguments
if (Number.isFinite(options)) {
[ size, options ] = [ options, size ]
console.warn('[StreamSaver] Deprecated pass an object as 2nd argument when creating a write stream')
opts.size = size
opts.writableStrategy = options
} else if (options && options.highWaterMark) {
console.warn('[StreamSaver] Deprecated pass an object as 2nd argument when creating a write stream')
opts.size = size
opts.writableStrategy = options
} else {
opts = options || {}
}
if (!useBlobFallback) {
loadTransporter()
channel = new MessageChannel()
// Make filename RFC5987 compatible
filename = encodeURIComponent(filename.replace(/\//g, ':'))
.replace(/['()]/g, escape)
.replace(/\*/g, '%2A')
const response = {
transferringReadable: supportsTransferable,
pathname: opts.pathname || Math.random().toString().slice(-6) + '/' + filename,
headers: {
'Content-Type': 'application/octet-stream; charset=utf-8',
'Content-Disposition': "attachment; filename*=UTF-8''" + filename
}
}
if (opts.size) {
response.headers['Content-Length'] = opts.size
}
const args = [ response, '*', [ channel.port2 ] ]
if (supportsTransferable) {
const transformer = downloadStrategy === 'iframe' ? undefined : {
// This transformer & flush method is only used by insecure context.
transform (chunk, controller) {
if (!(chunk instanceof Uint8Array)) {
throw new TypeError('Can only write Uint8Arrays')
}
bytesWritten += chunk.length
controller.enqueue(chunk)
if (downloadUrl) {
location.href = downloadUrl
downloadUrl = null
}
},
flush () {
if (downloadUrl) {
location.href = downloadUrl
}
}
}
ts = new streamSaver.TransformStream(
transformer,
opts.writableStrategy,
opts.readableStrategy
)
const readableStream = ts.readable
channel.port1.postMessage({ readableStream }, [ readableStream ])
}
channel.port1.onmessage = evt => {
// Service worker sent us a link that we should open.
if (evt.data.download) {
// Special treatment for popup...
if (downloadStrategy === 'navigate') {
mitmTransporter.remove()
mitmTransporter = null
if (bytesWritten) {
location.href = evt.data.download
} else {
downloadUrl = evt.data.download
}
} else {
if (mitmTransporter.isPopup) {
mitmTransporter.remove()
mitmTransporter = null
// Special case for firefox, they can keep sw alive with fetch
if (downloadStrategy === 'iframe') {
makeIframe(streamSaver.mitm)
}
}
// We never remove this iframes b/c it can interrupt saving
makeIframe(evt.data.download)
}
} else if (evt.data.abort) {
chunks = []
channel.port1.postMessage('abort') //send back so controller is aborted
channel.port1.onmessage = null
channel.port1.close()
channel.port2.close()
channel = null
}
}
if (mitmTransporter.loaded) {
mitmTransporter.postMessage(...args)
} else {
mitmTransporter.addEventListener('load', () => {
mitmTransporter.postMessage(...args)
}, { once: true })
}
}
let chunks = []
return (!useBlobFallback && ts && ts.writable) || new streamSaver.WritableStream({
write (chunk) {
if (!(chunk instanceof Uint8Array)) {
throw new TypeError('Can only write Uint8Arrays')
}
if (useBlobFallback) {
// Safari... The new IE6
// https://github.com/jimmywarting/StreamSaver.js/issues/69
//
// even though it has everything it fails to download anything
// that comes from the service worker..!
chunks.push(chunk)
return
}
// is called when a new chunk of data is ready to be written
// to the underlying sink. It can return a promise to signal
// success or failure of the write operation. The stream
// implementation guarantees that this method will be called
// only after previous writes have succeeded, and never after
// close or abort is called.
// TODO: Kind of important that service worker respond back when
// it has been written. Otherwise we can't handle backpressure
// EDIT: Transferable streams solves this...
channel.port1.postMessage(chunk)
bytesWritten += chunk.length
if (downloadUrl) {
location.href = downloadUrl
downloadUrl = null
}
},
close () {
if (useBlobFallback) {
const blob = new Blob(chunks, { type: 'application/octet-stream; charset=utf-8' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
} else {
channel.port1.postMessage('end')
}
},
abort () {
chunks = []
channel.port1.postMessage('abort')
channel.port1.onmessage = null
channel.port1.close()
channel.port2.close()
channel = null
}
}, opts.writableStrategy)
}
return streamSaver
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
// based on aurora-websocket.js https://github.com/fsbdev/aurora-websocket
// MIT licensed
(function() {
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
AV.XpraSource = (function(_super) {
__extends(XpraSource, _super);
function XpraSource() {
// constructor
}
XpraSource.prototype.start = function() {
return true;
};
XpraSource.prototype.pause = function() {
return true;
};
XpraSource.prototype.reset = function() {
return true;
};
XpraSource.prototype._on_data = function(data) {
var buf = new AV.Buffer(data);
return this.emit('data', buf);
};
return XpraSource;
})(AV.EventEmitter);
AV.Asset.fromXpraSource = function() {
var source;
source = new AV.XpraSource();
return new AV.Asset(source);
};
AV.Player.fromXpraSource = function() {
var asset;
asset = AV.Asset.fromXpraSource();
return new AV.Player(asset);
};
}).call(this);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,799 @@
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
exports.FLACDemuxer = require('./src/demuxer');
exports.FLACDecoder = require('./src/decoder');
require('./src/ogg');
},{"./src/decoder":2,"./src/demuxer":3,"./src/ogg":4}],2:[function(require,module,exports){
(function (global){
/*
* FLAC.js - Free Lossless Audio Codec decoder in JavaScript
* Original C version from FFmpeg (c) 2003 Alex Beregszaszi
* JavaScript port by Devon Govett and Jens Nockert of Official.fm Labs
*
* Licensed under the same terms as the original. The original
* license follows.
*
* FLAC.js 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.
*
* FLAC.js 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.
*
*/
var AV = (typeof window !== "undefined" ? window['AV'] : typeof global !== "undefined" ? global['AV'] : null);
var FLACDecoder = AV.Decoder.extend(function() {
AV.Decoder.register('flac', this);
this.prototype.setCookie = function(cookie) {
this.cookie = cookie;
// initialize arrays
this.decoded = [];
for (var i = 0; i < this.format.channelsPerFrame; i++) {
this.decoded[i] = new Int32Array(cookie.maxBlockSize);
}
// for 24 bit lpc frames, this is used to simulate a 64 bit int
this.lpc_total = new Int32Array(2);
};
const BLOCK_SIZES = new Int16Array([
0, 192, 576 << 0, 576 << 1, 576 << 2, 576 << 3, 0, 0,
256 << 0, 256 << 1, 256 << 2, 256 << 3, 256 << 4, 256 << 5, 256 << 6, 256 << 7
]);
const SAMPLE_RATES = new Int32Array([
0, 88200, 176400, 192000,
8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
0, 0, 0, 0
]);
const SAMPLE_SIZES = new Int8Array([
0, 8, 12, 0, 16, 20, 24, 0
]);
const MAX_CHANNELS = 8,
CHMODE_INDEPENDENT = 0,
CHMODE_LEFT_SIDE = 8,
CHMODE_RIGHT_SIDE = 9,
CHMODE_MID_SIDE = 10;
this.prototype.readChunk = function() {
var stream = this.bitstream;
if (!stream.available(32))
return;
// frame sync code
if ((stream.read(15) & 0x7FFF) !== 0x7FFC)
throw new Error('Invalid sync code');
var isVarSize = stream.read(1), // variable block size stream code
bsCode = stream.read(4), // block size
srCode = stream.read(4), // sample rate code
chMode = stream.read(4), // channel mode
bpsCode = stream.read(3); // bits per sample
stream.advance(1); // reserved bit
// channels
this.chMode = chMode;
var channels;
if (chMode < MAX_CHANNELS) {
channels = chMode + 1;
this.chMode = CHMODE_INDEPENDENT;
} else if (chMode <= CHMODE_MID_SIDE) {
channels = 2;
} else {
throw new Error('Invalid channel mode');
}
if (channels !== this.format.channelsPerFrame)
throw new Error('Switching channel layout mid-stream not supported.');
// bits per sample
if (bpsCode === 3 || bpsCode === 7)
throw new Error('Invalid sample size code');
this.bps = SAMPLE_SIZES[bpsCode];
if (this.bps !== this.format.bitsPerChannel)
throw new Error('Switching bits per sample mid-stream not supported.');
// sample number or frame number
// see http://www.hydrogenaudio.org/forums/index.php?s=ea7085ffe6d57132c36e6105c0d434c9&showtopic=88390&pid=754269&st=0&#entry754269
var ones = 0;
while (stream.read(1) === 1)
ones++;
var frame_or_sample_num = stream.read(7 - ones);
for (; ones > 1; ones--) {
stream.advance(2); // == 2
frame_or_sample_num = (frame_or_sample_num << 6) | stream.read(6);
}
// block size
if (bsCode === 0)
throw new Error('Reserved blocksize code');
else if (bsCode === 6)
this.blockSize = stream.read(8) + 1;
else if (bsCode === 7)
this.blockSize = stream.read(16) + 1;
else
this.blockSize = BLOCK_SIZES[bsCode];
// sample rate
var sampleRate;
if (srCode < 12)
sampleRate = SAMPLE_RATES[srCode];
else if (srCode === 12)
sampleRate = stream.read(8) * 1000;
else if (srCode === 13)
sampleRate = stream.read(16);
else if (srCode === 14)
sampleRate = stream.read(16) * 10;
else
throw new Error('Invalid sample rate code');
stream.advance(8); // skip CRC check
// subframes
for (var i = 0; i < channels; i++)
this.decodeSubframe(i);
stream.align();
stream.advance(16); // skip CRC frame footer
var is32 = this.bps > 16,
output = new ArrayBuffer(this.blockSize * channels * (is32 ? 4 : 2)),
buf = is32 ? new Int32Array(output) : new Int16Array(output),
blockSize = this.blockSize,
decoded = this.decoded,
j = 0;
switch (this.chMode) {
case CHMODE_INDEPENDENT:
for (var k = 0; k < blockSize; k++) {
for (var i = 0; i < channels; i++) {
buf[j++] = decoded[i][k];
}
}
break;
case CHMODE_LEFT_SIDE:
for (var i = 0; i < blockSize; i++) {
var left = decoded[0][i],
right = decoded[1][i];
buf[j++] = left;
buf[j++] = (left - right);
}
break;
case CHMODE_RIGHT_SIDE:
for (var i = 0; i < blockSize; i++) {
var left = decoded[0][i],
right = decoded[1][i];
buf[j++] = (left + right);
buf[j++] = right;
}
break;
case CHMODE_MID_SIDE:
for (var i = 0; i < blockSize; i++) {
var left = decoded[0][i],
right = decoded[1][i];
left -= right >> 1;
buf[j++] = (left + right);
buf[j++] = left;
}
break;
}
return buf;
};
this.prototype.decodeSubframe = function(channel) {
var wasted = 0,
stream = this.bitstream,
blockSize = this.blockSize,
decoded = this.decoded;
this.curr_bps = this.bps;
if (channel === 0) {
if (this.chMode === CHMODE_RIGHT_SIDE)
this.curr_bps++;
} else {
if (this.chMode === CHMODE_LEFT_SIDE || this.chMode === CHMODE_MID_SIDE)
this.curr_bps++;
}
if (stream.read(1))
throw new Error("Invalid subframe padding");
var type = stream.read(6);
if (stream.read(1)) {
wasted = 1;
while (!stream.read(1))
wasted++;
this.curr_bps -= wasted;
}
if (this.curr_bps > 32)
throw new Error("decorrelated bit depth > 32 (" + this.curr_bps + ")");
if (type === 0) {
var tmp = stream.read(this.curr_bps, true);
for (var i = 0; i < blockSize; i++)
decoded[channel][i] = tmp;
} else if (type === 1) {
var bps = this.curr_bps;
for (var i = 0; i < blockSize; i++)
decoded[channel][i] = stream.read(bps, true);
} else if ((type >= 8) && (type <= 12)) {
this.decode_subframe_fixed(channel, type & ~0x8);
} else if (type >= 32) {
this.decode_subframe_lpc(channel, (type & ~0x20) + 1);
} else {
throw new Error("Invalid coding type");
}
if (wasted) {
for (var i = 0; i < blockSize; i++)
decoded[channel][i] <<= wasted;
}
};
this.prototype.decode_subframe_fixed = function(channel, predictor_order) {
var decoded = this.decoded[channel],
stream = this.bitstream,
bps = this.curr_bps;
// warm up samples
for (var i = 0; i < predictor_order; i++)
decoded[i] = stream.read(bps, true);
this.decode_residuals(channel, predictor_order);
var a = 0, b = 0, c = 0, d = 0;
if (predictor_order > 0)
a = decoded[predictor_order - 1];
if (predictor_order > 1)
b = a - decoded[predictor_order - 2];
if (predictor_order > 2)
c = b - decoded[predictor_order - 2] + decoded[predictor_order - 3];
if (predictor_order > 3)
d = c - decoded[predictor_order - 2] + 2 * decoded[predictor_order - 3] - decoded[predictor_order - 4];
switch (predictor_order) {
case 0:
break;
case 1:
case 2:
case 3:
case 4:
var abcd = new Int32Array([a, b, c, d]),
blockSize = this.blockSize;
for (var i = predictor_order; i < blockSize; i++) {
abcd[predictor_order - 1] += decoded[i];
for (var j = predictor_order - 2; j >= 0; j--) {
abcd[j] += abcd[j + 1];
}
decoded[i] = abcd[0];
}
break;
default:
throw new Error("Invalid Predictor Order " + predictor_order);
}
};
this.prototype.decode_subframe_lpc = function(channel, predictor_order) {
var stream = this.bitstream,
decoded = this.decoded[channel],
bps = this.curr_bps,
blockSize = this.blockSize;
// warm up samples
for (var i = 0; i < predictor_order; i++) {
decoded[i] = stream.read(bps, true);
}
var coeff_prec = stream.read(4) + 1;
if (coeff_prec === 16)
throw new Error("Invalid coefficient precision");
var qlevel = stream.read(5, true);
if (qlevel < 0)
throw new Error("Negative qlevel, maybe buggy stream");
var coeffs = new Int32Array(32);
for (var i = 0; i < predictor_order; i++) {
coeffs[i] = stream.read(coeff_prec, true);
}
this.decode_residuals(channel, predictor_order);
if (this.bps <= 16) {
for (var i = predictor_order; i < blockSize - 1; i += 2) {
var d = decoded[i - predictor_order],
s0 = 0, s1 = 0, c = 0;
for (var j = predictor_order - 1; j > 0; j--) {
c = coeffs[j];
s0 += c * d;
d = decoded[i - j];
s1 += c * d;
}
c = coeffs[0];
s0 += c * d;
d = decoded[i] += (s0 >> qlevel);
s1 += c * d;
decoded[i + 1] += (s1 >> qlevel);
}
if (i < blockSize) {
var sum = 0;
for (var j = 0; j < predictor_order; j++)
sum += coeffs[j] * decoded[i - j - 1];
decoded[i] += (sum >> qlevel);
}
} else {
// simulate 64 bit integer using an array of two 32 bit ints
var total = this.lpc_total;
for (var i = predictor_order; i < blockSize; i++) {
// reset total to 0
total[0] = 0;
total[1] = 0;
for (j = 0; j < predictor_order; j++) {
// simulate `total += coeffs[j] * decoded[i - j - 1]`
multiply_add(total, coeffs[j], decoded[i - j - 1]);
}
// simulate `decoded[i] += total >> qlevel`
// we know that qlevel < 32 since it is a 5 bit field (see above)
decoded[i] += (total[0] >>> qlevel) | (total[1] << (32 - qlevel));
}
}
};
const TWO_PWR_32_DBL = Math.pow(2, 32);
// performs `total += a * b` on a simulated 64 bit int
// total is an Int32Array(2)
// a and b are JS numbers (32 bit ints)
function multiply_add(total, a, b) {
// multiply a * b (we can use normal JS multiplication for this)
var r = a * b;
var n = r < 0;
if (n)
r = -r;
var r_low = (r % TWO_PWR_32_DBL) | 0;
var r_high = (r / TWO_PWR_32_DBL) | 0;
if (n) {
r_low = ~r_low + 1;
r_high = ~r_high;
}
// add result to total
var a48 = total[1] >>> 16;
var a32 = total[1] & 0xFFFF;
var a16 = total[0] >>> 16;
var a00 = total[0] & 0xFFFF;
var b48 = r_high >>> 16;
var b32 = r_high & 0xFFFF;
var b16 = r_low >>> 16;
var b00 = r_low & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 + b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 + b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 + b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 + b48;
c48 &= 0xFFFF;
// store result back in total
total[0] = (c16 << 16) | c00;
total[1] = (c48 << 16) | c32;
}
const INT_MAX = 32767;
this.prototype.decode_residuals = function(channel, predictor_order) {
var stream = this.bitstream,
method_type = stream.read(2);
if (method_type > 1)
throw new Error('Illegal residual coding method ' + method_type);
var rice_order = stream.read(4),
samples = (this.blockSize >>> rice_order);
if (predictor_order > samples)
throw new Error('Invalid predictor order ' + predictor_order + ' > ' + samples);
var decoded = this.decoded[channel],
sample = predictor_order,
i = predictor_order;
for (var partition = 0; partition < (1 << rice_order); partition++) {
var tmp = stream.read(method_type === 0 ? 4 : 5);
if (tmp === (method_type === 0 ? 15 : 31)) {
tmp = stream.read(5);
for (; i < samples; i++)
decoded[sample++] = stream.read(tmp, true);
} else {
for (; i < samples; i++)
decoded[sample++] = this.golomb(tmp, INT_MAX, 0);
}
i = 0;
}
};
const MIN_CACHE_BITS = 25;
this.prototype.golomb = function(k, limit, esc_len) {
var data = this.bitstream,
offset = data.bitPosition,
buf = data.peek(32 - offset) << offset,
v = 0;
var log = 31 - clz(buf | 1); // log2(buf)
if (log - k >= 32 - MIN_CACHE_BITS && 32 - log < limit) {
buf >>>= log - k;
buf += (30 - log) << k;
data.advance(32 + k - log);
v = buf;
} else {
for (var i = 0; data.read(1) === 0; i++)
buf = data.peek(32 - offset) << offset;
if (i < limit - 1) {
if (k)
buf = data.read(k);
else
buf = 0;
v = buf + (i << k);
} else if (i === limit - 1) {
buf = data.read(esc_len);
v = buf + 1;
} else {
v = -1;
}
}
return (v >> 1) ^ -(v & 1);
};
// Should be in the damned standard library...
function clz(input) {
var output = 0,
curbyte = 0;
while(true) { // emulate goto in JS using the break statement :D
curbyte = input >>> 24;
if (curbyte) break;
output += 8;
curbyte = input >>> 16;
if (curbyte & 0xff) break;
output += 8;
curbyte = input >>> 8;
if (curbyte & 0xff) break;
output += 8;
curbyte = input;
if (curbyte & 0xff) break;
output += 8;
return output;
}
if (!(curbyte & 0xf0))
output += 4;
else
curbyte >>>= 4;
if (curbyte & 0x8)
return output;
if (curbyte & 0x4)
return output + 1;
if (curbyte & 0x2)
return output + 2;
if (curbyte & 0x1)
return output + 3;
// shouldn't get here
return output + 4;
}
});
module.exports = FLACDecoder;
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],3:[function(require,module,exports){
(function (global){
/*
* FLAC.js - Free Lossless Audio Codec decoder in JavaScript
* By Devon Govett and Jens Nockert of Official.fm Labs
*
* FLAC.js 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.
*
* FLAC.js 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.
*
*/
var AV = (typeof window !== "undefined" ? window['AV'] : typeof global !== "undefined" ? global['AV'] : null);
var FLACDemuxer = AV.Demuxer.extend(function() {
AV.Demuxer.register(this);
this.probe = function(buffer) {
return buffer.peekString(0, 4) === 'fLaC';
}
const STREAMINFO = 0,
PADDING = 1,
APPLICATION = 2,
SEEKTABLE = 3,
VORBIS_COMMENT = 4,
CUESHEET = 5,
PICTURE = 6,
INVALID = 127,
STREAMINFO_SIZE = 34;
this.prototype.readChunk = function() {
var stream = this.stream;
if (!this.readHeader && stream.available(4)) {
if (stream.readString(4) !== 'fLaC')
return this.emit('error', 'Invalid FLAC file.');
this.readHeader = true;
}
while (stream.available(1) && !this.last) {
if (!this.readBlockHeaders) {
var tmp = stream.readUInt8();
this.last = (tmp & 0x80) === 0x80,
this.type = tmp & 0x7F,
this.size = stream.readUInt24();
}
if (!this.foundStreamInfo && this.type !== STREAMINFO)
return this.emit('error', 'STREAMINFO must be the first block');
if (!stream.available(this.size))
return;
switch (this.type) {
case STREAMINFO:
if (this.foundStreamInfo)
return this.emit('error', 'STREAMINFO can only occur once.');
if (this.size !== STREAMINFO_SIZE)
return this.emit('error', 'STREAMINFO size is wrong.');
this.foundStreamInfo = true;
var bitstream = new AV.Bitstream(stream);
var cookie = {
minBlockSize: bitstream.read(16),
maxBlockSize: bitstream.read(16),
minFrameSize: bitstream.read(24),
maxFrameSize: bitstream.read(24)
};
this.format = {
formatID: 'flac',
sampleRate: bitstream.read(20),
channelsPerFrame: bitstream.read(3) + 1,
bitsPerChannel: bitstream.read(5) + 1
};
this.emit('format', this.format);
this.emit('cookie', cookie);
var sampleCount = bitstream.read(36);
this.emit('duration', sampleCount / this.format.sampleRate * 1000 | 0);
stream.advance(16); // skip MD5 hashes
this.readBlockHeaders = false;
break;
/*
I am only looking at the least significant 32 bits of sample number and offset data
This is more than sufficient for the longest flac file I have (~50 mins 2-channel 16-bit 44.1k which uses about 7.5% of the UInt32 space for the largest offset)
Can certainly be improved by storing sample numbers and offests as doubles, but would require additional overriding of the searchTimestamp and seek functions (possibly more?)
Also the flac faq suggests it would be possible to find frame lengths and thus create seek points on the fly via decoding but I assume this would be slow
I may look into these thigns though as my project progresses
*/
case SEEKTABLE:
for(var s=0; s<this.size/18; s++)
{
if(stream.peekUInt32(0) == 0xFFFFFFFF && stream.peekUInt32(1) == 0xFFFFFFFF)
{
//placeholder, ignore
stream.advance(18);
} else {
if(stream.readUInt32() > 0)
{
this.emit('error', 'Seek points with sample number >UInt32 not supported');
}
var samplenum = stream.readUInt32();
if(stream.readUInt32() > 0)
{
this.emit('error', 'Seek points with stream offset >UInt32 not supported');
}
var offset = stream.readUInt32();
stream.advance(2);
this.addSeekPoint(offset, samplenum);
}
}
break;
case VORBIS_COMMENT:
// see http://www.xiph.org/vorbis/doc/v-comment.html
this.metadata || (this.metadata = {});
var len = stream.readUInt32(true);
this.metadata.vendor = stream.readString(len);
var length = stream.readUInt32(true);
for (var i = 0; i < length; i++) {
len = stream.readUInt32(true);
var str = stream.readString(len, 'utf8'),
idx = str.indexOf('=');
this.metadata[str.slice(0, idx).toLowerCase()] = str.slice(idx + 1);
}
// TODO: standardize field names across formats
break;
case PICTURE:
var type = stream.readUInt32();
if (type !== 3) { // make sure this is album art (type 3)
stream.advance(this.size - 4);
} else {
var mimeLen = stream.readUInt32(),
mime = stream.readString(mimeLen),
descLen = stream.readUInt32(),
description = stream.readString(descLen),
width = stream.readUInt32(),
height = stream.readUInt32(),
depth = stream.readUInt32(),
colors = stream.readUInt32(),
length = stream.readUInt32(),
picture = stream.readBuffer(length);
this.metadata || (this.metadata = {});
this.metadata.coverArt = picture;
}
// does anyone want the rest of the info?
break;
default:
stream.advance(this.size);
this.readBlockHeaders = false;
}
if (this.last && this.metadata)
this.emit('metadata', this.metadata);
}
while (stream.available(1) && this.last) {
var buffer = stream.readSingleBuffer(stream.remainingBytes());
this.emit('data', buffer);
}
}
});
module.exports = FLACDemuxer;
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],4:[function(require,module,exports){
(function (global){
var AV = (typeof window !== "undefined" ? window['AV'] : typeof global !== "undefined" ? global['AV'] : null);
// if ogg.js exists, register a plugin
try {
var OggDemuxer = (typeof window !== "undefined" ? window['AV']['OggDemuxer'] : typeof global !== "undefined" ? global['AV']['OggDemuxer'] : null);
} catch (e) {};
if (!OggDemuxer) return;
OggDemuxer.plugins.push({
magic: "\177FLAC",
init: function() {
this.list = new AV.BufferList();
this.stream = new AV.Stream(this.list);
},
readHeaders: function(packet) {
var stream = this.stream;
this.list.append(new AV.Buffer(packet));
stream.advance(5); // magic
if (stream.readUInt8() != 1)
throw new Error('Unsupported FLAC version');
stream.advance(3);
if (stream.peekString(0, 4) != 'fLaC')
throw new Error('Not flac');
this.flac = AV.Demuxer.find(stream.peekSingleBuffer(0, stream.remainingBytes()));
if (!this.flac)
throw new Error('Flac demuxer not found');
this.flac.prototype.readChunk.call(this);
return true;
},
readPacket: function(packet) {
this.list.append(new AV.Buffer(packet));
this.flac.prototype.readChunk.call(this);
}
});
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}]},{},[1])
//# sourceMappingURL=flac.js.map

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,299 @@
/* Detect-zoom
* -----------
* Cross Browser Zoom and Pixel Ratio Detector
* Version 1.0.4 | Apr 1 2013
* dual-licensed under the WTFPL and MIT license
* Maintained by https://github/tombigel
* Original developer https://github.com/yonran
*/
//AMD and CommonJS initialization copied from https://github.com/zohararad/audio5js
(function (root, ns, factory) {
"use strict";
if (typeof (module) !== 'undefined' && module.exports) { // CommonJS
module.exports = factory(ns, root);
} else if (typeof (define) === 'function' && define.amd) { // AMD
define("detect-zoom", function () {
return factory(ns, root);
});
} else {
root[ns] = factory(ns, root);
}
}(window, 'detectZoom', function () {
/**
* Use devicePixelRatio if supported by the browser
* @return {Number}
* @private
*/
var devicePixelRatio = function () {
return window.devicePixelRatio || 1;
};
/**
* Fallback function to set default values
* @return {Object}
* @private
*/
var fallback = function () {
return {
zoom: 1,
devicePxPerCssPx: 1
};
};
/**
* For chrome
*
*/
var chrome = function()
{
var zoom = Math.round(((window.outerWidth) / window.innerWidth)*100) / 100;
return {
zoom: zoom,
devicePxPerCssPx: zoom * devicePixelRatio()
};
}
/**
* For safari (same as chrome)
*
*/
var safari= function()
{
var zoom = Math.round(((document.documentElement.clientWidth) / window.innerWidth)*100) / 100;
return {
zoom: zoom,
devicePxPerCssPx: zoom * devicePixelRatio()
};
}
/**
* Mobile WebKit
* the trick: window.innerWIdth is in CSS pixels, while
* screen.width and screen.height are in system pixels.
* And there are no scrollbars to mess up the measurement.
* @return {Object}
* @private
*/
var webkitMobile = function () {
var deviceWidth = (Math.abs(window.orientation) == 90) ? screen.height : screen.width;
var zoom = deviceWidth / window.innerWidth;
return {
zoom: zoom,
devicePxPerCssPx: zoom * devicePixelRatio()
};
};
/**
* Desktop Webkit
* the trick: an element's clientHeight is in CSS pixels, while you can
* set its line-height in system pixels using font-size and
* -webkit-text-size-adjust:none.
* device-pixel-ratio: http://www.webkit.org/blog/55/high-dpi-web-sites/
*
* Previous trick (used before http://trac.webkit.org/changeset/100847):
* documentElement.scrollWidth is in CSS pixels, while
* document.width was in system pixels. Note that this is the
* layout width of the document, which is slightly different from viewport
* because document width does not include scrollbars and might be wider
* due to big elements.
* @return {Object}
* @private
*/
var webkit = function () {
var important = function (str) {
return str.replace(/;/g, " !important;");
};
var div = document.createElement('div');
div.innerHTML = "1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>0";
div.setAttribute('style', important('font: 100px/1em sans-serif; -webkit-text-size-adjust: none; text-size-adjust: none; height: auto; width: 1em; padding: 0; overflow: visible;'));
// The container exists so that the div will be laid out in its own flow
// while not impacting the layout, viewport size, or display of the
// webpage as a whole.
// Add !important and relevant CSS rule resets
// so that other rules cannot affect the results.
var container = document.createElement('div');
container.setAttribute('style', important('width:0; height:0; overflow:hidden; visibility:hidden; position: absolute;'));
container.appendChild(div);
document.body.appendChild(container);
var zoom = 1000 / div.clientHeight;
zoom = Math.round(zoom * 100) / 100;
document.body.removeChild(container);
return{
zoom: zoom,
devicePxPerCssPx: zoom * devicePixelRatio()
};
};
/**
* no real trick; device-pixel-ratio is the ratio of device dpi / css dpi.
* (Note that this is a different interpretation than Webkit's device
* pixel ratio, which is the ratio device dpi / system dpi).
*
* Also, for Mozilla, there is no difference between the zoom factor and the device ratio.
*
* @return {Object}
* @private
*/
var firefox4 = function () {
var zoom = mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001);
zoom = Math.round(zoom * 100) / 100;
return {
zoom: zoom,
devicePxPerCssPx: zoom
};
};
/**
* Firefox 18.x
* Mozilla added support for devicePixelRatio to Firefox 18,
* but it is affected by the zoom level, so, like in older
* Firefox we can't tell if we are in zoom mode or in a device
* with a different pixel ratio
* @return {Object}
* @private
*/
var firefox18 = function () {
return {
zoom: firefox4().zoom,
devicePxPerCssPx: devicePixelRatio()
};
};
/**
* works starting Opera 11.11
* the trick: outerWidth is the viewport width including scrollbars in
* system px, while innerWidth is the viewport width including scrollbars
* in CSS px
* @return {Object}
* @private
*/
var opera11 = function () {
var zoom = window.top.outerWidth / window.top.innerWidth;
zoom = Math.round(zoom * 100) / 100;
return {
zoom: zoom,
devicePxPerCssPx: zoom * devicePixelRatio()
};
};
/**
* Use a binary search through media queries to find zoom level in Firefox
* @param property
* @param unit
* @param a
* @param b
* @param maxIter
* @param epsilon
* @return {Number}
*/
var mediaQueryBinarySearch = function (property, unit, a, b, maxIter, epsilon) {
var matchMedia;
var head, style, div;
if (window.matchMedia) {
matchMedia = window.matchMedia;
} else {
head = document.getElementsByTagName('head')[0];
style = document.createElement('style');
head.appendChild(style);
div = document.createElement('div');
div.className = 'mediaQueryBinarySearch';
div.style.display = 'none';
document.body.appendChild(div);
matchMedia = function (query) {
style.sheet.insertRule('@media ' + query + '{.mediaQueryBinarySearch ' + '{text-decoration: underline} }', 0);
var matched = getComputedStyle(div, null).textDecoration == 'underline';
style.sheet.deleteRule(0);
return {matches: matched};
};
}
var ratio = binarySearch(a, b, maxIter);
if (div) {
head.removeChild(style);
document.body.removeChild(div);
}
return ratio;
function binarySearch(a, b, maxIter) {
var mid = (a + b) / 2;
if (maxIter <= 0 || b - a < epsilon) {
return mid;
}
var query = "(" + property + ":" + mid + unit + ")";
if (matchMedia(query).matches) {
return binarySearch(mid, b, maxIter - 1);
} else {
return binarySearch(a, mid, maxIter - 1);
}
}
};
/**
* Generate detection function
* @private
*/
var detectFunction = (function () {
var func = fallback;
//chrome
if(!!window.chrome && !(!!window.opera || navigator.userAgent.indexOf(' Opera') >= 0)){
func = chrome;
}
//safari
else if(Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0){
func = safari;
}
//Mobile Webkit
else if ('orientation' in window && 'webkitRequestAnimationFrame' in window) {
func = webkitMobile;
}
//WebKit
else if ('webkitRequestAnimationFrame' in window) {
func = webkit;
}
//Opera
else if (navigator.userAgent.indexOf('Opera') >= 0) {
func = opera11;
}
//Last one is Firefox
//FF 18.x
else if (window.devicePixelRatio) {
func = firefox18;
}
//FF 4.0 - 17.x
else if (firefox4().zoom > 0.001) {
func = firefox4;
}
return func;
}());
return ({
/**
* Ratios.zoom shorthand
* @return {Number} Zoom level
*/
zoom: function () {
return detectFunction().zoom;
},
/**
* Ratios.devicePxPerCssPx shorthand
* @return {Number} devicePxPerCssPx level
*/
device: function () {
return detectFunction().devicePxPerCssPx;
}
});
}));

View File

@ -0,0 +1,270 @@
// https://gist.github.com/stevendesu/2d52f7b5e1f1184af3b667c0b5e054b8?permalink_comment_id=4535312
// A simple, open-source, HMAC-SHA256 implementation in pure JavaScript. Designed for efficient minification.
// Feel free to choose whatever license you find most permissible, but I offer no warranty for the code.
// It's 100% free to do with as you please.
// To ensure cross-browser support even without a proper SubtleCrypto
// impelmentation (or without access to the impelmentation, as is the case with
// Chrome loaded over HTTP instead of HTTPS), this library can create SHA-256
// HMAC signatures using nothing but raw JavaScript
/* eslint-disable no-magic-numbers, id-length, no-param-reassign, new-cap */
// By giving internal functions names that we can mangle, future calls to
// them are reduced to a single byte (minor space savings in minified file)
var uint8Array = Uint8Array;
var uint32Array = Uint32Array;
var pow = Math.pow;
// Will be initialized below
// Using a Uint32Array instead of a simple array makes the minified code
// a bit bigger (we lose our `unshift()` hack), but comes with huge
// performance gains
var DEFAULT_STATE = new uint32Array(8);
var ROUND_CONSTANTS = [];
// Reusable object for expanded message
// Using a Uint32Array instead of a simple array makes the minified code
// 7 bytes larger, but comes with huge performance gains
var M = new uint32Array(64);
// After minification the code to compute the default state and round
// constants is smaller than the output. More importantly, this serves as a
// good educational aide for anyone wondering where the magic numbers come
// from. No magic numbers FTW!
function getFractionalBits(n)
{
return ((n - (n | 0)) * pow(2, 32)) | 0;
}
var n = 2, nPrime = 0;
while (nPrime < 64)
{
// isPrime() was in-lined from its original function form to save
// a few bytes
var isPrime = true;
// Math.sqrt() was replaced with pow(n, 1/2) to save a few bytes
// var sqrtN = pow(n, 1 / 2);
// So technically to determine if a number is prime you only need to
// check numbers up to the square root. However this function only runs
// once and we're only computing the first 64 primes (up to 311), so on
// any modern CPU this whole function runs in a couple milliseconds.
// By going to n / 2 instead of sqrt(n) we net 8 byte savings and no
// scaling performance cost
for (var factor = 2; factor <= n / 2; factor++)
{
if (n % factor === 0)
{
isPrime = false;
}
}
if (isPrime)
{
if (nPrime < 8)
{
DEFAULT_STATE[nPrime] = getFractionalBits(pow(n, 1 / 2));
}
ROUND_CONSTANTS[nPrime] = getFractionalBits(pow(n, 1 / 3));
nPrime++;
}
n++;
}
// For cross-platform support we need to ensure that all 32-bit words are
// in the same endianness. A UTF-8 TextEncoder will return BigEndian data,
// so upon reading or writing to our ArrayBuffer we'll only swap the bytes
// if our system is LittleEndian (which is about 99% of CPUs)
var LittleEndian = !!new uint8Array(new uint32Array([1]).buffer)[0];
function convertEndian(word)
{
if (LittleEndian)
{
return (
// byte 1 -> byte 4
(word >>> 24) |
// byte 2 -> byte 3
(((word >>> 16) & 0xff) << 8) |
// byte 3 -> byte 2
((word & 0xff00) << 8) |
// byte 4 -> byte 1
(word << 24)
);
}
else
{
return word;
}
}
function rightRotate(word, bits)
{
return (word >>> bits) | (word << (32 - bits));
}
function sha256(data)
{
// Copy default state
var STATE = DEFAULT_STATE.slice();
// Caching this reduces occurrences of ".length" in minified JavaScript
// 3 more byte savings! :D
var legth = data.length;
// Pad data
var bitLength = legth * 8;
var newBitLength = (512 - ((bitLength + 64) % 512) - 1) + bitLength + 65;
// "bytes" and "words" are stored BigEndian
var bytes = new uint8Array(newBitLength / 8);
var words = new uint32Array(bytes.buffer);
bytes.set(data, 0);
// Append a 1
bytes[legth] = 0b10000000;
// Store length in BigEndian
words[words.length - 1] = convertEndian(bitLength);
// Loop iterator (avoid two instances of "var") -- saves 2 bytes
var round;
// Process blocks (512 bits / 64 bytes / 16 words at a time)
for (var block = 0; block < newBitLength / 32; block += 16)
{
var workingState = STATE.slice();
// Rounds
for (round = 0; round < 64; round++)
{
var MRound;
// Expand message
if (round < 16)
{
// Convert to platform Endianness for later math
MRound = convertEndian(words[block + round]);
}
else
{
var gamma0x = M[round - 15];
var gamma1x = M[round - 2];
MRound =
M[round - 7] + M[round - 16] + (
rightRotate(gamma0x, 7) ^
rightRotate(gamma0x, 18) ^
(gamma0x >>> 3)
) + (
rightRotate(gamma1x, 17) ^
rightRotate(gamma1x, 19) ^
(gamma1x >>> 10)
)
;
}
// M array matches platform endianness
M[round] = MRound |= 0;
// Computation
var t1 =
(
rightRotate(workingState[4], 6) ^
rightRotate(workingState[4], 11) ^
rightRotate(workingState[4], 25)
) +
(
(workingState[4] & workingState[5]) ^
(~workingState[4] & workingState[6])
) + workingState[7] + MRound + ROUND_CONSTANTS[round]
;
var t2 =
(
rightRotate(workingState[0], 2) ^
rightRotate(workingState[0], 13) ^
rightRotate(workingState[0], 22)
) +
(
(workingState[0] & workingState[1]) ^
(workingState[2] & (workingState[0] ^
workingState[1]))
)
;
for (var i = 7; i > 0; i--)
{
workingState[i] = workingState[i - 1];
}
workingState[0] = (t1 + t2) | 0;
workingState[4] = (workingState[4] + t1) | 0;
}
// Update state
for (round = 0; round < 8; round++)
{
STATE[round] = (STATE[round] + workingState[round]) | 0;
}
}
// Finally the state needs to be converted to BigEndian for output
// And we want to return a Uint8Array, not a Uint32Array
return new uint8Array(new uint32Array(
STATE.map(function(val) { return convertEndian(val); })
).buffer);
}
function hmac(key, data)
{
if (key.length > 64)
key = sha256(key);
if (key.length < 64)
{
const tmp = new Uint8Array(64);
tmp.set(key, 0);
key = tmp;
}
// Generate inner and outer keys
var innerKey = new Uint8Array(64);
var outerKey = new Uint8Array(64);
for (var i = 0; i < 64; i++)
{
innerKey[i] = 0x36 ^ key[i];
outerKey[i] = 0x5c ^ key[i];
}
// Append the innerKey
var msg = new Uint8Array(data.length + 64);
msg.set(innerKey, 0);
msg.set(data, 64);
// Has the previous message and append the outerKey
var result = new Uint8Array(64 + 32);
result.set(outerKey, 0);
result.set(sha256(msg), 64);
// Hash the previous message
return sha256(result);
}
// Convert a string to a Uint8Array, SHA-256 it, and convert back to string
const encoder = new TextEncoder("utf-8");
function sign(inputKey, inputData)
{
const key = typeof inputKey === "string" ? encoder.encode(inputKey) : inputKey;
const data = typeof inputData === "string" ? encoder.encode(inputData) : inputData;
return hmac(key, data);
}
function hash(str)
{
return hex(sha256(encoder.encode(str)));
}
function hex(bin)
{
return bin.reduce((acc, val) =>
acc + ("00" + val.toString(16)).substr(-2)
, "");
}

View File

@ -0,0 +1,229 @@
/*
* xpra client
* Licensed under MPL 2.0
*
* Copyright: 2021, Vasyl Gello
*
* Based on https://stackoverflow.com/a/24263015
* Copyright: 2014, gal
* License: CC-BY-SA 3.0
*/
/* General matrix transformations */
var Matrix = function(a, b, c, d, e, f, g, h, i) {
if ($.type(a) === 'array') {
this.elements = [
+a[0], +a[2], +a[4],
+a[1], +a[3], +a[5],
0, 0, 1
];
} else {
this.elements = [
a, b, c,
d, e, f,
g || 0, h || 0, i || 1
];
}
};
Matrix.prototype = {
/**
* Multiply a 3x3 matrix by a similar matrix or a vector
* @param {Matrix|Vector} matrix
* @return {Matrix|Vector} Returns a vector if multiplying by a vector
*/
x: function(matrix) {
var isVector = matrix instanceof Vector;
var a = this.elements,
b = matrix.elements;
if (isVector && b.length === 3) {
// b is actually a vector
return new Vector(
a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
);
} else if (b.length === a.length) {
// b is a 3x3 matrix
return new Matrix(
a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
a[0] * b[2] + a[1] * b[5] + a[2] * b[8],
a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
a[3] * b[2] + a[4] * b[5] + a[5] * b[8],
a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
);
}
return false; // fail
},
/**
* Generates an inverse of the current matrix
* @returns {Matrix}
*/
inverse: function() {
var d = 1 / this.determinant(),
a = this.elements;
return new Matrix(
d * (a[8] * a[4] - a[7] * a[5]),
d * (-(a[8] * a[1] - a[7] * a[2])),
d * (a[5] * a[1] - a[4] * a[2]),
d * (-(a[8] * a[3] - a[6] * a[5])),
d * (a[8] * a[0] - a[6] * a[2]),
d * (-(a[5] * a[0] - a[3] * a[2])),
d * (a[7] * a[3] - a[6] * a[4]),
d * (-(a[7] * a[0] - a[6] * a[1])),
d * (a[4] * a[0] - a[3] * a[1])
);
},
/**
* Calculates the determinant of the current matrix
* @returns {Number}
*/
determinant: function() {
var a = this.elements;
return a[0] * (a[8] * a[4] - a[7] * a[5]) - a[3] * (a[8] * a[1] - a[7] * a[2]) + a[6] * (a[5] * a[1] - a[4] * a[2]);
}
};
var Vector = function(x, y, z) {
this.elements = [x, y, z];
};
/**
* Get the element at zero-indexed index i
* @param {Number} i
*/
Vector.prototype.e = Matrix.prototype.e = function(i) {
if (this.elements[i] != undefined) {
return this.elements[i];
}
return this.elements;
};
/* Draggable transformation plugin */
$.ui.plugin.add("draggable", "transform", {
start: function(event, ui) {
if (!$(this).data('ui-draggable')) {
return false;
}
var inst = $(this).data("ui-draggable");
inst.matrix = new Matrix(function(matrix) {
var rmatrix = new RegExp(
'^matrix\\(' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\)$'
);
var matrix = rmatrix.exec(matrix);
if (matrix) {
matrix.shift();
}
return matrix || [1, 0, 0, 1, 0, 0];
}([$(this).parents('[style*="transform"]').css('transform')]));
},
drag: function(event, ui) {
if (!$(this).data('ui-draggable')) {
return false;
}
var inst = $(this).data("ui-draggable");
var t_pos = inst.matrix.inverse().x(new Vector(ui.position.left, ui.position.top, 0));
ui.position.left = t_pos.e(0);
ui.position.top = t_pos.e(1);
if (inst.options.grid) {
ui.position.left = ui.position.left - ui.position.left % inst.options.grid[0];
ui.position.top = ui.position.top - ui.position.top % inst.options.grid[1];
}
if (inst.containment) {
if (ui.position.left < inst.containment[0]) {
ui.position.left = inst.containment[0];
}
if (ui.position.left > inst.containment[2]) {
ui.position.left = inst.containment[2];
}
if (ui.position.top < inst.containment[1]) {
ui.position.top = inst.containment[1];
}
if (ui.position.top > inst.containment[3]) {
ui.position.top = inst.containment[3];
}
}
},
});
/* Resizable transformation plugin */
$.ui.plugin.add("resizable", "transform", {
start: function(event, ui) {
if (!$(this).data('ui-resizable')) {
return false;
}
var inst = $(this).data("ui-resizable");
inst.matrix = new Matrix(function(matrix) {
var rmatrix = new RegExp(
'^matrix\\(' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
'(\\-?[\\d\\.e]+)' + '\\)$'
);
var matrix = rmatrix.exec(matrix);
if (matrix) {
matrix.shift();
}
return matrix || [1, 0, 0, 1, 0, 0];
}([$(this).parents('[style*="transform"]').css('transform')]));
},
resize: function(event, ui) {
if (!$(this).data('ui-resizable')) {
return false;
}
var inst = $(this).data("ui-resizable");
if (inst.helper && inst.helper.length > 0) {
var t_pos = inst.matrix.inverse().x(new Vector(ui.size.width, ui.size.height, 0));
inst.helper[0].width = t_pos.e(0);
inst.helper[0].height = t_pos.e(1);
}
},
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,252 @@
/*!
* jQuery throttle / debounce - v1.1 - 3/7/2010
* http://benalman.com/projects/jquery-throttle-debounce-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
// Script: jQuery throttle / debounce: Sometimes, less is more!
//
// *Version: 1.1, Last updated: 3/7/2010*
//
// Project Home - http://benalman.com/projects/jquery-throttle-debounce-plugin/
// GitHub - http://github.com/cowboy/jquery-throttle-debounce/
// Source - http://github.com/cowboy/jquery-throttle-debounce/raw/master/jquery.ba-throttle-debounce.js
// (Minified) - http://github.com/cowboy/jquery-throttle-debounce/raw/master/jquery.ba-throttle-debounce.min.js (0.7kb)
//
// About: License
//
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
//
// About: Examples
//
// These working examples, complete with fully commented code, illustrate a few
// ways in which this plugin can be used.
//
// Throttle - http://benalman.com/code/projects/jquery-throttle-debounce/examples/throttle/
// Debounce - http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/
//
// About: Support and Testing
//
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
//
// jQuery Versions - none, 1.3.2, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome 4-5, Opera 9.6-10.1.
// Unit Tests - http://benalman.com/code/projects/jquery-throttle-debounce/unit/
//
// About: Release History
//
// 1.1 - (3/7/2010) Fixed a bug in <jQuery.throttle> where trailing callbacks
// executed later than they should. Reworked a fair amount of internal
// logic as well.
// 1.0 - (3/6/2010) Initial release as a stand-alone project. Migrated over
// from jquery-misc repo v0.4 to jquery-throttle repo v1.0, added the
// no_trailing throttle parameter and debounce functionality.
//
// Topic: Note for non-jQuery users
//
// jQuery isn't actually required for this plugin, because nothing internal
// uses any jQuery methods or properties. jQuery is just used as a namespace
// under which these methods can exist.
//
// Since jQuery isn't actually required for this plugin, if jQuery doesn't exist
// when this plugin is loaded, the method described below will be created in
// the `Cowboy` namespace. Usage will be exactly the same, but instead of
// $.method() or jQuery.method(), you'll need to use Cowboy.method().
(function(window,undefined){
'$:nomunge'; // Used by YUI compressor.
// Since jQuery really isn't required for this plugin, use `jQuery` as the
// namespace only if it already exists, otherwise use the `Cowboy` namespace,
// creating it if necessary.
var $ = window.jQuery || window.Cowboy || ( window.Cowboy = {} ),
// Internal method reference.
jq_throttle;
// Method: jQuery.throttle
//
// Throttle execution of a function. Especially useful for rate limiting
// execution of handlers on events like resize and scroll. If you want to
// rate-limit execution of a function to a single time, see the
// <jQuery.debounce> method.
//
// In this visualization, | is a throttled-function call and X is the actual
// callback execution:
//
// > Throttled with `no_trailing` specified as false or unspecified:
// > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
// > X X X X X X X X X X X X
// >
// > Throttled with `no_trailing` specified as true:
// > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
// > X X X X X X X X X X
//
// Usage:
//
// > var throttled = jQuery.throttle( delay, [ no_trailing, ] callback );
// >
// > jQuery('selector').bind( 'someevent', throttled );
// > jQuery('selector').unbind( 'someevent', throttled );
//
// This also works in jQuery 1.4+:
//
// > jQuery('selector').bind( 'someevent', jQuery.throttle( delay, [ no_trailing, ] callback ) );
// > jQuery('selector').unbind( 'someevent', callback );
//
// Arguments:
//
// delay - (Number) A zero-or-greater delay in milliseconds. For event
// callbacks, values around 100 or 250 (or even higher) are most useful.
// no_trailing - (Boolean) Optional, defaults to false. If no_trailing is
// true, callback will only execute every `delay` milliseconds while the
// throttled-function is being called. If no_trailing is false or
// unspecified, callback will be executed one final time after the last
// throttled-function call. (After the throttled-function has not been
// called for `delay` milliseconds, the internal counter is reset)
// callback - (Function) A function to be executed after delay milliseconds.
// The `this` context and all arguments are passed through, as-is, to
// `callback` when the throttled-function is executed.
//
// Returns:
//
// (Function) A new, throttled, function.
$.throttle = jq_throttle = function( delay, no_trailing, callback, debounce_mode ) {
// After wrapper has stopped being called, this timeout ensures that
// `callback` is executed at the proper times in `throttle` and `end`
// debounce modes.
var timeout_id,
// Keep track of the last time `callback` was executed.
last_exec = 0;
// `no_trailing` defaults to falsy.
if ( typeof no_trailing !== 'boolean' ) {
debounce_mode = callback;
callback = no_trailing;
no_trailing = undefined;
}
// The `wrapper` function encapsulates all of the throttling / debouncing
// functionality and when executed will limit the rate at which `callback`
// is executed.
function wrapper() {
var that = this,
elapsed = +new Date() - last_exec,
args = arguments;
// Execute `callback` and update the `last_exec` timestamp.
function exec() {
last_exec = +new Date();
callback.apply( that, args );
};
// If `debounce_mode` is true (at_begin) this is used to clear the flag
// to allow future `callback` executions.
function clear() {
timeout_id = undefined;
};
if ( debounce_mode && !timeout_id ) {
// Since `wrapper` is being called for the first time and
// `debounce_mode` is true (at_begin), execute `callback`.
exec();
}
// Clear any existing timeout.
timeout_id && clearTimeout( timeout_id );
if ( debounce_mode === undefined && elapsed > delay ) {
// In throttle mode, if `delay` time has been exceeded, execute
// `callback`.
exec();
} else if ( no_trailing !== true ) {
// In trailing throttle mode, since `delay` time has not been
// exceeded, schedule `callback` to execute `delay` ms after most
// recent execution.
//
// If `debounce_mode` is true (at_begin), schedule `clear` to execute
// after `delay` ms.
//
// If `debounce_mode` is false (at end), schedule `callback` to
// execute after `delay` ms.
timeout_id = setTimeout( debounce_mode ? clear : exec, debounce_mode === undefined ? delay - elapsed : delay );
}
};
// Set the guid of `wrapper` function to the same of original callback, so
// it can be removed in jQuery 1.4+ .unbind or .die by using the original
// callback as a reference.
if ( $.guid ) {
wrapper.guid = callback.guid = callback.guid || $.guid++;
}
// Return the wrapper function.
return wrapper;
};
// Method: jQuery.debounce
//
// Debounce execution of a function. Debouncing, unlike throttling,
// guarantees that a function is only executed a single time, either at the
// very beginning of a series of calls, or at the very end. If you want to
// simply rate-limit execution of a function, see the <jQuery.throttle>
// method.
//
// In this visualization, | is a debounced-function call and X is the actual
// callback execution:
//
// > Debounced with `at_begin` specified as false or unspecified:
// > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
// > X X
// >
// > Debounced with `at_begin` specified as true:
// > ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
// > X X
//
// Usage:
//
// > var debounced = jQuery.debounce( delay, [ at_begin, ] callback );
// >
// > jQuery('selector').bind( 'someevent', debounced );
// > jQuery('selector').unbind( 'someevent', debounced );
//
// This also works in jQuery 1.4+:
//
// > jQuery('selector').bind( 'someevent', jQuery.debounce( delay, [ at_begin, ] callback ) );
// > jQuery('selector').unbind( 'someevent', callback );
//
// Arguments:
//
// delay - (Number) A zero-or-greater delay in milliseconds. For event
// callbacks, values around 100 or 250 (or even higher) are most useful.
// at_begin - (Boolean) Optional, defaults to false. If at_begin is false or
// unspecified, callback will only be executed `delay` milliseconds after
// the last debounced-function call. If at_begin is true, callback will be
// executed only at the first debounced-function call. (After the
// throttled-function has not been called for `delay` milliseconds, the
// internal counter is reset)
// callback - (Function) A function to be executed after delay milliseconds.
// The `this` context and all arguments are passed through, as-is, to
// `callback` when the debounced-function is executed.
//
// Returns:
//
// (Function) A new, debounced, function.
$.debounce = function( delay, at_begin, callback ) {
return callback === undefined
? jq_throttle( delay, at_begin, false )
: jq_throttle( delay, callback, at_begin !== false );
};
})(this);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,752 @@
// lz4.js - An implementation of Lz4 in plain JavaScript.
//
// TODO:
// - Unify header parsing/writing.
// - Support options (block size, checksums)
// - Support streams
// - Better error handling (handle bad offset, etc.)
// - HC support (better search algorithm)
// - Tests/benchmarking
/********************************************************************************/
util = {}
// Simple hash function, from: http://burtleburtle.net/bob/hash/integer.html.
// Chosen because it doesn't use multiply and achieves full avalanche.
util.hashU32 = function hashU32(a) {
a = a | 0;
a = a + 2127912214 + (a << 12) | 0;
a = a ^ -949894596 ^ a >>> 19;
a = a + 374761393 + (a << 5) | 0;
a = a + -744332180 ^ a << 9;
a = a + -42973499 + (a << 3) | 0;
return a ^ -1252372727 ^ a >>> 16 | 0;
}
// Reads a 64-bit little-endian integer from an array.
util.readU64 = function readU64(b, n) {
var x = 0;
x |= b[n++] << 0;
x |= b[n++] << 8;
x |= b[n++] << 16;
x |= b[n++] << 24;
x |= b[n++] << 32;
x |= b[n++] << 40;
x |= b[n++] << 48;
x |= b[n++] << 56;
return x;
}
// Reads a 32-bit little-endian integer from an array.
util.readU32 = function readU32(b, n) {
var x = 0;
x |= b[n++] << 0;
x |= b[n++] << 8;
x |= b[n++] << 16;
x |= b[n++] << 24;
return x;
}
// Writes a 32-bit little-endian integer from an array.
util.writeU32 = function writeU32(b, n, x) {
b[n++] = (x >> 0) & 0xff;
b[n++] = (x >> 8) & 0xff;
b[n++] = (x >> 16) & 0xff;
b[n++] = (x >> 24) & 0xff;
}
// Multiplies two numbers using 32-bit integer multiplication.
// Algorithm from Emscripten.
util.imul = function imul(a, b) {
var ah = a >>> 16;
var al = a & 65535;
var bh = b >>> 16;
var bl = b & 65535;
return al * bl + (ah * bl + al * bh << 16) | 0;
};
/********************************************************************************/
// xxh32.js - implementation of xxhash32 in plain JavaScript
// xxhash32 primes
var prime1 = 0x9e3779b1;
var prime2 = 0x85ebca77;
var prime3 = 0xc2b2ae3d;
var prime4 = 0x27d4eb2f;
var prime5 = 0x165667b1;
// Utility functions/primitives
// --
function rotl32 (x, r) {
x = x | 0;
r = r | 0;
return x >>> (32 - r | 0) | x << r | 0;
}
function rotmul32 (h, r, m) {
h = h | 0;
r = r | 0;
m = m | 0;
return util.imul(h >>> (32 - r | 0) | h << r, m) | 0;
}
function shiftxor32 (h, s) {
h = h | 0;
s = s | 0;
return h >>> s ^ h | 0;
}
// Implementation
// --
function xxhapply (h, src, m0, s, m1) {
return rotmul32(util.imul(src, m0) + h, s, m1);
}
function xxh1 (h, src, index) {
return rotmul32((h + util.imul(src[index], prime5)), 11, prime1);
}
function xxh4 (h, src, index) {
return xxhapply(h, util.readU32(src, index), prime3, 17, prime4);
}
function xxh16 (h, src, index) {
return [
xxhapply(h[0], util.readU32(src, index + 0), prime2, 13, prime1),
xxhapply(h[1], util.readU32(src, index + 4), prime2, 13, prime1),
xxhapply(h[2], util.readU32(src, index + 8), prime2, 13, prime1),
xxhapply(h[3], util.readU32(src, index + 12), prime2, 13, prime1)
];
}
function xxh32 (seed, src, index, len) {
var h, l;
l = len;
if (len >= 16) {
h = [
seed + prime1 + prime2,
seed + prime2,
seed,
seed - prime1
];
while (len >= 16) {
h = xxh16(h, src, index);
index += 16;
len -= 16;
}
h = rotl32(h[0], 1) + rotl32(h[1], 7) + rotl32(h[2], 12) + rotl32(h[3], 18) + l;
} else {
h = (seed + prime5 + len) >>> 0;
}
while (len >= 4) {
h = xxh4(h, src, index);
index += 4;
len -= 4;
}
while (len > 0) {
h = xxh1(h, src, index);
index++;
len--;
}
h = shiftxor32(util.imul(shiftxor32(util.imul(shiftxor32(h, 15), prime2), 13), prime3), 16);
return h >>> 0;
}
xxhash = {}
xxhash.hash = xxh32;
/********************************************************************************/
lz4 = {};
// Constants
// --
// Compression format parameters/constants.
var minMatch = 4;
var minLength = 13;
var searchLimit = 5;
var skipTrigger = 6;
var hashSize = 1 << 16;
// Token constants.
var mlBits = 4;
var mlMask = (1 << mlBits) - 1;
var runBits = 4;
var runMask = (1 << runBits) - 1;
// Shared buffers
var blockBuf = makeBuffer(5 << 20);
var hashTable = makeHashTable();
// Frame constants.
var magicNum = 0x184D2204;
// Frame descriptor flags.
var fdContentChksum = 0x4;
var fdContentSize = 0x8;
var fdBlockChksum = 0x10;
// var fdBlockIndep = 0x20;
var fdVersion = 0x40;
var fdVersionMask = 0xC0;
// Block sizes.
var bsUncompressed = 0x80000000;
var bsDefault = 7;
var bsShift = 4;
var bsMask = 7;
var bsMap = {
4: 0x10000,
5: 0x40000,
6: 0x100000,
7: 0x400000
};
// Utility functions/primitives
// --
// Makes our hashtable. On older browsers, may return a plain array.
function makeHashTable () {
try {
return new Uint32Array(hashSize);
} catch (error) {
var hashTable = new Array(hashSize);
for (var i = 0; i < hashSize; i++) {
hashTable[i] = 0;
}
return hashTable;
}
}
// Clear hashtable.
function clearHashTable (table) {
for (var i = 0; i < hashSize; i++) {
hashTable[i] = 0;
}
}
// Makes a byte buffer. On older browsers, may return a plain array.
function makeBuffer (size) {
try {
return new Uint8Array(size);
} catch (error) {
var buf = new Array(size);
for (var i = 0; i < size; i++) {
buf[i] = 0;
}
return buf;
}
}
function sliceArray (array, start, end) {
if (typeof array.buffer !== undefined) {
if (Uint8Array.prototype.slice) {
return array.slice(start, end);
} else {
// Uint8Array#slice polyfill.
var len = array.length;
// Calculate start.
start = start | 0;
start = (start < 0) ? Math.max(len + start, 0) : Math.min(start, len);
// Calculate end.
end = (end === undefined) ? len : end | 0;
end = (end < 0) ? Math.max(len + end, 0) : Math.min(end, len);
// Copy into new array.
var arraySlice = new Uint8Array(end - start);
for (var i = start, n = 0; i < end;) {
arraySlice[n++] = array[i++];
}
return arraySlice;
}
} else {
// Assume normal array.
return array.slice(start, end);
}
}
// Implementation
// --
// Calculates an upper bound for lz4 compression.
lz4.compressBound = function compressBound (n) {
return (n + (n / 255) + 16) | 0;
};
// Calculates an upper bound for lz4 decompression, by reading the data.
lz4.decompressBound = function decompressBound (src) {
var sIndex = 0;
// Read magic number
if (util.readU32(src, sIndex) !== magicNum) {
throw new Error('invalid magic number');
}
sIndex += 4;
// Read descriptor
var descriptor = src[sIndex++];
// Check version
if ((descriptor & fdVersionMask) !== fdVersion) {
throw new Error('incompatible descriptor version ' + (descriptor & fdVersionMask));
}
// Read flags
var useBlockSum = (descriptor & fdBlockChksum) !== 0;
var useContentSize = (descriptor & fdContentSize) !== 0;
// Read block size
var bsIdx = (src[sIndex++] >> bsShift) & bsMask;
if (bsMap[bsIdx] === undefined) {
throw new Error('invalid block size ' + bsIdx);
}
var maxBlockSize = bsMap[bsIdx];
// Get content size
if (useContentSize) {
return util.readU64(src, sIndex);
}
// Checksum
sIndex++;
// Read blocks.
var maxSize = 0;
while (true) {
var blockSize = util.readU32(src, sIndex);
sIndex += 4;
if (blockSize & bsUncompressed) {
blockSize &= ~bsUncompressed;
maxSize += blockSize;
} else if (blockSize > 0) {
maxSize += maxBlockSize;
}
if (blockSize === 0) {
return maxSize;
}
if (useBlockSum) {
sIndex += 4;
}
sIndex += blockSize;
}
};
// Creates a buffer of a given byte-size, falling back to plain arrays.
lz4.makeBuffer = makeBuffer;
// Decompresses a block of Lz4.
lz4.decompressBlock = function decompressBlock (src, dst, sIndex, sLength, dIndex) {
var mLength, mOffset, sEnd, n, i;
var hasCopyWithin = dst.copyWithin !== undefined && dst.fill !== undefined;
// Setup initial state.
sEnd = sIndex + sLength;
// Consume entire input block.
while (sIndex < sEnd) {
var token = src[sIndex++];
// Copy literals.
var literalCount = (token >> 4);
if (literalCount > 0) {
// Parse length.
if (literalCount === 0xf) {
while (true) {
literalCount += src[sIndex];
if (src[sIndex++] !== 0xff) {
break;
}
}
}
// Copy literals
for (n = sIndex + literalCount; sIndex < n;) {
dst[dIndex++] = src[sIndex++];
}
}
if (sIndex >= sEnd) {
break;
}
// Copy match.
mLength = (token & 0xf);
// Parse offset.
mOffset = src[sIndex++] | (src[sIndex++] << 8);
// Parse length.
if (mLength === 0xf) {
while (true) {
mLength += src[sIndex];
if (src[sIndex++] !== 0xff) {
break;
}
}
}
mLength += minMatch;
// Copy match
// prefer to use typedarray.copyWithin for larger matches
// NOTE: copyWithin doesn't work as required by LZ4 for overlapping sequences
// e.g. mOffset=1, mLength=30 (repeach char 30 times)
// we special case the repeat char w/ array.fill
if (hasCopyWithin && mOffset === 1) {
dst.fill(dst[dIndex - 1] | 0, dIndex, dIndex + mLength);
dIndex += mLength;
} else if (hasCopyWithin && mOffset > mLength && mLength > 31) {
dst.copyWithin(dIndex, dIndex - mOffset, dIndex - mOffset + mLength);
dIndex += mLength;
} else {
for (i = dIndex - mOffset, n = i + mLength; i < n;) {
dst[dIndex++] = dst[i++] | 0;
}
}
}
return dIndex;
};
// Compresses a block with Lz4.
lz4.compressBlock = function compressBlock (src, dst, sIndex, sLength, hashTable) {
var mIndex, mAnchor, mLength, mOffset, mStep;
var literalCount, dIndex, sEnd, n;
// Setup initial state.
dIndex = 0;
sEnd = sLength + sIndex;
mAnchor = sIndex;
// Process only if block is large enough.
if (sLength >= minLength) {
var searchMatchCount = (1 << skipTrigger) + 3;
// Consume until last n literals (Lz4 spec limitation.)
while (sIndex + minMatch < sEnd - searchLimit) {
var seq = util.readU32(src, sIndex);
var hash = util.hashU32(seq) >>> 0;
// Crush hash to 16 bits.
hash = ((hash >> 16) ^ hash) >>> 0 & 0xffff;
// Look for a match in the hashtable. NOTE: remove one; see below.
mIndex = hashTable[hash] - 1;
// Put pos in hash table. NOTE: add one so that zero = invalid.
hashTable[hash] = sIndex + 1;
// Determine if there is a match (within range.)
if (mIndex < 0 || ((sIndex - mIndex) >>> 16) > 0 || util.readU32(src, mIndex) !== seq) {
mStep = searchMatchCount++ >> skipTrigger;
sIndex += mStep;
continue;
}
searchMatchCount = (1 << skipTrigger) + 3;
// Calculate literal count and offset.
literalCount = sIndex - mAnchor;
mOffset = sIndex - mIndex;
// We've already matched one word, so get that out of the way.
sIndex += minMatch;
mIndex += minMatch;
// Determine match length.
// N.B.: mLength does not include minMatch, Lz4 adds it back
// in decoding.
mLength = sIndex;
while (sIndex < sEnd - searchLimit && src[sIndex] === src[mIndex]) {
sIndex++;
mIndex++;
}
mLength = sIndex - mLength;
// Write token + literal count.
var token = mLength < mlMask ? mLength : mlMask;
if (literalCount >= runMask) {
dst[dIndex++] = (runMask << mlBits) + token;
for (n = literalCount - runMask; n >= 0xff; n -= 0xff) {
dst[dIndex++] = 0xff;
}
dst[dIndex++] = n;
} else {
dst[dIndex++] = (literalCount << mlBits) + token;
}
// Write literals.
for (var i = 0; i < literalCount; i++) {
dst[dIndex++] = src[mAnchor + i];
}
// Write offset.
dst[dIndex++] = mOffset;
dst[dIndex++] = (mOffset >> 8);
// Write match length.
if (mLength >= mlMask) {
for (n = mLength - mlMask; n >= 0xff; n -= 0xff) {
dst[dIndex++] = 0xff;
}
dst[dIndex++] = n;
}
// Move the anchor.
mAnchor = sIndex;
}
}
// Nothing was encoded.
if (mAnchor === 0) {
return 0;
}
// Write remaining literals.
// Write literal token+count.
literalCount = sEnd - mAnchor;
if (literalCount >= runMask) {
dst[dIndex++] = (runMask << mlBits);
for (n = literalCount - runMask; n >= 0xff; n -= 0xff) {
dst[dIndex++] = 0xff;
}
dst[dIndex++] = n;
} else {
dst[dIndex++] = (literalCount << mlBits);
}
// Write literals.
sIndex = mAnchor;
while (sIndex < sEnd) {
dst[dIndex++] = src[sIndex++];
}
return dIndex;
};
// Decompresses a frame of Lz4 data.
lz4.decompressFrame = function decompressFrame (src, dst) {
var useBlockSum, useContentSum, useContentSize, descriptor;
var sIndex = 0;
var dIndex = 0;
// Read magic number
if (util.readU32(src, sIndex) !== magicNum) {
throw new Error('invalid magic number');
}
sIndex += 4;
// Read descriptor
descriptor = src[sIndex++];
// Check version
if ((descriptor & fdVersionMask) !== fdVersion) {
throw new Error('incompatible descriptor version');
}
// Read flags
useBlockSum = (descriptor & fdBlockChksum) !== 0;
useContentSum = (descriptor & fdContentChksum) !== 0;
useContentSize = (descriptor & fdContentSize) !== 0;
// Read block size
var bsIdx = (src[sIndex++] >> bsShift) & bsMask;
if (bsMap[bsIdx] === undefined) {
throw new Error('invalid block size');
}
if (useContentSize) {
// TODO: read content size
sIndex += 8;
}
sIndex++;
// Read blocks.
while (true) {
var compSize;
compSize = util.readU32(src, sIndex);
sIndex += 4;
if (compSize === 0) {
break;
}
if (useBlockSum) {
// TODO: read block checksum
sIndex += 4;
}
// Check if block is compressed
if ((compSize & bsUncompressed) !== 0) {
// Mask off the 'uncompressed' bit
compSize &= ~bsUncompressed;
// Copy uncompressed data into destination buffer.
for (var j = 0; j < compSize; j++) {
dst[dIndex++] = src[sIndex++];
}
} else {
// Decompress into blockBuf
dIndex = lz4.decompressBlock(src, dst, sIndex, compSize, dIndex);
sIndex += compSize;
}
}
if (useContentSum) {
// TODO: read content checksum
sIndex += 4;
}
return dIndex;
};
// Compresses data to an Lz4 frame.
lz4.compressFrame = function compressFrame (src, dst) {
var dIndex = 0;
// Write magic number.
util.writeU32(dst, dIndex, magicNum);
dIndex += 4;
// Descriptor flags.
dst[dIndex++] = fdVersion;
dst[dIndex++] = bsDefault << bsShift;
// Descriptor checksum.
dst[dIndex] = xxhash.hash(0, dst, 4, dIndex - 4) >> 8;
dIndex++;
// Write blocks.
var maxBlockSize = bsMap[bsDefault];
var remaining = src.length;
var sIndex = 0;
// Clear the hashtable.
clearHashTable(hashTable);
// Split input into blocks and write.
while (remaining > 0) {
var compSize = 0;
var blockSize = remaining > maxBlockSize ? maxBlockSize : remaining;
compSize = lz4.compressBlock(src, blockBuf, sIndex, blockSize, hashTable);
if (compSize > blockSize || compSize === 0) {
// Output uncompressed.
util.writeU32(dst, dIndex, 0x80000000 | blockSize);
dIndex += 4;
for (var z = sIndex + blockSize; sIndex < z;) {
dst[dIndex++] = src[sIndex++];
}
remaining -= blockSize;
} else {
// Output compressed.
util.writeU32(dst, dIndex, compSize);
dIndex += 4;
for (var j = 0; j < compSize;) {
dst[dIndex++] = blockBuf[j++];
}
sIndex += blockSize;
remaining -= blockSize;
}
}
// Write blank end block.
util.writeU32(dst, dIndex, 0);
dIndex += 4;
return dIndex;
};
// Decompresses a buffer containing an Lz4 frame. maxSize is optional; if not
// provided, a maximum size will be determined by examining the data. The
// buffer returned will always be perfectly-sized.
lz4.decompress = function decompress (src, maxSize) {
var dst, size;
if (maxSize === undefined) {
maxSize = lz4.decompressBound(src);
}
dst = lz4.makeBuffer(maxSize);
size = lz4.decompressFrame(src, dst);
if (size !== maxSize) {
dst = sliceArray(dst, 0, size);
}
return dst;
};
// Compresses a buffer to an Lz4 frame. maxSize is optional; if not provided,
// a buffer will be created based on the theoretical worst output size for a
// given input size. The buffer returned will always be perfectly-sized.
lz4.compress = function compress (src, maxSize) {
var dst, size;
if (maxSize === undefined) {
maxSize = lz4.compressBound(src.length);
}
dst = lz4.makeBuffer(maxSize);
size = lz4.compressFrame(src, dst);
if (size !== maxSize) {
dst = sliceArray(dst, 0, size);
}
return dst;
};
/********************************************************************************/
//convenience function added for xpra:
lz4.decode = function(data) {
const length = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
if (length<=0) {
throw "invalid length: "+length;
}
if (length>1024*1024*1024) {
throw "length too long: "+length;
}
const inflated = new Uint8Array(length);
lz4.decompressBlock(data, inflated, 4, length, 0);
return inflated;
}

View File

@ -0,0 +1,553 @@
/*
* Copyright (c) 2021 Antoine Martin <antoine@xpra.org>
*/
const RENCODE = {
DEFAULT_FLOAT_BITS : 32,
MAX_INT_LENGTH : 64,
CHR_LIST : 59,
CHR_DICT : 60,
CHR_INT : 61,
CHR_INT1 : 62,
CHR_INT2 : 63,
CHR_INT4 : 64,
CHR_INT8 : 65,
CHR_FLOAT32 : 66,
CHR_FLOAT64 : 44,
CHR_TRUE : 67,
CHR_FALSE : 68,
CHR_NONE : 69,
CHR_TERM : 127,
INT_POS_FIXED_START : 0,
INT_POS_FIXED_COUNT : 44,
DICT_FIXED_START : 102,
DICT_FIXED_COUNT : 25,
INT_NEG_FIXED_START : 70,
INT_NEG_FIXED_COUNT : 32,
STR_FIXED_START : 128,
STR_FIXED_COUNT : 64,
LIST_FIXED_START : 128+64, //STR_FIXED_START + STR_FIXED_COUNT,
LIST_FIXED_COUNT : 64,
COLON_CHARCODE : ":".charCodeAt(0), //for char strings
SLASH_CHARCODE : "/".charCodeAt(0), //for byte strings
};
Number.isSafeInteger = Number.isSafeInteger || function (value) {
return Number.isInteger(value) && Math.abs(value) <= Number.MAX_SAFE_INTEGER;
};
function utf8ByteArrayToString(bytes) {
let out = [], pos = 0, c = 0;
while (pos < bytes.length) {
const c1 = bytes[pos++];
if (c1 < 128) {
out[c++] = String.fromCharCode(c1);
} else if (c1 > 191 && c1 < 224) {
const c2 = bytes[pos++];
out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
} else if (c1 > 239 && c1 < 365) {
// Surrogate Pair
const c2 = bytes[pos++];
const c3 = bytes[pos++];
const c4 = bytes[pos++];
const u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) - 0x10000;
out[c++] = String.fromCharCode(0xD800 + (u >> 10));
out[c++] = String.fromCharCode(0xDC00 + (u & 1023));
} else {
const c2 = bytes[pos++];
const c3 = bytes[pos++];
out[c++] = String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
}
}
return out.join('');
}
function stringToUtf8ByteArray(str) {
let out = [], p = 0;
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
if (c < 128) {
out[p++] = c;
} else if (c < 2048) {
out[p++] = (c >> 6) | 192;
out[p++] = (c & 63) | 128;
} else if (
((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&
((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
// Surrogate Pair
c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
out[p++] = (c >> 18) | 240;
out[p++] = ((c >> 12) & 63) | 128;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
} else {
out[p++] = (c >> 12) | 224;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
}
}
return out;
}
function rencode_string(str) {
const bytes = stringToUtf8ByteArray(str);
const len = bytes.length;
if (len < RENCODE.STR_FIXED_COUNT) {
const u8a = new Uint8Array(len+1);
u8a[0] = RENCODE.STR_FIXED_START+len;
for (let i=0; i<len; ++i) {
u8a[i+1] = bytes[i];
}
return u8a;
}
const len_str = len.toString();
const len_len = len_str.length;
const u8a = new Uint8Array(len_len+1+len);
for (let i=0; i<len_len; ++i) {
u8a[i] = len_str.charCodeAt(i);
}
const SEPARATOR = ":";
u8a[len_len] = SEPARATOR.charCodeAt(0);
for (let i=0; i<len_str; ++i) {
u8a[len_len+1+i] = bytes[i];
}
return u8a;
}
function rencode_int(i) {
let u8a = null;
if (0 <= i && i < RENCODE.INT_POS_FIXED_COUNT) {
u8a = new Uint8Array([RENCODE.INT_POS_FIXED_START + i])
}
else if (-RENCODE.INT_NEG_FIXED_COUNT <= i && i < 0) {
u8a = new Uint8Array(new Int8Array([RENCODE.INT_NEG_FIXED_START - 1 -i]).buffer);
}
else if (-128 <= i && i < 128) {
u8a = new Uint8Array(new Int8Array([RENCODE.CHR_INT1, i]).buffer);
}
else if (-32768 <= i && i < 32768) {
u8a = new Uint8Array(new Int8Array([RENCODE.CHR_INT2, Math.floor(i/256) % 256, i%256]).buffer);
}
else if (-2147483648 <= i && i< 2147483648) {
const i8a = new Int8Array(5);
i8a[0] = RENCODE.CHR_INT4;
i8a[1] = Math.floor(i/256/256/256);
i8a[2] = Math.floor(i/256/256) % 256;
i8a[3] = Math.floor(i/256) % 256;
i8a[4] = i%256;
u8a = new Uint8Array(i8a.buffer);
}
else if (-9223372036854775808 <= i && i < 9223372036854775808) {
const i8a = new Int8Array(9);
i8a[0] = RENCODE.CHR_INT8;
for (let j=0; j<8; ++j) {
i8a[8-j] = i%256;
i = Math.floor(i/256);
}
u8a = new Uint8Array(i8a.buffer);
}
else {
const str = i.toString();
if (str.length >= RENCODE.MAX_INT_LENGTH) {
throw "number too big: "+i;
}
const str_len = str.length;
u8a = new Uint8Array(str_len+2);
u8a[0] = RENCODE.CHR_INT;
for (let j=0; j<str_len; ++j) {
u8a[1+j] = str[j];
}
u8a[str_len+1] = RENCODE.CHR_TERM;
}
return u8a;
}
function rencode_merge_arrays(rlist) {
let len = 0;
for (let i=0; i<rlist.length; ++i) {
len += rlist[i].length;
}
const u8a = new Uint8Array(len);
let index = 0;
for (let i=0; i<rlist.length; ++i) {
u8a.set(rlist[i], index);
index += rlist[i].length;
}
return u8a;
}
function rencode_uint8(a) {
const len = a.length;
const len_str = len.toString();
const len_len = len_str.length;
const u8a = new Uint8Array(len_len+1+len);
for (let i=0; i<len_len; ++i) {
u8a[i] = len_str.charCodeAt(i);
}
const SEPARATOR = "/";
u8a[len_len] = SEPARATOR.charCodeAt(0);
u8a.set(a, len_len+1);
return u8a;
}
function rencode_list(l) {
const list_len = l.length;
const rlist = [];
if (list_len < RENCODE.LIST_FIXED_COUNT) {
rlist.push(new Uint8Array([RENCODE.LIST_FIXED_START + list_len]));
for (let i=0; i<list_len; ++i) {
rlist.push(rencode(l[i]));
}
}
else {
rlist.push(new Uint8Array([RENCODE.CHR_LIST]));
for (let i=0; i<list_len; ++i) {
rlist.push(rencode(l[i]));
}
rlist.push(new Uint8Array([RENCODE.CHR_TERM]));
}
return rencode_merge_arrays(rlist);
}
function rencode_dict(dict) {
const dict_len = Object.keys(dict).length;
const rlist = [];
if (dict_len < RENCODE.DICT_FIXED_COUNT) {
rlist.push(new Uint8Array([RENCODE.DICT_FIXED_START + dict_len]));
for(const key in dict) {
const value = dict[key];
rlist.push(rencode(key));
rlist.push(rencode(value));
}
}
else {
rlist.push(new Uint8Array([RENCODE.CHR_DICT]));
for(const key in dict) {
const value = dict[key];
rlist.push(rencode(key));
rlist.push(rencode(value));
}
rlist.push(new Uint8Array([RENCODE.CHR_TERM]));
}
return rencode_merge_arrays(rlist);
}
function rencode_bool(v) {
if (v) {
return new Uint8Array([RENCODE.CHR_TRUE]);
}
else {
return new Uint8Array([RENCODE.CHR_FALSE]);
}
}
function rencode_none() {
return new Uint8Array([RENCODE.CHR_NONE]);
}
function rencode(obj) {
if (obj === null || obj === undefined) {
return rencode_none();
}
const type = typeof obj;
if(type === 'object') {
if(typeof obj.length === 'undefined') {
return rencode_dict(obj);
}
if(obj.constructor===Uint8Array) {
return rencode_uint8(obj);
}
return rencode_list(obj);
}
switch(type) {
case "string": return rencode_string(obj);
case "number": return rencode_int(obj);
case "list": return rencode_list(obj);
case "dictionary": return rencode_dict(obj);
case "boolean": return rencode_bool(obj?1:0);
default: throw "invalid object type in source: "+type;
}
}
function rdecode_string(dec) {
let len = 0;
while (dec.buf[dec.pos+len]!=RENCODE.COLON_CHARCODE && dec.buf[dec.pos+len]!=RENCODE.SLASH_CHARCODE) {
len++;
}
const str_len_str = String.fromCharCode.apply(null, dec.buf.subarray(dec.pos, dec.pos+len));
const str_len = parseInt(str_len_str);
if (isNaN(str_len)) {
throw "invalid string length: '"+str_len_str+"'";
}
const binary = dec.buf[dec.pos+len]==RENCODE.SLASH_CHARCODE;
dec.pos += len+1;
const bytes = dec.buf.subarray(dec.pos, dec.pos+str_len);
dec.pos += str_len;
if (binary) {
return new Uint8Array(bytes);
}
if (str_len==0) {
return "";
}
return utf8ByteArrayToString(bytes)
}
function Uint8ToString(u8a){
if (typeof u8a == "string") {
return u8a;
}
const CHUNK_SZ = 0x8000;
const c = [];
for (let i=0; i < u8a.length; i+=CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i+CHUNK_SZ)));
}
return c.join("");
}
function rdecode_list(dec) {
dec.pos++;
const list = [];
while (dec.buf[dec.pos]!=RENCODE.CHR_TERM) {
list.push(_rdecode(dec));
}
dec.pos++;
return list;
}
function rdecode_dict(dec) {
dec.pos++;
const dict = {};
while (dec.buf[dec.pos]!=RENCODE.CHR_TERM) {
const key = _rdecode(dec);
const value = _rdecode(dec);
dict[key] = value;
}
dec.pos++;
return dict;
}
function rdecode_int(dec) {
dec.pos++;
let len = 0;
while (dec.buf[dec.pos+len]!=RENCODE.CHR_TERM) {
len++;
}
const int_str = String.fromCharCode.apply(null, dec.buf.subarray(dec.pos, dec.pos+len));
dec.pos += len+1;
const i = parseInt(int_str);
if (isNaN(i)) {
throw "invalid int: '"+int_str+"'";
}
return i;
}
function rdecode_intb(dec) {
//this magically makes the value signed:
let b = dec.buf[dec.pos+1]<<24>>24;
dec.pos += 2;
return b;
}
function rdecode_inth(dec) {
const slice = dec.buf.slice(dec.pos+1, dec.pos+3)
const dv = new DataView(slice.buffer);
const s = dv.getInt16(0);
dec.pos += 3;
return s;
}
function rdecode_intl(dec) {
const slice = dec.buf.slice(dec.pos+1, dec.pos+5)
const dv = new DataView(slice.buffer);
const s = dv.getInt32(0);
dec.pos += 5;
return s;
}
function rdecode_intq(dec) {
const slice = dec.buf.slice(dec.pos+1, dec.pos+9)
const dv = new DataView(slice.buffer);
const s = dv.getBigInt64(0);
dec.pos += 9;
if (!Number.isSafeInteger(s)) {
//console.warn("value is not a safe integer: ", s);
}
return parseInt(s);
}
function rdecode_true(dec) {
dec.pos++;
return true;
}
function rdecode_false(dec) {
dec.pos++;
return false;
}
function rdecode_none(dec) {
dec.pos++;
return null;
}
const decode_func = new Map();
for(let i=0; i<10; i++) {
const charcode = i.toString().charCodeAt(0);
decode_func[charcode] = rdecode_string;
}
decode_func[RENCODE.CHR_LIST] = rdecode_list
decode_func[RENCODE.CHR_DICT] = rdecode_dict
decode_func[RENCODE.CHR_INT] = rdecode_int
decode_func[RENCODE.CHR_INT1] = rdecode_intb
decode_func[RENCODE.CHR_INT2] = rdecode_inth
decode_func[RENCODE.CHR_INT4] = rdecode_intl
decode_func[RENCODE.CHR_INT8] = rdecode_intq
decode_func[RENCODE.CHR_TRUE] = rdecode_true
decode_func[RENCODE.CHR_FALSE] = rdecode_false
decode_func[RENCODE.CHR_NONE] = rdecode_none
function make_fixed_length_string_decoder(len) {
function fixed_length_string_decoder(dec) {
dec.pos++;
const u8a = dec.buf.subarray(dec.pos, dec.pos+len);
dec.pos += len;
return utf8ByteArrayToString(u8a);
}
return fixed_length_string_decoder;
}
for(let i=0; i<RENCODE.STR_FIXED_COUNT; i++) {
decode_func[RENCODE.STR_FIXED_START + i] = make_fixed_length_string_decoder(i);
}
function make_fixed_length_list_decoder(len) {
function fixed_length_list_decoder(dec) {
dec.pos++;
let list = [];
for (let i=0; i<len; i++) {
list.push(_rdecode(dec));
}
return list
}
return fixed_length_list_decoder;
}
for(let i=0; i<RENCODE.LIST_FIXED_COUNT; i++) {
decode_func[RENCODE.LIST_FIXED_START + i] = make_fixed_length_list_decoder(i);
}
function make_fixed_length_dict_decoder(len) {
function fixed_length_dict_decoder(dec) {
dec.pos++;
const dict = {};
for(let i=0; i<len; i++) {
const key = _rdecode(dec);
const value = _rdecode(dec);
dict[key] = value;
}
return dict;
}
return fixed_length_dict_decoder;
}
for(let i=0; i<RENCODE.DICT_FIXED_COUNT; i++) {
decode_func[RENCODE.DICT_FIXED_START + i] = make_fixed_length_dict_decoder(i);
}
function make_int_fixed_decoder(i) {
function int_fixed_decoder(dec) {
dec.pos++;
return i;
}
return int_fixed_decoder;
}
for(let i=0; i<RENCODE.INT_POS_FIXED_COUNT; i++) {
decode_func[RENCODE.INT_POS_FIXED_START + i] = make_int_fixed_decoder(i)
}
for(let i=0; i<RENCODE.INT_NEG_FIXED_COUNT; i++) {
decode_func[RENCODE.INT_NEG_FIXED_START + i] = make_int_fixed_decoder(-1 - i)
}
class DecodeBuffer {
constructor(u8a) {
this.buf = u8a;
this.pos = 0;
}
}
function _rdecode(dec) {
if (dec.pos>=dec.buf.length) {
throw "reached end of buffer"
}
const typecode = dec.buf[dec.pos];
const decode = decode_func[typecode];
if (decode === null || decode === undefined) {
//console.log("buffer pos:", dec.pos);
//console.log("buffer:", dec.buf.subarray(dec.pos, dec.pos+20))
//const str = String.fromCharCode.apply(null, dec.buf.subarray(dec.pos, dec.pos+20));
throw "no decoder for typecode "+typecode+" at position "+dec.pos;
}
return decode(dec);
}
function rdecode(buf) {
const type = typeof buf;
if (type === "string") {
const u8a = new Uint8Array(buf.length);
for(let i=0,j=buf.length;i<j;++i){
u8a[i] = buf.charCodeAt(i);
}
return _rdecode(new DecodeBuffer(u8a));
}
if (type === 'object' && buf.constructor===Uint8Array) {
return _rdecode(new DecodeBuffer(buf));
}
throw "cannot decode "+type;
}
function rencode_selftest() {
function test_value(input, output) {
const u8a_output = new Uint8Array(output);
const enc = rencode(input);
//console.log("test_value(", input, ", ", output, ") rencode("+input+")="+enc);
if (enc.length != u8a_output.length) {
throw "failed to encode '"+input+"', expected length "+u8a_output.length+" bytes but got "+enc.length;
}
for(let i=0,j=enc.length;i<j;++i){
if (enc[i] != u8a_output[i]) {
throw "failed to encode '"+input+"', expected '"+u8a_output+"' but got '"+enc+"', error at position "+i+": "+enc[i]+" vs "+u8a_output[i];
}
}
const dec = rdecode(enc);
if (dec != input) {
throw "failed to decode '"+enc+"', expected '"+input+"' but got '"+dec+"'";
}
}
try {
test_value(true, [67]);
test_value(false, [68]);
test_value(-10, [79]);
test_value(-29, [98]);
test_value(1, [1]);
test_value(40, [40]);
test_value('foobarbaz', [137, 102, 111, 111, 98, 97, 114, 98, 97, 122]);
//we don't handle floats
//test_value(1234.56, [66, 68, 154, 81, 236]);
test_value(100, [62, 100]);
test_value(-100, [62, 156]);
test_value(7483648, [64, 0, 114, 49, 0]);
test_value(-7483648, [64, 255, 141, 207, 0]);
test_value(8223372036854775808, [65, 114, 31, 73, 76, 88, 156, 0, 0]);
test_value(-8223372036854775808, [65, 141, 224, 182, 179, 167, 100, 0, 0]);
test_value(27123, [63, 105, 243]);
test_value(-27123, [63, 150, 13]);
test_value('\x00', [129, 0]);
test_value("fööbar", [136, 102, 195, 182, 195, 182, 98, 97, 114]);
return true;
}
catch (e) {
// console.log("rencode failed its self test", e);
return false;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
#
# Copyright (C) 2026 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
The bundled web UI must contain every file its consoles load. Anything built
from a git checkout (GitHub archives, Docker images) ships only what is in git,
and the server answers a missing asset with the web UI page, so a file left out
of git breaks the console at runtime (a JavaScript module served as text/html).
"""
import os
import re
WEB_UI_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "gns3server", "static", "web-ui")
# relative ES module imports and re-exports: import x from "./a.js", export * from "../b.js"
MODULE_IMPORT = re.compile(r"""^\s*(?:import|export)\s+(?:[^'";]*?\s+from\s+)?["'](\.{1,2}/[^"']+)["']""", re.MULTILINE)
SCRIPT_SRC = re.compile(r"""<script[^>]*\ssrc=["']([^"']+)["']""", re.IGNORECASE)
def missing_module_imports(entry: str) -> list[str]:
"""
Walks the relative imports from an ES module and returns the imported files that do not exist.
"""
missing, seen, pending = [], set(), [os.path.normpath(entry)]
while pending:
path = pending.pop()
if path in seen:
continue
seen.add(path)
if not os.path.isfile(path):
missing.append(os.path.relpath(path, WEB_UI_DIR))
continue
with open(path, encoding="utf-8") as fd:
source = fd.read()
for target in MODULE_IMPORT.findall(source):
pending.append(os.path.normpath(os.path.join(os.path.dirname(path), target)))
return sorted(missing)
def test_vnc_console_modules_are_bundled() -> None:
entry = os.path.join(WEB_UI_DIR, "assets", "vnc-console", "vnc-controller.js")
assert os.path.isfile(entry)
assert missing_module_imports(entry) == []
def test_xpra_console_scripts_are_bundled() -> None:
xpra_dir = os.path.join(WEB_UI_DIR, "assets", "xpra-html5")
pages = [name for name in os.listdir(xpra_dir) if name.endswith(".html")]
assert pages
missing = []
for page in pages:
with open(os.path.join(xpra_dir, page), encoding="utf-8") as fd:
html = fd.read()
for src in SCRIPT_SRC.findall(html):
if re.match(r"^(?:[a-z]+:)?//", src):
continue
if not os.path.isfile(os.path.join(xpra_dir, src.split("?")[0])):
missing.append(f"{page}: {src}")
assert missing == []