All Downloads are FREE. Search and download functionalities are using the official Maven repository.

emu.squidpony.LZSEncoding Maven / Gradle / Ivy

Go to download

SquidLib platform-independent logic and utility code. Please refer to https://github.com/SquidPony/SquidLib .

There is a newer version: 3.0.6
Show newest version
/*
 * LZString4Java By Rufus Huang
 * https://github.com/rufushuang/lz-string4java
 * MIT License
 *
 * Port from original JavaScript version by pieroxy
 * https://github.com/pieroxy/lz-string
 */

package squidpony;

public final class LZSEncoding {

    private LZSEncoding() {};
    private static final String keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
            keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",
            valStrBase64 = new String(new char[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 64, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                    0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}),
            valStrUriSafe = new String(new char[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 62, 0, 63, 0, 0,
                    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                    0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51});

    /**
     * Compress a String using LZ-String encoding but only using Base64 characters ('A'-'Z', 'a'-'z', '0'-'9', '+', '/',
     * and '=' for Base64 validity).
     * @param uncompressed an uncompressed String to encode
     * @return the encoded, compressed String
     */
    public static String compressToBase64(String uncompressed) {
        if (uncompressed == null)
            return null;
        String res = _compress(uncompressed, 6, keyStrBase64, 0);
        switch (res.length() & 3) { // 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 + "=";
        }
    }

    /**
     * Decompresses a String that had been compressed with {@link #compressToBase64(String)}.
     * @param compressed a Base64-encoded, compressed String
     * @return the original uncompressed version of the String
     */
    public static String decompressFromBase64(String compressed) {
        if (compressed == null)
            return null;
        if (compressed.isEmpty())
            return "";
        // function(index) { return getBaseValue(keyStrBase64,
        // input.charAt(index)); }
        return _decompress(compressed.length(), 32, compressed, valStrBase64, 0);
    }

    /**
     * Compresses a String using the properties of UTF-16 encoding to store approximately 15 bits of LZW-compressed text
     * in each 2-byte Unicode character, which does particularly well with ASCII text and can be smaller than UTF-8 in
     * some cases, especially where each char must be stored as UTF-16, e.g. Java Strings or browser-based LocalStorage.
     * @param uncompressed an uncompressed String to encode
     * @return the encoded, compressed String
     */
    public static String compressToUTF16(String uncompressed) {
        if (uncompressed == null)
            return null;
        return _compress(uncompressed, 15, null, 32) + " ";
    }
    /**
     * Decompresses a String that had been compressed with {@link #compressToUTF16(String)}.
     * @param compressed a UTF16-encoded (as by {@link #compressToUTF16(String)}), compressed String
     * @return the original uncompressed version of the String
     */
    public static String decompressFromUTF16(String compressed) {
        if (compressed == null)
            return null;
        if (compressed.isEmpty())
            return "";
        return _decompress(compressed.length(), 16384, compressed, null, -32);
    }

    /**
     * Compress a String using LZ-String encoding but only using valid URI component characters ('A'-'Z', 'a'-'z',
     * '0'-'9', '+', '-', and possibly '$').
     * @param uncompressed an uncompressed String to encode
     * @return the encoded, compressed String
     */
    public static String compressToEncodedURIComponent(String uncompressed) {
        if (uncompressed == null)
            return null;
        return _compress(uncompressed, 6, keyStrUriSafe, 0) + '+';
    }
    /**
     * Decompresses a String that had been compressed with {@link #compressToEncodedURIComponent(String)}.
     * @param compressed a URI-encoded, compressed String
     * @return the original uncompressed version of the String
     */
    public static String decompressFromEncodedURIComponent(String compressed) {
        if (compressed == null) return null;
        if (compressed.isEmpty()) return "";
        return _decompress(compressed.length(), 32, compressed, valStrUriSafe, 0);
    }

    /**
     * Compresses a String as tightly as possible by using 16 bits of each 16-bit character, which can infrequently
     * result in invalid UTF-16 codepoints, but that may not matter for all applications.
     * @param uncompressed an uncompressed String to encode
     * @return the encoded, compressed String
     */
    public static String compress(String uncompressed) {
        return _compress(uncompressed, 16, null, 0);
    }

