1
0
Fork 0
prefix-compression-test/test.html

236 lines
7.7 KiB
HTML

<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Compression test</title>
<script src="LZMA-JS/src/lzma_worker.js"></script>
<script src="node-lz4/build/lz4.js"></script>
<script src="lz-string/libs/lz-string.js"></script>
<script >
var Buffer = require('buffer').Buffer
var LZ4 = require('lz4')
class PrefixCompressor {
constructor(prefix, compress, decompress, bytesToIgnore) {
this.prefix = prefix;
this.prefixCompressed = compress(prefix);
this.bytesToIgnore = bytesToIgnore;
this.baseCompress = compress;
this.baseDecompress = decompress;
}
compress(str) {
const c = this.baseCompress(this.prefix + str);
for (var i = this.bytesToIgnore;
i < this.prefixCompressed.length
&& this.prefixCompressed[i] == c[i];
i++) { }
const omittedBytes = this.prefixCompressed.length - i;
const res = c.subarray(i - 1);
res[0] = omittedBytes;
return res;
}
decompress(arr) {
const omittedBytes = arr[0];
const c = new Uint8Array(Buffer.concat([
this.prefixCompressed
.subarray(0, this.prefixCompressed.length-omittedBytes),
arr.subarray(1)
]));
const d = this.baseDecompress(c);
return d.substring(this.prefix.length);
}
}
async function testCompression () {
async function loadExampleFiles() {
const exampleFiles = new Map();
const filesToLoad = ['rfc4566-example'];
for (var i = 1; i <= 44; i++) {
filesToLoad.push('draft-ietf-rtcweb-sdp-12-ex' + i);
}
for (const filename of filesToLoad) {
const response = await fetch('test-files/' + filename);
const text = await response.text();
exampleFiles[filename] = text;
}
return exampleFiles;
}
async function loadDictionary() {
const response = await fetch('test-dictionary/base');
return await response.text();
}
const exampleFiles = await loadExampleFiles();
const prefix = await loadDictionary();
const decoder = new TextDecoder("utf-8");
// https://github.com/pierrec/node-lz4
const LZ4Pre = new PrefixCompressor(prefix, str => {
const input = new Buffer(str);
var output = new Buffer(LZ4.encodeBound(input.length));
const compressedSize = LZ4.encodeBlock(input, output);
output = output.slice(0, compressedSize);
return output;
}, arr => {
var uncompressed = new Buffer(10000)
var size = LZ4.decodeBlock(arr, uncompressed);
uncompressed = uncompressed.slice(0, size);
return decoder.decode(uncompressed);
}, 0);
// https://github.com/LZMA-JS/LZMA-JS
const LZMAPre = new PrefixCompressor(prefix, str => {
const res = new Uint8Array(LZMA.compress(str, 9));
// Clear uncompressed size as it will be invalid.
for (var i = 5; i < 13; i++) res[i] = 255;
return res;
}, arr => {
return LZMA.decompress(arr).toString();
}, 12);
// https://github.com/pieroxy/lz-string
const LZStringPre = new PrefixCompressor(prefix, str => {
return LZString.compressToUint8Array(str);
}, arr => {
return LZString.decompressFromUint8Array(arr);
}, 0);
const compressors = {lzma: LZMAPre, lz4: LZ4Pre, lzString: LZStringPre};
const resultTable = document.getElementById('results');
const headerRow = resultTable.querySelector('thead tr');
const prefixRow = document.getElementById('prefix-row');
const tbody = resultTable.querySelector('tbody');
const prefixSize = document.createElement('td');
prefixSize.innerText = prefix.length;
prefixRow.appendChild(prefixSize);
function appendHeader(str) {
const th = document.createElement('th');
th.innerText = str;
headerRow.appendChild(th);
}
function appendPrefixCell(str) {
const td = document.createElement('td');
if (str) td.innerText = str;
prefixRow.appendChild(td);
}
for (const name in compressors) {
const algo = compressors[name];
appendHeader(name);
appendHeader('?');
appendHeader(name + " w/ prefix");
appendHeader('?');
appendHeader(name + " incl. prefix");
appendHeader("matched");
appendHeader("skipped");
appendHeader("remaining");
const prefix_compressed = algo.baseCompress(prefix);
const prefix_compressed_len = prefix_compressed.length;
appendPrefixCell(prefix_compressed_len);
for (var i = 0; i < 7; i++) appendPrefixCell();
for (const filename in exampleFiles) {
const str = exampleFiles[filename];
const rowId = 'row-' + filename;
var row = document.getElementById(rowId);
if (!row) {
row = document.createElement('tr');
row.id = rowId;
const rowHeader = document.createElement('th');
rowHeader.innerText = filename;
row.appendChild(rowHeader);
const size = document.createElement('td');
size.innerText = str.length;
row.appendChild(size);
tbody.appendChild(row);
}
function appendCell(str) {
const td = document.createElement('td');
if (str === undefined) str = '';
else if (str === true) str = '✔️';
else if (str === false) str = '❌';
td.innerText = str;
row.appendChild(td);
}
result = algo.baseCompress(str);
appendCell(result.length);
appendCell(str == algo.baseDecompress(result))
var bestLength = result.length;
result = algo.compress(str);
const decompressionSuccessful = (str == algo.decompress(result));
appendCell(result.length);
appendCell(decompressionSuccessful)
if (!decompressionSuccessful) {
var bestDescription = "Prefix compression failed.";
var bestCells = ['', '', ''];
result = algo.baseCompress(prefix + str)
appendCell(result.length);
for (var j = 0; j < prefix_compressed_len; j++) {
for (var i = j; i < prefix_compressed_len && prefix_compressed[i] == result[i]; i++) { }
const truncatedLength = result.length - i + j;
if (truncatedLength < bestLength) {
bestLength = truncatedLength;
bestDescription = filename + " compressed with prefix matches " + (i-j) + " bytes (skipping " + j + " bytes) of compressed prefix. Remaining length is " + truncatedLength + " bytes"
bestCells = [i-j, j, truncatedLength]
}
j = i;
}
for (const cell of bestCells) appendCell(cell);
} else {
for (var i = 0; i < 4; i++) appendCell();
}
}
}
}
testCompression();
</script>
<style>
td {
text-align: right;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
table, tr, th, td {
border: 1px solid #DDD;
}
tr:nth-child(2n) {
background-color: #f6f8fa;
}
thead th {
position: sticky;
top: 0;
background: white;
}
</style>
</head>
<body>
<table id="results">
<thead>
<tr>
<th>Filename</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<tr id="prefix-row">
<th>PREFIX</th>
</tr>
</tbody>
</table>
</body>
</html>