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;
*
* @return the number of BATBlocks needed
*/
-
public static int calculateStorageRequirements(final POIFSBigBlockSize bigBlockSize, final int entryCount)
{
int _entries_per_block = bigBlockSize.getBATEntriesPerBlock();
*
* @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();
}
/* ********** 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;
+ }
+ }
+}
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;
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() ));
+ }
}