]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.fastlz/src/org/simantics/fastlz/java/FastLZImpl.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.fastlz / src / org / simantics / fastlz / java / FastLZImpl.java
diff --git a/bundles/org.simantics.fastlz/src/org/simantics/fastlz/java/FastLZImpl.java b/bundles/org.simantics.fastlz/src/org/simantics/fastlz/java/FastLZImpl.java
new file mode 100644 (file)
index 0000000..ef2ade2
--- /dev/null
@@ -0,0 +1,775 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2011 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.fastlz.java;\r
+\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class FastLZImpl {\r
+\r
+    /** 256 + 8 */\r
+    private static final int     MAX_LEN                 = 264;\r
+    private static final int     MAX_COPY                = 32;\r
+    // For level 1 compression\r
+    private static final int     MAX_DISTANCE            = 8192;\r
+    // For level 2 compression\r
+    private static final int     MAX_DISTANCE2           = 8191;\r
+    private static final int     MAX_FARDISTANCE         = (65535 + MAX_DISTANCE2 - 1);\r
+\r
+    private static final boolean FASTLZ_SAFE             = false;\r
+\r
+    public static final int      FASTLZ_VERSION          = 0x000100;\r
+\r
+    public static final int      FASTLZ_VERSION_MAJOR    = 0;\r
+    public static final int      FASTLZ_VERSION_MINOR    = 1;\r
+    public static final int      FASTLZ_VERSION_REVISION = 0;\r
+\r
+    public static final String   FASTLZ_VERSION_STRING   = "0.1.0";\r
+\r
+    private static final int     HASH_LOG                = 13;\r
+    private static final int     HASH_SIZE               = (1 << HASH_LOG);\r
+    private static final int     HASH_MASK               = (HASH_SIZE - 1);\r
+\r
+    private static final int FASTLZ_READU16(byte[] array, int offset) {\r
+        final int b0 = array[offset] & 0xff; \r
+        final int b1 = array[offset + 1] & 0xff; \r
+        return b0 | (b1 << 8);\r
+    }\r
+\r
+    /**\r
+     * @param array\r
+     * @param offset\r
+     * @return\r
+     */\r
+    private static final int HASH_FUNCTION(byte[] array, int offset) {\r
+        final int b0 = array[offset] & 0xff;\r
+        final int b1 = array[offset+1] & 0xff;\r
+        final int b2 = array[offset+2] & 0xff;\r
+        int v = b0 | (b1 << 8);\r
+        final int v2 = b1 | (b2 << 8);\r
+        v ^= v2 ^ (v >>> (16 - HASH_LOG));\r
+        v &= HASH_MASK;\r
+        return v;\r
+    }\r
+\r
+    /**\r
+      Compress a block of data in the input buffer and returns the size of \r
+      compressed block. The size of input buffer is specified by length. The \r
+      minimum input buffer size is 16.\r
+\r
+      The output buffer must be at least 5% larger than the input buffer  \r
+      and can not be smaller than 66 bytes.\r
+\r
+      If the input is not compressible, the return value might be larger than\r
+      length (input buffer size).\r
+\r
+      The input buffer and the output buffer can not overlap.\r
+    */\r
+    public static int fastlz_compress(byte[] input, int inputOffset, int length, byte[] output, int outputOffset) {\r
+        /* for short block, choose fastlz1 */\r
+        if (length < 65536)\r
+          return fastlz1_compress(input, inputOffset, length, output, outputOffset);\r
+\r
+        /* else... */\r
+        return fastlz2_compress(input, inputOffset, length, output, outputOffset);\r
+    }\r
+\r
+    /**\r
+      Decompress a block of compressed data and returns the size of the \r
+      decompressed block. If error occurs, e.g. the compressed data is \r
+      corrupted or the output buffer is not large enough, then 0 (zero) \r
+      will be returned instead.\r
+\r
+      The input buffer and the output buffer can not overlap.\r
+\r
+      Decompression is memory safe and guaranteed not to write the output buffer\r
+      more than what is specified in maxout.\r
+     */\r
+    public static int fastlz_decompress(byte[] input, int inputOffset, int length, byte[] output, int outputOffset,\r
+            int maxout) {\r
+        // magic identifier for compression level\r
+        int level = (((int) input[0] & 0xff) >>> 5) + 1;\r
+\r
+        if (level == 1)\r
+          return fastlz1_decompress(input, inputOffset, length, output, outputOffset, maxout);\r
+        if (level == 2)\r
+          return fastlz2_decompress(input, inputOffset, length, output, outputOffset, maxout);\r
+\r
+        // unknown level, trigger error\r
+        return 0;\r
+    }\r
+\r
+    /**\r
+      Compress a block of data in the input buffer and returns the size of \r
+      compressed block. The size of input buffer is specified by length. The \r
+      minimum input buffer size is 16.\r
+\r
+      The output buffer must be at least 5% larger than the input buffer  \r
+      and can not be smaller than 66 bytes.\r
+\r
+      If the input is not compressible, the return value might be larger than\r
+      length (input buffer size).\r
+\r
+      The input buffer and the output buffer can not overlap.\r
+\r
+      Compression level can be specified in parameter level. At the moment, \r
+      only level 1 and level 2 are supported.\r
+      Level 1 is the fastest compression and generally useful for short data.\r
+      Level 2 is slightly slower but it gives better compression ratio.\r
+\r
+      Note that the compressed data, regardless of the level, can always be\r
+      decompressed using the function fastlz_decompress above.\r
+    */\r
+    public static int fastlz_compress_level(int level, byte[] input, int inputOffset, int length, byte[] output,\r
+            int outputOffset) {\r
+        if (level == 1)\r
+            return fastlz1_compress(input, inputOffset, length, output, outputOffset);\r
+        if (level == 2)\r
+            return fastlz2_compress(input, inputOffset, length, output, outputOffset);\r
+\r
+        return 0;\r
+    }\r
+\r
+\r
+    private static int fastlz1_compress(byte[] input, int inputOffset, int length, byte[] output, int outputOffset) {\r
+        int ip = inputOffset;\r
+        int ip_bound = inputOffset + length - 2;\r
+        int ip_limit = inputOffset + length - 12;\r
+        int op = outputOffset;\r
+\r
+        int[] htab = new int[HASH_SIZE];\r
+        int hslot;\r
+        int hval;\r
+\r
+        int copy;\r
+\r
+        // sanity check\r
+        if(length < 4)\r
+        {\r
+            if(length != 0)\r
+            {\r
+                // create literal copy only\r
+                output[op++] = (byte) (length-1);\r
+                ip_bound++;\r
+                while(ip <= ip_bound)\r
+                    output[op++] = input[ip++];\r
+                return length+1;\r
+            }\r
+            else\r
+                return 0;\r
+        }\r
+\r
+        // initializes hash table\r
+        for (hslot = 0; hslot < HASH_SIZE; ++hslot)\r
+            htab[hslot] = ip;\r
+\r
+        // we start with literal copy\r
+        copy = 2;\r
+        output[op++] = MAX_COPY-1;\r
+        output[op++] = input[ip++];\r
+        output[op++] = input[ip++];\r
+\r
+        // main loop\r
+        while (ip < ip_limit) {\r
+            int ref;\r
+            int distance;\r
+\r
+            // minimum match length\r
+            int len = 3;\r
+\r
+            // comparison starting-point\r
+            int anchor = ip;\r
+\r
+            // find potential match\r
+            hval = HASH_FUNCTION(input, ip);\r
+            hslot = hval;\r
+            ref = htab[hval];\r
+\r
+            // calculate distance to the match\r
+            distance = anchor - ref;\r
+\r
+            // update hash table\r
+            htab[hslot] = anchor;\r
+\r
+            // is this a match? check the first 3 bytes\r
+            if (distance==0 ||\r
+                    (distance >= MAX_DISTANCE) ||\r
+                    input[ref++] != input[ip++] ||\r
+                    input[ref++] != input[ip++] ||\r
+                    input[ref++] != input[ip++])\r
+            {\r
+                output[op++] = input[anchor++];\r
+                ip = anchor;\r
+                copy++;\r
+                if (copy == MAX_COPY) {\r
+                    copy = 0;\r
+                    output[op++] = MAX_COPY-1;\r
+                }\r
+            } else {\r
+                // last matched byte\r
+                ip = anchor + len;\r
+\r
+                // distance is biased\r
+                --distance;\r
+\r
+                if(distance == 0) {\r
+                    // zero distance means a run\r
+                    byte x = input[ip - 1];\r
+                    while (ip < ip_bound)\r
+                        if (input[ref++] != x) break;\r
+                        else ip++;\r
+                } else {\r
+                    for (;;) {\r
+                        // safe because the outer check against ip limit\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        while (ip < ip_bound)\r
+                            if (input[ref++] != input[ip++]) break;\r
+                        break;\r
+                    }\r
+                }\r
+\r
+                // if we have copied something, adjust the copy count\r
+                if (copy != 0)\r
+                    // copy is biased, '0' means 1 byte copy\r
+                    output[op-copy-1] = (byte)(copy-1);\r
+                else\r
+                    // back, to overwrite the copy count\r
+                    op--;\r
+\r
+                // reset literal counter\r
+                copy = 0;\r
+\r
+                // length is biased, '1' means a match of 3 bytes\r
+                ip -= 3;\r
+                len = ip - anchor;\r
+\r
+                // encode the match\r
+                if (len > MAX_LEN - 2)\r
+                    while (len > MAX_LEN - 2) {\r
+                        output[op++] = (byte) ((7 << 5) + (distance >>> 8));\r
+                        output[op++] = (byte) (MAX_LEN - 2 - 7 -2); \r
+                        output[op++] = (byte) (distance & 0xff);\r
+                        len -= MAX_LEN - 2;\r
+                    }\r
+\r
+                if (len < 7) {\r
+                    output[op++] = (byte) ((len << 5) + (distance >>> 8));\r
+                    output[op++] = (byte) (distance & 255);\r
+                } else {\r
+                    output[op++] = (byte) ((7 << 5) + (distance >>> 8));\r
+                    output[op++] = (byte) (len - 7);\r
+                    output[op++] = (byte) (distance & 255);\r
+                }\r
+\r
+                // update the hash at match boundary\r
+                hval = HASH_FUNCTION(input, ip);\r
+                htab[hval] = ip++;\r
+                hval = HASH_FUNCTION(input, ip);\r
+                htab[hval] = ip++;\r
+\r
+                // assuming literal copy\r
+                output[op++] = MAX_COPY-1;\r
+            }\r
+        }\r
+\r
+        // left-over as literal copy\r
+        ip_bound++;\r
+        while (ip <= ip_bound) {\r
+            output[op++] = input[ip++];\r
+            copy++;\r
+            if (copy == MAX_COPY) {\r
+                copy = 0;\r
+                output[op++] = MAX_COPY-1;\r
+            }\r
+        }\r
+\r
+        // if we have copied something, adjust the copy length\r
+        if (copy != 0)\r
+            output[op - copy - 1] = (byte) (copy - 1);\r
+        else\r
+            --op;\r
+\r
+        return op - outputOffset;\r
+    }\r
+\r
+    private static int fastlz2_compress(byte[] input, int inputOffset, int length, byte[] output, int outputOffset)\r
+    {\r
+        int ip = inputOffset;\r
+        int ip_bound = ip + length - 2;\r
+        int ip_limit = ip + length - 12;\r
+        int op = outputOffset;\r
+\r
+        int[] htab = new int[HASH_SIZE];\r
+        int hslot;\r
+        int hval;\r
+\r
+        int copy;\r
+\r
+        // sanity check\r
+        if (length < 4) {\r
+            if (length != 0) {\r
+                // create literal copy only\r
+                output[op++] = (byte) (length - 1);\r
+                ip_bound++;\r
+                while(ip <= ip_bound)\r
+                    output[op++] = input[ip++];\r
+                return length + 1;\r
+            } else\r
+                return 0;\r
+        }\r
+\r
+        // initializes hash table\r
+        for (hslot = 0; hslot < HASH_SIZE; ++hslot)\r
+            htab[hslot] = ip;\r
+\r
+        // we start with literal copy\r
+        copy = 2;\r
+        output[op++] = MAX_COPY-1;\r
+        output[op++] = input[ip++];\r
+        output[op++] = input[ip++];\r
+\r
+        // main loop\r
+        while (ip < ip_limit) {\r
+            int ref;\r
+            int distance;\r
+\r
+            // minimum match length\r
+            int len = 3;\r
+\r
+            // comparison starting-point\r
+            int anchor = ip;\r
+\r
+            // check for a run\r
+            if (input[ip] == input[ip-1] && FASTLZ_READU16(input, ip-1) == FASTLZ_READU16(input, ip+1)) {\r
+                distance = 1;\r
+                ip += 3;\r
+                ref = anchor - 1 + 3;\r
+\r
+                // NOTE: cannot do this goto, so copied the code here from line 487\r
+                //goto match;\r
+\r
+                // last matched byte\r
+                ip = anchor + len;\r
+\r
+                // distance is biased\r
+                --distance;\r
+\r
+                if (distance == 0) {\r
+                    // zero distance means a run\r
+                    byte x = input[ip - 1];\r
+                    while (ip < ip_bound)\r
+                        if (input[ref++] != x) break;\r
+                        else ++ip;\r
+                } else {\r
+                    for (;;) {\r
+                        // safe because the outer check against ip limit\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        while (ip < ip_bound)\r
+                            if (input[ref++] != input[ip++]) break;\r
+                        break;\r
+                    }\r
+                }\r
+\r
+                // if we have copied something, adjust the copy count\r
+                if (copy != 0)\r
+                    // copy is biased, '0' means 1 byte copy\r
+                    output[op - copy - 1] = (byte) (copy - 1);\r
+                else\r
+                    // back, to overwrite the copy count\r
+                    --op;\r
+\r
+                // reset literal counter\r
+                copy = 0;\r
+\r
+                // length is biased, '1' means a match of 3 bytes\r
+                ip -= 3;\r
+                len = ip - anchor;\r
+\r
+                // encode the match\r
+                if (distance < MAX_DISTANCE2) {\r
+                    if (len < 7) {\r
+                        output[op++] = (byte) ((len << 5) + (distance >>> 8));\r
+                        output[op++] = (byte) (distance & 255);\r
+                    } else {\r
+                        output[op++] = (byte) ((7 << 5) + (distance >>> 8));\r
+                        for (len -= 7; len >= 255; len -= 255)\r
+                            output[op++] = (byte) 255;\r
+                        output[op++] = (byte) len;\r
+                        output[op++] = (byte) (distance & 255);\r
+                    }\r
+                } else {\r
+                    // far away, but not yet in the another galaxy...\r
+                    if(len < 7) {\r
+                        distance -= MAX_DISTANCE2;\r
+                        output[op++] = (byte) ((len << 5) + 31);\r
+                        output[op++] = (byte) 255;\r
+                        output[op++] = (byte) (distance >>> 8);\r
+                        output[op++] = (byte) (distance & 255);\r
+                    } else {\r
+                        distance -= MAX_DISTANCE2;\r
+                        output[op++] = (byte) ((7 << 5) + 31);\r
+                        for (len -=7; len >= 255; len -= 255)\r
+                            output[op++] = (byte) 255;\r
+                        output[op++] = (byte) len;\r
+                        output[op++] = (byte) 255;\r
+                        output[op++] = (byte) (distance >>> 8);\r
+                        output[op++] = (byte) (distance & 255);\r
+                    }\r
+                }\r
+\r
+                // update the hash at match boundary\r
+                hval = HASH_FUNCTION(input, ip);\r
+                htab[hval] = ip++;\r
+                hval = HASH_FUNCTION(input, ip);\r
+                htab[hval] = ip++;\r
+\r
+                // assuming literal copy\r
+                output[op++] = MAX_COPY-1;\r
+\r
+                continue;\r
+            }\r
+\r
+            // find potential match\r
+            hval = HASH_FUNCTION(input, ip);\r
+            hslot = hval;\r
+            ref = htab[hval];\r
+\r
+            // calculate distance to the match\r
+            distance = anchor - ref;\r
+\r
+            // update hash table\r
+            htab[hslot] = anchor;\r
+\r
+            // is this a match? check the first 3 bytes\r
+            if (distance==0 || \r
+                    (distance >= MAX_FARDISTANCE) ||\r
+                    input[ref++] != input[ip++] ||\r
+                    input[ref++] != input[ip++] ||\r
+                    input[ref++] != input[ip++])\r
+            {\r
+                output[op++] = input[anchor++];\r
+                ip = anchor;\r
+                copy++;\r
+                if (copy == MAX_COPY) {\r
+                    copy = 0;\r
+                    output[op++] = MAX_COPY-1;\r
+                }\r
+            } else {\r
+                // far, needs at least 5-byte match\r
+                if (distance >= MAX_DISTANCE2) {\r
+                    if (input[ip++] != input[ref++] || input[ip++] != input[ref++]) {\r
+                        // LITERAL COPY, code copied from above\r
+                        output[op++] = input[anchor++];\r
+                        ip = anchor;\r
+                        copy++;\r
+                        if (copy == MAX_COPY) {\r
+                            copy = 0;\r
+                            output[op++] = MAX_COPY-1;\r
+                        }\r
+                        continue;\r
+                    }\r
+                    len += 2;\r
+                }\r
+\r
+                // NOTE: all of the following code within this block is copied above\r
+                // to line 357\r
+                //match:\r
+\r
+                // last matched byte\r
+                ip = anchor + len;\r
+\r
+                // distance is biased\r
+                --distance;\r
+\r
+                if (distance == 0) {\r
+                    // zero distance means a run\r
+                    byte x = input[ip - 1];\r
+                    while (ip < ip_bound)\r
+                        if (input[ref++] != x) break;\r
+                        else ++ip;\r
+                } else {\r
+                    for (;;) {\r
+                        // safe because the outer check against ip limit\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        if(input[ref++] != input[ip++]) break;\r
+                        while (ip < ip_bound)\r
+                            if (input[ref++] != input[ip++]) break;\r
+                        break;\r
+                    }\r
+                }\r
+\r
+                // if we have copied something, adjust the copy count\r
+                if (copy != 0)\r
+                    // copy is biased, '0' means 1 byte copy\r
+                    output[op - copy - 1] = (byte) (copy - 1);\r
+                else\r
+                    // back, to overwrite the copy count\r
+                    --op;\r
+\r
+                // reset literal counter\r
+                copy = 0;\r
+\r
+                // length is biased, '1' means a match of 3 bytes\r
+                ip -= 3;\r
+                len = ip - anchor;\r
+\r
+                // encode the match\r
+                if (distance < MAX_DISTANCE2) {\r
+                    if (len < 7) {\r
+                        output[op++] = (byte) ((len << 5) + (distance >>> 8));\r
+                        output[op++] = (byte) (distance & 255);\r
+                    } else {\r
+                        output[op++] = (byte) ((7 << 5) + (distance >>> 8));\r
+                        for (len -= 7; len >= 255; len -= 255)\r
+                            output[op++] = (byte) 255;\r
+                        output[op++] = (byte) len;\r
+                        output[op++] = (byte) (distance & 255);\r
+                    }\r
+                } else {\r
+                    // far away, but not yet in the another galaxy...\r
+                    if(len < 7) {\r
+                        distance -= MAX_DISTANCE2;\r
+                        output[op++] = (byte) ((len << 5) + 31);\r
+                        output[op++] = (byte) 255;\r
+                        output[op++] = (byte) (distance >>> 8);\r
+                        output[op++] = (byte) (distance & 255);\r
+                    } else {\r
+                        distance -= MAX_DISTANCE2;\r
+                        output[op++] = (byte) ((7 << 5) + 31);\r
+                        for (len -=7; len >= 255; len -= 255)\r
+                            output[op++] = (byte) 255;\r
+                        output[op++] = (byte) len;\r
+                        output[op++] = (byte) 255;\r
+                        output[op++] = (byte) (distance >>> 8);\r
+                        output[op++] = (byte) (distance & 255);\r
+                    }\r
+                }\r
+\r
+                // update the hash at match boundary\r
+                hval = HASH_FUNCTION(input, ip);\r
+                htab[hval] = ip++;\r
+                hval = HASH_FUNCTION(input, ip);\r
+                htab[hval] = ip++;\r
+\r
+                // assuming literal copy\r
+                output[op++] = MAX_COPY-1;\r
+            }\r
+        }\r
+\r
+        // left-over as literal copy\r
+        ip_bound++;\r
+        while (ip <= ip_bound) {\r
+            output[op++] = input[ip++];\r
+            copy++;\r
+            if (copy == MAX_COPY) {\r
+                copy = 0;\r
+                output[op++] = MAX_COPY-1;\r
+            }\r
+        }\r
+\r
+        // if we have copied something, adjust the copy length\r
+        if (copy != 0)\r
+            output[op-copy-1] = (byte) (copy - 1);\r
+        else\r
+            op--;\r
+\r
+        // marker for fastlz2\r
+        output[outputOffset] |= (1 << 5);\r
+\r
+        return op - outputOffset;\r
+    }\r
+\r
+    // ------------------------------------------------------------------------\r
+\r
+    private static int fastlz1_decompress(byte[] input, int inputOffset, int length, byte[] output, int outputOffset,\r
+            int maxout) {\r
+        int ip = inputOffset;\r
+        int ip_limit  = ip + length;\r
+        int op = outputOffset;\r
+        int op_limit = op + maxout;\r
+        int ctrl = (int) input[ip++] & 31;\r
+        boolean loop = true;\r
+\r
+        do {\r
+            int ref = op;\r
+            int len = ctrl >>> 5;\r
+            int ofs = (ctrl & 31) << 8;\r
+\r
+            if (ctrl >= 32) {\r
+                len--;\r
+                ref -= ofs;\r
+                if (len == 7-1)\r
+                    len += (int) input[ip++] & 0xff;\r
+                ref -= (int) input[ip++] & 0xff;\r
+\r
+                if (FASTLZ_SAFE) {\r
+                    if (op + len + 3 > op_limit)\r
+                        return 0;\r
+\r
+                    if (ref-1 < outputOffset)\r
+                        return 0;\r
+                }\r
+\r
+                if (ip < ip_limit)\r
+                    ctrl = (int) input[ip++] & 0xff;\r
+                else\r
+                    loop = false;\r
+\r
+                if (ref == op) {\r
+                    // optimize copy for a run\r
+                    byte b = output[ref-1];\r
+                    output[op++] = b;\r
+                    output[op++] = b;\r
+                    output[op++] = b;\r
+                    for(; len > 0; --len)\r
+                        output[op++] = b;\r
+                } else {\r
+                    /* copy from reference */\r
+                    ref--;\r
+                    output[op++] = output[ref++];\r
+                    output[op++] = output[ref++];\r
+                    output[op++] = output[ref++];\r
+                    for (; len > 0; --len)\r
+                        output[op++] = output[ref++];\r
+                }\r
+            } else {\r
+                ctrl++;\r
+                if (FASTLZ_SAFE) {\r
+                    if (op + ctrl > op_limit)\r
+                        return 0;\r
+                    if (ip + ctrl > ip_limit)\r
+                        return 0;\r
+                }\r
+\r
+                output[op++] = input[ip++];\r
+                for (--ctrl; ctrl > 0; --ctrl)\r
+                    output[op++] = input[ip++];\r
+\r
+                loop = ip < ip_limit;\r
+                if (loop)\r
+                    ctrl = (int) input[ip++] & 0xff;\r
+            }\r
+        } while(loop);\r
+\r
+        return op - outputOffset;\r
+    }\r
+\r
+    private static int fastlz2_decompress(byte[] input, int inputOffset, int length, byte[] output, int outputOffset,\r
+            int maxout) {\r
+        int ip = inputOffset;\r
+        int ip_limit = ip + length;\r
+        int op = outputOffset;\r
+        int op_limit = op + maxout;\r
+        int ctrl = (int) input[ip++] & 31;\r
+        boolean loop = true;\r
+\r
+        do {\r
+            int ref = op;\r
+            int len = ctrl >>> 5;\r
+            int ofs = (ctrl & 31) << 8;\r
+\r
+            if (ctrl >= 32) {\r
+                int code;\r
+                len--;\r
+                ref -= ofs;\r
+                if (len == 7-1) {\r
+                    do {\r
+                        code = (int) input[ip++] & 0xff;\r
+                        len += code;\r
+                    } while (code==255);\r
+                }\r
+                code = (int) input[ip++] & 0xff;\r
+                ref -= code;\r
+\r
+                // match from 16-bit distance\r
+                if (code == 255) {\r
+                    if (ofs == (31 << 8)) {\r
+                        ofs = ((int) input[ip++] & 0xff) << 8;\r
+                        ofs += (int) input[ip++] & 0xff;\r
+                        ref = op - ofs - MAX_DISTANCE2;\r
+                    }\r
+                }\r
+\r
+                if (FASTLZ_SAFE) {\r
+                    if (op + len + 3 > op_limit)\r
+                        return 0;\r
+\r
+                    if (ref-1 < outputOffset)\r
+                        return 0;\r
+                }\r
+\r
+                if (ip < ip_limit)\r
+                    ctrl = (int) input[ip++] & 0xff;\r
+                else\r
+                    loop = false;\r
+\r
+                if (ref == op) {\r
+                    /* optimize copy for a run */\r
+                    byte b = output[ref-1];\r
+                    output[op++] = b;\r
+                    output[op++] = b;\r
+                    output[op++] = b;\r
+                    for (; len > 0; --len)\r
+                        output[op++] = b;\r
+                } else {\r
+                    /* copy from reference */\r
+                    --ref;\r
+                    output[op++] = output[ref++];\r
+                    output[op++] = output[ref++];\r
+                    output[op++] = output[ref++];\r
+                    for (; len > 0; --len)\r
+                        output[op++] = output[ref++];\r
+                }\r
+            } else {\r
+                ++ctrl;\r
+                if (FASTLZ_SAFE) {\r
+                    if (op + ctrl > op_limit)\r
+                        return 0;\r
+                    if (ip + ctrl > ip_limit)\r
+                        return 0;\r
+                }\r
+\r
+                output[op++] = input[ip++]; \r
+                for (--ctrl; ctrl > 0; --ctrl)\r
+                    output[op++] = input[ip++];\r
+\r
+                loop = ip < ip_limit;\r
+                if (loop)\r
+                    ctrl = (int) input[ip++] & 0xff;\r
+            }\r
+        } while (loop);\r
+\r
+        return op - outputOffset;\r
+    }\r
+\r
+}\r