aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPJ Fanning <fanningpj@apache.org>2021-03-13 18:09:13 +0000
committerPJ Fanning <fanningpj@apache.org>2021-03-13 18:09:13 +0000
commitc70b649e7363040ccc7df8e7e3b8d6d37b36199b (patch)
tree4ea482ba24bbc1e6106aec970ff264879768a446 /src
parent5758145ce828a003f41cd48b78c57987fc69ac0a (diff)
downloadpoi-c70b649e7363040ccc7df8e7e3b8d6d37b36199b.tar.gz
poi-c70b649e7363040ccc7df8e7e3b8d6d37b36199b.zip
[bug-65184] Improve performance of POFSMiniStore getBlockAt. Thanks to sits
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1887604 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java304
1 files changed, 151 insertions, 153 deletions
diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java b/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java
index c381c517ab..701dad68a4 100644
--- a/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java
+++ b/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -33,141 +32,140 @@ import org.apache.poi.poifs.storage.HeaderBlock;
/**
* This class handles the MiniStream (small block store)
- * in the NIO case for {@link POIFSFileSystem}
+ * in the NIO case for {@link POIFSFileSystem}
*/
-public class POIFSMiniStore extends BlockStore
-{
+public class POIFSMiniStore extends BlockStore {
private final POIFSFileSystem _filesystem;
private POIFSStream _mini_stream;
- private final List<BATBlock> _sbat_blocks;
- private final HeaderBlock _header;
- private final RootProperty _root;
+ private final List<BATBlock> _sbat_blocks;
+ private final HeaderBlock _header;
+ private final RootProperty _root;
POIFSMiniStore(POIFSFileSystem filesystem, RootProperty root,
- List<BATBlock> sbats, HeaderBlock header)
- {
- this._filesystem = filesystem;
- this._sbat_blocks = sbats;
- this._header = header;
- this._root = root;
-
- this._mini_stream = new POIFSStream(filesystem, root.getStartBlock());
+ List<BATBlock> sbats, HeaderBlock header) {
+ this._filesystem = filesystem;
+ this._sbat_blocks = sbats;
+ this._header = header;
+ this._root = root;
+
+ this._mini_stream = new POIFSStream(filesystem, root.getStartBlock());
}
/**
* Load the block at the given offset.
*/
protected ByteBuffer getBlockAt(final int offset) {
- // Which big block is this?
- int byteOffset = offset * POIFSConstants.SMALL_BLOCK_SIZE;
- int bigBlockNumber = byteOffset / _filesystem.getBigBlockSize();
- int bigBlockOffset = byteOffset % _filesystem.getBigBlockSize();
-
- // Now locate the data block for it
- Iterator<ByteBuffer> it = _mini_stream.getBlockIterator();
- 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;
+ // Which big block is this?
+ int byteOffset = offset * POIFSConstants.SMALL_BLOCK_SIZE;
+ int bigBlockNumber = byteOffset / _filesystem.getBigBlockSize();
+ int bigBlockOffset = byteOffset % _filesystem.getBigBlockSize();
+
+ // Now locate the data block for it
+ Iterator<ByteBuffer> it = _mini_stream.getBlockIterator();
+ 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;
}
/**
* Load the block, extending the underlying stream if needed
*/
protected ByteBuffer createBlockIfNeeded(final int offset) throws IOException {
- boolean firstInStore = false;
- if (_mini_stream.getStartBlock() == POIFSConstants.END_OF_CHAIN) {
- firstInStore = true;
- }
-
- // Try to get it without extending the stream
- if (! firstInStore) {
- try {
- return getBlockAt(offset);
- } catch(NoSuchElementException ignored) {}
- }
-
- // Need to extend the stream
- // TODO Replace this with proper append support
- // For now, do the extending by hand...
-
- // Ask for another block
- int newBigBlock = _filesystem.getFreeBlock();
- _filesystem.createBlockIfNeeded(newBigBlock);
-
- // If we are the first block to be allocated, initialise the stream
- if (firstInStore) {
- _filesystem._get_property_table().getRoot().setStartBlock(newBigBlock);
- _mini_stream = new POIFSStream(_filesystem, newBigBlock);
- } else {
- // Tack it onto the end of our chain
- ChainLoopDetector loopDetector = _filesystem.getChainLoopDetector();
- int block = _mini_stream.getStartBlock();
- while(true) {
- loopDetector.claim(block);
- int next = _filesystem.getNextBlock(block);
- if(next == POIFSConstants.END_OF_CHAIN) {
- break;
- }
- block = next;
- }
- _filesystem.setNextBlock(block, newBigBlock);
- }
-
- // This is now the new end
- _filesystem.setNextBlock(newBigBlock, POIFSConstants.END_OF_CHAIN);
-
- // Now try again, to get the real small block
- return createBlockIfNeeded(offset);
+ boolean firstInStore = false;
+ if (_mini_stream.getStartBlock() == POIFSConstants.END_OF_CHAIN) {
+ firstInStore = true;
+ }
+
+ // Try to get it without extending the stream
+ if (!firstInStore) {
+ try {
+ return getBlockAt(offset);
+ } catch (NoSuchElementException ignored) {
+ }
+ }
+
+ // Need to extend the stream
+ // TODO Replace this with proper append support
+ // For now, do the extending by hand...
+
+ // Ask for another block
+ int newBigBlock = _filesystem.getFreeBlock();
+ _filesystem.createBlockIfNeeded(newBigBlock);
+
+ // If we are the first block to be allocated, initialise the stream
+ if (firstInStore) {
+ _filesystem._get_property_table().getRoot().setStartBlock(newBigBlock);
+ _mini_stream = new POIFSStream(_filesystem, newBigBlock);
+ } else {
+ // Tack it onto the end of our chain
+ ChainLoopDetector loopDetector = _filesystem.getChainLoopDetector();
+ int block = _mini_stream.getStartBlock();
+ while (true) {
+ loopDetector.claim(block);
+ int next = _filesystem.getNextBlock(block);
+ if (next == POIFSConstants.END_OF_CHAIN) {
+ break;
+ }
+ block = next;
+ }
+ _filesystem.setNextBlock(block, newBigBlock);
+ }
+
+ // This is now the new end
+ _filesystem.setNextBlock(newBigBlock, POIFSConstants.END_OF_CHAIN);
+
+ // Now try again, to get the real small block
+ return createBlockIfNeeded(offset);
}
/**
* Returns the BATBlock that handles the specified offset,
- * and the relative index within it
+ * and the relative index within it
*/
protected BATBlockAndIndex getBATBlockAndIndex(final int offset) {
- return BATBlock.getSBATBlockAndIndex(
- offset, _header, _sbat_blocks
- );
+ return BATBlock.getSBATBlockAndIndex(
+ offset, _header, _sbat_blocks
+ );
}
/**
* Works out what block follows the specified one.
*/
protected int getNextBlock(final int offset) {
- BATBlockAndIndex bai = getBATBlockAndIndex(offset);
- return bai.getBlock().getValueAt( bai.getIndex() );
+ BATBlockAndIndex bai = getBATBlockAndIndex(offset);
+ return bai.getBlock().getValueAt(bai.getIndex());
}
/**
* Changes the record of what block follows the specified one.
*/
protected void setNextBlock(final int offset, final int nextBlock) {
- BATBlockAndIndex bai = getBATBlockAndIndex(offset);
- bai.getBlock().setValueAt(
- bai.getIndex(), nextBlock
- );
+ BATBlockAndIndex bai = getBATBlockAndIndex(offset);
+ bai.getBlock().setValueAt(
+ bai.getIndex(), nextBlock
+ );
}
/**
* Finds a free block, and returns its offset.
* This method will extend the file if needed, and if doing
- * so, allocate new FAT blocks to address the extra space.
+ * so, allocate new FAT blocks to address the extra space.
*/
protected int getFreeBlock() throws IOException {
- int sectorsPerSBAT = _filesystem.getBigBlockSizeDetails().getBATEntriesPerBlock();
+ int sectorsPerSBAT = _filesystem.getBigBlockSizeDetails().getBATEntriesPerBlock();
- // First up, do we have any spare ones?
- int offset = 0;
+ // First up, do we have any spare ones?
+ int offset = 0;
for (BATBlock sbat : _sbat_blocks) {
// Check this one
if (sbat.hasFreeSectors()) {
@@ -185,79 +183,79 @@ public class POIFSMiniStore extends BlockStore
offset += sectorsPerSBAT;
}
- // If we get here, then there aren't any
- // free sectors in any of the SBATs
- // So, we need to extend the chain and add another
-
- // Create a new BATBlock
- BATBlock newSBAT = BATBlock.createEmptyBATBlock(_filesystem.getBigBlockSizeDetails(), false);
- int batForSBAT = _filesystem.getFreeBlock();
- newSBAT.setOurBlockIndex(batForSBAT);
-
- // Are we the first SBAT?
- if(_header.getSBATCount() == 0) {
- // Tell the header that we've got our first SBAT there
- _header.setSBATStart(batForSBAT);
- _header.setSBATBlockCount(1);
- } else {
- // Find the end of the SBAT stream, and add the sbat in there
- ChainLoopDetector loopDetector = _filesystem.getChainLoopDetector();
- int batOffset = _header.getSBATStart();
- while(true) {
- loopDetector.claim(batOffset);
- int nextBat = _filesystem.getNextBlock(batOffset);
- if(nextBat == POIFSConstants.END_OF_CHAIN) {
- break;
- }
- batOffset = nextBat;
- }
-
- // Add it in at the end
- _filesystem.setNextBlock(batOffset, batForSBAT);
-
- // And update the count
- _header.setSBATBlockCount(
- _header.getSBATCount() + 1
- );
- }
-
- // Finish allocating
- _filesystem.setNextBlock(batForSBAT, POIFSConstants.END_OF_CHAIN);
- _sbat_blocks.add(newSBAT);
-
- // Return our first spot
- return offset;
+ // If we get here, then there aren't any
+ // free sectors in any of the SBATs
+ // So, we need to extend the chain and add another
+
+ // Create a new BATBlock
+ BATBlock newSBAT = BATBlock.createEmptyBATBlock(_filesystem.getBigBlockSizeDetails(), false);
+ int batForSBAT = _filesystem.getFreeBlock();
+ newSBAT.setOurBlockIndex(batForSBAT);
+
+ // Are we the first SBAT?
+ if (_header.getSBATCount() == 0) {
+ // Tell the header that we've got our first SBAT there
+ _header.setSBATStart(batForSBAT);
+ _header.setSBATBlockCount(1);
+ } else {
+ // Find the end of the SBAT stream, and add the sbat in there
+ ChainLoopDetector loopDetector = _filesystem.getChainLoopDetector();
+ int batOffset = _header.getSBATStart();
+ while (true) {
+ loopDetector.claim(batOffset);
+ int nextBat = _filesystem.getNextBlock(batOffset);
+ if (nextBat == POIFSConstants.END_OF_CHAIN) {
+ break;
+ }
+ batOffset = nextBat;
+ }
+
+ // Add it in at the end
+ _filesystem.setNextBlock(batOffset, batForSBAT);
+
+ // And update the count
+ _header.setSBATBlockCount(
+ _header.getSBATCount() + 1
+ );
+ }
+
+ // Finish allocating
+ _filesystem.setNextBlock(batForSBAT, POIFSConstants.END_OF_CHAIN);
+ _sbat_blocks.add(newSBAT);
+
+ // Return our first spot
+ return offset;
}
@Override
protected ChainLoopDetector getChainLoopDetector() {
- return new ChainLoopDetector( _root.getSize() );
+ return new ChainLoopDetector(_root.getSize());
}
protected int getBlockStoreBlockSize() {
- return POIFSConstants.SMALL_BLOCK_SIZE;
+ return POIFSConstants.SMALL_BLOCK_SIZE;
}
/**
* Writes the SBATs to their backing blocks, and updates
- * the mini-stream size in the properties. Stream size is
- * based on full blocks used, not the data within the streams
+ * the mini-stream size in the properties. Stream size is
+ * based on full blocks used, not the data within the streams
*/
void syncWithDataSource() throws IOException {
- int blocksUsed = 0;
- for (BATBlock sbat : _sbat_blocks) {
- ByteBuffer block = _filesystem.getBlockAt(sbat.getOurBlockIndex());
- sbat.writeData(block);
-
- if (!sbat.hasFreeSectors()) {
- blocksUsed += _filesystem.getBigBlockSizeDetails().getBATEntriesPerBlock();
- } else {
- blocksUsed += sbat.getOccupiedSize();
- }
- }
- // Set the size on the root in terms of the number of SBAT blocks
- // RootProperty.setSize does the sbat -> bytes conversion for us
- _filesystem._get_property_table().getRoot().setSize(blocksUsed);
+ int blocksUsed = 0;
+ for (BATBlock sbat : _sbat_blocks) {
+ ByteBuffer block = _filesystem.getBlockAt(sbat.getOurBlockIndex());
+ sbat.writeData(block);
+
+ if (!sbat.hasFreeSectors()) {
+ blocksUsed += _filesystem.getBigBlockSizeDetails().getBATEntriesPerBlock();
+ } else {
+ blocksUsed += sbat.getOccupiedSize();
+ }
+ }
+ // Set the size on the root in terms of the number of SBAT blocks
+ // RootProperty.setSize does the sbat -> bytes conversion for us
+ _filesystem._get_property_table().getRoot().setSize(blocksUsed);
}
@Override