git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1053562 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_8_BETA1
@@ -144,7 +144,7 @@ public abstract class POIDocument { | |||
//directory can be null when creating new documents | |||
if(directory == null) return null; | |||
InputStream dis; | |||
DocumentInputStream dis; | |||
try { | |||
// Find the entry, and get an input stream for it | |||
dis = directory.createDocumentInputStream( directory.getEntry(setName) ); |
@@ -171,14 +171,7 @@ public class DirectoryNode | |||
final String documentName) | |||
throws IOException | |||
{ | |||
Entry document = getEntry(documentName); | |||
if (!document.isDocumentEntry()) | |||
{ | |||
throw new IOException("Entry '" + documentName | |||
+ "' is not a DocumentEntry"); | |||
} | |||
return new DocumentInputStream(( DocumentEntry ) document); | |||
return createDocumentInputStream(getEntry(documentName)); | |||
} | |||
/** | |||
@@ -191,7 +184,7 @@ public class DirectoryNode | |||
* @exception IOException if the document does not exist or the | |||
* name is that of a DirectoryEntry | |||
*/ | |||
public InputStream createDocumentInputStream( | |||
public DocumentInputStream createDocumentInputStream( | |||
final Entry document) | |||
throws IOException | |||
{ | |||
@@ -201,11 +194,7 @@ public class DirectoryNode | |||
} | |||
DocumentEntry entry = (DocumentEntry)document; | |||
if(_ofilesystem != null) { | |||
return new DocumentInputStream(entry); | |||
} else { | |||
return new NDocumentInputStream(entry); | |||
} | |||
return new DocumentInputStream(entry); | |||
} | |||
/** |
@@ -20,40 +20,26 @@ package org.apache.poi.poifs.filesystem; | |||
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 | |||
* {@link POIFSFileSystem} instance. | |||
* | |||
* @author Marc Johnson (mjohnson at apache dot org) | |||
* {@link POIFSFileSystem} or {@link NPOIFSFileSystem} instance. | |||
* It creates the appropriate one, and delegates, allowing us to | |||
* work transparently with the two. | |||
*/ | |||
public final class DocumentInputStream extends InputStream implements LittleEndianInput { | |||
public class DocumentInputStream extends InputStream implements LittleEndianInput { | |||
/** returned by read operations if we're at end of document */ | |||
private static final int EOF = -1; | |||
protected 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; | |||
protected static final int SIZE_SHORT = 2; | |||
protected static final int SIZE_INT = 4; | |||
protected static final int SIZE_LONG = 8; | |||
private DocumentInputStream delegate; | |||
/** For use by downstream implementations */ | |||
protected DocumentInputStream() {} | |||
/** | |||
* Create an InputStream from the specified DocumentEntry | |||
@@ -64,20 +50,21 @@ public final class DocumentInputStream extends InputStream implements LittleEndi | |||
* been deleted?) | |||
*/ | |||
public DocumentInputStream(DocumentEntry document) throws IOException { | |||
if (!(document instanceof DocumentNode)) { | |||
throw new IOException("Cannot open internal document storage"); | |||
} | |||
DocumentNode documentNode = (DocumentNode)document; | |||
if(documentNode.getDocument() == null) { | |||
throw new IOException("Cannot open internal document storage"); | |||
} | |||
_current_offset = 0; | |||
_marked_offset = 0; | |||
_document_size = document.getSize(); | |||
_closed = false; | |||
_document = documentNode.getDocument(); | |||
_currentBlock = getDataInputBlock(0); | |||
if (!(document instanceof DocumentNode)) { | |||
throw new IOException("Cannot open internal document storage"); | |||
} | |||
DocumentNode documentNode = (DocumentNode)document; | |||
DirectoryNode parentNode = (DirectoryNode)document.getParent(); | |||
if(documentNode.getDocument() != null) { | |||
delegate = new ODocumentInputStream(document); | |||
} else if(parentNode.getFileSystem() != null) { | |||
delegate = new ODocumentInputStream(document); | |||
} else if(parentNode.getNFileSystem() != null) { | |||
delegate = new NDocumentInputStream(document); | |||
} else { | |||
throw new IOException("No FileSystem bound on the parent, can't read contents"); | |||
} | |||
} | |||
/** | |||
@@ -86,27 +73,28 @@ public final class DocumentInputStream extends InputStream implements LittleEndi | |||
* @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); | |||
delegate = new ODocumentInputStream(document); | |||
} | |||
/** | |||
* Create an InputStream from the specified Document | |||
* | |||
* @param document the Document to be read | |||
*/ | |||
public DocumentInputStream(NPOIFSDocument document) { | |||
delegate = new NDocumentInputStream(document); | |||
} | |||
public int available() { | |||
if (_closed) { | |||
throw new IllegalStateException("cannot perform requested operation on a closed stream"); | |||
} | |||
return _document_size - _current_offset; | |||
return delegate.available(); | |||
} | |||
public void close() { | |||
_closed = true; | |||
delegate.close(); | |||
} | |||
public void mark(int ignoredReadlimit) { | |||
_marked_offset = _current_offset; | |||
delegate.mark(ignoredReadlimit); | |||
} | |||
/** | |||
@@ -118,21 +106,8 @@ public final class DocumentInputStream extends InputStream implements LittleEndi | |||
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; | |||
return delegate.read(); | |||
} | |||
public int read(byte[] b) throws IOException { | |||
@@ -140,22 +115,7 @@ public final class DocumentInputStream extends InputStream implements LittleEndi | |||
} | |||
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; | |||
return delegate.read(b, off, len); | |||
} | |||
/** | |||
@@ -164,169 +124,46 @@ public final class DocumentInputStream extends InputStream implements LittleEndi | |||
* method repositions the stream to its beginning. | |||
*/ | |||
public void reset() { | |||
_current_offset = _marked_offset; | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
delegate.reset(); | |||
} | |||
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 IllegalStateException("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"); | |||
} | |||
return delegate.skip(n); | |||
} | |||
public byte readByte() { | |||
return (byte) readUByte(); | |||
return delegate.readByte(); | |||
} | |||
public double readDouble() { | |||
return Double.longBitsToDouble(readLong()); | |||
} | |||
public void readFully(byte[] buf) { | |||
readFully(buf, 0, buf.length); | |||
return delegate.readDouble(); | |||
} | |||
public short readShort() { | |||
return (short) readUShort(); | |||
} | |||
public void readFully(byte[] buf) { | |||
readFully(buf, 0, buf.length); | |||
} | |||
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(); | |||
} | |||
} | |||
delegate.readFully(buf, off, len); | |||
} | |||
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; | |||
return delegate.readLong(); | |||
} | |||
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; | |||
return delegate.readInt(); | |||
} | |||
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; | |||
return delegate.readUShort(); | |||
} | |||
public int readUByte() { | |||
checkAvaliable(1); | |||
int result = _currentBlock.readUByte(); | |||
_current_offset++; | |||
if (_currentBlock.available() < 1) { | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
} | |||
return result; | |||
return delegate.readUByte(); | |||
} | |||
} |
@@ -18,26 +18,17 @@ | |||
package org.apache.poi.poifs.filesystem; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.nio.ByteBuffer; | |||
import java.util.Iterator; | |||
import org.apache.poi.poifs.property.DocumentProperty; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
/** | |||
* This class provides methods to read a DocumentEntry managed by a | |||
* {@link POIFSFileSystem} instance. | |||
* {@link NPOIFSFileSystem} instance. | |||
*/ | |||
public final class NDocumentInputStream 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; | |||
public final class NDocumentInputStream extends DocumentInputStream { | |||
/** current offset into the Document */ | |||
private int _current_offset; | |||
/** current block count */ | |||
@@ -104,6 +95,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
_data = _document.getBlockIterator(); | |||
} | |||
@Override | |||
public int available() { | |||
if (_closed) { | |||
throw new IllegalStateException("cannot perform requested operation on a closed stream"); | |||
@@ -111,24 +103,18 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
return _document_size - _current_offset; | |||
} | |||
@Override | |||
public void close() { | |||
_closed = true; | |||
} | |||
@Override | |||
public void mark(int ignoredReadlimit) { | |||
_marked_offset = _current_offset; | |||
_marked_offset_count = _current_block_count; | |||
} | |||
/** | |||
* Tests if this input stream supports the mark and reset methods. | |||
* | |||
* @return <code>true</code> always | |||
*/ | |||
public boolean markSupported() { | |||
return true; | |||
} | |||
@Override | |||
public int read() throws IOException { | |||
dieIfClosed(); | |||
if (atEOD()) { | |||
@@ -145,10 +131,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
return result; | |||
} | |||
public int read(byte[] b) throws IOException { | |||
return read(b, 0, b.length); | |||
} | |||
@Override | |||
public int read(byte[] b, int off, int len) throws IOException { | |||
dieIfClosed(); | |||
if (b == null) { | |||
@@ -173,6 +156,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
* last called on this input stream. If mark() has not been called this | |||
* method repositions the stream to its beginning. | |||
*/ | |||
@Override | |||
public void reset() { | |||
// Special case for reset to the start | |||
if(_marked_offset == 0 && _marked_offset_count == 0) { | |||
@@ -207,6 +191,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
_current_offset = _marked_offset; | |||
} | |||
@Override | |||
public long skip(long n) throws IOException { | |||
dieIfClosed(); | |||
if (n < 0) { | |||
@@ -249,22 +234,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
} | |||
} | |||
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(); | |||
} | |||
@Override | |||
public void readFully(byte[] buf, int off, int len) { | |||
checkAvaliable(len); | |||
@@ -282,6 +252,17 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
} | |||
} | |||
@Override | |||
public byte readByte() { | |||
return (byte) readUByte(); | |||
} | |||
@Override | |||
public double readDouble() { | |||
return Double.longBitsToDouble(readLong()); | |||
} | |||
@Override | |||
public long readLong() { | |||
checkAvaliable(SIZE_LONG); | |||
byte[] data = new byte[SIZE_LONG]; | |||
@@ -289,6 +270,12 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
return LittleEndian.getLong(data, 0); | |||
} | |||
@Override | |||
public short readShort() { | |||
return (short) readUShort(); | |||
} | |||
@Override | |||
public int readInt() { | |||
checkAvaliable(SIZE_INT); | |||
byte[] data = new byte[SIZE_INT]; | |||
@@ -296,6 +283,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
return LittleEndian.getInt(data); | |||
} | |||
@Override | |||
public int readUShort() { | |||
checkAvaliable(SIZE_SHORT); | |||
byte[] data = new byte[SIZE_SHORT]; | |||
@@ -303,6 +291,7 @@ public final class NDocumentInputStream extends InputStream implements LittleEnd | |||
return LittleEndian.getShort(data); | |||
} | |||
@Override | |||
public int readUByte() { | |||
checkAvaliable(1); | |||
byte[] data = new byte[1]; |
@@ -0,0 +1,321 @@ | |||
/* ==================================================================== | |||
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.filesystem; | |||
import java.io.IOException; | |||
import org.apache.poi.poifs.storage.DataInputBlock; | |||
/** | |||
* This class provides methods to read a DocumentEntry managed by a | |||
* {@link POIFSFileSystem} instance. | |||
* | |||
* @author Marc Johnson (mjohnson at apache dot org) | |||
*/ | |||
public final class ODocumentInputStream extends DocumentInputStream { | |||
/** 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 ODocumentInputStream(DocumentEntry document) throws IOException { | |||
if (!(document instanceof DocumentNode)) { | |||
throw new IOException("Cannot open internal document storage"); | |||
} | |||
DocumentNode documentNode = (DocumentNode)document; | |||
if(documentNode.getDocument() == null) { | |||
throw new IOException("Cannot open internal document storage"); | |||
} | |||
_current_offset = 0; | |||
_marked_offset = 0; | |||
_document_size = document.getSize(); | |||
_closed = false; | |||
_document = documentNode.getDocument(); | |||
_currentBlock = getDataInputBlock(0); | |||
} | |||
/** | |||
* Create an InputStream from the specified Document | |||
* | |||
* @param document the Document to be read | |||
*/ | |||
public ODocumentInputStream(POIFSDocument document) { | |||
_current_offset = 0; | |||
_marked_offset = 0; | |||
_document_size = document.getSize(); | |||
_closed = false; | |||
_document = document; | |||
_currentBlock = getDataInputBlock(0); | |||
} | |||
@Override | |||
public int available() { | |||
if (_closed) { | |||
throw new IllegalStateException("cannot perform requested operation on a closed stream"); | |||
} | |||
return _document_size - _current_offset; | |||
} | |||
@Override | |||
public void close() { | |||
_closed = true; | |||
} | |||
@Override | |||
public void mark(int ignoredReadlimit) { | |||
_marked_offset = _current_offset; | |||
} | |||
private DataInputBlock getDataInputBlock(int offset) { | |||
return _document.getDataInputBlock(offset); | |||
} | |||
@Override | |||
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; | |||
} | |||
@Override | |||
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. | |||
*/ | |||
@Override | |||
public void reset() { | |||
_current_offset = _marked_offset; | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
} | |||
@Override | |||
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 IllegalStateException("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"); | |||
} | |||
} | |||
@Override | |||
public byte readByte() { | |||
return (byte) readUByte(); | |||
} | |||
@Override | |||
public double readDouble() { | |||
return Double.longBitsToDouble(readLong()); | |||
} | |||
@Override | |||
public short readShort() { | |||
return (short) readUShort(); | |||
} | |||
@Override | |||
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(); | |||
} | |||
} | |||
} | |||
@Override | |||
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; | |||
} | |||
@Override | |||
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; | |||
} | |||
@Override | |||
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; | |||
} | |||
@Override | |||
public int readUByte() { | |||
checkAvaliable(1); | |||
int result = _currentBlock.readUByte(); | |||
_current_offset++; | |||
if (_currentBlock.available() < 1) { | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
} | |||
return result; | |||
} | |||
} |