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:
Javen O'Neal 2016-04-10 14:05:33 +00:00
parent 5a3c735215
commit 46a29e4a24
1 changed files with 273 additions and 273 deletions

View File

@ -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);
} }
} }