Tweak comments, layout and exceptions in IOUtils and RawDataBlock. It should now be clearer exactly what they do, and when they become unhappy. Also include a test that ensures that when reading from a slow inputstream (as per bug #42834), we really are ok with the data dribbling in, and do not require it to all come in in blocksize chunks at one
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@610439 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1558c1472f
commit
400bfacc62
@ -42,34 +42,44 @@ public class RawDataBlock
|
|||||||
* @param stream the InputStream from which the data will be read
|
* @param stream the InputStream from which the data will be read
|
||||||
*
|
*
|
||||||
* @exception IOException on I/O errors, and if an insufficient
|
* @exception IOException on I/O errors, and if an insufficient
|
||||||
* amount of data is read
|
* amount of data is read (the InputStream must
|
||||||
|
* be an exact multiple of the block size)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public RawDataBlock(final InputStream stream)
|
public RawDataBlock(final InputStream stream)
|
||||||
throws IOException
|
throws IOException {
|
||||||
{
|
this(stream, POIFSConstants.BIG_BLOCK_SIZE);
|
||||||
_data = new byte[ POIFSConstants.BIG_BLOCK_SIZE ];
|
}
|
||||||
|
/**
|
||||||
|
* Constructor RawDataBlock
|
||||||
|
*
|
||||||
|
* @param stream the InputStream from which the data will be read
|
||||||
|
* @param blockSize the size of the POIFS blocks, normally 512 bytes {@link POIFSConstants#BIG_BLOCK_SIZE}
|
||||||
|
*
|
||||||
|
* @exception IOException on I/O errors, and if an insufficient
|
||||||
|
* amount of data is read (the InputStream must
|
||||||
|
* be an exact multiple of the block size)
|
||||||
|
*/
|
||||||
|
public RawDataBlock(final InputStream stream, int blockSize)
|
||||||
|
throws IOException {
|
||||||
|
_data = new byte[ blockSize ];
|
||||||
int count = IOUtils.readFully(stream, _data);
|
int count = IOUtils.readFully(stream, _data);
|
||||||
|
|
||||||
if (count == -1)
|
if (count == -1) {
|
||||||
{
|
|
||||||
_eof = true;
|
_eof = true;
|
||||||
}
|
}
|
||||||
else if (count != POIFSConstants.BIG_BLOCK_SIZE)
|
else if (count != blockSize) {
|
||||||
{
|
// IOUtils.readFully will always read the
|
||||||
if (count == -1)
|
// requested number of bytes, unless it hits
|
||||||
//Cant have -1 bytes read in the error message!
|
// an EOF
|
||||||
count = 0;
|
_eof = true;
|
||||||
|
|
||||||
String type = " byte" + ((count == 1) ? ("")
|
String type = " byte" + ((count == 1) ? ("")
|
||||||
: ("s"));
|
: ("s"));
|
||||||
|
|
||||||
throw new IOException("Unable to read entire block; " + count
|
throw new IOException("Unable to read entire block; " + count
|
||||||
+ type + " read; expected "
|
+ type + " read before EOF; expected "
|
||||||
+ POIFSConstants.BIG_BLOCK_SIZE + " bytes");
|
+ blockSize + " bytes");
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
_eof = false;
|
_eof = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +92,6 @@ public class RawDataBlock
|
|||||||
*
|
*
|
||||||
* @exception IOException
|
* @exception IOException
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public boolean eof()
|
public boolean eof()
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -98,7 +107,6 @@ public class RawDataBlock
|
|||||||
*
|
*
|
||||||
* @exception IOException if there is no data
|
* @exception IOException if there is no data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public byte [] getData()
|
public byte [] getData()
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
@ -58,11 +58,16 @@ public class IOUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as the normal <tt>in.read(b, off, len)</tt>, but tries to ensure that
|
* Same as the normal <tt>in.read(b, off, len)</tt>, but
|
||||||
* the entire len number of bytes is read.
|
* tries to ensure that the entire len number of bytes
|
||||||
|
* is read.
|
||||||
* <p>
|
* <p>
|
||||||
* If the end of file is reached before any bytes are read, returns -1.
|
* If the end of file is reached before any bytes
|
||||||
* Otherwise, returns the number of bytes read.
|
* are read, returns -1.
|
||||||
|
* If the end of the file is reached after some bytes are
|
||||||
|
* read, returns the number of bytes read.
|
||||||
|
* If the end of the file isn't reached before len
|
||||||
|
* bytes have been read, will return len bytes.
|
||||||
*/
|
*/
|
||||||
public static int readFully(InputStream in, byte[] b, int off, int len)
|
public static int readFully(InputStream in, byte[] b, int off, int len)
|
||||||
throws IOException
|
throws IOException
|
||||||
@ -79,5 +84,4 @@ public class IOUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -20,6 +20,7 @@
|
|||||||
package org.apache.poi.poifs.storage;
|
package org.apache.poi.poifs.storage;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import junit.framework.*;
|
import junit.framework.*;
|
||||||
|
|
||||||
@ -124,6 +125,118 @@ public class TestRawDataBlock
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that when using a slow input stream, which
|
||||||
|
* won't return a full block at a time, we don't
|
||||||
|
* incorrectly think that there's not enough data
|
||||||
|
*/
|
||||||
|
public void testSlowInputStream() throws Exception {
|
||||||
|
for (int k = 1; k < 512; k++) {
|
||||||
|
byte[] data = new byte[ 512 ];
|
||||||
|
for (int j = 0; j < data.length; j++) {
|
||||||
|
data[j] = (byte) j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shouldn't complain, as there is enough data,
|
||||||
|
// even if it dribbles through
|
||||||
|
RawDataBlock block =
|
||||||
|
new RawDataBlock(new SlowInputStream(data, k));
|
||||||
|
assertFalse(block.eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
// But if there wasn't enough data available, will
|
||||||
|
// complain
|
||||||
|
for (int k = 1; k < 512; k++) {
|
||||||
|
byte[] data = new byte[ 511 ];
|
||||||
|
for (int j = 0; j < data.length; j++) {
|
||||||
|
data[j] = (byte) j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shouldn't complain, as there is enough data
|
||||||
|
try {
|
||||||
|
RawDataBlock block =
|
||||||
|
new RawDataBlock(new SlowInputStream(data, k));
|
||||||
|
fail();
|
||||||
|
} catch(IOException e) {
|
||||||
|
// as expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input stream which will return a maximum of
|
||||||
|
* a given number of bytes to read, and often claims
|
||||||
|
* not to have any data
|
||||||
|
*/
|
||||||
|
public static class SlowInputStream extends InputStream {
|
||||||
|
private Random rnd = new Random();
|
||||||
|
private byte[] data;
|
||||||
|
private int chunkSize;
|
||||||
|
private int pos = 0;
|
||||||
|
|
||||||
|
public SlowInputStream(byte[] data, int chunkSize) {
|
||||||
|
this.chunkSize = chunkSize;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 75% of the time, claim there's no data available
|
||||||
|
*/
|
||||||
|
private boolean claimNoData() {
|
||||||
|
if(rnd.nextFloat() < 0.25f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read() throws IOException {
|
||||||
|
if(pos >= data.length) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int ret = data[pos];
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if(ret < 0) ret += 256;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the requested number of bytes, or the chunk
|
||||||
|
* size, whichever is lower.
|
||||||
|
* Quite often will simply claim to have no data
|
||||||
|
*/
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
// Keep the length within the chunk size
|
||||||
|
if(len > chunkSize) {
|
||||||
|
len = chunkSize;
|
||||||
|
}
|
||||||
|
// Don't read off the end of the data
|
||||||
|
if(pos + len > data.length) {
|
||||||
|
len = data.length - pos;
|
||||||
|
|
||||||
|
// Spot when we're out of data
|
||||||
|
if(len == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 75% of the time, claim there's no data
|
||||||
|
if(claimNoData()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy, and return what we read
|
||||||
|
System.arraycopy(data, pos, b, off, len);
|
||||||
|
pos += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main method to run the unit tests
|
* main method to run the unit tests
|
||||||
|
Loading…
Reference in New Issue
Block a user