diff --git a/libs/base64-string.js b/libs/base64-string.js index 4bb19aa..c925bdf 100644 --- a/libs/base64-string.js +++ b/libs/base64-string.js @@ -7,248 +7,299 @@ // For more information, the home page: // http://pieroxy.net/blog/pages/lz-string/index.html // -// Base64 compression / decompression for already compressed content (gif, png, jpg, mp3, ...) +// Base64 compression / decompression for already compressed content (gif, png, jpg, mp3, ...) // version 1.4.1 + +var Base64String = (function(){ +var f = String.fromCharCode; +var keyEnc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(''); +var keyDec = (function (){ + var dict = {}, + i = keyEnc.length; + while(i--){ + dict[keyEnc[i]] = i; + } + return dict; + })(); + var Base64String = { - + compressToUTF16 : function (input) { var output = [], i,c, current, status = 0; - - input = this.compress(input); - + + input = this.compressToArray(input); + for (i=0 ; i> 1)+32)); + output.push(f((c >> 1)+32)); current = (c & 1) << 14; break; case 1: - output.push(String.fromCharCode((current + (c >> 2))+32)); + output.push(f((current + (c >> 2))+32)); current = (c & 3) << 13; break; case 2: - output.push(String.fromCharCode((current + (c >> 3))+32)); + output.push(f((current + (c >> 3))+32)); current = (c & 7) << 12; break; case 3: - output.push(String.fromCharCode((current + (c >> 4))+32)); + output.push(f((current + (c >> 4))+32)); current = (c & 15) << 11; break; case 4: - output.push(String.fromCharCode((current + (c >> 5))+32)); + output.push(f((current + (c >> 5))+32)); current = (c & 31) << 10; break; case 5: - output.push(String.fromCharCode((current + (c >> 6))+32)); + output.push(f((current + (c >> 6))+32)); current = (c & 63) << 9; break; case 6: - output.push(String.fromCharCode((current + (c >> 7))+32)); + output.push(f((current + (c >> 7))+32)); current = (c & 127) << 8; break; case 7: - output.push(String.fromCharCode((current + (c >> 8))+32)); + output.push(f((current + (c >> 8))+32)); current = (c & 255) << 7; break; case 8: - output.push(String.fromCharCode((current + (c >> 9))+32)); + output.push(f((current + (c >> 9))+32)); current = (c & 511) << 6; break; case 9: - output.push(String.fromCharCode((current + (c >> 10))+32)); + output.push(f((current + (c >> 10))+32)); current = (c & 1023) << 5; break; case 10: - output.push(String.fromCharCode((current + (c >> 11))+32)); + output.push(f((current + (c >> 11))+32)); current = (c & 2047) << 4; break; case 11: - output.push(String.fromCharCode((current + (c >> 12))+32)); + output.push(f((current + (c >> 12))+32)); current = (c & 4095) << 3; break; case 12: - output.push(String.fromCharCode((current + (c >> 13))+32)); + output.push(f((current + (c >> 13))+32)); current = (c & 8191) << 2; break; case 13: - output.push(String.fromCharCode((current + (c >> 14))+32)); + output.push(f((current + (c >> 14))+32)); current = (c & 16383) << 1; break; case 14: - output.push(String.fromCharCode((current + (c >> 15))+32, (c & 32767)+32)); + output.push(f((current + (c >> 15))+32, (c & 32767)+32)); status = 0; break; } } - output.push(String.fromCharCode(current + 32)); + output.push(f(current + 32)); return output.join(''); }, - + decompressFromUTF16 : function (input) { var output = [], current,c, status=0, i = 0; - + while (i < input.length) { c = input.charCodeAt(i) - 32; - + switch (status++) { case 0: current = c << 1; break; case 1: - output.push(String.fromCharCode(current | (c >> 14))); + output.push(f(current | (c >> 14))); current = (c&16383) << 2; break; case 2: - output.push(String.fromCharCode(current | (c >> 13))); + output.push(f(current | (c >> 13))); current = (c&8191) << 3; break; case 3: - output.push(String.fromCharCode(current | (c >> 12))); + output.push(f(current | (c >> 12))); current = (c&4095) << 4; break; case 4: - output.push(String.fromCharCode(current | (c >> 11))); + output.push(f(current | (c >> 11))); current = (c&2047) << 5; break; case 5: - output.push(String.fromCharCode(current | (c >> 10))); + output.push(f(current | (c >> 10))); current = (c&1023) << 6; break; case 6: - output.push(String.fromCharCode(current | (c >> 9))); + output.push(f(current | (c >> 9))); current = (c&511) << 7; break; case 7: - output.push(String.fromCharCode(current | (c >> 8))); + output.push(f(current | (c >> 8))); current = (c&255) << 8; break; case 8: - output.push(String.fromCharCode(current | (c >> 7))); + output.push(f(current | (c >> 7))); current = (c&127) << 9; break; case 9: - output.push(String.fromCharCode(current | (c >> 6))); + output.push(f(current | (c >> 6))); current = (c&63) << 10; break; case 10: - output.push(String.fromCharCode(current | (c >> 5))); + output.push(f(current | (c >> 5))); current = (c&31) << 11; break; case 11: - output.push(String.fromCharCode(current | (c >> 4))); + output.push(f(current | (c >> 4))); current = (c&15) << 12; break; case 12: - output.push(String.fromCharCode(current | (c >> 3))); + output.push(f(current | (c >> 3))); current = (c&7) << 13; break; case 13: - output.push(String.fromCharCode(current | (c >> 2))); + output.push(f(current | (c >> 2))); current = (c&3) << 14; break; case 14: - output.push(String.fromCharCode(current | (c >> 1))); + output.push(f(current | (c >> 1))); current = (c&1) << 15; break; case 15: - output.push(String.fromCharCode(current | c)); + output.push(f(current | c)); status=0; break; } - - + + i++; } - - return this.decompress(output.join('')); - //return output; - - }, + return this.decompressFromArray(output); + + }, - // private property - _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", - decompress : function (input) { - var output = []; - var chr1, chr2, chr3, enc1, enc2, enc3, enc4; - var i = 1; - var odd = input.charCodeAt(0) >> 8; - + var output = [], + chrc, chr1, chr2, chr3, + i = 1, + odd = input.charCodeAt(0) >> 8; + while (i < input.length*2 && (i < input.length*2-1 || odd==0)) { - + if (i%2==0) { - chr1 = input.charCodeAt(i/2) >> 8; - chr2 = input.charCodeAt(i/2) & 255; - if (i/2+1 < input.length) + chrc = input.charCodeAt(i/2); + chr1 = chrc >> 8; + chr2 = chrc & 255; + if (i/2+1 < input.length) chr3 = input.charCodeAt(i/2+1) >> 8; - else + else chr3 = NaN; } else { chr1 = input.charCodeAt((i-1)/2) & 255; if ((i+1)/2 < input.length) { - chr2 = input.charCodeAt((i+1)/2) >> 8; - chr3 = input.charCodeAt((i+1)/2) & 255; - } else + chrc = input.charCodeAt((i+1)/2); + chr2 = chrc >> 8; + chr3 = chrc & 255; + } else chr2=chr3=NaN; } i+=3; - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2) || (i==input.length*2+1 && odd)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3) || (i==input.length*2 && odd)) { - enc4 = 64; + + this._chrToOutput(i, input.length, chr1, chr2, chr3, output); + } + + return output.join(''); + }, + + decompressFromArray : function (input) { + var output = [], + chrc, chr1, chr2, chr3, + i = 1, + odd = input[0].charCodeAt() >> 8; + + while (i < input.length*2 && (i < input.length*2-1 || odd==0)) { + + if (i%2==0) { + chrc = input[i/2].charCodeAt(); + chr1 = chrc >> 8; + chr2 = chrc & 255; + if (i/2+1 < input.length) + chr3 = input[i/2+1].charCodeAt() >> 8; + else + chr3 = NaN; + } else { + chr1 = input[(i-1)/2].charCodeAt() & 255; + if ((i+1)/2 < input.length) { + chrc = input[(i+1)/2].charCodeAt() + chr2 = chrc >> 8; + chr3 = chrc & 255; + } else + chr2=chr3=NaN; } - - output.push(this._keyStr.charAt(enc1)); - output.push(this._keyStr.charAt(enc2)); - output.push(this._keyStr.charAt(enc3)); - output.push(this._keyStr.charAt(enc4)); + i+=3; + + this._chrToOutput(i, input.length, chr1, chr2, chr3, output); } - + return output.join(''); }, - + + // private function to reduce code duplication between + // decompress and decompressFromArray + _chrToOutput : function (i, length, chr1, chr2, chr3, output) { + var enc1 = chr1 >> 2, + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4), + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6), + enc4 = chr3 & 63; + if (isNaN(chr2) || (i==length*2+1 && odd)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3) || (i==length*2 && odd)) { + enc4 = 64; + } + output.push(keyDec[enc1]); + output.push(keyDec[enc2]); + output.push(keyDec[enc3]); + output.push(keyDec[enc4]); + }, + compress : function (input) { + return this.compressToArray(input).join(''); + }, + + compressToArray : function (input) { var output = [], - ol = 1, + ol = 1, output_, chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0, flush=false; - + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - + while (i < input.length) { - - enc1 = this._keyStr.indexOf(input.charAt(i++)); - enc2 = this._keyStr.indexOf(input.charAt(i++)); - enc3 = this._keyStr.indexOf(input.charAt(i++)); - enc4 = this._keyStr.indexOf(input.charAt(i++)); - + + enc1 = keyEnc[input.charAt(i++)]; + enc2 = keyEnc[input.charAt(i++)]; + enc3 = keyEnc[input.charAt(i++)]; + enc4 = keyEnc[input.charAt(i++)]; + chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; - + if (ol%2==0) { output_ = chr1 << 8; flush = true; - + if (enc3 != 64) { - output.push(String.fromCharCode(output_ | chr2)); + output.push(f(output_ | chr2)); flush = false; } if (enc4 != 64) { @@ -256,30 +307,28 @@ var Base64String = { flush = true; } } else { - output.push(String.fromCharCode(output_ | chr1)); + output.push(f(output_ | chr1)); flush = false; - + if (enc3 != 64) { output_ = chr2 << 8; flush = true; } if (enc4 != 64) { - output.push(String.fromCharCode(output_ | chr3)); + output.push(f(output_ | chr3)); flush = false; } } ol+=3; } - + if (flush) { - output.push(String.fromCharCode(output_)); - output = output.join(''); - output = String.fromCharCode(output.charCodeAt(0)|256) + output.substring(1); - } else { - output = output.join(''); + output.push(f(output_)); + output[0] = f(output[0].charCodeAt()|256); } - return output; - - } + }, + } +return Base64String; +})() diff --git a/libs/lz-string.js b/libs/lz-string.js index 1194e25..7697d31 100644 --- a/libs/lz-string.js +++ b/libs/lz-string.js @@ -6,501 +6,380 @@ // 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 62) { + reverseDict[UriSafeCharArray[i].charCodeAt(0)] = i; } + reverseDict[Base64CharArray[i].charCodeAt(0)] = 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+"="; + function streamBits(value, numBitsMask) { + for (var i = 0; numBitsMask >>= 1; i++) { + // shifting has precedence over bitmasking + streamDataVal = value >> i & 1 | streamDataVal << 1; + if (++streamDataPosition === streamBitsPerChar) { + streamDataPosition = 0; + streamData.push(streamGetCharFromInt(streamDataVal)); + streamDataVal = 0; + } } - }, + } - 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)); }); - }, + function getCharFromBase64(a) { return Base64CharArray[a]; } + function getCharFromURISafe(a) { return UriSafeCharArray[a]; } + function getCharFromUTF16(a) { return fromCharCode(a + 32); } + function _node(val) { return { v: val, d: {} }; } + function _compress(uncompressed, bitsPerChar, getCharFromInt) { + // data - empty stream + streamData = []; + + if (uncompressed != null) { + // davaVal + streamDataVal = 0; + // dataPosition + streamDataPosition = 0; + streamBitsPerChar = bitsPerChar; + streamGetCharFromInt = getCharFromInt; + var j = 0, value = 0, + dictionary = {}, + freshNode = true, + c = 0, + node = _node(3), // first node will always be initialised like this. + nextNode, + dictSize = 3, + numBitsMask = 0b100; + + 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) + streamBits(value, numBitsMask); + streamBits(c, value ? 0b10000000000000000 : 0b100000000); + + // Add charCode to the dictionary. + dictionary[c] = node; + + for (j = 1; j < uncompressed.length; j++) { + c = uncompressed.charCodeAt(j); + // does the new charCode match an existing prefix? + nextNode = node.d[c]; + if (nextNode) { + // continue with next prefix + node = nextNode; + } else { - compressToUTF16 : function (input) { - if (input == null) return ""; - return LZString._compress(input, 15, function(a){return f(a+32);}) + " "; - }, + // 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. - 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>> 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= numBitsMask) { + numBitsMask <<= 1; + } - } - }, - - - //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; - } + // insert "new 8/16 bit charCode" token, + // see comments above for explanation + value = c < 256 ? 0 : 1 + streamBits(value, numBitsMask); + streamBits(c, value ? 0b10000000000000000 : 0b100000000); - 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> 1; - } - } else { - value = 1; - for (i=0 ; i> 1; + dictionary[c] = _node(dictSize) + // Note of that we already wrote + // the charCode token to the bitstream + freshNode = true; } - } - 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= numBitsMask) { + numBitsMask <<= 1; } - value = value >> 1; - } - - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = Math.pow(2, context_numBits); - context_numBits++; + // set node to first charCode of new prefix + node = dictionary[c]; + } } - // 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> 1; - } + // === Write last prefix to output === + if (freshNode) { + // character token already written to output + freshNode = false; } else { - value = 1; - for (i=0 ; i> 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> 1; + // write out the prefix token + streamBits(node.v, numBitsMask); } + // Is c a new character? + if (dictionary[c] == undefined) { + // increase token bitlength if necessary + if (++dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + // insert "new 8/16 bit charCode" token, + // see comments above for explanation + value = c < 256 ? 0 : 1 + streamBits(value, numBitsMask); + streamBits(c, 0b100000000 << value); + } + // increase token bitlength if necessary + if (++dictSize >= numBitsMask) { + numBitsMask <<= 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; + // Mark the end of the stream + streamBits(2, numBitsMask); + // Flush the last char + streamDataVal <<= streamBitsPerChar - streamDataPosition; + streamData.push(streamGetCharFromInt(streamDataVal)); } + return streamData; - // 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; + } + function _decompress(length, resetBits, getNextValue) { + var dictionary = ['', '', ''], + enlargeIn = 4, + dictSize = 4, + numBits = 3, + entry = "", + result = [], + 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++); } - 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; + // if end of stream token, return empty string + if (bits == 2) { + return ""; } - 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++); + // 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++); } - 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 ""; } + c = fromCharCode(bits); 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++); + // 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++); } - 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; + // 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); - c = dictSize-1; - enlargeIn--; - break; - case 2: - return result.join(''); - } - - if (enlargeIn == 0) { - enlargeIn = Math.pow(2, numBits); - numBits++; + } + dictionary[dictSize] = fromCharCode(bits); + bits = dictSize++; + if (--enlargeIn == 0) { + enlargeIn = 1 << numBits++; + } + } else if (bits == 2) { + // end of stream token + return result.join(''); } - if (dictionary[c]) { - entry = dictionary[c]; - } else { - if (c === dictSize) { - entry = w + w.charAt(0); - } else { - return null; - } + if (bits > dictionary.length) { + return null; } + entry = bits < dictionary.length ? dictionary[bits] : c + c.charAt(0); result.push(entry); + // Add c+entry[0] to the dictionary. + dictionary[dictSize++] = c + entry.charAt(0); - // Add w+entry[0] to the dictionary. - dictionary[dictSize++] = w + entry.charAt(0); - enlargeIn--; - - w = entry; + c = entry; - if (enlargeIn == 0) { - enlargeIn = Math.pow(2, numBits); - numBits++; + if (--enlargeIn == 0) { + enlargeIn = 1 << numBits++; } } + return ""; + } + function _compressToArray(uncompressed) { + return _compress(uncompressed, 16, fromCharCode); + } + function _decompressFromArray(compressed) { + if (compressed == null) return ""; + if (compressed.length == 0) return null; + return _decompress(compressed.length, 16, function (index) { return compressed[index].charCodeAt(0); }); } -}; - return LZString; + + return { + compressToBase64: function (input) { + if (input == null) return ""; + var res = _compress(input, 6, getCharFromBase64), + i = res.length % 4; // To produce valid Base64 + while (i--) { + res.push("="); + } + + return res.join(''); + }, + + decompressFromBase64: function (input) { + if (input == null) return ""; + if (input == "") return null; + return _decompress(input.length, 6, function (index) { return reverseDict[input.charCodeAt(index)]; }); + }, + + compressToUTF16: function (input) { + if (input == null) return ""; + var compressed = _compress(input, 15, getCharFromUTF16); + compressed.push(" "); + return compressed.join(''); + }, + + decompressFromUTF16: function (compressed) { + if (compressed == null) return ""; + if (compressed == "") return null; + return _decompress(compressed.length, 15, function (index) { return compressed.charCodeAt(index) - 32; }); + }, + + //compress into uint8array (UCS-2 big endian format) + compressToUint8Array: function (uncompressed) { + var compressed = _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 _decompressFromArray(compressed); + } else if (compressed.length == 0) { + return null; + } + return _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 _compress(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 _decompress(input.length, 6, function (index) { return reverseDict[input.charCodeAt(index)]; }); + }, + + compress: function (uncompressed) { + return _compressToArray(uncompressed).join(''); + }, + + compressToArray: _compressToArray, + + decompress: function (compressed) { + if (compressed == null) return ""; + if (compressed == "") return null; + return _decompress(compressed.length, 16, function (index) { return compressed.charCodeAt(index); }); + }, + + decompressFromArray: _decompressFromArray + }; })(); if (typeof define === 'function' && define.amd) { define(function () { return LZString; }); -} else if( typeof module !== 'undefined' && module != null ) { +} else if (typeof module !== 'undefined' && module != null) { module.exports = LZString -} else if( typeof angular !== 'undefined' && angular != null ) { +} else if (typeof angular !== 'undefined' && angular != null) { angular.module('LZString', []) - .factory('LZString', function () { - return LZString; - }); + .factory('LZString', function () { + return LZString; + }); } diff --git a/libs/lz-string.min.js b/libs/lz-string.min.js index 2d1900a..c205ff6 100644 --- a/libs/lz-string.min.js +++ b/libs/lz-string.min.js @@ -1 +1 @@ -var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;ne;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;ie;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString); +var LZString=function(){function b(y,z){for(var A=0;z>>=1;A++)q=1&y>>A|q<<1,++r===s&&(r=0,p.push(t(q)),q=0)}function d(y){return w[y]}function e(y){return x[y]}function f(y){return o(y+32)}function g(y){return{v:y,d:{}}}function h(y,z,A){if(p=[],null!=y){q=0,r=0,s=z,t=A;var H,B=0,C=0,D={},E=!0,F=0,G=g(3),I=3,J=4;if(y.length){for(F=y.charCodeAt(0),C=256>F?0:1,b(C,J),b(F,C?65536:256),D[F]=G,B=1;B=J&&(J<<=1),C=256>F?0:1,b(C,J),b(F,C?65536:256),D[F]=g(I),E=!0),G.d[F]=g(++I),I>=J&&(J<<=1),G=D[F]);E?E=!1:b(G.v,J),void 0==D[F]&&(++I>=J&&(J<<=1),C=256>F?0:1,b(C,J),b(F,256<=J&&(J<<=1)}b(2,J),q<<=s-r,p.push(t(q))}return p}function k(y,z,A){for(var B=["","",""],C=4,D=4,E=3,F="",G=[],H=0,I=2,J=0,K="",L=A(0),M=z,N=1;J!=I;)H+=(1&L>>--M)<>--M)<>--M)<H){for(I=8+8*H,H=J=0;J!=I;)H+=(1&L>>--M)<B.length)return null;F=Hn;)62>>8,A[2*B+1]=255&D;return A},decompressFromUint8Array:function(y){if(null===y||y===void 0)return m(y);return 0==y.length?null:k(y.length,8,function(z){return y[z]})},compressToEncodedURIComponent:function(y){return null==y?"":h(y,6,e).join("")},decompressFromEncodedURIComponent:function(y){return null==y?"":""==y?null:(y=y.replace(/ /g,"+"),k(y.length,6,function(z){return u[y.charCodeAt(z)]}))},compress:function(y){return l(y).join("")},compressToArray:l,decompress:function(y){return null==y?"":""==y?null:k(y.length,16,function(z){return y.charCodeAt(z)})},decompressFromArray:m}}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module?module.exports=LZString:"undefined"!=typeof angular&&null!=angular&&angular.module("LZString",[]).factory("LZString",function(){return LZString}); \ No newline at end of file diff --git a/libs/lz-uint8.js b/libs/lz-uint8.js new file mode 100644 index 0000000..4a58c53 --- /dev/null +++ b/libs/lz-uint8.js @@ -0,0 +1,396 @@ +/** + * Using modern module syntax, because seriously + */ + + + + +var LZu8 = (function () { + + /** + * Integer to (safe) UTF16 character + * + * Basic ranges of printable UTF16 values (as found in LZ-string): + * [32, 127), [160, 55296), [63744, 65536) + * One use of this library is to embed the string into code. For that reason, + * we want to filter out out string characters like: + * " (34) + * ' (39) + * ` (44) + * (Forward tick is safe: ยด (96)) + * So: + * 32, 33, [35, 39), [40, 44), [45, 127), [160, 55296), [63744, 65536) + */ + function itou(i) { + i += 32; + if (i > 33 && i < 39) { + i++; + } else if (i > 38 && i < 44) { + i += 2; + } else if (i > 43 && i < 127) { + i += 3; + } else if (i > 126 && i < 55258) { + i += 37; // === 160 - 128 + 3 + } else if (i > 55295) { + i += 8485; // === 63744 - 55296 + 37 + } + return String.fromCharCode(i); + } + + /** + * UTF16 charCode to integer + */ + function utoi(i) { + return i - (i > 63743 ? 8517 : + i > 159 ? 69 : + i > 46 && i < 130 ? 35 : + i > 40 && i < 46 ? 34 : + i > 34 && i < 40 ? 33 : + 32); + } + + function StringStream(length) { + // data + this.d = length ? [ + // Write length of the input as the first four unicode characters, + // Or 45 bits. 1<<45 bytes is about 35 terabytes, so more than enough. + itou(length / 40000000 & 0x7FFF), + itou((length >>> 15) & 0x7FFF), + itou(length & 0x7FFF), + ] : + []; // empty stream + // davaVal + this.v = 0; + // dataPosition + this.p = 0; + } + + StringStream.prototype.s = function (value, numBitsMask) { //streamBits + for (let i = 0, t = this; numBitsMask>>=1; i++) { + // shifting has precedence over bitmasking + t.v = value >> i & 1 | t.v << 1; + if (++t.p === 15) { + t.p = 0; + t.d.push(itou(t.v)); + t.v = 0; + } + } + } + + StringStream.prototype.f = function () { // finalise + let t = this; + // Flush the last char + t.v <<= 15 - t.p; + t.d.push(itou(t.v)); + t.d.push(' '); + return t.d.join(''); + } + /* + function Uint8Stream(length){ + // data + this.d = length ? [ + // Write length of the input as the first four bytes, + // Or 45 bits. 1<<45 bytes is about 35 terabytes, so more than enough. + length >>> 24, + (length >>> 16) & 255, + (length >>> 8) & 255, + length & 255, + ] : + []; // empty stream + // davaVal + this.v = 0; + // dataPosition + this.p = 0; + } + Uint8Stream.prototype.s = function(value, numBitsMask){ + for (let i = 0, t=this; numBitsMask>>i; i++) { + // shifting has precedence over bitmasking + t.v = value >> i & 1 | t.v << 1; + if (++t.p === 8) { + t.p = 0; + t.d.push(t.v); + t.v = 0; + } + } + } + Uint8Stream.prototype.f = function(){ + let t = this; + // Flush the last char + t.v <<= 8 - t.p; + t.d.push(t.v); + t.d.push(0); + return Uint8Array.from(t.d); + } + */ + + var _node = function (val) { return { v: val, d: {} }; } + return { + itou, + utoi, + compress: function (input) { + if (input === null) return ''; + let i = 0, + j = 0, + freshNode = true, + c = 0, + node = _node(2), // first node will always be initialised like this. + nextNode, + numBitsMask = 0b100, + dictionary = _node(0), + dictSize = 2, + stringStream = new StringStream(input.length); + + if (input.length) { + + // If there is an array, the first byte 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 = input[0]; + + // === Write first byte token to output == + + // insert new byte token into bitstream + stringStream.s(0, numBitsMask); + // insert byte bits into bitstream + stringStream.s(c, 0b100000000); + + // Add charCode to the dictionary. + dictionary[c] = node; + + for (j = 1; j < input.length; j++) { + c = input[j]; + // does the new charCode match an existing prefix? + nextNode = node.d[c]; + if (nextNode) { + // continue with next prefix + node = nextNode; + } else { + + // 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 + stringStream.s(node.v, numBitsMask); + } + + // Is the byte a new byte + // that needs to be stored at the root? + if (dictionary[c] === undefined) { + // increase token bitlength if necessary + if (dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + + // insert new byte token into bitstream + stringStream.s(0, numBitsMask); + // insert byte bits into bitstream + stringStream.s(c, 0b100000000); + + dictionary[c] = _node(++dictSize); + // Note of that we already wrote + // the charCode token to the bitstream + freshNode = true; + } + // add node representing prefix + new charCode to trie + node.d[c] = _node(++dictSize); + // increase token bitlength if necessary + if (dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + // set node to first charCode of new prefix + node = dictionary[c]; + } + } + + // === Write last prefix to output === + if (freshNode) { + // character token already written to output + freshNode = false; + } else { + // write out the prefix token + stringStream.s(node.v, numBitsMask); + } + + // Is c a new character? + if (dictionary[c] === undefined) { + // increase token bitlength if necessary + if (dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + // insert new byte token into bitstream + stringStream.s(0, numBitsMask); + // insert byte bits into bitstream + stringStream.s(c, 0b100000000); + + } + // increase token bitlength if necessary + if (dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + } + + // Mark the end of the stream + stringStream.s(1, numBitsMask); + + // Flush the last char + return stringStream.f(); + + }, + + decompress: function (compressed) { + if (compressed === null || compressed.length < 4) return null; + + let length = compressed.length, + getNextValue = function (index) { return utoi(compressed.charCodeAt(index)); }; + let dictionary = [0, 1], + enlargeIn = 1, + dictSize = 3, + numBits = 2, + bytes = null, + bytes_concat = null, + result = new Uint8Array( + getNextValue(0) * 0x40000000 + + (getNextValue(1) << 15) + + getNextValue(2)), + result_index = 0, + bits = 0, + maxPower = 2, + power = 0, + data_val = getNextValue(3), + data_position = 15, + data_index = 4; + + // Get first token, guaranteed to be either + // a new byte token 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 = 15; + data_val = getNextValue(data_index++); + } + } + + if (bits === 1) { + return null; + } + + // else, get byte value + bits = power = 0; + while (power < 8) { + // shifting has precedence over bitmasking + bits += (data_val >> --data_position & 1) << power++; + if (data_position === 0) { + data_position = 15; + data_val = getNextValue(data_index++); + } + } + bytes = [bits]; + dictionary[2] = bytes; + result[result_index++] = bits; + + // 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 = 15; + data_val = getNextValue(data_index++); + } + } + + // 0 implies new byte + if (!bits) { + bits = power = 0; + while (power < 8) { + // shifting has precedence over bitmasking + bits += (data_val >> --data_position & 1) << power++; + if (data_position === 0) { + data_position = 15; + data_val = getNextValue(data_index++); + } + } + dictionary[dictSize] = [bits]; + bits = dictSize++; + if (--enlargeIn === 0) { + enlargeIn = 1 << numBits++; + } + } else if (bits === 1) { + // end of stream token + return result; + } + + if (bits > dictionary.length) { + return null; + } + bytes_concat = bits < dictionary.length ? dictionary[bits] : bytes.concat(bytes[0]); + for (let i = 0; i < bytes_concat.length; i++) { + result[result_index++] = bytes_concat[i]; + } + dictionary[dictSize++] = bytes.concat(bytes_concat[0]); + bytes = bytes_concat; + + if (--enlargeIn === 0) { + enlargeIn = 1 << numBits++; + } + + } + return null; + }, + + compressToUint8Array: function () { }, + decompressFromUint8Array: function () { }, + }; +})(); + +function testCompression(LZ) { + console.log('Testing utoi/itou functions'); + let utoiMismatches = []; + for (let i = 0; i < 1 << 15; i++) { + let j = LZ.utoi(LZ.itou(i).charCodeAt(0)); + if (i !== j) { + utoiMismatches.push({ itou: i, utio: j }); + } + } + + if (utoiMismatches.length) { + console.log("Errors in itou/utoi conversion detected:", utoiMismatches); + } else { + console.log('No errors in itou/utoi conversion detected'); + } + + let input = new Uint16Array(1 << 15); + for (let i = 0; i < input.length; i++) { + input[i] = i & 256; + } + let inputUint8 = new Uint8Array(input.buffer); + let compressed = LZ.compress(inputUint8); + let decompressed = new Uint16Array(LZ.decompress(compressed).buffer); + let mismatches = []; + for (let i = 0; i < input.length; i++) { + if (input[i] !== decompressed[i]) { + mismatches.push({ index: i, input: input[i], decompressed: decompressed[i] }); + } + } + console.log({ + compressed, + mismatches, + length: compressed.length, + inputLength: input.length, + }); +} diff --git a/lz-string-unsafe.js b/lz-string-unsafe.js new file mode 100644 index 0000000..ca5c10a --- /dev/null +++ b/lz-string-unsafe.js @@ -0,0 +1,394 @@ +var LZStringUnsafe = (function () { + // private property + var i = 0, + reverseDict = {}, + fromCharCode = String.fromCharCode, + streamData, + streamDataVal, + streamDataPosition, + streamBitsPerChar, + streamGetCharFromInt, + base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+", + Base64CharArray = (base + "/=").split(''), + UriSafeCharArray = (base + "-$").split(''); + while (i < 65) { + if (i > 62) { + reverseDict[UriSafeCharArray[i].charCodeAt(0)] = i; + } + reverseDict[Base64CharArray[i].charCodeAt(0)] = i++; + } + + function streamBits(value, numBitsMask) { + for (var i = 0; numBitsMask >>= 1; i++) { + // shifting has precedence over bitmasking + streamDataVal = value >> i & 1 | streamDataVal << 1; + if (++streamDataPosition === streamBitsPerChar) { + streamDataPosition = 0; + streamData.push(streamGetCharFromInt(streamDataVal)); + streamDataVal = 0; + } + } + } + + function getCharFromBase64(a) { return Base64CharArray[a]; } + function getCharFromURISafe(a) { return UriSafeCharArray[a]; } + function getCharFromUTF16(a) { return fromCharCode(a + 32); } + function _compress(uncompressed, bitsPerChar, getCharFromInt) { + // data - empty stream + streamData = []; + + if (uncompressed != null) { + // davaVal + streamDataVal = 0; + // dataPosition + streamDataPosition = 0; + streamBitsPerChar = bitsPerChar; + streamGetCharFromInt = getCharFromInt; + var 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 first charCode + dictionary = [2, 2, node], + freshNode = true, + c = 0, + dictSize = 3, + numBitsMask = 0b100; + + 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) + streamBits(value, numBitsMask); + streamBits(c, value ? 0b10000000000000000 : 0b100000000); + + // 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 + streamBits(node[0], numBitsMask); + } + + // 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 (++dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + + // insert "new 8/16 bit charCode" token, + // see comments above for explanation + value = c < 256 ? 0 : 1 + streamBits(value, numBitsMask); + streamBits(c, value ? 0b10000000000000000 : 0b100000000); + + 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 (dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + // 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 + streamBits(node[0], numBitsMask); + + } + + // 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 (++dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + // insert "new 8/16 bit charCode" token, + // see comments above for explanation + value = c < 256 ? 0 : 1 + streamBits(value, numBitsMask); + streamBits(c, value ? 0b10000000000000000 : 0b100000000); + } + // increase token bitlength if necessary + if (++dictSize >= numBitsMask) { + numBitsMask <<= 1; + } + } + + // Mark the end of the stream + streamBits(2, numBitsMask); + + + // Flush the last char + streamDataVal <<= streamBitsPerChar - streamDataPosition; + streamData.push(streamGetCharFromInt(streamDataVal)); + } + return streamData; + } + + function _decompress(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 = fromCharCode(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] = fromCharCode(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 ""; + } + function _compressToArray(uncompressed) { + return _compress(uncompressed, 16, fromCharCode); + } + function _decompressFromArray(compressed) { + if (compressed == null) return ""; + if (compressed.length == 0) return null; + return _decompress(compressed.length, 16, function (index) { return compressed[index].charCodeAt(0); }); + } + + return { + compressToBase64: function (input) { + if (input == null) return ""; + var res = _compress(input, 6, getCharFromBase64), + i = res.length % 4; // To produce valid Base64 + while (i--) { + res.push("="); + } + + return res.join(''); + }, + + decompressFromBase64: function (input) { + if (input == null) return ""; + if (input == "") return null; + return _decompress(input.length, 6, function (index) { return reverseDict[input.charCodeAt(index)]; }); + }, + + compressToUTF16: function (input) { + if (input == null) return ""; + var compressed = _compress(input, 15, getCharFromUTF16); + compressed.push(" "); + return compressed.join(''); + }, + + decompressFromUTF16: function (compressed) { + if (compressed == null) return ""; + if (compressed == "") return null; + return _decompress(compressed.length, 15, function (index) { return compressed.charCodeAt(index) - 32; }); + }, + + //compress into uint8array (UCS-2 big endian format) + compressToUint8Array: function (uncompressed) { + var compressed = _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 _decompressFromArray(compressed); + } else if (compressed.length == 0) { + return null; + } + return _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 _compress(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 _decompress(input.length, 6, function (index) { return reverseDict[input.charCodeAt(index)]; }); + }, + + compress: function (uncompressed) { + return _compressToArray(uncompressed).join(''); + }, + compressToArray: _compressToArray, + + decompress: function (compressed) { + if (compressed == null) return ""; + if (compressed == "") return null; + return _decompress(compressed.length, 16, function (index) { return compressed.charCodeAt(index); }); + }, + + decompressFromArray: _decompressFromArray + }; +})(); + +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; + }); +} diff --git a/lz-string-unsafe.min.js b/lz-string-unsafe.min.js new file mode 100644 index 0000000..02d7136 --- /dev/null +++ b/lz-string-unsafe.min.js @@ -0,0 +1 @@ +var LZStringUnsafe=function(){function b(z,A){for(var B=0;A>>=1;B++)r=1&z>>B|r<<1,++s===t&&(s=0,q.push(u(r)),r=0)}function d(z){return x[z]}function e(z){return y[z]}function f(z){return p(z+32)}function g(z,A,B){if(q=[],null!=z){r=0,s=0,t=A,u=B;var C=0,D=0,E=0,F=[3],G=[2,2,F],H=!0,I=0,J=3,K=4;if(z.length){I=z.charCodeAt(0),E=256>I?0:1,b(E,K),b(I,E?65536:256),G[1]=I;nextchar:for(C=1;C=K&&(K<<=1),E=256>I?0:1,b(E,K),b(I,E?65536:256),G.push(I),G.push([J]),H=!0),F.push(I),F.push([++J]),J>=K&&(K<<=1),F=G[D+1]}for(H?H=!1:b(F[0],K),D=1;G[D]!=I&&D=K&&(K<<=1),E=256>I?0:1,b(E,K),b(I,E?65536:256)),++J>=K&&(K<<=1)}b(2,K),r<<=t-s,q.push(u(r))}return q}function h(z,A,B){for(var C=[0,1,2],D=4,E=4,F=3,G="",H=[],I="",J=0,K=2,L=0,M="",N=B(0),O=A,P=1;L!=K;)J+=(1&N>>--O)<>--O)<>--O)<J){for(K=8+8*J,J=L=0;L!=K;)J+=(1&N>>--O)<C.length)return null;G=Jn;)62>>8,B[2*C+1]=255&E;return B},decompressFromUint8Array:function(z){if(null===z||z===void 0)return m(z);return 0==z.length?null:h(z.length,8,function(A){return z[A]})},compressToEncodedURIComponent:function(z){return null==z?"":g(z,6,e).join("")},decompressFromEncodedURIComponent:function(z){return null==z?"":""==z?null:(z=z.replace(/ /g,"+"),h(z.length,6,function(A){return o[z.charCodeAt(A)]}))},compress:function(z){return l(z).join("")},compressToArray:l,decompress:function(z){return null==z?"":""==z?null:h(z.length,16,function(A){return z.charCodeAt(A)})},decompressFromArray:m}}();"function"==typeof define&&define.amd?define(function(){return LZStringUnsafe}):"undefined"!=typeof module&&null!=module?module.exports=LZStringUnsafe:"undefined"!=typeof angular&&null!=angular&&angular.module("LZStringUnsafe",[]).factory("LZStringUnsafe",function(){return LZStringUnsafe}); \ No newline at end of file diff --git a/tests/SpecRunner-unsafe.html b/tests/SpecRunner-unsafe.html new file mode 100644 index 0000000..da23a80 --- /dev/null +++ b/tests/SpecRunner-unsafe.html @@ -0,0 +1,54 @@ + + + + LZString tests + + + + + + + + + + + + + + + + + + + +