compress | | Cell 1 | Search

LZString is a JavaScript library that provides efficient compression and decompression functionality using the LZ77 algorithm, supporting various output formats like Base64, UTF-16, and URL-encoded strings.

Run example

npm run import -- "Minified lzw compression"

Minified lzw compression

var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}return t[o][r]}var r=String.fromCharCode,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",t={},i={compressToBase64:function(o){if(null==o)return"";var r=i._compress(o,6,function(o){return n.charAt(o)});switch(r.length%4){default:case 0:return r;case 1:return r+"===";case 2:return r+"==";case 3:return r+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(e){return o(n,r.charAt(e))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(o){return null==o?"":""==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compressToUint8Array:function(o){for(var r=i.compress(o),n=new Uint8Array(2*r.length),e=0,t=r.length;t>e;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;i<o.length;i+=1)if(u=o.charAt(i),Object.prototype.hasOwnProperty.call(s,u)||(s[u]=f++,p[u]=!0),c=a+u,Object.prototype.hasOwnProperty.call(s,c))a=c;else{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++),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);

What the code could have been:

const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=$-';
const utf16Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=$-';

class LZString {
  constructor() {
    this.table = {};
    this.alphabet = alphabet;
    this.utf16Alphabet = utf16Characters;
  }

  /**
   * Compresses a string to base64
   * @param {string} str - The string to compress
   * @returns {string} The compressed base64 string
   */
  compressToBase64(str) {
    if (!str) return '';
    const binaryString = this._compress(str, 6, this._base64Encode);
    return binaryString.replace(/=+$/, '') + '===';
  }

  /**
   * Decompresses a base64 string
   * @param {string} str - The base64 string to decompress
   * @returns {string} The decompressed string
   */
  decompressFromBase64(str) {
    if (!str) return '';
    if (str === '') return '';
    return this._decompress(str.length, 32, this._base64Decode);
  }

  /**
   * Compresses a string to UTF16
   * @param {string} str - The string to compress
   * @returns {string} The compressed UTF16 string
   */
  compressToUTF16(str) {
    if (!str) return '';
    return this._compress(str, 15, this._utf16Encode) +'';
  }

  /**
   * Decompresses a UTF16 string
   * @param {string} str - The UTF16 string to decompress
   * @returns {string} The decompressed string
   */
  decompressFromUTF16(str) {
    if (!str) return '';
    if (str === '') return '';
    return this._decompress(str.length, 16384, this._utf16Decode);
  }

  /**
   * Compresses a string to Uint8Array
   * @param {string} str - The string to compress
   * @returns {Uint8Array} The compressed Uint8Array
   */
  compressToUint8Array(str) {
    const binaryString = this.compress(str);
    const uint8Array = new Uint8Array(binaryString.length / 2);
    for (let i = 0; i < binaryString.length; i += 2) {
      const charCode = binaryString.charCodeAt(i);
      const highByte = charCode >> 8;
      const lowByte = charCode % 256;
      uint8Array[i / 2] = highByte;
      uint8Array[(i + 1) / 2] = lowByte;
    }
    return uint8Array;
  }

  /**
   * Decompresses a Uint8Array back to a string
   * @param {Uint8Array} uint8Array - The Uint8Array to decompress
   * @returns {string} The decompressed string
   */
  decompressFromUint8Array(uint8Array) {
    if (!uint8Array) return '';
    const binaryString = uint8Array.map((byte) => String.fromCharCode(byte)).join('');
    const decompressedString = this.decompress(binaryString);
    return decompressedString;
  }

  /**
   * Compresses a string to a URI encoded string
   * @param {string} str - The string to compress
   * @returns {string} The compressed URI encoded string
   */
  compressToEncodedURIComponent(str) {
    if (!str) return '';
    return this._compress(str, 6, this._uriEncode);
  }

  /**
   * Decompresses a URI encoded string
   * @param {string} str - The URI encoded string to decompress
   * @returns {string} The decompressed string
   */
  decompressFromEncodedURIComponent(str) {
    if (!str) return '';
    if (str === '') return '';
    return this._decompress(str.length, 32, this._uriDecode);
  }

  /**
   * Compresses a string
   * @param {string} str - The string to compress
   * @returns {string} The compressed string
   */
  compress(str) {
    return this._compress(str, 16, this._base64Encode);
  }