    private native static String _compress(String uncompressed, int bitsPerChar, String mapping, int offset) /*-{
    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,
        getCharFromInt;
    if(mapping == null) {
        getCharFromInt = function(a){ return String.fromCharCode(a + offset); };
    }
    else
    {
        getCharFromInt = function(a){ return mapping.charAt(a); };
    }

    for (ii = 0; ii < uncompressed.length; ii += 1) {
      context_c = uncompressed.charAt(ii);
      if (!Object.prototype.hasOwnProperty.call(context_dictionary,context_c)) {
        context_dictionary[context_c] = context_dictSize++;
        context_dictionaryToCreate[context_c] = true;
      }

      context_wc = context_w + context_c;
      if (Object.prototype.hasOwnProperty.call(context_dictionary,context_wc)) {
        context_w = context_wc;
      } else {
        if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
          if (context_w.charCodeAt(0)<256) {
            for (i=0 ; i> 1;
            }
          } else {
            value = 1;
            for (i=0 ; i> 1;
            }
          }
          context_enlargeIn--;
          if (context_enlargeIn == 0) {
            context_enlargeIn = (1 << context_numBits);
            context_numBits++;
          }
          delete context_dictionaryToCreate[context_w];
        } else {
          value = context_dictionary[context_w];
          for (i=0 ; i> 1;
          }


        }
        context_enlargeIn--;
        if (context_enlargeIn == 0) {
          context_enlargeIn = (1 << context_numBits);
          context_numBits++;
        }
        // Add wc to the dictionary.
        context_dictionary[context_wc] = context_dictSize++;
        context_w = String(context_c);
      }
    }

    // Output the code for w.
    if (context_w !== "") {
      if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
        if (context_w.charCodeAt(0)<256) {
          for (i=0 ; i> 1;
          }
        } else {
          value = 1;
          for (i=0 ; i> 1;
          }
        }
        context_enlargeIn--;
        if (context_enlargeIn == 0) {
          context_enlargeIn = (1 << context_numBits);
          context_numBits++;
        }
        delete context_dictionaryToCreate[context_w];
      } else {
        value = context_dictionary[context_w];
        for (i=0 ; i> 1;
        }


      }
      context_enlargeIn--;
      if (context_enlargeIn == 0) {
        context_enlargeIn = (1 << 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('');
}-*/;
    /**
     * Decompresses a String that had been compressed with {@link #compress(String)}.
     * @param compressed a compressed String using the default encoding from {@link #compress(String)}
     * @return the original uncompressed version of the String
     */
    public static String decompress(final String compressed) {
        if (compressed == null)
            return null;
        if (compressed.isEmpty())
            return "";
        return _decompress(compressed.length(), 32768, compressed, null, 0);
    }

    //val = (modify == null) ? (char) (getNextValue[0] + offset) : modify[getNextValue[0]];
    private native static String _decompress(int length, int resetValue, String comp, String modify, int offset) /*-{
    var dictionary = [],
        next,
        enlargeIn = 4,
        dictSize = 4,
        numBits = 3,
        entry = "",
        result = [],
        i,
        w,
        bits, resb, maxpower, power,
        c,
        getNextValue;
    if(modify == null){
        getNextValue = function(index) { return comp.charCodeAt(index) + offset; };
    }
    else {
        getNextValue = function(index) { return modify.charCodeAt(comp.charCodeAt(index)); };
    }
    var data = {val:getNextValue(0), position:resetValue, index:1};

    for (i = 0; i < 3; i += 1) {
      dictionary[i] = i;
    }

    bits = 0;
    maxpower = 4;
    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 = 256;
          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 = String.fromCharCode(bits);
        break;
      case 1:
          bits = 0;
          maxpower = 65536;
          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 = String.fromCharCode(bits);
        break;
      case 2:
        return "";
    }
    dictionary[3] = c;
    w = c;
    result.push(c);
    while (true) {
      if (data.index > length) {
        return "";
      }

      bits = 0;
      maxpower = (1 << 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 = 256;
          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++] = String.fromCharCode(bits);
          c = dictSize-1;
          enlargeIn--;
          break;
        case 1:
          bits = 0;
          maxpower = 65536;
          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 ? power : 0);
            power <<= 1;
          }
          dictionary[dictSize++] = String.fromCharCode(bits);
          c = dictSize-1;
          enlargeIn--;
          break;
        case 2:
          return result.join('');
      }

      if (enlargeIn == 0) {
        enlargeIn = (1 << 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 = (1 << numBits);
        numBits++;
      }
    }
}-*/;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy