You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by jo...@apache.org on 2008/10/25 01:13:45 UTC
svn commit: r707778 - in /poi/trunk/src:
contrib/src/org/apache/poi/contrib/poibrowser/
java/org/apache/poi/hssf/record/ java/org/apache/poi/poifs/filesystem/
java/org/apache/poi/poifs/storage/ testcases/org/apache/poi/poifs/storage/
Author: josh
Date: Fri Oct 24 16:13:44 2008
New Revision: 707778
URL: http://svn.apache.org/viewvc?rev=707778&view=rev
Log:
Optimisation of RecordInputStream - removed intermediate 8K byte buffer. Expected performance gain was not realised immediately, so LittleEndianInput stuff has been pushed down into DocumentInputStream to help.
Added:
poi/trunk/src/java/org/apache/poi/poifs/storage/DataInputBlock.java
Modified:
poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java
poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java
poi/trunk/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java
poi/trunk/src/java/org/apache/poi/poifs/storage/DocumentBlock.java
poi/trunk/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java
poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java
poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java
Modified: poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java?rev=707778&r1=707777&r2=707778&view=diff
==============================================================================
--- poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java (original)
+++ poi/trunk/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java Fri Oct 24 16:13:44 2008
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,11 +14,9 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.contrib.poibrowser;
-import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -160,17 +157,7 @@
throw new RuntimeException(t.getMessage());
}
- try
- {
- is.close();
- }
- catch (IOException ex)
- {
- System.err.println
- ("Unexpected exception while closing " +
- event.getName() + " in " + event.getPath().toString());
- ex.printStackTrace(System.err);
- }
+ is.close();
final MutableTreeNode parentNode = getNode(d.path, filename, rootNode);
final MutableTreeNode nameNode = new DefaultMutableTreeNode(d.name);
Modified: poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java?rev=707778&r1=707777&r2=707778&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java Fri Oct 24 16:13:44 2008
@@ -17,12 +17,13 @@
package org.apache.poi.hssf.record;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianInput;
-
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.ByteArrayOutputStream;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianInputStream;
/**
* Title: Record Input Stream<P>
@@ -34,106 +35,131 @@
/** Maximum size of a single record (minus the 4 byte header) without a continue*/
public final static short MAX_RECORD_DATA_SIZE = 8224;
private static final int INVALID_SID_VALUE = -1;
+ private static final int DATA_LEN_NEEDS_TO_BE_READ = -1;
+ private static final byte[] EMPTY_BYTE_ARRAY = { };
- private InputStream in;
- private short currentSid;
- private short currentLength = -1;
- private short nextSid;
-
- private final byte[] data = new byte[MAX_RECORD_DATA_SIZE];
- private short recordOffset;
- private long pos;
-
- private boolean autoContinue = true;
-
- public RecordInputStream(InputStream in) throws RecordFormatException {
- this.in = in;
- try {
- nextSid = LittleEndian.readShort(in);
- //Don't increment the pos just yet (technically we are at the start of
- //the record stream until nextRecord is called).
- } catch (IOException ex) {
- throw new RecordFormatException("Error reading bytes", ex);
- }
- }
+ private final InputStream _in;
+ /** {@link LittleEndianInput} facet of field {@link #_in} */
+ private final LittleEndianInput _le;
+ private int currentSid;
+ private int _currentDataLength;
+ private int nextSid;
+ private int recordOffset;
+ private boolean autoContinue; // TODO - remove this
+
+ public RecordInputStream(InputStream in) throws RecordFormatException {
+ _in = in;
+ if (in instanceof LittleEndianInput) {
+ // accessing directly is an optimisation
+ _le = (LittleEndianInput) in;
+ } else {
+ // less optimal, but should work OK just the same. Often occurs in junit tests.
+ _le = new LittleEndianInputStream(in);
+ }
+ try {
+ if (_in.available() < LittleEndian.SHORT_SIZE) {
+ nextSid = INVALID_SID_VALUE;
+ } else {
+ nextSid = LittleEndian.readShort(in);
+ }
+ } catch (IOException ex) {
+ throw new RecordFormatException("Error reading bytes", ex);
+ }
+ _currentDataLength = DATA_LEN_NEEDS_TO_BE_READ;
+ autoContinue = true;
+ }
- /** This method will read a byte from the current record*/
public int read() {
checkRecordPosition(LittleEndian.BYTE_SIZE);
-
- byte result = data[recordOffset];
recordOffset += LittleEndian.BYTE_SIZE;
- pos += LittleEndian.BYTE_SIZE;
- return result;
+ return _le.readUByte();
+ }
+ public int read(byte[] b, int off, int len) {
+ int limit = Math.min(len, remaining());
+ if (limit == 0) {
+ return 0;
+ }
+ readFully(b, off,limit);
+ return limit;
}
public short getSid() {
- return currentSid;
+ return (short) currentSid;
}
- public short getLength() {
- return currentLength;
+ public short getLength() { // TODO - remove
+ return (short) _currentDataLength;
}
- public short getRecordOffset() {
- return recordOffset;
- }
- public long getPos() {
- return pos;
- }
+ /**
+ * Note - this method is expected to be called only when completed reading the current BIFF record.
+ * Calling this before reaching the end of the current record will cause all remaining data to be
+ * discarded
+ */
+ public boolean hasNextRecord() {
+ if (_currentDataLength != -1 && _currentDataLength != recordOffset) {
+ System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(currentSid));
+ // discard unread data
+ while (recordOffset < _currentDataLength) {
+ readByte();
+ }
+ }
+ if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) {
+ nextSid = readNextSid();
+ _currentDataLength = DATA_LEN_NEEDS_TO_BE_READ;
+ }
+ return nextSid != INVALID_SID_VALUE;
+ }
- public boolean hasNextRecord() {
- return nextSid != INVALID_SID_VALUE;
- }
+ /**
+ *
+ * @return the sid of the next record or {@link #INVALID_SID_VALUE} if at end of stream
+ */
+ private int readNextSid() {
+ int nAvailable;
+ try {
+ nAvailable = _in.available();
+ } catch (IOException e) {
+ throw new RecordFormatException("Error checking stream available bytes", e);
+ }
+ 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
+ }
+ return INVALID_SID_VALUE;
+ }
+ int result = _le.readUShort();
+ if (result == INVALID_SID_VALUE) {
+ throw new RecordFormatException("Found invalid sid (" + result + ")");
+ }
+ return result;
+ }
- /** Moves to the next record in the stream.
- *
- * <i>Note: The auto continue flag is reset to true</i>
- */
- public void nextRecord() throws RecordFormatException {
- if ((currentLength != -1) && (currentLength != recordOffset)) {
- System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(currentSid));
- }
- currentSid = nextSid;
- pos += LittleEndian.SHORT_SIZE;
- autoContinue = true;
- try {
- recordOffset = 0;
- currentLength = LittleEndian.readShort(in);
- if (currentLength > MAX_RECORD_DATA_SIZE)
- throw new RecordFormatException("The content of an excel record cannot exceed "+MAX_RECORD_DATA_SIZE+" bytes");
- pos += LittleEndian.SHORT_SIZE;
- in.read(data, 0, currentLength);
-
- //Read the Sid of the next record
- if (in.available() < EOFRecord.ENCODED_SIZE) {
- if (in.available() > 0) {
- // some scrap left over?
- // ex45582-22397.xls has one extra byte after the last record
- // Excel reads that file OK
- }
- nextSid = INVALID_SID_VALUE;
- } else {
- nextSid = LittleEndian.readShort(in);
- if (nextSid == INVALID_SID_VALUE) {
- throw new RecordFormatException("Found sid " + nextSid + " after record with sid 0x"
- + Integer.toHexString(currentSid).toUpperCase());
- }
- }
- } catch (IOException ex) {
- throw new RecordFormatException("Error reading bytes", ex);
- }
- }
+ /** Moves to the next record in the stream.
+ *
+ * <i>Note: The auto continue flag is reset to true</i>
+ */
+ public void nextRecord() throws RecordFormatException {
+ if (nextSid == INVALID_SID_VALUE) {
+ throw new IllegalStateException("EOF - next record not available");
+ }
+ currentSid = nextSid;
+ autoContinue = true;
+ recordOffset = 0;
+ _currentDataLength = _le.readUShort();
+ if (_currentDataLength > MAX_RECORD_DATA_SIZE) {
+ throw new RecordFormatException("The content of an excel record cannot exceed "
+ + MAX_RECORD_DATA_SIZE + " bytes");
+ }
+ }
public void setAutoContinue(boolean enable) {
this.autoContinue = enable;
}
- public boolean getAutoContinue() {
- return autoContinue;
- }
-
private void checkRecordPosition(int requiredByteCount) {
if (remaining() < requiredByteCount) {
@@ -150,11 +176,8 @@
*/
public byte readByte() {
checkRecordPosition(LittleEndian.BYTE_SIZE);
-
- byte result = data[recordOffset];
recordOffset += LittleEndian.BYTE_SIZE;
- pos += LittleEndian.BYTE_SIZE;
- return result;
+ return _le.readByte();
}
/**
@@ -162,29 +185,20 @@
*/
public short readShort() {
checkRecordPosition(LittleEndian.SHORT_SIZE);
-
- short result = LittleEndian.getShort(data, recordOffset);
recordOffset += LittleEndian.SHORT_SIZE;
- pos += LittleEndian.SHORT_SIZE;
- return result;
+ return _le.readShort();
}
public int readInt() {
checkRecordPosition(LittleEndian.INT_SIZE);
-
- int result = LittleEndian.getInt(data, recordOffset);
recordOffset += LittleEndian.INT_SIZE;
- pos += LittleEndian.INT_SIZE;
- return result;
+ return _le.readInt();
}
public long readLong() {
checkRecordPosition(LittleEndian.LONG_SIZE);
-
- long result = LittleEndian.getLong(data, recordOffset);
recordOffset += LittleEndian.LONG_SIZE;
- pos += LittleEndian.LONG_SIZE;
- return result;
+ return _le.readLong();
}
/**
@@ -200,22 +214,18 @@
*/
public int readUShort() {
checkRecordPosition(LittleEndian.SHORT_SIZE);
-
- int result = LittleEndian.getUShort(data, recordOffset);
recordOffset += LittleEndian.SHORT_SIZE;
- pos += LittleEndian.SHORT_SIZE;
- return result;
+ return _le.readUShort();
}
public double readDouble() {
checkRecordPosition(LittleEndian.DOUBLE_SIZE);
- long valueLongBits = LittleEndian.getLong(data, recordOffset);
+ recordOffset += LittleEndian.DOUBLE_SIZE;
+ long valueLongBits = _le.readLong();
double result = Double.longBitsToDouble(valueLongBits);
if (Double.isNaN(result)) {
throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN
}
- recordOffset += LittleEndian.DOUBLE_SIZE;
- pos += LittleEndian.DOUBLE_SIZE;
return result;
}
public void readFully(byte[] buf) {
@@ -224,9 +234,8 @@
public void readFully(byte[] buf, int off, int len) {
checkRecordPosition(len);
- System.arraycopy(data, recordOffset, buf, off, len);
+ _le.readFully(buf, off, len);
recordOffset+=len;
- pos+=len;
}
public String readString() {
@@ -315,18 +324,19 @@
return new UnicodeString(this);
}
- /** Returns the remaining bytes for the current record.
- *
- * @return The remaining bytes of the current record.
- */
- public byte[] readRemainder() {
- int size = remaining();
- byte[] result = new byte[size];
- System.arraycopy(data, recordOffset, result, 0, size);
- recordOffset += size;
- pos += size;
- return result;
- }
+ /** Returns the remaining bytes for the current record.
+ *
+ * @return The remaining bytes of the current record.
+ */
+ public byte[] readRemainder() {
+ int size = remaining();
+ if (size ==0) {
+ return EMPTY_BYTE_ARRAY;
+ }
+ byte[] result = new byte[size];
+ readFully(result);
+ return result;
+ }
/** Reads all byte data for the current record, including any
* that overlaps into any following continue records.
@@ -350,19 +360,29 @@
return out.toByteArray();
}
- /** The remaining number of bytes in the <i>current</i> record.
- *
- * @return The number of bytes remaining in the current record
- */
- public int remaining() {
- return (currentLength - recordOffset);
- }
+ /** The remaining number of bytes in the <i>current</i> record.
+ *
+ * @return The number of bytes remaining in the current record
+ */
+ public int remaining() {
+ if (_currentDataLength == DATA_LEN_NEEDS_TO_BE_READ) {
+ // already read sid of next record. so current one is finished
+ return 0;
+ }
+ return (_currentDataLength - recordOffset);
+ }
- /** Returns true iif a Continue record is next in the excel stream
- *
- * @return True when a ContinueRecord is next.
- */
- public boolean isContinueNext() {
- return (nextSid == ContinueRecord.sid);
- }
+ /**
+ *
+ * @return <code>true</code> when a {@link ContinueRecord} is next.
+ */
+ public boolean isContinueNext() {
+ if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ && recordOffset != _currentDataLength) {
+ throw new IllegalStateException("Should never be called before end of current record");
+ }
+ if (!hasNextRecord()) {
+ return false;
+ }
+ return nextSid == ContinueRecord.sid;
+ }
}
Modified: poi/trunk/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java?rev=707778&r1=707777&r2=707778&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java Fri Oct 24 16:13:44 2008
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,437 +14,312 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.poifs.filesystem;
-import java.io.*;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.poifs.storage.DataInputBlock;
+import org.apache.poi.util.LittleEndianInput;
/**
* This class provides methods to read a DocumentEntry managed by a
- * Filesystem instance.
+ * {@link POIFSFileSystem} instance.
*
* @author Marc Johnson (mjohnson at apache dot org)
*/
-
-public class DocumentInputStream
- extends InputStream
-{
-
- // current offset into the Document
- private int _current_offset;
-
- // current marked offset into the Document (used by mark and
- // reset)
- private int _marked_offset;
-
- // the Document's size
- private int _document_size;
-
- // have we been closed?
- private boolean _closed;
-
- // the actual Document
- private POIFSDocument _document;
-
- // buffer used to read one byte at a time
- private byte[] _tiny_buffer;
-
- // returned by read operations if we're at end of document
- static private final int EOD = -1;
-
- /**
- * Create an InputStream from the specified DocumentEntry
- *
- * @param document the DocumentEntry to be read
- *
- * @exception IOException if the DocumentEntry cannot be opened
- * (like, maybe it has been deleted?)
- */
-
- public DocumentInputStream(final DocumentEntry document)
- throws IOException
- {
- _current_offset = 0;
- _marked_offset = 0;
- _document_size = document.getSize();
- _closed = false;
- _tiny_buffer = null;
- if (document instanceof DocumentNode)
- {
- _document = (( DocumentNode ) document).getDocument();
- }
- else
- {
- throw new IOException("Cannot open internal document storage");
- }
- }
-
- /**
- * Create an InputStream from the specified Document
- *
- * @param document the Document to be read
- *
- * @exception IOException if the DocumentEntry cannot be opened
- * (like, maybe it has been deleted?)
- */
-
- public DocumentInputStream(final POIFSDocument document)
- throws IOException
- {
- _current_offset = 0;
- _marked_offset = 0;
- _document_size = document.getSize();
- _closed = false;
- _tiny_buffer = null;
- _document = document;
- }
-
- /**
- * Returns the number of bytes that can be read (or skipped over)
- * from this input stream without blocking by the next caller of a
- * method for this input stream. The next caller might be the same
- * thread or or another thread.
- *
- * @return the number of bytes that can be read from this input
- * stream without blocking.
- *
- * @exception IOException on error (such as the stream has been
- * closed)
- */
-
- public int available()
- throws IOException
- {
- dieIfClosed();
- return _document_size - _current_offset;
- }
-
- /**
- * Closes this input stream and releases any system resources
- * associated with the stream.
- *
- * @exception IOException
- */
-
- public void close()
- throws IOException
- {
- _closed = true;
- }
-
- /**
- * Marks the current position in this input stream. A subsequent
- * call to the reset method repositions this stream at the last
- * marked position so that subsequent reads re-read the same
- * bytes.
- * <p>
- * The readlimit arguments tells this input stream to allow that
- * many bytes to be read before the mark position gets
- * invalidated. This implementation, however, does not care.
- * <p>
- * The general contract of mark is that, if the method
- * markSupported returns true, the stream somehow remembers all
- * the bytes read after the call to mark and stands ready to
- * supply those same bytes again if and whenever the method reset
- * is called. However, the stream is not required to remember any
- * data at all if more than readlimit bytes are read from the
- * stream before reset is called. But this stream will.
- *
- * @param ignoredReadlimit the maximum limit of bytes that can be
- * read before the mark position becomes
- * invalid. Ignored by this
- * implementation.
- */
-
- public void mark(int ignoredReadlimit)
- {
- _marked_offset = _current_offset;
- }
-
- /**
- * Tests if this input stream supports the mark and reset methods.
- *
- * @return true
- */
-
- public boolean markSupported()
- {
- return true;
- }
-
- /**
- * Reads the next byte of data from the input stream. The value
- * byte is returned as an int in the range 0 to 255. If no byte is
- * available because the end of the stream has been reached, the
- * value -1 is returned. The definition of this method in
- * java.io.InputStream allows this method to block, but it won't.
- *
- * @return the next byte of data, or -1 if the end of the stream
- * is reached.
- *
- * @exception IOException
- */
-
- public int read()
- throws IOException
- {
- dieIfClosed();
- if (atEOD())
- {
- return EOD;
- }
- if (_tiny_buffer == null)
- {
- _tiny_buffer = new byte[ 1 ];
- }
- _document.read(_tiny_buffer, _current_offset++);
- return ((int)_tiny_buffer[ 0 ]) & 0x000000FF;
- }
-
- /**
- * Reads some number of bytes from the input stream and stores
- * them into the buffer array b. The number of bytes actually read
- * is returned as an integer. The definition of this method in
- * java.io.InputStream allows this method to block, but it won't.
- * <p>
- * If b is null, a NullPointerException is thrown. If the length
- * of b is zero, then no bytes are read and 0 is returned;
- * otherwise, there is an attempt to read at least one byte. If no
- * byte is available because the stream is at end of file, the
- * value -1 is returned; otherwise, at least one byte is read and
- * stored into b.
- * <p>
- * The first byte read is stored into element b[0], the next one
- * into b[1], and so on. The number of bytes read is, at most,
- * equal to the length of b. Let k be the number of bytes actually
- * read; these bytes will be stored in elements b[0] through
- * b[k-1], leaving elements b[k] through b[b.length-1] unaffected.
- * <p>
- * If the first byte cannot be read for any reason other than end
- * of file, then an IOException is thrown. In particular, an
- * IOException is thrown if the input stream has been closed.
- * <p>
- * The read(b) method for class InputStream has the same effect as:
- * <p>
- * <code>read(b, 0, b.length)</code>
- *
- * @param b the buffer into which the data is read.
- *
- * @return the total number of bytes read into the buffer, or -1
- * if there is no more data because the end of the stream
- * has been reached.
- *
- * @exception IOException
- * @exception NullPointerException
- */
-
- public int read(final byte [] b)
- throws IOException, NullPointerException
- {
- return read(b, 0, b.length);
- }
-
- /**
- * Reads up to len bytes of data from the input stream into an
- * array of bytes. An attempt is made to read as many as len
- * bytes, but a smaller number may be read, possibly zero. The
- * number of bytes actually read is returned as an integer.
- * <p>
- * The definition of this method in java.io.InputStream allows it
- * to block, but it won't.
- * <p>
- * If b is null, a NullPointerException is thrown.
- * <p>
- * If off is negative, or len is negative, or off+len is greater
- * than the length of the array b, then an
- * IndexOutOfBoundsException is thrown.
- * <p>
- * If len is zero, then no bytes are read and 0 is returned;
- * otherwise, there is an attempt to read at least one byte. If no
- * byte is available because the stream is at end of file, the
- * value -1 is returned; otherwise, at least one byte is read and
- * stored into b.
- * <p>
- * The first byte read is stored into element b[off], the next one
- * into b[off+1], and so on. The number of bytes read is, at most,
- * equal to len. Let k be the number of bytes actually read; these
- * bytes will be stored in elements b[off] through b[off+k-1],
- * leaving elements b[off+k] through b[off+len-1] unaffected.
- * <p>
- * In every case, elements b[0] through b[off] and elements
- * b[off+len] through b[b.length-1] are unaffected.
- * <p>
- * If the first byte cannot be read for any reason other than end
- * of file, then an IOException is thrown. In particular, an
- * IOException is thrown if the input stream has been closed.
- *
- * @param b the buffer into which the data is read.
- * @param off the start offset in array b at which the data is
- * written.
- * @param len the maximum number of bytes to read.
- *
- * @return the total number of bytes read into the buffer, or -1
- * if there is no more data because the end of the stream
- * has been reached.
- *
- * @exception IOException
- * @exception NullPointerException
- * @exception IndexOutOfBoundsException
- */
-
- public int read(final byte [] b, final int off, final int len)
- throws IOException, NullPointerException, IndexOutOfBoundsException
- {
- dieIfClosed();
- if (b == null)
- {
- throw new NullPointerException("buffer is null");
- }
- if ((off < 0) || (len < 0) || (b.length < (off + len)))
- {
- throw new IndexOutOfBoundsException(
- "can't read past buffer boundaries");
- }
- if (len == 0)
- {
- return 0;
- }
- if (atEOD())
- {
- return EOD;
- }
- int limit = Math.min(available(), len);
-
- if ((off == 0) && (limit == b.length))
- {
- _document.read(b, _current_offset);
- }
- else
- {
- byte[] buffer = new byte[ limit ];
-
- _document.read(buffer, _current_offset);
- System.arraycopy(buffer, 0, b, off, limit);
- }
- _current_offset += limit;
- return limit;
- }
-
- /**
- * Repositions this stream to the position at the time the mark
- * method was last called on this input stream.
- * <p>
- * The general contract of reset is:
- * <p>
- * <ul>
- * <li>
- * If the method markSupported returns true, then:
- * <ul>
- * <li>
- * If the method mark has not been called since the
- * stream was created, or the number of bytes read
- * from the stream since mark was last called is
- * larger than the argument to mark at that last
- * call, then an IOException might be thrown.
- * </li>
- * <li>
- * If such an IOException is not thrown, then the
- * stream is reset to a state such that all the
- * bytes read since the most recent call to mark
- * (or since the start of the file, if mark has not
- * been called) will be resupplied to subsequent
- * callers of the read method, followed by any
- * bytes that otherwise would have been the next
- * input data as of the time of the call to reset.
- * </li>
- * </ul>
- * </li>
- * <li>
- * If the method markSupported returns false, then:
- * <ul>
- * <li>
- * The call to reset may throw an IOException.
- * </li>
- * <li>
- * If an IOException is not thrown, then the
- * stream is reset to a fixed state that depends
- * on the particular type of the input and how it
- * was created. The bytes that will be supplied to
- * subsequent callers of the read method depend on
- * the particular type of the input stream.
- * </li>
- * </ul>
- * </li>
- * </ul>
- * <p>
- * All well and good ... this class's markSupported method returns
- * true and this method does not care whether you've called mark
- * at all, or whether you've exceeded the number of bytes
- * specified in the last call to mark. We're basically walking a
- * byte array ... mark and reset to your heart's content.
- */
-
- public void reset()
- {
- _current_offset = _marked_offset;
- }
-
- /**
- * Skips over and discards n bytes of data from this input
- * stream. The skip method may, for a variety of reasons, end up
- * skipping over some smaller number of bytes, possibly 0. This
- * may result from any of a number of conditions; reaching end of
- * file before n bytes have been skipped is only one
- * possibility. The actual number of bytes skipped is returned. If
- * n is negative, no bytes are skipped.
- *
- * @param n the number of bytes to be skipped.
- *
- * @return the actual number of bytes skipped.
- *
- * @exception IOException
- */
-
- public long skip(final long n)
- throws IOException
- {
- dieIfClosed();
- if (n < 0)
- {
- return 0;
- }
- int new_offset = _current_offset + ( int ) n;
-
- if (new_offset < _current_offset)
- {
-
- // wrap around in converting a VERY large long to an int
- new_offset = _document_size;
- }
- else if (new_offset > _document_size)
- {
- new_offset = _document_size;
- }
- long rval = new_offset - _current_offset;
-
- _current_offset = new_offset;
- return rval;
- }
-
- private void dieIfClosed()
- throws IOException
- {
- if (_closed)
- {
- throw new IOException(
- "cannot perform requested operation on a closed stream");
- }
- }
-
- private boolean atEOD()
- {
- return _current_offset == _document_size;
- }
-} // end public class DocumentInputStream
-
+public final class DocumentInputStream extends InputStream implements LittleEndianInput {
+ /** returned by read operations if we're at end of document */
+ private static final int EOF = -1;
+
+ private static final int SIZE_SHORT = 2;
+ private static final int SIZE_INT = 4;
+ private static final int SIZE_LONG = 8;
+
+ /** current offset into the Document */
+ private int _current_offset;
+
+ /** current marked offset into the Document (used by mark and reset) */
+ private int _marked_offset;
+
+ /** the Document's size */
+ private int _document_size;
+
+ /** have we been closed? */
+ private boolean _closed;
+
+ /** the actual Document */
+ private POIFSDocument _document;
+
+ /** the data block containing the current stream pointer */
+ private DataInputBlock _currentBlock;
+
+ /**
+ * Create an InputStream from the specified DocumentEntry
+ *
+ * @param document the DocumentEntry to be read
+ *
+ * @exception IOException if the DocumentEntry cannot be opened (like, maybe it has
+ * been deleted?)
+ */
+ public DocumentInputStream(DocumentEntry document) throws IOException {
+ if (!(document instanceof DocumentNode)) {
+ throw new IOException("Cannot open internal document storage");
+ }
+ _current_offset = 0;
+ _marked_offset = 0;
+ _document_size = document.getSize();
+ _closed = false;
+ _document = ((DocumentNode) document).getDocument();
+ _currentBlock = getDataInputBlock(0);
+ }
+
+ /**
+ * Create an InputStream from the specified Document
+ *
+ * @param document the Document to be read
+ */
+ public DocumentInputStream(POIFSDocument document) {
+ _current_offset = 0;
+ _marked_offset = 0;
+ _document_size = document.getSize();
+ _closed = false;
+ _document = document;
+ _currentBlock = getDataInputBlock(0);
+ }
+
+ public int available() throws IOException {
+ dieIfClosed();
+ return _document_size - _current_offset;
+ }
+
+ public void close() {
+ _closed = true;
+ }
+
+ public void mark(int ignoredReadlimit) {
+ _marked_offset = _current_offset;
+ }
+
+ /**
+ * Tests if this input stream supports the mark and reset methods.
+ *
+ * @return <code>true</code> always
+ */
+ public boolean markSupported() {
+ return true;
+ }
+
+ private DataInputBlock getDataInputBlock(int offset) {
+ return _document.getDataInputBlock(offset);
+ }
+
+ public int read() throws IOException {
+ dieIfClosed();
+ if (atEOD()) {
+ return EOF;
+ }
+ int result = _currentBlock.readUByte();
+ _current_offset++;
+ if (_currentBlock.available() < 1) {
+ _currentBlock = getDataInputBlock(_current_offset);
+ }
+ return result;
+ }
+
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ dieIfClosed();
+ if (b == null) {
+ throw new IllegalArgumentException("buffer must not be null");
+ }
+ if (off < 0 || len < 0 || b.length < off + len) {
+ throw new IndexOutOfBoundsException("can't read past buffer boundaries");
+ }
+ if (len == 0) {
+ return 0;
+ }
+ if (atEOD()) {
+ return EOF;
+ }
+ int limit = Math.min(available(), len);
+ readFully(b, off, limit);
+ return limit;
+ }
+
+ /**
+ * Repositions this stream to the position at the time the mark() method was
+ * last called on this input stream. If mark() has not been called this
+ * method repositions the stream to its beginning.
+ */
+ public void reset() {
+ _current_offset = _marked_offset;
+ _currentBlock = getDataInputBlock(_current_offset);
+ }
+
+ public long skip(long n) throws IOException {
+ dieIfClosed();
+ if (n < 0) {
+ return 0;
+ }
+ int new_offset = _current_offset + (int) n;
+
+ if (new_offset < _current_offset) {
+
+ // wrap around in converting a VERY large long to an int
+ new_offset = _document_size;
+ } else if (new_offset > _document_size) {
+ new_offset = _document_size;
+ }
+ long rval = new_offset - _current_offset;
+
+ _current_offset = new_offset;
+ _currentBlock = getDataInputBlock(_current_offset);
+ return rval;
+ }
+
+ private void dieIfClosed() throws IOException {
+ if (_closed) {
+ throw new IOException("cannot perform requested operation on a closed stream");
+ }
+ }
+
+ private boolean atEOD() {
+ return _current_offset == _document_size;
+ }
+
+ private void checkAvaliable(int requestedSize) {
+ if (_closed) {
+ throw new RuntimeException("cannot perform requested operation on a closed stream");
+ }
+ if (requestedSize > _document_size - _current_offset) {
+ throw new RuntimeException("Buffer underrun - requested " + requestedSize
+ + " bytes but " + (_document_size - _current_offset) + " was available");
+ }
+ }
+
+ public byte readByte() {
+ return (byte) readUByte();
+ }
+
+ public double readDouble() {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ public void readFully(byte[] buf) {
+ readFully(buf, 0, buf.length);
+ }
+
+ public short readShort() {
+ return (short) readUShort();
+ }
+
+ public void readFully(byte[] buf, int off, int len) {
+ checkAvaliable(len);
+ int blockAvailable = _currentBlock.available();
+ if (blockAvailable > len) {
+ _currentBlock.readFully(buf, off, len);
+ _current_offset += len;
+ return;
+ }
+ // else read big amount in chunks
+ int remaining = len;
+ int writePos = off;
+ while (remaining > 0) {
+ boolean blockIsExpiring = remaining >= blockAvailable;
+ int reqSize;
+ if (blockIsExpiring) {
+ reqSize = blockAvailable;
+ } else {
+ reqSize = remaining;
+ }
+ _currentBlock.readFully(buf, writePos, reqSize);
+ remaining -= reqSize;
+ writePos += reqSize;
+ _current_offset += reqSize;
+ if (blockIsExpiring) {
+ if (_current_offset == _document_size) {
+ if (remaining > 0) {
+ throw new IllegalStateException(
+ "reached end of document stream unexpectedly");
+ }
+ _currentBlock = null;
+ break;
+ }
+ _currentBlock = getDataInputBlock(_current_offset);
+ blockAvailable = _currentBlock.available();
+ }
+ }
+ }
+
+ public long readLong() {
+ checkAvaliable(SIZE_LONG);
+ int blockAvailable = _currentBlock.available();
+ long result;
+ if (blockAvailable > SIZE_LONG) {
+ result = _currentBlock.readLongLE();
+ } else {
+ DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
+ if (blockAvailable == SIZE_LONG) {
+ result = _currentBlock.readLongLE();
+ } else {
+ result = nextBlock.readLongLE(_currentBlock, blockAvailable);
+ }
+ _currentBlock = nextBlock;
+ }
+ _current_offset += SIZE_LONG;
+ return result;
+ }
+
+ public int readInt() {
+ checkAvaliable(SIZE_INT);
+ int blockAvailable = _currentBlock.available();
+ int result;
+ if (blockAvailable > SIZE_INT) {
+ result = _currentBlock.readIntLE();
+ } else {
+ DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
+ if (blockAvailable == SIZE_INT) {
+ result = _currentBlock.readIntLE();
+ } else {
+ result = nextBlock.readIntLE(_currentBlock, blockAvailable);
+ }
+ _currentBlock = nextBlock;
+ }
+ _current_offset += SIZE_INT;
+ return result;
+ }
+
+ public int readUShort() {
+ checkAvaliable(SIZE_SHORT);
+ int blockAvailable = _currentBlock.available();
+ int result;
+ if (blockAvailable > SIZE_SHORT) {
+ result = _currentBlock.readUShortLE();
+ } else {
+ DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable);
+ if (blockAvailable == SIZE_SHORT) {
+ result = _currentBlock.readUShortLE();
+ } else {
+ result = nextBlock.readUShortLE(_currentBlock);
+ }
+ _currentBlock = nextBlock;
+ }
+ _current_offset += SIZE_SHORT;
+ return result;
+ }
+
+ public int readUByte() {
+ checkAvaliable(1);
+ int result = _currentBlock.readUByte();
+ _current_offset++;
+ if (_currentBlock.available() < 1) {
+ _currentBlock = getDataInputBlock(_current_offset);
+ }
+ return result;
+ }
+}
Modified: poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java?rev=707778&r1=707777&r2=707778&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java Fri Oct 24 16:13:44 2008
@@ -31,6 +31,7 @@
import org.apache.poi.poifs.property.DocumentProperty;
import org.apache.poi.poifs.property.Property;
import org.apache.poi.poifs.storage.BlockWritable;
+import org.apache.poi.poifs.storage.DataInputBlock;
import org.apache.poi.poifs.storage.DocumentBlock;
import org.apache.poi.poifs.storage.ListManagedBlock;
import org.apache.poi.poifs.storage.RawDataBlock;
@@ -194,12 +195,62 @@
*
* @param buffer the buffer to write to
* @param offset the offset into our storage to read from
+ * This method is currently (Oct 2008) only used by test code. Perhaps it can be deleted
*/
void read(byte[] buffer, int offset) {
+ int len = buffer.length;
+
+ DataInputBlock currentBlock = getDataInputBlock(offset);
+
+ int blockAvailable = currentBlock.available();
+ if (blockAvailable > len) {
+ currentBlock.readFully(buffer, 0, len);
+ return;
+ }
+ // else read big amount in chunks
+ int remaining = len;
+ int writePos = 0;
+ int currentOffset = offset;
+ while (remaining > 0) {
+ boolean blockIsExpiring = remaining >= blockAvailable;
+ int reqSize;
+ if (blockIsExpiring) {
+ reqSize = blockAvailable;
+ } else {
+ reqSize = remaining;
+ }
+ currentBlock.readFully(buffer, writePos, reqSize);
+ remaining-=reqSize;
+ writePos+=reqSize;
+ currentOffset += reqSize;
+ if (blockIsExpiring) {
+ if (currentOffset == _size) {
+ if (remaining > 0) {
+ throw new IllegalStateException("reached end of document stream unexpectedly");
+ }
+ currentBlock = null;
+ break;
+ }
+ currentBlock = getDataInputBlock(currentOffset);
+ blockAvailable = currentBlock.available();
+ }
+ }
+ }
+
+ /**
+ * @return <code>null</code> if <tt>offset</tt> points to the end of the document stream
+ */
+ DataInputBlock getDataInputBlock(int offset) {
+ if (offset >= _size) {
+ if (offset > _size) {
+ throw new RuntimeException("Request for Offset " + offset + " doc size is " + _size);
+ }
+ return null;
+ }
if (_property.shouldUseSmallBlocks()) {
- SmallDocumentBlock.read(_small_store.getBlocks(), buffer, offset);
+ return SmallDocumentBlock.getDataInputBlock(_small_store.getBlocks(), offset);
} else {
- DocumentBlock.read(_big_store.getBlocks(), buffer, offset);
+ return DocumentBlock.getDataInputBlock(_big_store.getBlocks(), offset);
}
}
Added: poi/trunk/src/java/org/apache/poi/poifs/storage/DataInputBlock.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/storage/DataInputBlock.java?rev=707778&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/storage/DataInputBlock.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/storage/DataInputBlock.java Fri Oct 24 16:13:44 2008
@@ -0,0 +1,186 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ 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 obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.storage;
+
+/**
+ * Wraps a <tt>byte</tt> array and provides simple data input access.
+ * Internally, this class maintains a buffer read index, so that for the most part, primitive
+ * data can be read in a data-input-stream-like manner.<p/>
+ *
+ * Note - the calling class should call the {@link #available()} method to detect end-of-buffer
+ * and move to the next data block when the current is exhausted.
+ * For optimisation reasons, no error handling is performed in this class. Thus, mistakes in
+ * calling code ran may raise ugly exceptions here, like {@link ArrayIndexOutOfBoundsException},
+ * etc .<p/>
+ *
+ * The multi-byte primitive input methods ({@link #readUShortLE()}, {@link #readIntLE()} and
+ * {@link #readLongLE()}) have corresponding 'spanning read' methods which (when required) perform
+ * a read across the block boundary. These spanning read methods take the previous
+ * {@link DataInputBlock} as a parameter.
+ * Reads of larger amounts of data (into <tt>byte</tt> array buffers) must be managed by the caller
+ * since these could conceivably involve more than two blocks.
+ *
+ * @author Josh Micich
+ */
+public final class DataInputBlock {
+
+ /**
+ * Possibly any size (usually 512K or 64K). Assumed to be at least 8 bytes for all blocks
+ * before the end of the stream. The last block in the stream can be any size except zero.
+ */
+ private final byte[] _buf;
+ private int _readIndex;
+ private int _maxIndex;
+
+ DataInputBlock(byte[] data, int startOffset) {
+ _buf = data;
+ _readIndex = startOffset;
+ _maxIndex = _buf.length;
+ }
+ public int available() {
+ return _maxIndex-_readIndex;
+ }
+
+ public int readUByte() {
+ return _buf[_readIndex++] & 0xFF;
+ }
+
+ /**
+ * Reads a <tt>short</tt> which was encoded in <em>little endian</em> format.
+ */
+ public int readUShortLE() {
+ int i = _readIndex;
+
+ int b0 = _buf[i++] & 0xFF;
+ int b1 = _buf[i++] & 0xFF;
+ _readIndex = i;
+ return (b1 << 8) + (b0 << 0);
+ }
+
+ /**
+ * Reads a <tt>short</tt> which spans the end of <tt>prevBlock</tt> and the start of this block.
+ */
+ public int readUShortLE(DataInputBlock prevBlock) {
+ // simple case - will always be one byte in each block
+ int i = prevBlock._buf.length-1;
+
+ int b0 = prevBlock._buf[i++] & 0xFF;
+ int b1 = _buf[_readIndex++] & 0xFF;
+ return (b1 << 8) + (b0 << 0);
+ }
+
+ /**
+ * Reads an <tt>int</tt> which was encoded in <em>little endian</em> format.
+ */
+ public int readIntLE() {
+ int i = _readIndex;
+
+ int b0 = _buf[i++] & 0xFF;
+ int b1 = _buf[i++] & 0xFF;
+ int b2 = _buf[i++] & 0xFF;
+ int b3 = _buf[i++] & 0xFF;
+ _readIndex = i;
+ return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0);
+ }
+
+ /**
+ * Reads an <tt>int</tt> which spans the end of <tt>prevBlock</tt> and the start of this block.
+ */
+ public int readIntLE(DataInputBlock prevBlock, int prevBlockAvailable) {
+ byte[] buf = new byte[4];
+
+ readSpanning(prevBlock, prevBlockAvailable, buf);
+ int b0 = buf[0] & 0xFF;
+ int b1 = buf[1] & 0xFF;
+ int b2 = buf[2] & 0xFF;
+ int b3 = buf[3] & 0xFF;
+ return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0);
+ }
+
+ /**
+ * Reads a <tt>long</tt> which was encoded in <em>little endian</em> format.
+ */
+ public long readLongLE() {
+ int i = _readIndex;
+
+ int b0 = _buf[i++] & 0xFF;
+ int b1 = _buf[i++] & 0xFF;
+ int b2 = _buf[i++] & 0xFF;
+ int b3 = _buf[i++] & 0xFF;
+ int b4 = _buf[i++] & 0xFF;
+ int b5 = _buf[i++] & 0xFF;
+ int b6 = _buf[i++] & 0xFF;
+ int b7 = _buf[i++] & 0xFF;
+ _readIndex = i;
+ return (((long)b7 << 56) +
+ ((long)b6 << 48) +
+ ((long)b5 << 40) +
+ ((long)b4 << 32) +
+ ((long)b3 << 24) +
+ (b2 << 16) +
+ (b1 << 8) +
+ (b0 << 0));
+ }
+
+ /**
+ * Reads a <tt>long</tt> which spans the end of <tt>prevBlock</tt> and the start of this block.
+ */
+ public long readLongLE(DataInputBlock prevBlock, int prevBlockAvailable) {
+ byte[] buf = new byte[8];
+
+ readSpanning(prevBlock, prevBlockAvailable, buf);
+
+ int b0 = buf[0] & 0xFF;
+ int b1 = buf[1] & 0xFF;
+ int b2 = buf[2] & 0xFF;
+ int b3 = buf[3] & 0xFF;
+ int b4 = buf[4] & 0xFF;
+ int b5 = buf[5] & 0xFF;
+ int b6 = buf[6] & 0xFF;
+ int b7 = buf[7] & 0xFF;
+ return (((long)b7 << 56) +
+ ((long)b6 << 48) +
+ ((long)b5 << 40) +
+ ((long)b4 << 32) +
+ ((long)b3 << 24) +
+ (b2 << 16) +
+ (b1 << 8) +
+ (b0 << 0));
+ }
+
+ /**
+ * Reads a small amount of data from across the boundary between two blocks.
+ * The {@link #_readIndex} of this (the second) block is updated accordingly.
+ * Note- this method (and other code) assumes that the second {@link DataInputBlock}
+ * always is big enough to complete the read without being exhausted.
+ */
+ private void readSpanning(DataInputBlock prevBlock, int prevBlockAvailable, byte[] buf) {
+ System.arraycopy(prevBlock._buf, prevBlock._readIndex, buf, 0, prevBlockAvailable);
+ int secondReadLen = buf.length-prevBlockAvailable;
+ System.arraycopy(_buf, 0, buf, prevBlockAvailable, secondReadLen);
+ _readIndex = secondReadLen;
+ }
+
+ /**
+ * Reads <tt>len</tt> bytes from this block into the supplied buffer.
+ */
+ public void readFully(byte[] buf, int off, int len) {
+ System.arraycopy(_buf, _readIndex, buf, off, len);
+ _readIndex += len;
+ }
+}
Modified: poi/trunk/src/java/org/apache/poi/poifs/storage/DocumentBlock.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/storage/DocumentBlock.java?rev=707778&r1=707777&r2=707778&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/storage/DocumentBlock.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/storage/DocumentBlock.java Fri Oct 24 16:13:44 2008
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,31 +14,27 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.poifs.storage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-
import java.util.Arrays;
import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.IntegerField;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianConsts;
/**
* A block of document data.
*
* @author Marc Johnson (mjohnson at apache dot org)
*/
+public final class DocumentBlock extends BigBlock {
+ private static final int BLOCK_SHIFT = 9;
+ private static final int BLOCK_SIZE = 1 << BLOCK_SHIFT;
+ private static final int BLOCK_MASK = BLOCK_SIZE-1;
-public class DocumentBlock
- extends BigBlock
-{
private static final byte _default_value = ( byte ) 0xFF;
private byte[] _data;
private int _bytes_read;
@@ -161,45 +156,10 @@
return rval;
}
- /**
- * read data from an array of DocumentBlocks
- *
- * @param blocks the blocks to read from
- * @param buffer the buffer to write the data into
- * @param offset the offset into the array of blocks to read from
- */
-
- public static void read(final DocumentBlock [] blocks,
- final byte [] buffer, final int offset)
- {
- int firstBlockIndex = offset / POIFSConstants.BIG_BLOCK_SIZE;
- int firstBlockOffset = offset % POIFSConstants.BIG_BLOCK_SIZE;
- int lastBlockIndex = (offset + buffer.length - 1)
- / POIFSConstants.BIG_BLOCK_SIZE;
-
- if (firstBlockIndex == lastBlockIndex)
- {
- System.arraycopy(blocks[ firstBlockIndex ]._data,
- firstBlockOffset, buffer, 0, buffer.length);
- }
- else
- {
- int buffer_offset = 0;
-
- System.arraycopy(blocks[ firstBlockIndex ]._data,
- firstBlockOffset, buffer, buffer_offset,
- POIFSConstants.BIG_BLOCK_SIZE
- - firstBlockOffset);
- buffer_offset += POIFSConstants.BIG_BLOCK_SIZE - firstBlockOffset;
- for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++)
- {
- System.arraycopy(blocks[ j ]._data, 0, buffer, buffer_offset,
- POIFSConstants.BIG_BLOCK_SIZE);
- buffer_offset += POIFSConstants.BIG_BLOCK_SIZE;
- }
- System.arraycopy(blocks[ lastBlockIndex ]._data, 0, buffer,
- buffer_offset, buffer.length - buffer_offset);
- }
+ public static DataInputBlock getDataInputBlock(DocumentBlock[] blocks, int offset) {
+ int firstBlockIndex = offset >> BLOCK_SHIFT;
+ int firstBlockOffset= offset & BLOCK_MASK;
+ return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
}
/* ********** START extension of BigBlock ********** */
Modified: poi/trunk/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java?rev=707778&r1=707777&r2=707778&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java Fri Oct 24 16:13:44 2008
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,13 +14,15 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.poifs.storage;
-import java.io.*;
-
-import java.util.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import org.apache.poi.poifs.common.POIFSConstants;
@@ -31,13 +32,14 @@
*
* @author Marc Johnson (mjohnson at apache dot org)
*/
+public final class SmallDocumentBlock implements BlockWritable, ListManagedBlock {
+ private static final int BLOCK_SHIFT = 6;
-public class SmallDocumentBlock
- implements BlockWritable, ListManagedBlock
-{
private byte[] _data;
private static final byte _default_fill = ( byte ) 0xff;
- private static final int _block_size = 64;
+ private static final int _block_size = 1 << BLOCK_SHIFT;
+ private static final int BLOCK_MASK = _block_size-1;
+
private static final int _blocks_per_big_block =
POIFSConstants.BIG_BLOCK_SIZE / _block_size;
@@ -178,46 +180,10 @@
return sdbs;
}
- /**
- * read data from an array of SmallDocumentBlocks
- *
- * @param blocks the blocks to read from
- * @param buffer the buffer to write the data into
- * @param offset the offset into the array of blocks to read from
- */
-
- public static void read(final BlockWritable [] blocks,
- final byte [] buffer, final int offset)
- {
- int firstBlockIndex = offset / _block_size;
- int firstBlockOffset = offset % _block_size;
- int lastBlockIndex = (offset + buffer.length - 1) / _block_size;
-
- if (firstBlockIndex == lastBlockIndex)
- {
- System.arraycopy(
- (( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data,
- firstBlockOffset, buffer, 0, buffer.length);
- }
- else
- {
- int buffer_offset = 0;
-
- System.arraycopy(
- (( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data,
- firstBlockOffset, buffer, buffer_offset,
- _block_size - firstBlockOffset);
- buffer_offset += _block_size - firstBlockOffset;
- for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++)
- {
- System.arraycopy((( SmallDocumentBlock ) blocks[ j ])._data,
- 0, buffer, buffer_offset, _block_size);
- buffer_offset += _block_size;
- }
- System.arraycopy(
- (( SmallDocumentBlock ) blocks[ lastBlockIndex ])._data, 0,
- buffer, buffer_offset, buffer.length - buffer_offset);
- }
+ public static DataInputBlock getDataInputBlock(SmallDocumentBlock[] blocks, int offset) {
+ int firstBlockIndex = offset >> BLOCK_SHIFT;
+ int firstBlockOffset= offset & BLOCK_MASK;
+ return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
}
/**
Modified: poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java?rev=707778&r1=707777&r2=707778&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java Fri Oct 24 16:13:44 2008
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,25 +14,21 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.poifs.storage;
-import java.io.*;
-
-import java.util.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
-import junit.framework.*;
+import junit.framework.TestCase;
/**
* Class to test DocumentBlock functionality
*
* @author Marc Johnson
*/
-
-public class TestDocumentBlock
- extends TestCase
-{
+public final class TestDocumentBlock extends TestCase {
static final private byte[] _testdata;
static
@@ -44,25 +39,10 @@
_testdata[ j ] = ( byte ) j;
}
}
- ;
-
- /**
- * Constructor TestDocumentBlock
- *
- * @param name
- */
-
- public TestDocumentBlock(String name)
- {
- super(name);
- }
/**
* Test the writing DocumentBlock constructor.
- *
- * @exception IOException
*/
-
public void testConstructor()
throws IOException
{
@@ -88,46 +68,10 @@
assertEquals(_testdata.length, size);
}
- /**
- * test static read method
- *
- * @exception IOException
- */
-
- public void testRead()
- throws IOException
- {
- DocumentBlock[] blocks = new DocumentBlock[ 4 ];
- ByteArrayInputStream input = new ByteArrayInputStream(_testdata);
-
- for (int j = 0; j < 4; j++)
- {
- blocks[ j ] = new DocumentBlock(input);
- }
- for (int j = 1; j <= 2000; j += 17)
- {
- byte[] buffer = new byte[ j ];
- int offset = 0;
-
- for (int k = 0; k < (2000 / j); k++)
- {
- DocumentBlock.read(blocks, buffer, offset);
- for (int n = 0; n < buffer.length; n++)
- {
- assertEquals("checking byte " + (k * j) + n,
- _testdata[ (k * j) + n ], buffer[ n ]);
- }
- offset += j;
- }
- }
- }
/**
* Test 'reading' constructor
- *
- * @exception IOException
*/
-
public void testReadingConstructor()
throws IOException
{
@@ -164,17 +108,4 @@
assertEquals(( byte ) 0xFF, copy[ j ]);
}
}
-
- /**
- * main method to run the unit tests
- *
- * @param ignored_args
- */
-
- public static void main(String [] ignored_args)
- {
- System.out
- .println("Testing org.apache.poi.poifs.storage.DocumentBlock");
- junit.textui.TestRunner.run(TestDocumentBlock.class);
- }
}
Modified: poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java?rev=707778&r1=707777&r2=707778&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java Fri Oct 24 16:13:44 2008
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,25 +14,24 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.poifs.storage;
-import java.io.*;
-
-import java.util.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
-import junit.framework.*;
+import junit.framework.TestCase;
/**
* Class to test SmallDocumentBlock functionality
*
* @author Marc Johnson
*/
-
-public class TestSmallDocumentBlock
- extends TestCase
-{
+public final class TestSmallDocumentBlock extends TestCase {
static final private byte[] _testdata;
static final private int _testdata_size = 2999;
@@ -45,25 +43,10 @@
_testdata[ j ] = ( byte ) j;
}
}
- ;
-
- /**
- * constructor
- *
- * @param name
- */
-
- public TestSmallDocumentBlock(String name)
- {
- super(name);
- }
/**
* Test conversion from DocumentBlocks
- *
- * @exception IOException
*/
-
public void testConvert1()
throws IOException
{
@@ -113,12 +96,7 @@
/**
* Test conversion from byte array
- *
- * @exception IOException;
- *
- * @exception IOException
*/
-
public void testConvert2()
throws IOException
{
@@ -155,56 +133,8 @@
}
/**
- * Test read method
- *
- * @exception IOException
- */
-
- public void testRead()
- throws IOException
- {
- ByteArrayInputStream stream = new ByteArrayInputStream(_testdata);
- List documents = new ArrayList();
-
- while (true)
- {
- DocumentBlock block = new DocumentBlock(stream);
-
- documents.add(block);
- if (block.partiallyRead())
- {
- break;
- }
- }
- SmallDocumentBlock[] blocks =
- SmallDocumentBlock
- .convert(( BlockWritable [] ) documents
- .toArray(new DocumentBlock[ 0 ]), _testdata_size);
-
- for (int j = 1; j <= _testdata_size; j += 38)
- {
- byte[] buffer = new byte[ j ];
- int offset = 0;
-
- for (int k = 0; k < (_testdata_size / j); k++)
- {
- SmallDocumentBlock.read(blocks, buffer, offset);
- for (int n = 0; n < buffer.length; n++)
- {
- assertEquals("checking byte " + (k * j) + n,
- _testdata[ (k * j) + n ], buffer[ n ]);
- }
- offset += j;
- }
- }
- }
-
- /**
* test fill
- *
- * @exception IOException
*/
-
public void testFill()
throws IOException
{
@@ -294,17 +224,4 @@
}
}
}
-
- /**
- * main method to run the unit tests
- *
- * @param ignored_args
- */
-
- public static void main(String [] ignored_args)
- {
- System.out.println(
- "Testing org.apache.poi.poifs.storage.SmallDocumentBlock");
- junit.textui.TestRunner.run(TestSmallDocumentBlock.class);
- }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org