Last active
July 5, 2017 15:45
-
-
Save JobLeonard/7af663013ca789a1141980fbcec43871 to your computer and use it in GitHub Desktop.
LZString 1.4.4 vs pull request #98 - base64 decompression (http://jsbench.github.io/#7af663013ca789a1141980fbcec43871) #jsbench #jsperf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"/> | |
<title>LZString 1.4.4 vs pull request #98 - base64 decompression</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/1.0.0/benchmark.min.js"></script> | |
<script src="./suite.js"></script> | |
</head> | |
<body> | |
<h1>Open the console to view the results</h1> | |
<h2><code>cmd + alt + j</code> or <code>ctrl + alt + j</code></h2> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
(function (factory) { | |
if (typeof Benchmark !== "undefined") { | |
factory(Benchmark); | |
} else { | |
factory(require("benchmark")); | |
} | |
})(function (Benchmark) { | |
var suite = new Benchmark.Suite; | |
Benchmark.prototype.setup = function () { | |
var LZString = (function() { | |
// private property | |
var f = String.fromCharCode; | |
var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | |
var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; | |
var baseReverseDic = {}; | |
function getBaseValue(alphabet, character) { | |
if (!baseReverseDic[alphabet]) { | |
baseReverseDic[alphabet] = {}; | |
for (var i=0 ; i<alphabet.length ; i++) { | |
baseReverseDic[alphabet][alphabet.charAt(i)] = i; | |
} | |
} | |
return baseReverseDic[alphabet][character]; | |
} | |
var LZString = { | |
compressToBase64 : function (input) { | |
if (input == null) return ""; | |
var res = LZString._compress(input, 6, function(a){return keyStrBase64.charAt(a);}); | |
switch (res.length % 4) { // To produce valid Base64 | |
default: // When could this happen ? | |
case 0 : return res; | |
case 1 : return res+"==="; | |
case 2 : return res+"=="; | |
case 3 : return res+"="; | |
} | |
}, | |
decompressFromBase64 : function (input) { | |
if (input == null) return ""; | |
if (input == "") return null; | |
return LZString._decompress(input.length, 32, function(index) { return getBaseValue(keyStrBase64, input.charAt(index)); }); | |
}, | |
compressToUTF16 : function (input) { | |
if (input == null) return ""; | |
return LZString._compress(input, 15, function(a){return f(a+32);}) + " "; | |
}, | |
decompressFromUTF16: function (compressed) { | |
if (compressed == null) return ""; | |
if (compressed == "") return null; | |
return LZString._decompress(compressed.length, 16384, function(index) { return compressed.charCodeAt(index) - 32; }); | |
}, | |
//compress into uint8array (UCS-2 big endian format) | |
compressToUint8Array: function (uncompressed) { | |
var compressed = LZString.compress(uncompressed); | |
var buf=new Uint8Array(compressed.length*2); // 2 bytes per character | |
for (var i=0, TotalLen=compressed.length; i<TotalLen; i++) { | |
var current_value = compressed.charCodeAt(i); | |
buf[i*2] = current_value >>> 8; | |
buf[i*2+1] = current_value % 256; | |
} | |
return buf; | |
}, | |
//decompress from uint8array (UCS-2 big endian format) | |
decompressFromUint8Array:function (compressed) { | |
if (compressed===null || compressed===undefined){ | |
return LZString.decompress(compressed); | |
} else { | |
var buf=new Array(compressed.length/2); // 2 bytes per character | |
for (var i=0, TotalLen=buf.length; i<TotalLen; i++) { | |
buf[i]=compressed[i*2]*256+compressed[i*2+1]; | |
} | |
var result = []; | |
buf.forEach(function (c) { | |
result.push(f(c)); | |
}); | |
return LZString.decompress(result.join('')); | |
} | |
}, | |
//compress into a string that is already URI encoded | |
compressToEncodedURIComponent: function (input) { | |
if (input == null) return ""; | |
return LZString._compress(input, 6, function(a){return keyStrUriSafe.charAt(a);}); | |
}, | |
//decompress from an output of compressToEncodedURIComponent | |
decompressFromEncodedURIComponent:function (input) { | |
if (input == null) return ""; | |
if (input == "") return null; | |
input = input.replace(/ /g, "+"); | |
return LZString._decompress(input.length, 32, function(index) { return getBaseValue(keyStrUriSafe, input.charAt(index)); }); | |
}, | |
compress: function (uncompressed) { | |
return LZString._compress(uncompressed, 16, function(a){return f(a);}); | |
}, | |
_compress: function (uncompressed, bitsPerChar, getCharFromInt) { | |
if (uncompressed == null) return ""; | |
var i, value, | |
context_dictionary= {}, | |
context_dictionaryToCreate= {}, | |
context_c="", | |
context_wc="", | |
context_w="", | |
context_enlargeIn= 2, // Compensate for the first entry which should not count | |
context_dictSize= 3, | |
context_numBits= 2, | |
context_data=[], | |
context_data_val=0, | |
context_data_position=0, | |
ii; | |
for (ii = 0; ii < uncompressed.length; ii += 1) { | |
context_c = uncompressed.charAt(ii); | |
if (!Object.prototype.hasOwnProperty.call(context_dictionary,context_c)) { | |
context_dictionary[context_c] = context_dictSize++; | |
context_dictionaryToCreate[context_c] = true; | |
} | |
context_wc = context_w + context_c; | |
if (Object.prototype.hasOwnProperty.call(context_dictionary,context_wc)) { | |
context_w = context_wc; | |
} else { | |
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { | |
if (context_w.charCodeAt(0)<256) { | |
for (i=0 ; i<context_numBits ; i++) { | |
context_data_val = (context_data_val << 1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
} | |
value = context_w.charCodeAt(0); | |
for (i=0 ; i<8 ; i++) { | |
context_data_val = (context_data_val << 1) | (value&1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = value >> 1; | |
} | |
} else { | |
value = 1; | |
for (i=0 ; i<context_numBits ; i++) { | |
context_data_val = (context_data_val << 1) | value; | |
if (context_data_position ==bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = 0; | |
} | |
value = context_w.charCodeAt(0); | |
for (i=0 ; i<16 ; i++) { | |
context_data_val = (context_data_val << 1) | (value&1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = value >> 1; | |
} | |
} | |
context_enlargeIn--; | |
if (context_enlargeIn == 0) { | |
context_enlargeIn = Math.pow(2, context_numBits); | |
context_numBits++; | |
} | |
delete context_dictionaryToCreate[context_w]; | |
} else { | |
value = context_dictionary[context_w]; | |
for (i=0 ; i<context_numBits ; i++) { | |
context_data_val = (context_data_val << 1) | (value&1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = value >> 1; | |
} | |
} | |
context_enlargeIn--; | |
if (context_enlargeIn == 0) { | |
context_enlargeIn = Math.pow(2, context_numBits); | |
context_numBits++; | |
} | |
// Add wc to the dictionary. | |
context_dictionary[context_wc] = context_dictSize++; | |
context_w = String(context_c); | |
} | |
} | |
// Output the code for w. | |
if (context_w !== "") { | |
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { | |
if (context_w.charCodeAt(0)<256) { | |
for (i=0 ; i<context_numBits ; i++) { | |
context_data_val = (context_data_val << 1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
} | |
value = context_w.charCodeAt(0); | |
for (i=0 ; i<8 ; i++) { | |
context_data_val = (context_data_val << 1) | (value&1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = value >> 1; | |
} | |
} else { | |
value = 1; | |
for (i=0 ; i<context_numBits ; i++) { | |
context_data_val = (context_data_val << 1) | value; | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = 0; | |
} | |
value = context_w.charCodeAt(0); | |
for (i=0 ; i<16 ; i++) { | |
context_data_val = (context_data_val << 1) | (value&1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = value >> 1; | |
} | |
} | |
context_enlargeIn--; | |
if (context_enlargeIn == 0) { | |
context_enlargeIn = Math.pow(2, context_numBits); | |
context_numBits++; | |
} | |
delete context_dictionaryToCreate[context_w]; | |
} else { | |
value = context_dictionary[context_w]; | |
for (i=0 ; i<context_numBits ; i++) { | |
context_data_val = (context_data_val << 1) | (value&1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = value >> 1; | |
} | |
} | |
context_enlargeIn--; | |
if (context_enlargeIn == 0) { | |
context_enlargeIn = Math.pow(2, context_numBits); | |
context_numBits++; | |
} | |
} | |
// Mark the end of the stream | |
value = 2; | |
for (i=0 ; i<context_numBits ; i++) { | |
context_data_val = (context_data_val << 1) | (value&1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data_position = 0; | |
context_data.push(getCharFromInt(context_data_val)); | |
context_data_val = 0; | |
} else { | |
context_data_position++; | |
} | |
value = value >> 1; | |
} | |
// Flush the last char | |
while (true) { | |
context_data_val = (context_data_val << 1); | |
if (context_data_position == bitsPerChar-1) { | |
context_data.push(getCharFromInt(context_data_val)); | |
break; | |
} | |
else context_data_position++; | |
} | |
return context_data.join(''); | |
}, | |
decompress: function (compressed) { | |
if (compressed == null) return ""; | |
if (compressed == "") return null; | |
return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); }); | |
}, | |
_decompress: function (length, resetValue, getNextValue) { | |
var dictionary = [], | |
next, | |
enlargeIn = 4, | |
dictSize = 4, | |
numBits = 3, | |
entry = "", | |
result = [], | |
i, | |
w, | |
bits, resb, maxpower, power, | |
c, | |
data = {val:getNextValue(0), position:resetValue, index:1}; | |
for (i = 0; i < 3; i += 1) { | |
dictionary[i] = i; | |
} | |
bits = 0; | |
maxpower = Math.pow(2,2); | |
power=1; | |
while (power!=maxpower) { | |
resb = data.val & data.position; | |
data.position >>= 1; | |
if (data.position == 0) { | |
data.position = resetValue; | |
data.val = getNextValue(data.index++); | |
} | |
bits |= (resb>0 ? 1 : 0) * power; | |
power <<= 1; | |
} | |
switch (next = bits) { | |
case 0: | |
bits = 0; | |
maxpower = Math.pow(2,8); | |
power=1; | |
while (power!=maxpower) { | |
resb = data.val & data.position; | |
data.position >>= 1; | |
if (data.position == 0) { | |
data.position = resetValue; | |
data.val = getNextValue(data.index++); | |
} | |
bits |= (resb>0 ? 1 : 0) * power; | |
power <<= 1; | |
} | |
c = f(bits); | |
break; | |
case 1: | |
bits = 0; | |
maxpower = Math.pow(2,16); | |
power=1; | |
while (power!=maxpower) { | |
resb = data.val & data.position; | |
data.position >>= 1; | |
if (data.position == 0) { | |
data.position = resetValue; | |
data.val = getNextValue(data.index++); | |
} | |
bits |= (resb>0 ? 1 : 0) * power; | |
power <<= 1; | |
} | |
c = f(bits); | |
break; | |
case 2: | |
return ""; | |
} | |
dictionary[3] = c; | |
w = c; | |
result.push(c); | |
while (true) { | |
if (data.index > length) { | |
return ""; | |
} | |
bits = 0; | |
maxpower = Math.pow(2,numBits); | |
power=1; | |
while (power!=maxpower) { | |
resb = data.val & data.position; | |
data.position >>= 1; | |
if (data.position == 0) { | |
data.position = resetValue; | |
data.val = getNextValue(data.index++); | |
} | |
bits |= (resb>0 ? 1 : 0) * power; | |
power <<= 1; | |
} | |
switch (c = bits) { | |
case 0: | |
bits = 0; | |
maxpower = Math.pow(2,8); | |
power=1; | |
while (power!=maxpower) { | |
resb = data.val & data.position; | |
data.position >>= 1; | |
if (data.position == 0) { | |
data.position = resetValue; | |
data.val = getNextValue(data.index++); | |
} | |
bits |= (resb>0 ? 1 : 0) * power; | |
power <<= 1; | |
} | |
dictionary[dictSize++] = f(bits); | |
c = dictSize-1; | |
enlargeIn--; | |
break; | |
case 1: | |
bits = 0; | |
maxpower = Math.pow(2,16); | |
power=1; | |
while (power!=maxpower) { | |
resb = data.val & data.position; | |
data.position >>= 1; | |
if (data.position == 0) { | |
data.position = resetValue; | |
data.val = getNextValue(data.index++); | |
} | |
bits |= (resb>0 ? 1 : 0) * power; | |
power <<= 1; | |
} | |
dictionary[dictSize++] = f(bits); | |
c = dictSize-1; | |
enlargeIn--; | |
break; | |
case 2: | |
return result.join(''); | |
} | |
if (enlargeIn == 0) { | |
enlargeIn = Math.pow(2, numBits); | |
numBits++; | |
} | |
if (dictionary[c]) { | |
entry = dictionary[c]; | |
} else { | |
if (c === dictSize) { | |
entry = w + w.charAt(0); | |
} else { | |
return null; | |
} | |
} | |
result.push(entry); | |
// Add w+entry[0] to the dictionary. | |
dictionary[dictSize++] = w + entry.charAt(0); | |
enlargeIn--; | |
w = entry; | |
if (enlargeIn == 0) { | |
enlargeIn = Math.pow(2, numBits); | |
numBits++; | |
} | |
} | |
} | |
}; | |
return LZString; | |
})(); | |
var LZStringNew = ( | |
function () { | |
// private property | |
var f = String.fromCharCode, | |
Base64CharArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(''), | |
UriSafeCharArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$".split(''), | |
Base64ReverseDic = {}, | |
UriSafeReverseDic = {}, | |
i = 65; | |
while (i--) { | |
Base64ReverseDic[Base64CharArray[i].charCodeAt(0)] = i; | |
UriSafeReverseDic[UriSafeCharArray[i].charCodeAt(0)] = i; | |
} | |
var getChar16Bits = function (a) { return f(a); }, | |
getCharFromBase64 = function (a) { return Base64CharArray[a]; }, | |
getCharFromURISafe = function (a) { return UriSafeCharArray[a]; }, | |
getCharFromUTF16 = function (a) { return f(a + 32); }; | |
var LZString = { | |
compressToBase64: function (input) { | |
if (input == null) return ""; | |
var res = LZString._compressToArray(input, 6, getCharFromBase64); | |
// To produce valid Base64 | |
var i = res.length % 4; | |
while (i--) { | |
res.push("="); | |
} | |
return res.join(''); | |
}, | |
decompressFromBase64: function (input) { | |
if (input == null) return ""; | |
if (input == "") return null; | |
return LZString._decompress(input.length, 32, function (index) { return Base64ReverseDic[input.charCodeAt(index)]; }); | |
}, | |
compressToUTF16: function (input) { | |
if (input == null) return ""; | |
var compressed = LZString._compressToArray(input, 15, getCharFromUTF16); | |
compressed.push(" "); | |
return compressed.join(''); | |
}, | |
decompressFromUTF16: function (compressed) { | |
if (compressed == null) return ""; | |
if (compressed == "") return null; | |
return LZString._decompress(compressed.length, 16384, function (index) { return compressed.charCodeAt(index) - 32; }); | |
}, | |
//compress into uint8array (UCS-2 big endian format) | |
compressToUint8Array: function (uncompressed) { | |
var compressed = LZString.compressToArray(uncompressed); | |
var buf = new Uint8Array(compressed.length * 2); // 2 bytes per character | |
for (var i = 0, TotalLen = compressed.length; i < TotalLen; i++) { | |
var current_value = compressed[i].charCodeAt(0); | |
buf[i * 2] = current_value >>> 8; | |
buf[i * 2 + 1] = current_value & 0xFF; | |
} | |
return buf; | |
}, | |
//decompress from uint8array (UCS-2 big endian format) | |
decompressFromUint8Array: function (compressed) { | |
if (compressed === null || compressed === undefined) { | |
return LZString.decompressFromArray(compressed); | |
} else if (compressed.length == 0) { | |
return null; | |
} | |
return LZString._decompress(compressed.length, 128, function (index) { return compressed[index]; }); | |
}, | |
//compress into a string that is already URI encoded | |
compressToEncodedURIComponent: function (input) { | |
if (input == null) return ""; | |
return LZString._compressToArray(input, 6, getCharFromURISafe).join(''); | |
}, | |
//decompress from an output of compressToEncodedURIComponent | |
decompressFromEncodedURIComponent: function (input) { | |
if (input == null) return ""; | |
if (input == "") return null; | |
input = input.replace(/ /g, "+"); | |
return LZString._decompress(input.length, 32, function (index) { return UriSafeReverseDic[input.charCodeAt(index)]; }); | |
}, | |
compress: function (uncompressed) { | |
return LZString.compressToArray(uncompressed).join(''); | |
}, | |
compressToArray: function (uncompressed) { | |
return LZString._compressToArray(uncompressed, 16, getChar16Bits); | |
}, | |
_compressToArray: function (uncompressed, bitsPerChar, getCharFromInt) { | |
if (uncompressed == null) return []; | |
var i = 0, j = 0, value = 0, | |
dictionary = {}, | |
freshNode = true, | |
c = 0, | |
c0 = 1, | |
node = dictionary, | |
new_node = {}, | |
enlargeIn = 2, | |
dictSize = 3, | |
numBits = 2, | |
data = [], | |
data_val = 0, | |
data_position = 0; | |
if (uncompressed.length) { | |
// If there IS a charCode, the first is guaranteed to be new, | |
// so we write it to output stream, add it to the dictionary, | |
// initialize freshNode as true, and set it as the root node. | |
c = uncompressed.charCodeAt(0); | |
c0 = c + 1; | |
// == Write first charCode token to output == | |
// 8 or 16 bit? | |
value = c < 256 ? 0 : 1 | |
// insert "new 8/16 bit charCode" token | |
// into bitstream (value 1) | |
for (i = 0; i < numBits; i++) { | |
// Value is 0 (8 bit) or 1 (16 bit). | |
// We shift it into the bitstream in reverse | |
// (shifting has precedence over bitmasking) | |
data_val = value >> i | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
// insert charCode | |
// Nasty but effective hack: | |
// loop 8 or 16 times based on token value | |
value = 8 + 8 * value; | |
for (i = 0; i < value; i++) { | |
// shifting has precedence over bitmasking | |
data_val = c >> i & 1 | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
// Add charCode to the dictionary. | |
new_node = {}; | |
new_node[0] = dictSize++; | |
node[c0] = new_node; | |
// start in this node | |
node = new_node; | |
// increase token bitlength if necessary | |
if (--enlargeIn == 0) { | |
enlargeIn = 1 << numBits++; | |
} | |
for (j = 1; j < uncompressed.length; j++) { | |
c = uncompressed.charCodeAt(j); | |
c0 = c + 1; | |
// does the new charCode match an existing prefix? | |
new_node = node[c0]; | |
if (new_node) { | |
// continue with next prefix | |
node = new_node; | |
} else { | |
// write out the current prefix token | |
if (freshNode) { | |
// character token already written to output | |
freshNode = false; | |
} else { | |
value = node[0]; | |
for (i = 0; i < numBits; i++) { | |
// shifting has precedence over bitmasking | |
data_val = value >> i & 1 | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
} | |
// Is the new charCode a new character | |
// that needs to be stored at the root? | |
new_node = dictionary[c0]; | |
if (new_node == undefined) { | |
// increase token bitlength if necessary | |
if (--enlargeIn == 0) { | |
enlargeIn = 1 << numBits++; | |
} | |
// insert "new 8/16 bit charCode" token, | |
// see comments above for explanation | |
value = c < 256 ? 0 : 1 | |
for (i = 0; i < numBits; i++) { | |
data_val = value >> i | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
value = 8 + 8 * value; | |
for (i = 0; i < value; i++) { | |
data_val = c >> i & 1 | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
new_node = {}; | |
new_node[0] = dictSize++; | |
dictionary[c0] = new_node; | |
// Note of that we already wrote | |
// the charCode token to the bitstream | |
freshNode = true; | |
} | |
// add node representing prefix + new charCode to trie | |
new_node = {}; | |
new_node[0] = dictSize++; | |
node[c0] = new_node; | |
// increase token bitlength if necessary | |
if (--enlargeIn == 0) { | |
enlargeIn = 1 << numBits++; | |
} | |
// set node to first charCode of new prefix | |
node = dictionary[c0]; | |
} | |
} | |
if (node) { // Write last node to output | |
if (freshNode) { | |
// character token already written to output | |
freshNode = false; | |
} else { | |
// write out the prefix token | |
value = node[0]; | |
for (i = 0; i < numBits; i++) { | |
// shifting has precedence over bitmasking | |
data_val = value >> i & 1 | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
} | |
// Is c a new character? | |
new_node = dictionary[c0]; | |
if (new_node == undefined) { | |
// increase token bitlength if necessary | |
if (--enlargeIn == 0) { | |
enlargeIn = 1 << numBits++; | |
} | |
// insert "new 8/16 bit charCode" token, | |
// see comments above for explanation | |
value = c < 256 ? 0 : 1 | |
for (i = 0; i < numBits; i++) { | |
data_val = value >> i | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
value = 8 + 8 * value; | |
for (i = 0; i < value; i++) { | |
data_val = c >> i & 1 | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
} | |
// increase token bitlength if necessary | |
if (--enlargeIn == 0) { | |
enlargeIn = 1 << numBits++; | |
} | |
} | |
} | |
// Mark the end of the stream | |
for (i = 0; i < numBits; i++) { | |
// shifting has precedence over bitmasking | |
data_val = 2 >> i & 1 | data_val << 1; | |
if (++data_position == bitsPerChar) { | |
data_position = 0; | |
data.push(getCharFromInt(data_val)); | |
data_val = 0; | |
} | |
} | |
// Flush the last char | |
data_val <<= bitsPerChar - data_position; | |
data.push(getCharFromInt(data_val)); | |
return data; | |
}, | |
decompress: function (compressed) { | |
if (compressed == null) return ""; | |
if (compressed == "") return null; | |
return LZString._decompress(compressed.length, 32768, function (index) { return compressed.charCodeAt(index); }); | |
}, | |
decompressFromArray: function (compressed) { | |
if (compressed == null) return ""; | |
if (compressed.length == 0) return null; | |
return LZString._decompress(compressed.length, 32768, function (index) { return compressed[index].charCodeAt(0); }); | |
}, | |
_decompress: function (length, resetValue, getNextValue) { | |
// "Math.log2(resetValue)" is ES6, so we use | |
// this while loop instead for backwards compatibility | |
var _resetValue = 0; | |
while(resetValue >> ++_resetValue){} | |
var dictionary = [0, 1, 2], | |
enlargeIn = 4, | |
dictSize = 4, | |
numBits = 3, | |
entry = "", | |
result = [], | |
w = "", | |
bits = 0, | |
maxpower = 2, | |
power = 0, | |
c = "", | |
data_val = getNextValue(0), | |
data_position = _resetValue, | |
data_index = 1; | |
// Get first token, guaranteed to be either | |
// a new character token (8 or 16 bits) | |
// or end of stream token. | |
while (power != maxpower) { | |
// shifting has precedence over bitmasking | |
bits += (data_val >> --data_position & 1) << power++; | |
if (data_position == 0) { | |
data_position = _resetValue; | |
data_val = getNextValue(data_index++); | |
} | |
} | |
// if end of stream token, return empty string | |
if (bits == 2) { | |
return ""; | |
} | |
// else, get character | |
maxpower = bits * 8 + 8; | |
bits = power = 0; | |
while (power != maxpower) { | |
// shifting has precedence over bitmasking | |
bits += (data_val >> --data_position & 1) << power++; | |
if (data_position == 0) { | |
data_position = _resetValue; | |
data_val = getNextValue(data_index++); | |
} | |
} | |
c = f(bits); | |
dictionary[3] = c; | |
w = c; | |
result.push(c); | |
// read rest of string | |
while (data_index <= length) { | |
// read out next token | |
maxpower = numBits; | |
bits = power = 0; | |
while (power != maxpower) { | |
// shifting has precedence over bitmasking | |
bits += (data_val >> --data_position & 1) << power++; | |
if (data_position == 0) { | |
data_position = _resetValue; | |
data_val = getNextValue(data_index++); | |
} | |
} | |
// 0 or 1 implies new character token | |
if (bits < 2) { | |
maxpower = (8 + 8 * bits); | |
bits = power = 0; | |
while (power != maxpower) { | |
// shifting has precedence over bitmasking | |
bits += (data_val >> --data_position & 1) << power++; | |
if (data_position == 0) { | |
data_position = _resetValue; | |
data_val = getNextValue(data_index++); | |
} | |
} | |
dictionary[dictSize] = f(bits); | |
bits = dictSize++; | |
if (--enlargeIn == 0) { | |
enlargeIn = 1 << numBits++; | |
} | |
} else if (bits == 2) { | |
// end of stream token | |
return result.join(''); | |
} | |
if (bits > dictionary.length) { | |
return null; | |
} | |
entry = bits < dictionary.length ? dictionary[bits] : w + w.charAt(0); | |
result.push(entry); | |
// Add w+entry[0] to the dictionary. | |
dictionary[dictSize++] = w + entry.charAt(0); | |
w = entry; | |
if (--enlargeIn == 0) { | |
enlargeIn = 1 << numBits++; | |
} | |
} | |
return ""; | |
} | |
}; | |
return LZString; | |
} | |
)(); | |
var testString = 'During tattooing, ink is injected into the skin, initiating an immune response, and cells called "macrophages" move into the area and "eat up" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. "When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect," he said. "We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory. During tattooing, ink is injected into the skin, initiating an immune response, and cells called "macrophages" move into the area and "eat up" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. "When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect," he said. "We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory. During tattooing, ink is injected into the skin, initiating an immune response, and cells called "macrophages" move into the area and "eat up" the ink. The macrophages carry some of the ink to the body\'s lymph nodes, but some that are filled with ink stay put, embedded in the skin. That\'s what makes the tattoo visible under the skin. Dalhousie Uiversity\'s Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. "When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we\'ve designed a drug that doesn\'t really have much off-target effect," he said. "We\'re not targeting any of the normal skin cells, so you won\'t see a lot of inflammation. In fact, based on the process that we\'re actually using, we don\'t think there will be any inflammation at all and it would actually be anti-inflammatory.'; | |
var testStringRandomNrs = ''; | |
var i; | |
for (i=0 ; i<1000 ; i++) | |
testStringRandomNrs += Math.random() + " "; | |
var testStringRepeat1024 = 'aaaaabaaaaacaaaaadaaaaaeaaaaa'; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
testStringRepeat1024 += testStringRepeat1024; | |
var testStringAllUTF16 = ''; | |
var i; | |
for (i = 32; i < 127; ++i) { | |
testStringAllUTF16 += String.fromCharCode(i); | |
} | |
for (i = 128 + 32; i < 55296; ++i) { | |
testStringAllUTF16 += String.fromCharCode(i); | |
} | |
for (i = 63744; i < 65536; ++i) { | |
testStringAllUTF16 += String.fromCharCode(i); | |
} | |
var compressedString = LZString.compressToBase64(testString); | |
var compressedStringRandomNrs = LZString.compressToBase64(testStringRandomNrs); | |
var compressedStringRepeat1024 = LZString.compressToBase64(testStringRepeat1024); | |
var compressedStringAllUTF16 = LZString.compressToBase64(testStringAllUTF16); | |
}; | |
suite.add("OLD Tattoing", function () { | |
// OLD Tattoing | |
var t = LZString.decompressFromBase64(compressedString); | |
}); | |
suite.add("NEW Tattoing", function () { | |
// NEW Tattoing | |
var t = LZStringNew.decompressFromBase64(compressedString); | |
}); | |
suite.add("OLD RandomNrs", function () { | |
// OLD RandomNrs | |
var t = LZString.decompressFromBase64(compressedStringRandomNrs); | |
}); | |
suite.add("NEW RandomNrs", function () { | |
// NEW RandomNrs | |
var t = LZStringNew.decompressFromBase64(compressedStringRandomNrs); | |
}); | |
suite.add("OLD Repeat1024", function () { | |
// OLD Repeat1024 | |
var t = LZString.decompressFromBase64(compressedStringRepeat1024); | |
}); | |
suite.add("NEW Repeat1024", function () { | |
// NEW Repeat1024 | |
var t = LZStringNew.decompressFromBase64(compressedStringRepeat1024); | |
}); | |
suite.add("OLD All Printable UTF16", function () { | |
// OLD All Printable UTF16 | |
var t = LZString.decompressFromBase64(compressedStringAllUTF16); | |
}); | |
suite.add("NEW All Printable UTF16", function () { | |
// NEW All Printable UTF16 | |
var t = LZStringNew.decompressFromBase64(compressedStringAllUTF16); | |
}); | |
suite.on("cycle", function (evt) { | |
console.log(" - " + evt.target); | |
}); | |
suite.on("complete", function (evt) { | |
console.log(new Array(30).join("-")); | |
var results = evt.currentTarget.sort(function (a, b) { | |
return b.hz - a.hz; | |
}); | |
results.forEach(function (item) { | |
console.log((idx + 1) + ". " + item); | |
}); | |
}); | |
console.log("LZString 1.4.4 vs pull request #98 - base64 decompression"); | |
console.log(new Array(30).join("-")); | |
suite.run(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment