From d004673137ef3f80e55f6438ae998b04734ace35 Mon Sep 17 00:00:00 2001
From: Nick Burch <nick@apache.org>
Date: Wed, 29 Dec 2010 07:28:10 +0000
Subject: Refactor DocumentInputStream so that it can transparently handle both
 old style and new style POIFS Documents

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1053562 13f79535-47bb-0310-9956-ffa450edef68
---
 src/java/org/apache/poi/POIDocument.java           |   2 +-
 .../apache/poi/poifs/filesystem/DirectoryNode.java |  17 +-
 .../poi/poifs/filesystem/DocumentInputStream.java  | 275 ++++--------------
 .../poi/poifs/filesystem/NDocumentInputStream.java |  69 ++---
 .../poi/poifs/filesystem/ODocumentInputStream.java | 321 +++++++++++++++++++++
 5 files changed, 410 insertions(+), 274 deletions(-)
 create mode 100644 src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java

diff --git a/src/java/org/apache/poi/POIDocument.java b/src/java/org/apache/poi/POIDocument.java
index 0bd6b1395c..6e1527e858 100644
--- a/src/java/org/apache/poi/POIDocument.java
+++ b/src/java/org/apache/poi/POIDocument.java
@@ -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) );
diff --git a/src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java b/src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java
index 29be005b8b..ea733ea879 100644
--- a/src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java
+++ b/src/java/org/apache/poi/poifs/filesystem/DirectoryNode.java
@@ -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);
     }
 
     /**
diff --git a/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java b/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
index 577f3d93b1..d39582f7ad 100644
--- a/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
+++ b/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
@@ -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();
 	}
 }
diff --git a/src/java/org/apache/poi/poifs/filesystem/NDocumentInputStream.java b/src/java/org/apache/poi/poifs/filesystem/NDocumentInputStream.java
index 2595ba0259..602cb5ff56 100644
--- a/src/java/org/apache/poi/poifs/filesystem/NDocumentInputStream.java
+++ b/src/java/org/apache/poi/poifs/filesystem/NDocumentInputStream.java
@@ -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];
diff --git a/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java b/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java
new file mode 100644
index 0000000000..a1bc5da95f
--- /dev/null
+++ b/src/java/org/apache/poi/poifs/filesystem/ODocumentInputStream.java
@@ -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;
+	}
+}
-- 
cgit v1.2.3