]> source.dussan.org Git - poi.git/commitdiff
svn propset svn:eol-style native
authorJaven O'Neal <onealj@apache.org>
Sun, 10 Apr 2016 14:05:33 +0000 (14:05 +0000)
committerJaven O'Neal <onealj@apache.org>
Sun, 10 Apr 2016 14:05:33 +0000 (14:05 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1738435 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/util/RLEDecompressingInputStream.java

index c482fc6cd55045f9fbfc6180bc8f889159cbb1df..0090ebb3f8d3087ed694979da1a0a591bc19f2b5 100644 (file)
-/* ====================================================================\r
-   Licensed to the Apache Software Foundation (ASF) under one or more\r
-   contributor license agreements.  See the NOTICE file distributed with\r
-   this work for additional information regarding copyright ownership.\r
-   The ASF licenses this file to You under the Apache License, Version 2.0\r
-   (the "License"); you may not use this file except in compliance with\r
-   the License.  You may obtain a copy of the License at\r
-\r
-       http://www.apache.org/licenses/LICENSE-2.0\r
-\r
-   Unless required by applicable law or agreed to in writing, software\r
-   distributed under the License is distributed on an "AS IS" BASIS,\r
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-   See the License for the specific language governing permissions and\r
-   limitations under the License.\r
-==================================================================== */\r
-\r
-package org.apache.poi.util;\r
-\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-\r
-/**\r
- * Wrapper of InputStream which provides Run Length Encoding (RLE) \r
- *  decompression on the fly. Uses MS-OVBA decompression algorithm. See\r
- * http://download.microsoft.com/download/2/4/8/24862317-78F0-4C4B-B355-C7B2C1D997DB/[MS-OVBA].pdf\r
- */\r
-public class RLEDecompressingInputStream extends InputStream {\r
-\r
-    /**\r
-     * Bitmasks for performance\r
-     */\r
-    private static final int[] POWER2 = new int[] { 0x0001, // 0\r
-            0x0002, // 1\r
-            0x0004, // 2\r
-            0x0008, // 3\r
-            0x0010, // 4\r
-            0x0020, // 5\r
-            0x0040, // 6\r
-            0x0080, // 7\r
-            0x0100, // 8\r
-            0x0200, // 9\r
-            0x0400, // 10\r
-            0x0800, // 11\r
-            0x1000, // 12\r
-            0x2000, // 13\r
-            0x4000, // 14\r
-            0x8000 // 15\r
-    };\r
-\r
-    /** the wrapped inputstream */\r
-    private InputStream in;\r
-\r
-    /** a byte buffer with size 4096 for storing a single chunk */\r
-    private byte[] buf;\r
-\r
-    /** the current position in the byte buffer for reading */\r
-    private int pos;\r
-\r
-    /** the number of bytes in the byte buffer */\r
-    private int len;\r
-\r
-    /**\r
-     * Creates a new wrapper RLE Decompression InputStream.\r
-     * \r
-     * @param in\r
-     * @throws IOException\r
-     */\r
-    public RLEDecompressingInputStream(InputStream in) throws IOException {\r
-        this.in = in;\r
-        buf = new byte[4096];\r
-        pos = 0;\r
-        int header = in.read();\r
-        if (header != 0x01) {\r
-            throw new IllegalArgumentException(String.format("Header byte 0x01 expected, received 0x%02X", header & 0xFF));\r
-        }\r
-        len = readChunk();\r
-    }\r
-\r
-    @Override\r
-    public int read() throws IOException {\r
-        if (len == -1) {\r
-            return -1;\r
-        }\r
-        if (pos >= len) {\r
-            if ((len = readChunk()) == -1) {\r
-                return -1;\r
-            }\r
-        }\r
-        return buf[pos++];\r
-    }\r
-\r
-    @Override\r
-    public int read(byte[] b) throws IOException {\r
-        return read(b, 0, b.length);\r
-    }\r
-\r
-    @Override\r
-    public int read(byte[] b, int off, int l) throws IOException {\r
-        if (len == -1) {\r
-            return -1;\r
-        }\r
-        int offset = off;\r
-        int length = l;\r
-        while (length > 0) {\r
-            if (pos >= len) {\r
-                if ((len = readChunk()) == -1) {\r
-                    return offset > off ? offset - off : -1;\r
-                }\r
-            }\r
-            int c = Math.min(length, len - pos);\r
-            System.arraycopy(buf, pos, b, offset, c);\r
-            pos += c;\r
-            length -= c;\r
-            offset += c;\r
-        }\r
-        return l;\r
-    }\r
-\r
-    @Override\r
-    public long skip(long n) throws IOException {\r
-        long length = n;\r
-        while (length > 0) {\r
-            if (pos >= len) {\r
-                if ((len = readChunk()) == -1) {\r
-                    return -1;\r
-                }\r
-            }\r
-            int c = (int) Math.min(n, len - pos);\r
-            pos += c;\r
-            length -= c;\r
-        }\r
-        return n;\r
-    }\r
-\r
-    @Override\r
-    public int available() {\r
-        return (len > 0 ? len - pos : 0);\r
-    }\r
-\r
-    @Override\r
-    public void close() throws IOException {\r
-        in.close();\r
-    }\r
-\r
-    /**\r
-     * Reads a single chunk from the underlying inputstream.\r
-     * \r
-     * @return\r
-     * @throws IOException\r
-     */\r
-    private int readChunk() throws IOException {\r
-        pos = 0;\r
-        int w = readShort(in);\r
-        if (w == -1) {\r
-            return -1;\r
-        }\r
-        int chunkSize = (w & 0x0FFF) + 1; // plus 3 bytes minus 2 for the length\r
-        if ((w & 0x7000) != 0x3000) {\r
-            throw new IllegalArgumentException(String.format("Chunksize header A should be 0x3000, received 0x%04X", w & 0xE000));\r
-        }\r
-        boolean rawChunk = (w & 0x8000) == 0;\r
-        if (rawChunk) {\r
-            if (in.read(buf, 0, chunkSize) < chunkSize) {\r
-                throw new IllegalStateException(String.format("Not enough bytes read, expected %d", chunkSize));\r
-            }\r
-            return chunkSize;\r
-        } else {\r
-            int inOffset = 0;\r
-            int outOffset = 0;\r
-            while (inOffset < chunkSize) {\r
-                int tokenFlags = in.read();\r
-                inOffset++;\r
-                if (tokenFlags == -1) {\r
-                    break;\r
-                }\r
-                for (int n = 0; n < 8; n++) {\r
-                    if (inOffset >= chunkSize) {\r
-                        break;\r
-                    }\r
-                    if ((tokenFlags & POWER2[n]) == 0) {\r
-                        // literal\r
-                        final int b = in.read();\r
-                        if (b == -1) {\r
-                            return -1;\r
-                        }\r
-                        buf[outOffset++] = (byte) b;\r
-                        inOffset++;\r
-                    } else {\r
-                        // compressed token\r
-                        int token = readShort(in);\r
-                        if (token == -1) {\r
-                            return -1;\r
-                        }\r
-                        inOffset += 2;\r
-                        int copyLenBits = getCopyLenBits(outOffset - 1);\r
-                        int copyOffset = (token >> (copyLenBits)) + 1;\r
-                        int copyLen = (token & (POWER2[copyLenBits] - 1)) + 3;\r
-                        int startPos = outOffset - copyOffset;\r
-                        int endPos = startPos + copyLen;\r
-                        for (int i = startPos; i < endPos; i++) {\r
-                            buf[outOffset++] = buf[i];\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-            return outOffset;\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Helper method to determine how many bits in the CopyToken are used for the CopyLength.\r
-     * \r
-     * @param offset\r
-     * @return\r
-     */\r
-    static int getCopyLenBits(int offset) {\r
-        for (int n = 11; n >= 4; n--) {\r
-            if ((offset & POWER2[n]) != 0) {\r
-                return 15 - n;\r
-            }\r
-        }\r
-        return 12;\r
-    }\r
-\r
-    /**\r
-     * Convenience method for read a 2-bytes short in little endian encoding.\r
-     * \r
-     * @return\r
-     * @throws IOException\r
-     */\r
-    public int readShort() throws IOException {\r
-        return readShort(this);\r
-    }\r
-\r
-    /**\r
-     * Convenience method for read a 4-bytes int in little endian encoding.\r
-     * \r
-     * @return\r
-     * @throws IOException\r
-     */\r
-    public int readInt() throws IOException {\r
-        return readInt(this);\r
-    }\r
-\r
-    private int readShort(InputStream stream) throws IOException {\r
-        int b0, b1;\r
-        if ((b0 = stream.read()) == -1) {\r
-            return -1;\r
-        }\r
-        if ((b1 = stream.read()) == -1) {\r
-            return -1;\r
-        }\r
-        return (b0 & 0xFF) | ((b1 & 0xFF) << 8);\r
-    }\r
-\r
-    private int readInt(InputStream stream) throws IOException {\r
-        int b0, b1, b2, b3;\r
-        if ((b0 = stream.read()) == -1) {\r
-            return -1;\r
-        }\r
-        if ((b1 = stream.read()) == -1) {\r
-            return -1;\r
-        }\r
-        if ((b2 = stream.read()) == -1) {\r
-            return -1;\r
-        }\r
-        if ((b3 = stream.read()) == -1) {\r
-            return -1;\r
-        }\r
-        return (b0 & 0xFF) | ((b1 & 0xFF) << 8) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 24);\r
-    }\r
-}\r
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Wrapper of InputStream which provides Run Length Encoding (RLE) 
+ *  decompression on the fly. Uses MS-OVBA decompression algorithm. See
+ * http://download.microsoft.com/download/2/4/8/24862317-78F0-4C4B-B355-C7B2C1D997DB/[MS-OVBA].pdf
+ */
+public class RLEDecompressingInputStream extends InputStream {
+
+    /**
+     * Bitmasks for performance
+     */
+    private static final int[] POWER2 = new int[] { 0x0001, // 0
+            0x0002, // 1
+            0x0004, // 2
+            0x0008, // 3
+            0x0010, // 4
+            0x0020, // 5
+            0x0040, // 6
+            0x0080, // 7
+            0x0100, // 8
+            0x0200, // 9
+            0x0400, // 10
+            0x0800, // 11
+            0x1000, // 12
+            0x2000, // 13
+            0x4000, // 14
+            0x8000 // 15
+    };
+
+    /** the wrapped inputstream */
+    private InputStream in;
+
+    /** a byte buffer with size 4096 for storing a single chunk */
+    private byte[] buf;
+
+    /** the current position in the byte buffer for reading */
+    private int pos;
+
+    /** the number of bytes in the byte buffer */
+    private int len;
+
+    /**
+     * Creates a new wrapper RLE Decompression InputStream.
+     * 
+     * @param in
+     * @throws IOException
+     */
+    public RLEDecompressingInputStream(InputStream in) throws IOException {
+        this.in = in;
+        buf = new byte[4096];
+        pos = 0;
+        int header = in.read();
+        if (header != 0x01) {
+            throw new IllegalArgumentException(String.format("Header byte 0x01 expected, received 0x%02X", header & 0xFF));
+        }
+        len = readChunk();
+    }
+
+    @Override
+    public int read() throws IOException {
+        if (len == -1) {
+            return -1;
+        }
+        if (pos >= len) {
+            if ((len = readChunk()) == -1) {
+                return -1;
+            }
+        }
+        return buf[pos++];
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int read(byte[] b, int off, int l) throws IOException {
+        if (len == -1) {
+            return -1;
+        }
+        int offset = off;
+        int length = l;
+        while (length > 0) {
+            if (pos >= len) {
+                if ((len = readChunk()) == -1) {
+                    return offset > off ? offset - off : -1;
+                }
+            }
+            int c = Math.min(length, len - pos);
+            System.arraycopy(buf, pos, b, offset, c);
+            pos += c;
+            length -= c;
+            offset += c;
+        }
+        return l;
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        long length = n;
+        while (length > 0) {
+            if (pos >= len) {
+                if ((len = readChunk()) == -1) {
+                    return -1;
+                }
+            }
+            int c = (int) Math.min(n, len - pos);
+            pos += c;
+            length -= c;
+        }
+        return n;
+    }
+
+    @Override
+    public int available() {
+        return (len > 0 ? len - pos : 0);
+    }
+
+    @Override
+    public void close() throws IOException {
+        in.close();
+    }
+
+    /**
+     * Reads a single chunk from the underlying inputstream.
+     * 
+     * @return
+     * @throws IOException
+     */
+    private int readChunk() throws IOException {
+        pos = 0;
+        int w = readShort(in);
+        if (w == -1) {
+            return -1;
+        }
+        int chunkSize = (w & 0x0FFF) + 1; // plus 3 bytes minus 2 for the length
+        if ((w & 0x7000) != 0x3000) {
+            throw new IllegalArgumentException(String.format("Chunksize header A should be 0x3000, received 0x%04X", w & 0xE000));
+        }
+        boolean rawChunk = (w & 0x8000) == 0;
+        if (rawChunk) {
+            if (in.read(buf, 0, chunkSize) < chunkSize) {
+                throw new IllegalStateException(String.format("Not enough bytes read, expected %d", chunkSize));
+            }
+            return chunkSize;
+        } else {
+            int inOffset = 0;
+            int outOffset = 0;
+            while (inOffset < chunkSize) {
+                int tokenFlags = in.read();
+                inOffset++;
+                if (tokenFlags == -1) {
+                    break;
+                }
+                for (int n = 0; n < 8; n++) {
+                    if (inOffset >= chunkSize) {
+                        break;
+                    }
+                    if ((tokenFlags & POWER2[n]) == 0) {
+                        // literal
+                        final int b = in.read();
+                        if (b == -1) {
+                            return -1;
+                        }
+                        buf[outOffset++] = (byte) b;
+                        inOffset++;
+                    } else {
+                        // compressed token
+                        int token = readShort(in);
+                        if (token == -1) {
+                            return -1;
+                        }
+                        inOffset += 2;
+                        int copyLenBits = getCopyLenBits(outOffset - 1);
+                        int copyOffset = (token >> (copyLenBits)) + 1;
+                        int copyLen = (token & (POWER2[copyLenBits] - 1)) + 3;
+                        int startPos = outOffset - copyOffset;
+                        int endPos = startPos + copyLen;
+                        for (int i = startPos; i < endPos; i++) {
+                            buf[outOffset++] = buf[i];
+                        }
+                    }
+                }
+            }
+            return outOffset;
+        }
+    }
+
+    /**
+     * Helper method to determine how many bits in the CopyToken are used for the CopyLength.
+     * 
+     * @param offset
+     * @return
+     */
+    static int getCopyLenBits(int offset) {
+        for (int n = 11; n >= 4; n--) {
+            if ((offset & POWER2[n]) != 0) {
+                return 15 - n;
+            }
+        }
+        return 12;
+    }
+
+    /**
+     * Convenience method for read a 2-bytes short in little endian encoding.
+     * 
+     * @return
+     * @throws IOException
+     */
+    public int readShort() throws IOException {
+        return readShort(this);
+    }
+
+    /**
+     * Convenience method for read a 4-bytes int in little endian encoding.
+     * 
+     * @return
+     * @throws IOException
+     */
+    public int readInt() throws IOException {
+        return readInt(this);
+    }
+
+    private int readShort(InputStream stream) throws IOException {
+        int b0, b1;
+        if ((b0 = stream.read()) == -1) {
+            return -1;
+        }
+        if ((b1 = stream.read()) == -1) {
+            return -1;
+        }
+        return (b0 & 0xFF) | ((b1 & 0xFF) << 8);
+    }
+
+    private int readInt(InputStream stream) throws IOException {
+        int b0, b1, b2, b3;
+        if ((b0 = stream.read()) == -1) {
+            return -1;
+        }
+        if ((b1 = stream.read()) == -1) {
+            return -1;
+        }
+        if ((b2 = stream.read()) == -1) {
+            return -1;
+        }
+        if ((b3 = stream.read()) == -1) {
+            return -1;
+        }
+        return (b0 & 0xFF) | ((b1 & 0xFF) << 8) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 24);
+    }
+}