From: Andreas Beeker Date: Fri, 14 Sep 2018 21:35:43 +0000 (+0000) Subject: Buffered LittleEndianInputStream X-Git-Tag: REL_4_1_0~189^2~25 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=33a4b2f3fbc4b83e844dd0e33becbc03635afd67;p=poi.git Buffered LittleEndianInputStream git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1840955 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java index cb17d84b56..631be8fd2d 100644 --- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java +++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java @@ -527,20 +527,16 @@ public final class BiffViewer { } @Override public int read(byte[] b, int off, int len) throws IOException { + if (b == null || off < 0 || len < 0 || b.length < off+len) { + throw new IllegalArgumentException(); + } if (_currentPos >= _currentSize) { fillNextBuffer(); } if (_currentPos >= _currentSize) { return -1; } - int availSize = _currentSize - _currentPos; - int result; - if (len > availSize) { - System.err.println("Unexpected request to read past end of current biff record"); - result = availSize; - } else { - result = len; - } + final int result = Math.min(len, _currentSize - _currentPos); System.arraycopy(_data, _currentPos, b, off, result); _currentPos += result; _overallStreamPos += result; diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java index 4f5f589032..3c9b977c79 100644 --- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java +++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java @@ -105,8 +105,8 @@ public final class RecordInputStream implements LittleEndianInput { private final LittleEndianInput _lei; - public SimpleHeaderInput(InputStream in) { - _lei = getLEI(in); + private SimpleHeaderInput(LittleEndianInput lei) { + _lei = lei; } @Override public int available() { @@ -128,8 +128,12 @@ public final class RecordInputStream implements LittleEndianInput { public RecordInputStream(InputStream in, EncryptionInfo key, int initialOffset) throws RecordFormatException { if (key == null) { - _dataInput = getLEI(in); - _bhi = new SimpleHeaderInput(in); + _dataInput = (in instanceof LittleEndianInput) + // accessing directly is an optimisation + ? (LittleEndianInput)in + // less optimal, but should work OK just the same. Often occurs in junit tests. + : new LittleEndianInputStream(in); + _bhi = new SimpleHeaderInput(_dataInput); } else { Biff8DecryptingStream bds = new Biff8DecryptingStream(in, initialOffset, key); _dataInput = bds; @@ -138,15 +142,6 @@ public final class RecordInputStream implements LittleEndianInput { _nextSid = readNextSid(); } - static LittleEndianInput getLEI(InputStream is) { - if (is instanceof LittleEndianInput) { - // accessing directly is an optimisation - return (LittleEndianInput) is; - } - // less optimal, but should work OK just the same. Often occurs in junit tests. - return new LittleEndianInputStream(is); - } - /** * @return the number of bytes available in the current BIFF record * @see #remaining() @@ -194,11 +189,9 @@ public final class RecordInputStream implements LittleEndianInput { private int readNextSid() { int nAvailable = _bhi.available(); if (nAvailable < EOFRecord.ENCODED_SIZE) { - if (nAvailable > 0) { - // some scrap left over? - // ex45582-22397.xls has one extra byte after the last record - // Excel reads that file OK - } + // some scrap left over, if nAvailable > 0? + // ex45582-22397.xls has one extra byte after the last record + // Excel reads that file OK return INVALID_SID_VALUE; } int result = _bhi.readRecordSID(); @@ -302,17 +295,13 @@ public final class RecordInputStream implements LittleEndianInput { return _dataInput.readUShort(); } + /** + * + * @return a double - might return NaN + */ @Override public double readDouble() { - long valueLongBits = readLong(); - double result = Double.longBitsToDouble(valueLongBits); - if (Double.isNaN(result)) { - // YK: Excel doesn't write NaN but instead converts the cell type into {@link CellType#ERROR}. - // HSSF prior to version 3.7 had a bug: it could write Double.NaN but could not read such a file back. - // This behavior was fixed in POI-3.7. - //throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN) - } - return result; + return Double.longBitsToDouble(readLong()); } public void readPlain(byte[] buf, int off, int len) { @@ -329,7 +318,7 @@ public final class RecordInputStream implements LittleEndianInput { readFully(buf, off, len, false); } - protected void readFully(byte[] buf, int off, int len, boolean isPlain) { + private void readFully(byte[] buf, int off, int len, boolean isPlain) { int origLen = len; if (buf == null) { throw new NullPointerException(); diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java index 839663cda3..035c6ee762 100644 --- a/src/java/org/apache/poi/util/IOUtils.java +++ b/src/java/org/apache/poi/util/IOUtils.java @@ -50,6 +50,7 @@ public final class IOUtils { * @param maxOverride The number of bytes that should be possible to be allocated in one step. * @since 4.0.0 */ + @SuppressWarnings("unused") public static void setByteArrayMaxOverride(int maxOverride) { BYTE_ARRAY_MAX_OVERRIDE = maxOverride; } @@ -395,13 +396,35 @@ public final class IOUtils { * @throws IOException If copying the data fails. */ public static long copy(InputStream inp, OutputStream out) throws IOException { + return copy(inp, out, -1); + } + + /** + * Copies all the data from the given InputStream to the OutputStream. It + * leaves both streams open, so you will still need to close them once done. + * + * @param inp The {@link InputStream} which provides the data + * @param out The {@link OutputStream} to write the data to + * @param limit limit the copied bytes - use {@code -1} for no limit + * @return the amount of bytes copied + * + * @throws IOException If copying the data fails. + */ + public static long copy(InputStream inp, OutputStream out, long limit) throws IOException { final byte[] buff = new byte[4096]; long totalCount = 0; - for (int count; (count = inp.read(buff)) != -1; totalCount += count) { - if (count > 0) { - out.write(buff, 0, count); + int readBytes = -1; + do { + int todoBytes = (int)((limit < 0) ? buff.length : Math.min(limit-totalCount, buff.length)); + if (todoBytes > 0) { + readBytes = inp.read(buff, 0, todoBytes); + if (readBytes > 0) { + out.write(buff, 0, readBytes); + totalCount += readBytes; + } } - } + } while (readBytes >= 0 && (limit == -1 || totalCount < limit)); + return totalCount; } diff --git a/src/java/org/apache/poi/util/LittleEndianInputStream.java b/src/java/org/apache/poi/util/LittleEndianInputStream.java index 886720f3f1..6199c4dd12 100644 --- a/src/java/org/apache/poi/util/LittleEndianInputStream.java +++ b/src/java/org/apache/poi/util/LittleEndianInputStream.java @@ -17,6 +17,7 @@ package org.apache.poi.util; +import java.io.BufferedInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; @@ -29,12 +30,16 @@ import java.io.InputStream; */ public class LittleEndianInputStream extends FilterInputStream implements LittleEndianInput { + private static final int BUFFERED_SIZE = 8096; + private static final int EOF = -1; + private int readIndex = 0; + private int markIndex = -1; public LittleEndianInputStream(InputStream is) { - super(is); + super(is.markSupported() ? is : new BufferedInputStream(is, BUFFERED_SIZE)); } - + @Override @SuppressForbidden("just delegating") public int available() { @@ -60,7 +65,18 @@ public class LittleEndianInputStream extends FilterInputStream implements Little } return LittleEndian.getUByte(buf); } - + + /** + * get a float value, reads it in little endian format + * then converts the resulting revolting IEEE 754 (curse them) floating + * point number to a happy java float + * + * @return the float (32-bit) value + */ + public float readFloat() { + return Float.intBitsToFloat( readInt() ); + } + @Override public double readDouble() { return Double.longBitsToDouble(readLong()); @@ -137,14 +153,42 @@ public class LittleEndianInputStream extends FilterInputStream implements Little } } - //Makes repeated calls to super.read() until length is read or EOF is reached + @Override + public int read(byte[] b, int off, int len) throws IOException { + int readBytes = super.read(b, off, len); + readIndex += readBytes; + return readBytes; + } + + @Override + public synchronized void mark(int readlimit) { + super.mark(readlimit); + markIndex = readIndex; + } + + @Override + public synchronized void reset() throws IOException { + super.reset(); + if (markIndex > -1) { + readIndex = markIndex; + markIndex = -1; + } + } + + public int getReadIndex() { + return readIndex; + } + + + + //Makes repeated calls to super.read() until length is read or EOF is reached private int _read(byte[] buffer, int offset, int length) throws IOException { //lifted directly from org.apache.commons.io.IOUtils 2.4 int remaining = length; while (remaining > 0) { int location = length - remaining; int count = read(buffer, offset + location, remaining); - if (EOF == count) { // EOF + if (EOF == count) { break; } remaining -= count; @@ -157,4 +201,9 @@ public class LittleEndianInputStream extends FilterInputStream implements Little public void readPlain(byte[] buf, int off, int len) { readFully(buf, off, len); } + + + public void skipFully(int len) throws IOException { + IOUtils.skipFully(this, len); + } }