]> source.dussan.org Git - poi.git/commitdiff
More BATBlock related methods for identifying size and list offsets, plus tests
authorNick Burch <nick@apache.org>
Wed, 22 Dec 2010 08:41:28 +0000 (08:41 +0000)
committerNick Burch <nick@apache.org>
Wed, 22 Dec 2010 08:41:28 +0000 (08:41 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1051791 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/poifs/storage/BATBlock.java
src/testcases/org/apache/poi/poifs/storage/TestBATBlock.java

index 23188c1c0206b1d3f81fe8c4b4ec8b30522842d5..3c4bb68a4639c166edae63850ef7b4920a95b37a 100644 (file)
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.List;
 
 import org.apache.poi.poifs.common.POIFSBigBlockSize;
 import org.apache.poi.poifs.common.POIFSConstants;
@@ -194,7 +195,6 @@ public final class BATBlock extends BigBlock {
      *
      * @return the number of BATBlocks needed
      */
-
     public static int calculateStorageRequirements(final POIFSBigBlockSize bigBlockSize, final int entryCount)
     {
         int _entries_per_block = bigBlockSize.getBATEntriesPerBlock();
@@ -209,14 +209,62 @@ public final class BATBlock extends BigBlock {
      *
      * @return the number of XBATBlocks needed
      */
-
     public static int calculateXBATStorageRequirements(final POIFSBigBlockSize bigBlockSize, final int entryCount)
     {
         int _entries_per_xbat_block = bigBlockSize.getXBATEntriesPerBlock();
         return (entryCount + _entries_per_xbat_block - 1)
                / _entries_per_xbat_block;
     }
+    
+    /**
+     * Calculates the maximum size of a file which is addressable given the
+     *  number of FAT (BAT and XBAT) sectors specified.
+     * The actual file size will be between [size of fatCount-1 blocks] and
+     *  [size of fatCount blocks].
+     * For 512 byte block sizes, this means we may over-estimate by up to 65kb.
+     * For 4096 byte block sizes, this means we may over-estimate by up to 4mb
+     */
+    public static int calculateMaximumSize(final POIFSBigBlockSize bigBlockSize,
+          final int numBAT, final int numXBAT) {
+       int size = 1; // Header isn't FAT addressed
+       size += (numBAT * bigBlockSize.getBATEntriesPerBlock());
+       size += (numXBAT * bigBlockSize.getXBATEntriesPerBlock());
+       return size * bigBlockSize.getBigBlockSize();
+    }
+    public static int calculateMaximumSize(final HeaderBlock header)
+    {
+       return calculateMaximumSize(header.getBigBlockSize(), header.getBATCount(), header.getXBATCount());
+    }
 
+    /**
+     * Returns the BATBlock that handles the specified offset,
+     *  and the relative index within it.
+     * The List of BATBlocks must be in sequential order
+     */
+    public static BATBlockAndIndex getBATBlockAndIndex(final int offset, 
+                final HeaderBlock header, final List<BATBlock> blocks) {
+       POIFSBigBlockSize bigBlockSize = header.getBigBlockSize();
+       
+       // Are we in the BAT or XBAT range
+       int batRangeEndsAt = bigBlockSize.getBATEntriesPerBlock() *
+                            header.getBATCount();
+       
+       if(offset < batRangeEndsAt) {
+          int whichBAT = (int)Math.floor(offset / bigBlockSize.getBATEntriesPerBlock());
+          int index = offset % bigBlockSize.getBATEntriesPerBlock();
+          return new BATBlockAndIndex( index, blocks.get(whichBAT) );
+       }
+       
+       // XBATs hold slightly less
+       int relOffset = offset - batRangeEndsAt;
+       int whichXBAT = (int)Math.floor(relOffset / bigBlockSize.getXBATEntriesPerBlock());
+       int index = relOffset % bigBlockSize.getXBATEntriesPerBlock();
+       return new BATBlockAndIndex(
+             index,
+             blocks.get(header.getBATCount() + whichXBAT)
+       );
+    }
+    
     private void setXBATChain(final POIFSBigBlockSize bigBlockSize, int chainIndex)
     {
         int _entries_per_xbat_block = bigBlockSize.getXBATEntriesPerBlock();
@@ -277,5 +325,21 @@ public final class BATBlock extends BigBlock {
     }
 
     /* **********  END  extension of BigBlock ********** */
-}   // end public class BATBlock
+    
+    
+    public static class BATBlockAndIndex {
+       private final int index;
+       private final BATBlock block;
+       private BATBlockAndIndex(int index, BATBlock block) {
+          this.index = index;
+          this.block = block;
+       }
+       public int getIndex() {
+          return index;
+       }
+       public BATBlock getBlock() {
+          return block;
+       }
+    }
+}
 
index c0b8b88135a53924d0c681ed9805535a23c7d600..82b49e11547543b50845e367bd6952b14cfd6ded 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.poifs.storage;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import org.apache.poi.poifs.common.POIFSConstants;
 
@@ -208,4 +211,175 @@ public final class TestBATBlock extends TestCase {
     public void testGetXBATChainOffset() {
         assertEquals(508, POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS.getNextXBATChainOffset());
     }
+    
+    public void testCalculateMaximumSize() throws Exception {
+       // Zero fat blocks isn't technically valid, but it'd be header only
+       assertEquals(
+             512, 
+             BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 0, 0)
+       );
+       assertEquals(
+             4096, 
+             BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 0, 0)
+       );
+       
+       // A single FAT block can address 128/1024 blocks
+       assertEquals(
+             512 + 512*128, 
+             BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 1, 0)
+       );
+       assertEquals(
+             4096 + 4096*1024, 
+             BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 1, 0)
+       );
+       
+       assertEquals(
+             512 + 4*512*128, 
+             BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 4, 0)
+       );
+       assertEquals(
+             4096 + 4*4096*1024, 
+             BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 4, 0)
+       );
+       
+       // Once we get into XBAT blocks, they address a little bit less
+       assertEquals(
+             512 + 109*512*128, 
+             BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 109, 0)
+       );
+       assertEquals(
+             4096 + 109*4096*1024, 
+             BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 109, 0)
+       );
+             
+       assertEquals(
+             512 + 109*512*128 + 512*127, 
+             BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 109, 1)
+       );
+       assertEquals(
+             4096 + 109*4096*1024 + 4096*1023, 
+             BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 109, 1)
+       );
+       
+       assertEquals(
+             512 + 109*512*128 + 3*512*127, 
+             BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 109, 3)
+       );
+       assertEquals(
+             4096 + 109*4096*1024 + 3*4096*1023, 
+             BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 109, 3)
+       );
+    }
+    
+    public void testGetBATBlockAndIndex() throws Exception {
+       HeaderBlock header = new HeaderBlock(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
+       List<BATBlock> blocks = new ArrayList<BATBlock>();
+       int offset;
+       
+       
+       // First, try a one BAT block file
+       header.setBATCount(1);
+       blocks.add(
+             BATBlock.createBATBlock(header.getBigBlockSize(), ByteBuffer.allocate(512))
+       );
+       
+       offset = 0;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 1;
+       assertEquals(1, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 127;
+       assertEquals(127, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       
+       // Now go for one with multiple BAT blocks
+       header.setBATCount(2);
+       blocks.add(
+             BATBlock.createBATBlock(header.getBigBlockSize(), ByteBuffer.allocate(512))
+       );
+       
+       offset = 0;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 127;
+       assertEquals(127, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 128;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(1, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 129;
+       assertEquals(1, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(1, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       
+       // And finally one with XBATs too
+       // This is a naughty file, but we should be able to cope...
+       // (We'll decide everything is XBAT not BAT)
+       header.setBATCount(0);
+       offset = 0;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 126;
+       assertEquals(126, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 127;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(1, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 128;
+       assertEquals(1, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(1, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 129;
+       assertEquals(2, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(1, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       
+       // Check with the bigger block size too
+       header = new HeaderBlock(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS);
+       
+       offset = 0;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 1022;
+       assertEquals(1022, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 1023;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(1, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 1024;
+       assertEquals(1, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(1, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+
+       // Biggr block size, back to real BATs
+       header.setBATCount(2);
+       
+       offset = 0;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 1022;
+       assertEquals(1022, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 1023;
+       assertEquals(1023, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(0, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+       
+       offset = 1024;
+       assertEquals(0, BATBlock.getBATBlockAndIndex(offset, header, blocks).getIndex());
+       assertEquals(1, blocks.indexOf( BATBlock.getBATBlockAndIndex(offset, header, blocks).getBlock() ));
+    }
 }