[bug-65184] Improve performance of POFSMiniStore getBlockAt. Thanks to sits

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1887657 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java b/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java
index 701dad6..623bd99 100644
--- a/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java
+++ b/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java
@@ -61,20 +61,25 @@
         int bigBlockOffset = byteOffset % _filesystem.getBigBlockSize();
 
         // Now locate the data block for it
-        Iterator<ByteBuffer> it = _mini_stream.getBlockIterator();
+        Iterator<Integer> it = _mini_stream.getBlockOffsetIterator();
         for (int i = 0; i < bigBlockNumber; i++) {
             it.next();
         }
-        ByteBuffer dataBlock = it.next();
-        assert (dataBlock != null);
 
-        // Position ourselves, and take a slice
-        dataBlock.position(
-                dataBlock.position() + bigBlockOffset
-        );
-        ByteBuffer miniBuffer = dataBlock.slice();
-        miniBuffer.limit(POIFSConstants.SMALL_BLOCK_SIZE);
-        return miniBuffer;
+        try {
+            ByteBuffer dataBlock = _filesystem.getBlockAt(it.next());
+            assert(dataBlock != null);
+
+            // Position ourselves, and take a slice
+            dataBlock.position(
+                    dataBlock.position() + bigBlockOffset
+            );
+            ByteBuffer miniBuffer = dataBlock.slice();
+            miniBuffer.limit(POIFSConstants.SMALL_BLOCK_SIZE);
+            return miniBuffer;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     /**
diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSStream.java b/src/java/org/apache/poi/poifs/filesystem/POIFSStream.java
index 356b78c..8d8f7c5 100644
--- a/src/java/org/apache/poi/poifs/filesystem/POIFSStream.java
+++ b/src/java/org/apache/poi/poifs/filesystem/POIFSStream.java
@@ -96,6 +96,15 @@
       return new StreamBlockByteBufferIterator(startBlock);
    }
 
+   Iterator<Integer> getBlockOffsetIterator() {
+      if(startBlock == POIFSConstants.END_OF_CHAIN) {
+         throw new IllegalStateException(
+               "Can't read from a new stream before it has been written to"
+         );
+      }
+      return new StreamBlockOffsetIterator(startBlock);
+   }
+
    /**
     * Updates the contents of the stream to the new
     *  set of bytes.
@@ -140,11 +149,11 @@
    /**
     * Class that handles a streaming read of one stream
     */
-   private class StreamBlockByteBufferIterator implements Iterator<ByteBuffer> {
+   private class StreamBlockOffsetIterator implements Iterator<Integer> {
       private final ChainLoopDetector loopDetector;
       private int nextBlock;
 
-      StreamBlockByteBufferIterator(int firstBlock) {
+       StreamBlockOffsetIterator(int firstBlock) {
          this.nextBlock = firstBlock;
          try {
             this.loopDetector = blockStore.getChainLoopDetector();
@@ -157,19 +166,15 @@
           return nextBlock != POIFSConstants.END_OF_CHAIN;
       }
 
-      public ByteBuffer next() {
+      public Integer next() {
          if (!hasNext()) {
             throw new NoSuchElementException("Can't read past the end of the stream");
          }
 
-         try {
-            loopDetector.claim(nextBlock);
-            ByteBuffer data = blockStore.getBlockAt(nextBlock);
-            nextBlock = blockStore.getNextBlock(nextBlock);
-            return data;
-         } catch(IOException e) {
-            throw new RuntimeException(e);
-         }
+          loopDetector.claim(nextBlock);
+          int currentBlock = nextBlock;
+          nextBlock = blockStore.getNextBlock(nextBlock);
+          return currentBlock;
       }
 
       public void remove() {
@@ -177,6 +182,38 @@
       }
    }
 
+    /**
+     * Class that handles a streaming read of one stream
+     */
+    private class StreamBlockByteBufferIterator implements Iterator<ByteBuffer> {
+        private final StreamBlockOffsetIterator offsetIterator;
+
+        StreamBlockByteBufferIterator(int firstBlock) {
+            offsetIterator = new StreamBlockOffsetIterator(firstBlock);
+        }
+
+        public boolean hasNext() {
+            return offsetIterator.hasNext();
+        }
+
+        public ByteBuffer next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException("Can't read past the end of the stream");
+            }
+
+            try {
+                return blockStore.getBlockAt(offsetIterator.next());
+            } catch(IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+
    protected class StreamBlockByteBuffer extends OutputStream {
        byte[] oneByte = new byte[1];
        ByteBuffer buffer;