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