From 6fb9d2d157914d97153b61c5dc5e96cb7264c310 Mon Sep 17 00:00:00 2001 From: tophf Date: Sun, 10 Dec 2017 11:30:57 +0300 Subject: [PATCH] switch to a much faster LZStringUnsafe https://github.com/pieroxy/lz-string/pull/98 --- background/storage.js | 6 +- manifest.json | 2 +- .../lz-string/LZString-2xspeedup.js | 512 ------------------ vendor/lz-string/lz-string-unsafe.js | 449 +++++++++++++++ 4 files changed, 453 insertions(+), 516 deletions(-) delete mode 100644 vendor-overwrites/lz-string/LZString-2xspeedup.js create mode 100644 vendor/lz-string/lz-string-unsafe.js diff --git a/background/storage.js b/background/storage.js index 0db0ea64..aca6e466 100644 --- a/background/storage.js +++ b/background/storage.js @@ -1,4 +1,4 @@ -/* global LZString */ +/* global LZStringUnsafe */ 'use strict'; const RX_NAMESPACE = new RegExp([/[\s\r\n]*/, @@ -63,13 +63,13 @@ var [chromeLocal, chromeSync] = [ return wrapper.get(keys).then((data = {}) => { for (const key of keys) { const value = data[key]; - data[key] = value && tryJSONparse(LZString.decompressFromUTF16(value)); + data[key] = value && tryJSONparse(LZStringUnsafe.decompressFromUTF16(value)); } return data; }); }, setLZValue(key, value) { - return wrapper.set({[key]: LZString.compressToUTF16(JSON.stringify(value))}); + return wrapper.set({[key]: LZStringUnsafe.compressToUTF16(JSON.stringify(value))}); } }; return wrapper; diff --git a/manifest.json b/manifest.json index 7b01b5f4..dc023116 100644 --- a/manifest.json +++ b/manifest.json @@ -21,7 +21,7 @@ "background": { "scripts": [ "js/messaging.js", - "vendor-overwrites/lz-string/LZString-2xspeedup.js", + "vendor/lz-string/lz-string-unsafe.js", "js/color-parser.js", "js/usercss.js", "background/storage.js", diff --git a/vendor-overwrites/lz-string/LZString-2xspeedup.js b/vendor-overwrites/lz-string/LZString-2xspeedup.js deleted file mode 100644 index 1a3a5a06..00000000 --- a/vendor-overwrites/lz-string/LZString-2xspeedup.js +++ /dev/null @@ -1,512 +0,0 @@ -// ==UserScript== -// @name LZString-2xspeedup -// @description 2x speedup via ES6 Map and Set -// @version 1.4.4 -// ==/UserScript== - -// Copyright (c) 2013 Pieroxy -// This work is free. You can redistribute it and/or modify it -// under the terms of the WTFPL, Version 2 -// For more information see LICENSE.txt or http://www.wtfpl.net/ -// -// For more information, the home page: -// http://pieroxy.net/blog/pages/lz-string/testing.html -// -// LZ-based compression algorithm, version 1.4.4 -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>> 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> 1; - } - } else { - value = 1; - for (i=0 ; i> 1; - } - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = Math.pow(2, context_numBits); - context_numBits++; - } - context_dictionaryToCreate.delete(context_w); - } else { - value = context_dictionary.get(context_w); - for (i=0 ; i> 1; - } - - - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = Math.pow(2, context_numBits); - context_numBits++; - } - // Add wc to the dictionary. - context_dictionary.set(context_wc, context_dictSize++); - context_w = String(context_c); - } - } - - // Output the code for w. - if (context_w !== "") { - if (context_dictionaryToCreate.has(context_w)) { - if (context_w.charCodeAt(0)<256) { - for (i=0 ; i> 1; - } - } else { - value = 1; - for (i=0 ; i> 1; - } - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = Math.pow(2, context_numBits); - context_numBits++; - } - context_dictionaryToCreate.delete(context_w); - } else { - value = context_dictionary.get(context_w); - for (i=0 ; i> 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> 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; -})(); - -if (typeof define === 'function' && define.amd) { - define(function () { return LZString; }); -} else if( typeof module !== 'undefined' && module != null ) { - module.exports = LZString -} else if( typeof angular !== 'undefined' && angular != null ) { - angular.module('LZString', []) - .factory('LZString', function () { - return LZString; - }); -} diff --git a/vendor/lz-string/lz-string-unsafe.js b/vendor/lz-string/lz-string-unsafe.js new file mode 100644 index 00000000..8e914e80 --- /dev/null +++ b/vendor/lz-string/lz-string-unsafe.js @@ -0,0 +1,449 @@ + +var LZStringUnsafe = ( + 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, 6, 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, 15, 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, 8, 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, 6, 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, k = 0, value = 0, + node = [3], // first node will always be initialised like this. + // we should never output the root anyway, + // so we initiate with terminating token + // Also, dictionary[1] will be overwritten + // by the firs charCode + dictionary = [2, 2, node], + freshNode = true, + c = 0, + nextNode, + enlargeIn = 1, + dictSize = 4, + numBits = 2, + data = [], + data_val = 0, + data_position = 0; + + if (uncompressed.length) { + // If there is a string, the first charCode is guaranteed to + // be new, so we write it to output stream, and add it to the + // dictionary. For the same reason we can initialize freshNode + // as true, and new_node, node and dictSize as if + // it was already added to the dictionary (see above). + + c = uncompressed.charCodeAt(0); + + // == 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 bits into bitstream + // 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. + dictionary[1] = c; + + nextchar: + for (j = 1; j < uncompressed.length; j++) { + c = uncompressed.charCodeAt(j); + // does the new charCode match an existing prefix? + for (k = 1; k < node.length; k += 2) { + if (node[k] == c) { + node = node[k + 1]; + continue nextchar; + } + } + // we only end up here if there is no matching char + + // Prefix+charCode does not exist in trie yet. + // We write the prefix to the bitstream, and add + // the new charCode to the dictionary if it's new + // Then we set `node` to the root node matching + // the charCode. + + if (freshNode) { + // Prefix is a freshly added character token, + // which was already written to the bitstream + freshNode = false; + } else { + // write out the current 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 the new charCode a new character + // that needs to be stored at the root? + k = 1; + while (dictionary[k] != c && k < dictionary.length) { + k += 2; + } + if (k == dictionary.length) { + // 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; + } + } + dictionary.push(c); + dictionary.push([dictSize++]); + // Note of that we already wrote + // the charCode token to the bitstream + freshNode = true; + } + // add node representing prefix + new charCode to trie + node.push(c); + node.push([dictSize++]); + // increase token bitlength if necessary + if (--enlargeIn == 0) { + enlargeIn = 1 << numBits++; + } + // set node to first charCode of new prefix + // k is guaranteed to be at the current charCode, + // since we either broke out of the while loop + // when it matched, or just added the new charCode + node = dictionary[k + 1]; + + } + + // === Write last prefix 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? + k = 1; + while (dictionary[k] != c && k < dictionary.length) { + k += 2; + } + if (k == dictionary.length) { + // 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, 16, function (index) { return compressed.charCodeAt(index); }); + }, + + decompressFromArray: function (compressed) { + if (compressed == null) return ""; + if (compressed.length == 0) return null; + return LZString._decompress(compressed.length, 16, function (index) { return compressed[index].charCodeAt(0); }); + }, + + _decompress: function (length, resetBits, getNextValue) { + 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 = resetBits, + 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 = resetBits; + 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 = resetBits; + 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 = resetBits; + 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 = resetBits; + 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; + } +)(); + +if (typeof define === 'function' && define.amd) { + define(function () { return LZStringUnsafe; }); +} else if (typeof module !== 'undefined' && module != null) { + module.exports = LZStringUnsafe +} else if (typeof angular !== 'undefined' && angular != null) { + angular.module('LZStringUnsafe', []) + .factory('LZStringUnsafe', function () { + return LZStringUnsafe; + }); +}