summaryrefslogtreecommitdiffstats
path: root/src/js/lz4.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/lz4.js')
-rw-r--r--src/js/lz4.js190
1 files changed, 190 insertions, 0 deletions
diff --git a/src/js/lz4.js b/src/js/lz4.js
new file mode 100644
index 0000000..608cdd8
--- /dev/null
+++ b/src/js/lz4.js
@@ -0,0 +1,190 @@
+/*******************************************************************************
+
+ uBlock Origin - a comprehensive, efficient content blocker
+ Copyright (C) 2018-present Raymond Hill
+
+ 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/}.
+
+ Home: https://github.com/gorhill/uBlock
+*/
+
+/* global lz4BlockCodec */
+
+'use strict';
+
+/******************************************************************************/
+
+import µb from './background.js';
+
+/*******************************************************************************
+
+ Experimental support for storage compression.
+
+ For background information on the topic, see:
+ https://github.com/uBlockOrigin/uBlock-issues/issues/141#issuecomment-407737186
+
+**/
+
+/******************************************************************************/
+
+let promisedInstance;
+let textEncoder, textDecoder;
+let ttlCount = 0;
+let ttlDelay = 60000;
+
+const init = function() {
+ ttlDelay = µb.hiddenSettings.autoUpdateAssetFetchPeriod * 2 * 1000;
+ if ( promisedInstance === undefined ) {
+ let flavor;
+ if ( µb.hiddenSettings.disableWebAssembly === true ) {
+ flavor = 'js';
+ }
+ promisedInstance = lz4BlockCodec.createInstance(flavor);
+ }
+ return promisedInstance;
+};
+
+// We can't shrink memory usage of lz4 codec instances, and in the
+// current case memory usage can grow to a significant amount given
+// that a single contiguous memory buffer is required to accommodate
+// both input and output data. Thus a time-to-live implementation
+// which will cause the wasm instance to be forgotten after enough
+// time elapse without the instance being used.
+
+const destroy = function() {
+ //if ( lz4CodecInstance !== undefined ) {
+ // console.info(
+ // 'uBO: freeing lz4-block-codec instance (%s KB)',
+ // lz4CodecInstance.bytesInUse() >>> 10
+ // );
+ //}
+ promisedInstance = undefined;
+ textEncoder = textDecoder = undefined;
+ ttlCount = 0;
+};
+
+const ttlTimer = vAPI.defer.create(destroy);
+
+const ttlManage = function(count) {
+ ttlTimer.off();
+ ttlCount += count;
+ if ( ttlCount > 0 ) { return; }
+ ttlTimer.on(ttlDelay);
+};
+
+const encodeValue = function(lz4CodecInstance, dataIn) {
+ if ( !lz4CodecInstance ) { return; }
+ //let t0 = window.performance.now();
+ if ( textEncoder === undefined ) {
+ textEncoder = new TextEncoder();
+ }
+ const inputArray = textEncoder.encode(dataIn);
+ const inputSize = inputArray.byteLength;
+ const outputArray = lz4CodecInstance.encodeBlock(inputArray, 8);
+ if ( outputArray instanceof Uint8Array === false ) { return; }
+ outputArray[0] = 0x18;
+ outputArray[1] = 0x4D;
+ outputArray[2] = 0x22;
+ outputArray[3] = 0x04;
+ outputArray[4] = (inputSize >>> 0) & 0xFF;
+ outputArray[5] = (inputSize >>> 8) & 0xFF;
+ outputArray[6] = (inputSize >>> 16) & 0xFF;
+ outputArray[7] = (inputSize >>> 24) & 0xFF;
+ //console.info(
+ // 'uBO: [%s] compressed %d KB => %d KB (%s%%) in %s ms',
+ // inputArray.byteLength >> 10,
+ // outputArray.byteLength >> 10,
+ // (outputArray.byteLength / inputArray.byteLength * 100).toFixed(0),
+ // (window.performance.now() - t0).toFixed(1)
+ //);
+ return outputArray;
+};
+
+const decodeValue = function(lz4CodecInstance, inputArray) {
+ if ( !lz4CodecInstance ) { return; }
+ //let t0 = window.performance.now();
+ if (
+ inputArray[0] !== 0x18 || inputArray[1] !== 0x4D ||
+ inputArray[2] !== 0x22 || inputArray[3] !== 0x04
+ ) {
+ console.error('decodeValue: invalid input array');
+ return;
+ }
+ const outputSize =
+ (inputArray[4] << 0) | (inputArray[5] << 8) |
+ (inputArray[6] << 16) | (inputArray[7] << 24);
+ const outputArray = lz4CodecInstance.decodeBlock(inputArray, 8, outputSize);
+ if ( outputArray instanceof Uint8Array === false ) { return; }
+ if ( textDecoder === undefined ) {
+ textDecoder = new TextDecoder();
+ }
+ const s = textDecoder.decode(outputArray);
+ //console.info(
+ // 'uBO: [%s] decompressed %d KB => %d KB (%s%%) in %s ms',
+ // inputArray.byteLength >>> 10,
+ // outputSize >>> 10,
+ // (inputArray.byteLength / outputSize * 100).toFixed(0),
+ // (window.performance.now() - t0).toFixed(1)
+ //);
+ return s;
+};
+
+const lz4Codec = {
+ // Arguments:
+ // dataIn: must be a string
+ // Returns:
+ // A Uint8Array, or the input string as is if compression is not
+ // possible.
+ encode: async function(dataIn, serialize = undefined) {
+ if ( typeof dataIn !== 'string' || dataIn.length < 4096 ) {
+ return dataIn;
+ }
+ ttlManage(1);
+ const lz4CodecInstance = await init();
+ let dataOut = encodeValue(lz4CodecInstance, dataIn);
+ ttlManage(-1);
+ if ( serialize instanceof Function ) {
+ dataOut = await serialize(dataOut);
+ }
+ return dataOut || dataIn;
+ },
+ // Arguments:
+ // dataIn: must be a Uint8Array
+ // Returns:
+ // A string, or the input argument as is if decompression is not
+ // possible.
+ decode: async function(dataIn, deserialize = undefined) {
+ if ( deserialize instanceof Function ) {
+ dataIn = await deserialize(dataIn);
+ }
+ if ( dataIn instanceof Uint8Array === false ) {
+ return dataIn;
+ }
+ ttlManage(1);
+ const lz4CodecInstance = await init();
+ const dataOut = decodeValue(lz4CodecInstance, dataIn);
+ ttlManage(-1);
+ return dataOut || dataIn;
+ },
+ relinquish: function() {
+ ttlDelay = 1;
+ ttlManage(0);
+ },
+};
+
+/******************************************************************************/
+
+export default lz4Codec;
+
+/******************************************************************************/