}
@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;
private final LittleEndianInput _lei;
- public SimpleHeaderInput(InputStream in) {
- _lei = getLEI(in);
+ private SimpleHeaderInput(LittleEndianInput lei) {
+ _lei = lei;
}
@Override
public int available() {
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;
_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()
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();
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) {
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();
* @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;
}
* @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;
}
package org.apache.poi.util;
+import java.io.BufferedInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
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() {
}
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());
}
}
- //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;
public void readPlain(byte[] buf, int off, int len) {
readFully(buf, off, len);
}
+
+
+ public void skipFully(int len) throws IOException {
+ IOUtils.skipFully(this, len);
+ }
}