From 0181d2abd90142edc4572ca3acdc88cefeb6a3a4 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 10 Jun 2020 17:30:28 +0000 Subject: [PATCH] [github-182] Fix root property size calculation. Thanks to netmackan. This closes #182 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1878721 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/poifs/filesystem/POIFSMiniStore.java | 2 +- .../apache/poi/poifs/storage/BATBlock.java | 24 +- .../poi/poifs/storage/TestBATBlock.java | 433 ++++++++++-------- 3 files changed, 271 insertions(+), 188 deletions(-) diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java b/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java index 069ebc0292..c381c517ab 100644 --- a/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java +++ b/src/java/org/apache/poi/poifs/filesystem/POIFSMiniStore.java @@ -252,7 +252,7 @@ public class POIFSMiniStore extends BlockStore if (!sbat.hasFreeSectors()) { blocksUsed += _filesystem.getBigBlockSizeDetails().getBATEntriesPerBlock(); } else { - blocksUsed += sbat.getUsedSectors(false); + blocksUsed += sbat.getOccupiedSize(); } } // Set the size on the root in terms of the number of SBAT blocks diff --git a/src/java/org/apache/poi/poifs/storage/BATBlock.java b/src/java/org/apache/poi/poifs/storage/BATBlock.java index 4a2f3f3b8f..9ef00ad1f5 100644 --- a/src/java/org/apache/poi/poifs/storage/BATBlock.java +++ b/src/java/org/apache/poi/poifs/storage/BATBlock.java @@ -192,6 +192,27 @@ public final class BATBlock implements BlockWritable { return usedSectors; } + /** + * How much of this block is occupied?. + * This counts the number of sectors up and including the last used sector. + * Note that this is different from {@link #getUsedSectors(boolean)} which + * could be smaller as it does not count unused sectors where there are + * used ones after it (i.e. fragmentation). + * + * @since POI 5.0.0 + */ + public int getOccupiedSize() { + int usedSectors = _values.length; + for (int k = _values.length - 1; k >= 0; k--) { + if(_values[k] == POIFSConstants.UNUSED_BLOCK) { + usedSectors--; + } else { + break; + } + } + return usedSectors; + } + public int getValueAt(int relativeOffset) { if(relativeOffset >= _values.length) { throw new ArrayIndexOutOfBoundsException( @@ -201,6 +222,7 @@ public final class BATBlock implements BlockWritable { } return _values[relativeOffset]; } + public void setValueAt(int relativeOffset, int value) { int oldValue = _values[relativeOffset]; _values[relativeOffset] = value; @@ -221,6 +243,7 @@ public final class BATBlock implements BlockWritable { public void setOurBlockIndex(int index) { this.ourBlockIndex = index; } + /** * Retrieve where in the file we live */ @@ -237,7 +260,6 @@ public final class BATBlock implements BlockWritable { * @exception IOException on problems writing to the specified * stream */ - public void writeBlocks(final OutputStream stream) throws IOException { // Save it out stream.write( serialize() ); diff --git a/src/testcases/org/apache/poi/poifs/storage/TestBATBlock.java b/src/testcases/org/apache/poi/poifs/storage/TestBATBlock.java index 72afb0346b..10689b2e21 100644 --- a/src/testcases/org/apache/poi/poifs/storage/TestBATBlock.java +++ b/src/testcases/org/apache/poi/poifs/storage/TestBATBlock.java @@ -37,7 +37,7 @@ public final class TestBATBlock { @Test public void testEntriesPerBlock() { - assertEquals(128, POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS.getBATEntriesPerBlock()); + assertEquals(128, POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS.getBATEntriesPerBlock()); } @Test @@ -52,81 +52,81 @@ public final class TestBATBlock { @Test public void testCalculateMaximumSize() { - // Zero fat blocks isn't technically valid, but it'd be header only - assertEquals( - 512, - BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 0) - ); - assertEquals( - 4096, - BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 0) - ); - - // A single FAT block can address 128/1024 blocks - assertEquals( - 512 + 512*128, - BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 1) - ); - assertEquals( - 4096 + 4096*1024, - BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 1) - ); - - assertEquals( - 512 + 4*512*128, - BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 4) - ); - assertEquals( - 4096 + 4*4096*1024, - BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 4) - ); - - // One XBAT block holds 127/1023 individual BAT blocks, so they can address - // a fairly hefty amount of space themselves - // However, the BATs continue as before - assertEquals( - 512 + 109*512*128, - BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 109) - ); - assertEquals( - 4096 + 109*4096*1024, - BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 109) - ); - - assertEquals( - 512 + 110*512*128, - BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 110) - ); - assertEquals( - 4096 + 110*4096*1024, - BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 110) - ); - - assertEquals( - 512 + 112*512*128, - BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 112) - ); - assertEquals( - 4096 + 112*4096*1024, - BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 112) - ); - - // Check for >2gb, which we only support via a File - assertEquals( - 512 + 8030L *512*128, - BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 8030) - ); - assertEquals( - 4096 + 8030L *4096*1024, - BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 8030) - ); + // Zero fat blocks isn't technically valid, but it'd be header only + assertEquals( + 512, + BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 0) + ); + assertEquals( + 4096, + BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 0) + ); + + // A single FAT block can address 128/1024 blocks + assertEquals( + 512 + 512 * 128, + BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 1) + ); + assertEquals( + 4096 + 4096 * 1024, + BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 1) + ); + + assertEquals( + 512 + 4 * 512 * 128, + BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 4) + ); + assertEquals( + 4096 + 4 * 4096 * 1024, + BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 4) + ); + + // One XBAT block holds 127/1023 individual BAT blocks, so they can address + // a fairly hefty amount of space themselves + // However, the BATs continue as before + assertEquals( + 512 + 109 * 512 * 128, + BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 109) + ); + assertEquals( + 4096 + 109 * 4096 * 1024, + BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 109) + ); + + assertEquals( + 512 + 110 * 512 * 128, + BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 110) + ); + assertEquals( + 4096 + 110 * 4096 * 1024, + BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 110) + ); + + assertEquals( + 512 + 112 * 512 * 128, + BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 112) + ); + assertEquals( + 4096 + 112 * 4096 * 1024, + BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 112) + ); + + // Check for >2gb, which we only support via a File + assertEquals( + 512 + 8030L * 512 * 128, + BATBlock.calculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 8030) + ); + assertEquals( + 4096 + 8030L * 4096 * 1024, + BATBlock.calculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 8030) + ); } @Test public void testUsedSectors() { POIFSBigBlockSize b512 = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS; POIFSBigBlockSize b4096 = POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS; - + // Try first with 512 block sizes, which can hold 128 entries BATBlock block512 = BATBlock.createEmptyBATBlock(b512, false); assertTrue(block512.hasFreeSectors()); @@ -138,36 +138,36 @@ public final class TestBATBlock { block512.setValueAt(20, 42); assertTrue(block512.hasFreeSectors()); assertEquals(3, block512.getUsedSectors(false)); - + // Allocate all - for (int i=0; i blocks = new ArrayList<>(); - 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() )); - - - // The XBAT count makes no difference, as we flatten in memory - header.setBATCount(1); - header.setXBATCount(1); - 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(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() )); - - - // 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(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() )); + public void testOccupiedSize() { + POIFSBigBlockSize b512 = POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS; + POIFSBigBlockSize b4096 = POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS; - // 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() )); + // Try first with 512 block sizes, which can hold 128 entries + BATBlock block512 = BATBlock.createEmptyBATBlock(b512, false); + assertTrue(block512.hasFreeSectors()); + assertEquals(0, block512.getUsedSectors(false)); + + // Allocate a few + block512.setValueAt(0, 42); + block512.setValueAt(10, 42); + block512.setValueAt(20, 42); + assertTrue(block512.hasFreeSectors()); + assertEquals(21, block512.getOccupiedSize()); + + // Release one in the middle should not lower size + block512.setValueAt(10, POIFSConstants.UNUSED_BLOCK); + assertTrue(block512.hasFreeSectors()); + assertEquals(21, block512.getOccupiedSize()); + + // Release the last one should lower the size + block512.setValueAt(20, POIFSConstants.UNUSED_BLOCK); + assertTrue(block512.hasFreeSectors()); + assertEquals(1, block512.getOccupiedSize()); + + // Release first one should lower the size + block512.setValueAt(0, POIFSConstants.UNUSED_BLOCK); + assertTrue(block512.hasFreeSectors()); + assertEquals(0, block512.getOccupiedSize()); + + // Set the last one + block512.setValueAt(127, 42); + assertTrue(block512.hasFreeSectors()); + assertEquals(128, block512.getOccupiedSize()); + + block512.setValueAt(126, 42); + assertTrue(block512.hasFreeSectors()); + assertEquals(128, block512.getOccupiedSize()); + + block512.setValueAt(127, POIFSConstants.UNUSED_BLOCK); + assertTrue(block512.hasFreeSectors()); + assertEquals(127, block512.getOccupiedSize()); + + // Allocate all + for (int i = 0; i < b512.getBATEntriesPerBlock(); i++) { + block512.setValueAt(i, 82); + } + // Check + assertFalse(block512.hasFreeSectors()); + assertEquals(128, block512.getOccupiedSize()); + + // Release some in the beginning should not lower size + block512.setValueAt(0, POIFSConstants.UNUSED_BLOCK); + block512.setValueAt(1, POIFSConstants.UNUSED_BLOCK); + block512.setValueAt(13, POIFSConstants.UNUSED_BLOCK); + assertTrue(block512.hasFreeSectors()); + assertEquals(128, block512.getOccupiedSize()); + } + + @Test + public void testGetBATBlockAndIndex() { + HeaderBlock header = new HeaderBlock(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS); + List blocks = new ArrayList<>(); + 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())); + + + // The XBAT count makes no difference, as we flatten in memory + header.setBATCount(1); + header.setXBATCount(1); + 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(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())); + + + // 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(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())); + + // 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())); } }