001/*
002MIT License
003
004Copyright (c) 2018 Tommy Ettinger
005
006Based on lz-string4java, which is:
007Copyright (c) 2016 rufushuang
008
009Permission is hereby granted, free of charge, to any person obtaining a copy
010of this software and associated documentation files (the "Software"), to deal
011in the Software without restriction, including without limitation the rights
012to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
013copies of the Software, and to permit persons to whom the Software is
014furnished to do so, subject to the following conditions:
015
016The above copyright notice and this permission notice shall be included in all
017copies or substantial portions of the Software.
018
019THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
020IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
021FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
022AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
023LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
024OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
025SOFTWARE.
026 */
027package squidpony;
028
029/**
030 * LZ-String compression, taking Strings and compressing them to other Strings, optionally with some encryption.
031 * This role was performed by the <a href="https://github.com/tommyettinger/BlazingChain">BlazingChain library</a>,
032 * which was a dependency of squidlib-extra, but recent developments have allowed all dependencies to be removed other
033 * than SquidLib, while also probably reducing memory usage by a fair amount. The actual implementation of this class is
034 * very unusual, with the LZ-String encoding part derived from
035 * <a href="https://github.com/rufushuang/lz-string4java">rufushuang's lz-string4java</a> (which is a port of
036 * <a href="https://github.com/pieroxy/lz-string">pieroxy's lz-string</a>), while the encryption-like part (which is not
037 * very strong) was added in SquidLib. This uses {@link Garbler} to do the encryption and {@link LZSEncoding} to do the
038 * compression; LZSEncoding uses the original JavaScript lz-string library almost verbatim when run on GWT, so it
039 * performs better than code that has been compiled to JavaScript from Java by GWT, and it performs like Java when run
040 * on a real JVM. 
041 * <br>
042 * Created by Tommy Ettinger on 7/13/2017.
043 */
044public final class LZSPlus {
045
046    /**
047     * Compresses the given text using LZ-String compression; does not encrypt the result.
048     * @param uncompressedStr text to compress
049     * @return a compressed version of the given text
050     */
051    public static String compress(String uncompressedStr) {
052        return LZSEncoding.compressToUTF16(uncompressedStr);
053    }
054
055    /**
056     * Compresses the given text using LZ-String compression and encrypts (somewhat) the compressed result so it can't
057     * be read back without the same keys as a long array. Shorter long arrays give less security to encryption, though
058     * there isn't much security to begin with. You can produce a decent-quality array for this purpose with
059     * {@link Garbler#makeKeyArray(int, String)}; the size parameter could reasonably be anywhere from 2 to 32. If the
060     * keys array is null or empty, this only compresses and does not perform an additional encryption step.
061     * @param uncompressedStr text to compress and optionally encrypt
062     * @param keys the long array that will be used to encrypt the output, and will be required to decrypt the result; may be null
063     * @return a compressed and optionally encrypted version of the given text
064     */
065    public static String compress(String uncompressedStr, long[] keys) {
066        if(keys == null) return LZSEncoding.compressToUTF16(uncompressedStr);
067        if (uncompressedStr == null) return null;
068        if (uncompressedStr.isEmpty()) return " ";
069        return Garbler.garble(LZSEncoding.compressToUTF16(uncompressedStr), keys);
070    }
071    /**
072     * Decompresses text that was compressed with LZ-String compression; does not reverse decryption so it can only
073     * decompress Strings produced by {@link #compress(String)}, or {@link #compress(String, long[])} with an empty or
074     * null keys parameter.
075     * @param compressed text that was compressed by {@link #compress(String)}
076     * @return the original text, decompressed from the given text
077     */
078
079    public static String decompress(String compressed) {
080        return LZSEncoding.decompressFromUTF16(compressed);
081    }
082
083    /**
084     * Decompresses text that was compressed with LZ-String compression, reversing any encryption if the keys long array
085     * matches the long array passed to {@link #compress(String, long[])} (keys can be null if no array was passed).
086     * @param compressed text that was compressed by {@link #compress(String, long[])}
087     * @param keys the long array that was used to encrypt the output, and must match to decrypt the result; may be null
088     * @return the original text, decompressed and decrypted from compressed
089     */
090    public static String decompress(String compressed, long[] keys) {
091        if(keys == null) return LZSEncoding.decompressFromUTF16(compressed);
092        if (compressed == null) return null;
093        if (compressed.isEmpty()) return "";
094        return LZSEncoding.decompressFromUTF16(Garbler.degarble(compressed, keys));
095    }
096}