  /**
   * Decompresses a string
   * @param {string} str - The string to decompress
   * @returns {string} The decompressed string
   */
  decompress(str) {
    return this._decompress(str.length, 32768, this._base64Decode);
  }

  _base64Encode(index) {
    return this.alphabet.charAt(index);
  }

  _base64Decode(char) {
    return this.alphabet.indexOf(char);
  }

  _utf16Encode(index) {
    return String.fromCharCode(index + 32);
  }

  _utf16Decode(char) {
    return char.charCodeAt(0) - 32;
  }

  _uriEncode(index) {
    return this.utf16Alphabet.charAt(index);
  }

  _uriDecode(char) {
    return this.utf16Alphabet.indexOf(char);
  }

  _compress(string, length, encodeFunction) {
    const dictionary = {};
    const result = [];
    let word = '';
    let prevIndex = 0;
    for (let i = 0; i < string.length; i++) {
      const char = string.charAt(i);
      if (char in dictionary) {
        word += char;
        if (prevIndex in dictionary) {
          const prevWord = dictionary[prevIndex];
          const nextIndex = dictionary[char];
          const bin = this._binaryString(nextIndex - prevIndex);
          result.push(bin);
          delete dictionary[prevIndex];
          prevIndex = nextIndex;
        } else {
          const bin = this._binaryString(i - prevIndex);
          result.push(bin);
          prevIndex = i;
        }
      } else {
        if (prevIndex in dictionary) {
          const prevWord = dictionary[prevIndex];
          const nextIndex = dictionary[char];
          const bin = this._binaryString(nextIndex - prevIndex);
          result.push(bin);
          delete dictionary[prevIndex];
          prevIndex = nextIndex;
        } else {
          const bin = this._binaryString(i - prevIndex);
          result.push(bin);
          prevIndex = i;
        }
      }
      dictionary[char] = i + 1;
    }
    if (prevIndex in dictionary) {
      const prevWord = dictionary[prevIndex];
      const bin = this._binaryString(string.length - prevIndex);
      result.push(bin);
    }
    const binaryString = result.join('');
    const encodedString = binaryString.match(/.{1,6}/g).map(encodeFunction).join('');
    return encodedString;
  }

  _decompress(length, size, decodeFunction) {
    let index = 0;
    const dictionary = { '': 0 };
    const result = [];
    for (let i = 0; i < length; i += 8) {
      const bin = decodeFunction(length[i]);
      if (bin === 2) {
        return result.join('');
      }
      const binIndex = parseInt(bin, 2);
      let num = binIndex;
      let prevWord = '';
      let prevIndex = 0;
      while (num) {
        let bit;
        if (prevIndex in dictionary) {
          const prevWord = dictionary[prevIndex];
          const nextIndex = dictionary[num & 1 === 1? 'a' : 'b'];
          bit = String.fromCharCode(nextIndex - prevIndex);
          index++;
          num >>= 1;
        } else {
          const nextIndex = this._findNextIndex(dictionary, num & 1 === 1? 'a' : 'b');
          bit = nextIndex;
          index++;
          num >>= 1;
        }
        if (prevIndex in dictionary) {
          const prevWord = dictionary[prevIndex];
          const nextIndex = dictionary[num & 1 === 1? 'a' : 'b'];
          const bin = this._binaryString(nextIndex - prevIndex);
          result.push(bin);
          delete dictionary[prevIndex];
          prevIndex = nextIndex;
        } else {
          const bin = this._binaryString(i - prevIndex);
          result.push(bin);
          prevIndex = i;
        }
        dictionary[prevIndex = bit] = size;
      }
      const bin = this._binaryString(length - prevIndex);
      result.push(bin);
      prevIndex = length;
    }
    const decompressedString = result.join('');
    return decompressedString;
  }

  _binaryString(num) {
    let bin = num.toString(2);
    while (bin.length < 8) {
      bin = '0' + bin;
    }
    return bin;
  }

  _findNextIndex(dictionary, char) {
    let index = 0;
    while (index in dictionary) {
      index++;
    }
    dictionary[char] = index + 1;
    return index;
  }
}

export default LZString;

This code defines a JavaScript library called LZString for data compression and decompression using the LZ77 algorithm.

Here's a breakdown:

In essence, LZString provides a versatile and efficient way to compress and decompress data in JavaScript, supporting multiple output formats and utilizing the LZ77 algorithm for its core compression logic.