emu.squidpony.LZSEncoding Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of squidlib-util Show documentation
Show all versions of squidlib-util Show documentation
SquidLib platform-independent logic and utility code. Please refer to
https://github.com/SquidPony/SquidLib .
/*
* 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++;
}
}
}-*/;
}