Пример #1
0
        public void TestCreateBATBlocks()
        {
            // Test 0 Length array (basic sanity)
            BATBlock[] rvalue = BATBlock.CreateBATBlocks(CreateTestArray(0));

            Assert.AreEqual(0, rvalue.Length);

            // Test array of Length 1
            rvalue = BATBlock.CreateBATBlocks(CreateTestArray(1));
            Assert.AreEqual(1, rvalue.Length);
            VerifyContents(rvalue, 1);

            // Test array of Length 127
            rvalue = BATBlock.CreateBATBlocks(CreateTestArray(127));
            Assert.AreEqual(1, rvalue.Length);
            VerifyContents(rvalue, 127);

            // Test array of Length 128
            rvalue = BATBlock.CreateBATBlocks(CreateTestArray(128));
            Assert.AreEqual(1, rvalue.Length);
            VerifyContents(rvalue, 128);

            // Test array of Length 129
            rvalue = BATBlock.CreateBATBlocks(CreateTestArray(129));
            Assert.AreEqual(2, rvalue.Length);
            VerifyContents(rvalue, 129);
        }
Пример #2
0
        public void TestCreateXBATBlocks()
        {
            // Test 0 Length array (basic sanity)
            BATBlock[] rvalue = BATBlock.CreateXBATBlocks(CreateTestArray(0), 1);

            Assert.AreEqual(0, rvalue.Length);

            // Test array of Length 1
            rvalue = BATBlock.CreateXBATBlocks(CreateTestArray(1), 1);
            Assert.AreEqual(1, rvalue.Length);
            verifyXBATContents(rvalue, 1, 1);

            // Test array of Length 127
            rvalue = BATBlock.CreateXBATBlocks(CreateTestArray(127), 1);
            Assert.AreEqual(1, rvalue.Length);
            verifyXBATContents(rvalue, 127, 1);

            // Test array of Length 128
            rvalue = BATBlock.CreateXBATBlocks(CreateTestArray(128), 1);
            Assert.AreEqual(2, rvalue.Length);
            verifyXBATContents(rvalue, 128, 1);

            // Test array of Length 254
            rvalue = BATBlock.CreateXBATBlocks(CreateTestArray(254), 1);
            Assert.AreEqual(2, rvalue.Length);
            verifyXBATContents(rvalue, 254, 1);

            // Test array of Length 255
            rvalue = BATBlock.CreateXBATBlocks(CreateTestArray(255), 1);
            Assert.AreEqual(3, rvalue.Length);
            verifyXBATContents(rvalue, 255, 1);
        }
Пример #3
0
        /**
         * Create a POIFSFileSystem from an <tt>InputStream</tt>.  Normally the stream is read until
         * EOF.  The stream is always closed.<p/>
         *
         * Some streams are usable After reaching EOF (typically those that return <code>true</code>
         * for <tt>markSupported()</tt>).  In the unlikely case that the caller has such a stream
         * <i>and</i> needs to use it After this constructor completes, a work around is to wrap the
         * stream in order to trap the <tt>close()</tt> call.  A convenience method (
         * <tt>CreateNonClosingInputStream()</tt>) has been provided for this purpose:
         * <pre>
         * InputStream wrappedStream = POIFSFileSystem.CreateNonClosingInputStream(is);
         * HSSFWorkbook wb = new HSSFWorkbook(wrappedStream);
         * is.Reset();
         * doSomethingElse(is);
         * </pre>
         * Note also the special case of <tt>MemoryStream</tt> for which the <tt>close()</tt>
         * method does nothing.
         * <pre>
         * MemoryStream bais = ...
         * HSSFWorkbook wb = new HSSFWorkbook(bais); // calls bais.Close() !
         * bais.Reset(); // no problem
         * doSomethingElse(bais);
         * </pre>
         *
         * @param stream the InputStream from which to read the data
         *
         * @exception IOException on errors Reading, or on invalid data
         */

        public NPOIFSFileSystem(Stream stream)
            : this(false)
        {
            Stream channel = null;
            bool   success = false;

            try
            {
                // Turn our InputStream into something NIO based
                channel = stream;

                // Get the header
                ByteBuffer headerBuffer = ByteBuffer.CreateBuffer(POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
                IOUtils.ReadFully(channel, headerBuffer.Buffer);

                // Have the header Processed
                _header = new HeaderBlock(headerBuffer);

                // Sanity check the block count
                BlockAllocationTableReader.SanityCheckBlockCount(_header.BATCount);

                // We need to buffer the whole file into memory when
                //  working with an InputStream.
                // The max possible size is when each BAT block entry is used
                int maxSize = BATBlock.CalculateMaximumSize(_header);
                //ByteBuffer data = ByteBuffer.allocate(maxSize);
                // byte[] data = new byte[maxSize];
                //// Copy in the header
                //for(int i = 0; i < headerBuffer.Length; i++)
                //{
                //    data[i] = headerBuffer[i];
                //}
                // byte[] temp = new byte[channel.Length];
                // Now read the rest of the stream

                ByteBuffer data = ByteBuffer.CreateBuffer(maxSize);
                headerBuffer.Position = 0;
                data.Write(headerBuffer.Buffer);
                data.Position = headerBuffer.Length;

                //IOUtils.ReadFully(channel, data);
                data.Position += IOUtils.ReadFully(channel, data.Buffer, data.Position, (int)channel.Length);
                success        = true;

                // Turn it into a DataSource
                _data = new ByteArrayBackedDataSource(data.Buffer, data.Position);
            }
            finally
            {
                // As per the constructor contract, always close the stream
                if (channel != null)
                {
                    channel.Close();
                }
                CloseInputStream(stream, success);
            }

            // Now process the various entries
            ReadCoreContents();
        }
Пример #4
0
        public void TestCreateBATBlocks()
        {
            // Test 0 Length array (basic sanity)
            BATBlock[] rvalue = BATBlock.CreateBATBlocks(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, CreateTestArray(0));

            Assert.AreEqual(0, rvalue.Length);

            // Test array of Length 1
            rvalue = BATBlock.CreateBATBlocks(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, CreateTestArray(1));
            Assert.AreEqual(1, rvalue.Length);
            VerifyContents(rvalue, 1);

            // Test array of Length 127
            rvalue = BATBlock.CreateBATBlocks(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, CreateTestArray(127));
            Assert.AreEqual(1, rvalue.Length);
            VerifyContents(rvalue, 127);

            // Test array of Length 128
            rvalue = BATBlock.CreateBATBlocks(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, CreateTestArray(128));
            Assert.AreEqual(1, rvalue.Length);
            VerifyContents(rvalue, 128);

            // Test array of Length 129
            rvalue = BATBlock.CreateBATBlocks(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, CreateTestArray(129));
            Assert.AreEqual(2, rvalue.Length);
            VerifyContents(rvalue, 129);
        }
Пример #5
0
        public void TestBATandXBAT()
        {
            byte[]          hugeStream = new byte[8 * 1024 * 1024];
            POIFSFileSystem fs         = new POIFSFileSystem();

            fs.Root.CreateDocument("BIG", new MemoryStream(hugeStream));

            MemoryStream baos = new MemoryStream();

            fs.WriteFileSystem(baos);
            byte[] fsData = baos.ToArray();


            // Check the header was written properly
            Stream      inp    = new MemoryStream(fsData);
            HeaderBlock header = new HeaderBlock(inp);

            Assert.AreEqual(109 + 21, header.BATCount);
            Assert.AreEqual(1, header.XBATCount);

            ByteBuffer xbatData = ByteBuffer.CreateBuffer(512);

            xbatData.Write(fsData, (1 + header.XBATIndex) * 512, 512);

            xbatData.Position = 0;

            BATBlock xbat = BATBlock.CreateBATBlock(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, xbatData);

            for (int i = 0; i < 21; i++)
            {
                Assert.IsTrue(xbat.GetValueAt(i) != POIFSConstants.UNUSED_BLOCK);
            }

            for (int i = 21; i < 127; i++)
            {
                Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, xbat.GetValueAt(i));
            }

            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, xbat.GetValueAt(127));

            RawDataBlockList blockList = new RawDataBlockList(inp, POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);

            Assert.AreEqual(fsData.Length / 512, blockList.BlockCount() + 1);
            new BlockAllocationTableReader(header.BigBlockSize,
                                           header.BATCount,
                                           header.BATArray,
                                           header.XBATCount,
                                           header.XBATIndex,
                                           blockList);
            Assert.AreEqual(fsData.Length / 512, blockList.BlockCount() + 1);

            //fs = null;
            //fs = new POIFSFileSystem(new MemoryStream(fsData));


            //DirectoryNode root = fs.Root;
            //Assert.AreEqual(1, root.EntryCount);
            //DocumentNode big = (DocumentNode)root.GetEntry("BIG");
            //Assert.AreEqual(hugeStream.Length, big.Size);
        }
Пример #6
0
        /**
         * Read and process the PropertiesTable and the
         *  FAT / XFAT blocks, so that we're Ready to
         *  work with the file
         */
        private void ReadCoreContents()
        {
            // Grab the block size
            bigBlockSize = _header.BigBlockSize;

            // Each block should only ever be used by one of the
            //  FAT, XFAT or Property Table. Ensure it does
            ChainLoopDetector loopDetector = GetChainLoopDetector();

            // Read the FAT blocks
            foreach (int fatAt in _header.BATArray)
            {
                ReadBAT(fatAt, loopDetector);
            }

            // Now read the XFAT blocks, and the FATs within them
            BATBlock xfat;
            int      nextAt = _header.XBATIndex;

            for (int i = 0; i < _header.XBATCount; i++)
            {
                loopDetector.Claim(nextAt);
                ByteBuffer fatData = GetBlockAt(nextAt);
                xfat = BATBlock.CreateBATBlock(bigBlockSize, fatData);
                xfat.OurBlockIndex = nextAt;
                nextAt             = xfat.GetValueAt(bigBlockSize.GetXBATEntriesPerBlock());
                _xbat_blocks.Add(xfat);

                for (int j = 0; j < bigBlockSize.GetXBATEntriesPerBlock(); j++)
                {
                    int fatAt = xfat.GetValueAt(j);
                    if (fatAt == POIFSConstants.UNUSED_BLOCK)
                    {
                        break;
                    }
                    ReadBAT(fatAt, loopDetector);
                }
            }

            // We're now able to load steams
            // Use this to read in the properties
            _property_table = new NPropertyTable(_header, this);

            // Finally read the Small Stream FAT (SBAT) blocks
            BATBlock        sfat;
            List <BATBlock> sbats = new List <BATBlock>();

            _mini_store = new NPOIFSMiniStore(this, _property_table.Root, sbats, _header);
            nextAt      = _header.SBATStart;
            for (int i = 0; i < _header.SBATCount; i++)
            {
                loopDetector.Claim(nextAt);
                ByteBuffer fatData = GetBlockAt(nextAt);
                sfat = BATBlock.CreateBATBlock(bigBlockSize, fatData);
                sfat.OurBlockIndex = nextAt;
                sbats.Add(sfat);
                nextAt = GetNextBlock(nextAt);
            }
        }
Пример #7
0
 private void ReadBAT(int batAt, ChainLoopDetector loopDetector)
 {
     loopDetector.Claim(batAt);
     ByteBuffer fatData = GetBlockAt(batAt);
     // byte[] fatData = GetBlockAt(batAt);
     BATBlock bat = BATBlock.CreateBATBlock(bigBlockSize, fatData);
     bat.OurBlockIndex = batAt;
     _bat_blocks.Add(bat);
 }
Пример #8
0
        /**
         * Create a POIFSFileSystem from an <tt>InputStream</tt>.  Normally the stream is read until
         * EOF.  The stream is always closed.<p/>
         *
         * Some streams are usable After reaching EOF (typically those that return <code>true</code>
         * for <tt>markSupported()</tt>).  In the unlikely case that the caller has such a stream
         * <i>and</i> needs to use it After this constructor completes, a work around is to wrap the
         * stream in order to trap the <tt>close()</tt> call.  A convenience method (
         * <tt>CreateNonClosingInputStream()</tt>) has been provided for this purpose:
         * <pre>
         * InputStream wrappedStream = POIFSFileSystem.CreateNonClosingInputStream(is);
         * HSSFWorkbook wb = new HSSFWorkbook(wrappedStream);
         * is.Reset();
         * doSomethingElse(is);
         * </pre>
         * Note also the special case of <tt>MemoryStream</tt> for which the <tt>close()</tt>
         * method does nothing.
         * <pre>
         * MemoryStream bais = ...
         * HSSFWorkbook wb = new HSSFWorkbook(bais); // calls bais.Close() !
         * bais.Reset(); // no problem
         * doSomethingElse(bais);
         * </pre>
         *
         * @param stream the InputStream from which to read the data
         *
         * @exception IOException on errors Reading, or on invalid data
         */

        public NPOIFSFileSystem(Stream stream)
            : this(false)
        {
            Stream channel = null;
            bool   success = false;

            try
            {
                // Turn our InputStream into something NIO based
                channel = stream;

                // Get the header
                ByteBuffer headerBuffer = ByteBuffer.CreateBuffer(POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
                IOUtils.ReadFully(channel, headerBuffer.Buffer);

                // Have the header Processed
                _header = new HeaderBlock(headerBuffer);

                // Sanity check the block count
                BlockAllocationTableReader.SanityCheckBlockCount(_header.BATCount);

                // We need to buffer the whole file into memory when
                //  working with an InputStream.
                // The max possible size is when each BAT block entry is used
                long maxSize = BATBlock.CalculateMaximumSize(_header);
                if (maxSize > int.MaxValue)
                {
                    throw new ArgumentException("Unable read a >2gb file via an InputStream");
                }

                ByteBuffer data = ByteBuffer.CreateBuffer((int)maxSize);
                headerBuffer.Position = 0;
                data.Write(headerBuffer.Buffer);
                data.Position = headerBuffer.Length;

                //IOUtils.ReadFully(channel, data);
                data.Position += IOUtils.ReadFully(channel, data.Buffer, data.Position, (int)channel.Length);
                success        = true;

                // Turn it into a DataSource
                _data = new ByteArrayBackedDataSource(data.Buffer, data.Position);
            }
            finally
            {
                // As per the constructor contract, always close the stream
                if (channel != null)
                {
                    channel.Close();
                    channel.Dispose();
                }
                CloseInputStream(stream, success);
            }

            // Now process the various entries
            ReadCoreContents();
        }
Пример #9
0
        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);

            Assert.AreEqual(true, block512.HasFreeSectors);
            Assert.AreEqual(0, block512.GetUsedSectors(false));
            // Allocate a few
            block512.SetValueAt(0, 42);
            block512.SetValueAt(10, 42);
            block512.SetValueAt(20, 42);
            Assert.AreEqual(true, block512.HasFreeSectors);
            Assert.AreEqual(3, block512.GetUsedSectors(false));

            // Allocate all
            for (int i = 0; i < b512.GetBATEntriesPerBlock(); i++)
            {
                block512.SetValueAt(i, 82);
            }
            // Check
            Assert.AreEqual(false, block512.HasFreeSectors);
            Assert.AreEqual(128, block512.GetUsedSectors(false));
            Assert.AreEqual(127, block512.GetUsedSectors(true));

            // Release one
            block512.SetValueAt(10, POIFSConstants.UNUSED_BLOCK);
            Assert.AreEqual(true, block512.HasFreeSectors);
            Assert.AreEqual(127, block512.GetUsedSectors(false));
            Assert.AreEqual(126, block512.GetUsedSectors(true));


            // Now repeat with 4096 block sizes
            BATBlock block4096 = BATBlock.CreateEmptyBATBlock(b4096, false);

            Assert.AreEqual(true, block4096.HasFreeSectors);
            Assert.AreEqual(0, block4096.GetUsedSectors(false));

            block4096.SetValueAt(0, 42);
            block4096.SetValueAt(10, 42);
            block4096.SetValueAt(20, 42);
            Assert.AreEqual(true, block4096.HasFreeSectors);
            Assert.AreEqual(3, block4096.GetUsedSectors(false));

            // Allocate all
            for (int i = 0; i < b4096.GetBATEntriesPerBlock(); i++)
            {
                block4096.SetValueAt(i, 82);
            }
            // Check
            Assert.AreEqual(false, block4096.HasFreeSectors);
            Assert.AreEqual(1024, block4096.GetUsedSectors(false));
            Assert.AreEqual(1023, block4096.GetUsedSectors(true));
        }
Пример #10
0
 private BATBlock CreateBAT(int offset, bool isBAT)
 {
     // Create a new BATBlock
     BATBlock newBAT = BATBlock.CreateEmptyBATBlock(bigBlockSize, !isBAT);
     newBAT.OurBlockIndex = offset;
     // Ensure there's a spot in the file for it
     ByteBuffer buffer = ByteBuffer.CreateBuffer(bigBlockSize.GetBigBlockSize());
     int WriteTo = (1 + offset) * bigBlockSize.GetBigBlockSize(); // Header isn't in BATs
     _data.Write(buffer, WriteTo);
     // All done
     return newBAT;
 }
Пример #11
0
        /**
         * Constructor, intended for writing
         */
        public NPOIFSFileSystem()
            : this(true)
        {
            // Mark us as having a single empty BAT at offset 0
            _header.BATCount = 1;
            _header.BATArray = new int[] { 0 };
            _bat_blocks.Add(BATBlock.CreateEmptyBATBlock(bigBlockSize, false));
            SetNextBlock(0, POIFSConstants.FAT_SECTOR_BLOCK);

            // Now associate the properties with the empty block
            _property_table.StartBlock = 1;
            SetNextBlock(1, POIFSConstants.END_OF_CHAIN);
        }
Пример #12
0
        /**
         * Constructor, intended for writing
         */
        public NPOIFSFileSystem()
            : this(true)
        {
            // Mark us as having a single empty BAT at offset 0
            _header.BATCount = 1;
            _header.BATArray = new int[] { 0 };

            BATBlock bb = BATBlock.CreateEmptyBATBlock(bigBlockSize, false);
            bb.OurBlockIndex = 0;
            _bat_blocks.Add(bb);

            SetNextBlock(0, POIFSConstants.FAT_SECTOR_BLOCK);
            SetNextBlock(1, POIFSConstants.END_OF_CHAIN);

            _property_table.StartBlock = (POIFSConstants.END_OF_CHAIN);
        }
Пример #13
0
        /**
         * Constructor, intended for writing
         */
        public NPOIFSFileSystem()
            : this(true)
        {
            // Reserve block 0 for the start of the Properties Table
            // Create a single empty BAT, at pop that at offset 1
            _header.BATCount = 1;
            _header.BATArray = new int[] { 1 };

            BATBlock bb = BATBlock.CreateEmptyBATBlock(bigBlockSize, false);
            bb.OurBlockIndex = 1;
            _bat_blocks.Add(bb);

            SetNextBlock(0, POIFSConstants.END_OF_CHAIN);
            SetNextBlock(1, POIFSConstants.FAT_SECTOR_BLOCK);

            _property_table.StartBlock = 0;
        }
Пример #14
0
        public void TestCalculateXBATStorageRequirements()
        {
            int[] blockCounts =
            {
                0, 1, 127, 128
            };
            int[] requirements =
            {
                0, 1, 1, 2
            };

            for (int j = 0; j < blockCounts.Length; j++)
            {
                Assert.AreEqual(
                    requirements[j],
                    BATBlock.CalculateXBATStorageRequirements(blockCounts[j]),
                    "requirement for " + blockCounts[j]);
            }
        }
Пример #15
0
        /**
         * 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.
         */
        public override int GetFreeBlock()
        {
            int sectorsPerSBAT = _filesystem.GetBigBlockSizeDetails().GetBATEntriesPerBlock();

            // First up, do we have any spare ones?
            int offset = 0;

            for (int i = 0; i < _sbat_blocks.Count; i++)
            {
                // Check this one
                BATBlock sbat = _sbat_blocks[i];
                if (sbat.HasFreeSectors)
                {
                    // Claim one of them and return it
                    for (int j = 0; j < sectorsPerSBAT; j++)
                    {
                        int sbatValue = sbat.GetValueAt(j);
                        if (sbatValue == POIFSConstants.UNUSED_BLOCK)
                        {
                            // Bingo
                            return(offset + j);
                        }
                    }
                }

                // Move onto the next SBAT
                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.OurBlockIndex = batForSBAT;

            // Are we the first SBAT?
            if (_header.SBATCount == 0)
            {
                _header.SBATStart      = batForSBAT;
                _header.SBATBlockCount = 1;
            }
            else
            {
                // Find the end of the SBAT stream, and add the sbat in there
                ChainLoopDetector loopDetector = _filesystem.GetChainLoopDetector();
                int batOffset = _header.SBATStart;
                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.SBATBlockCount = _header.SBATCount + 1;
            }

            // Finish allocating
            _filesystem.SetNextBlock(batForSBAT, POIFSConstants.END_OF_CHAIN);
            _sbat_blocks.Add(newSBAT);

            // Return our first spot
            return(offset);
        }
Пример #16
0
        /**
         * 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.
         */
        public override int GetFreeBlock()
        {
            int numSectors = bigBlockSize.GetBATEntriesPerBlock();
            // First up, do we have any spare ones?
            int offset = 0;

            foreach (BATBlock temp in _bat_blocks)
            {
                if (temp.HasFreeSectors)
                {
                    // Claim one of them and return it
                    for (int j = 0; j < numSectors; j++)
                    {
                        int batValue = temp.GetValueAt(j);
                        if (batValue == POIFSConstants.UNUSED_BLOCK)
                        {
                            // Bingo
                            return(offset + j);
                        }
                    }
                }

                // Move onto the next BAT
                offset += numSectors;
            }

            // If we Get here, then there aren't any free sectors
            //  in any of the BATs, so we need another BAT
            BATBlock bat = CreateBAT(offset, true);

            bat.SetValueAt(0, POIFSConstants.FAT_SECTOR_BLOCK);
            _bat_blocks.Add(bat);

            // Now store a reference to the BAT in the required place
            if (_header.BATCount >= 109)
            {
                // Needs to come from an XBAT
                BATBlock xbat = null;
                foreach (BATBlock x in _xbat_blocks)
                {
                    if (x.HasFreeSectors)
                    {
                        xbat = x;
                        break;
                    }
                }
                if (xbat == null)
                {
                    // Oh joy, we need a new XBAT too...
                    xbat = CreateBAT(offset + 1, false);
                    // Allocate our new BAT as the first block in the XBAT
                    xbat.SetValueAt(0, offset);
                    // And allocate the XBAT in the BAT
                    bat.SetValueAt(1, POIFSConstants.DIFAT_SECTOR_BLOCK);

                    // Will go one place higher as XBAT Added in
                    offset++;

                    // Chain it
                    if (_xbat_blocks.Count == 0)
                    {
                        _header.XBATStart = offset;
                    }
                    else
                    {
                        _xbat_blocks[_xbat_blocks.Count - 1].SetValueAt(
                            bigBlockSize.GetXBATEntriesPerBlock(), offset
                            );
                    }
                    _xbat_blocks.Add(xbat);
                    _header.XBATCount = _xbat_blocks.Count;
                }
                else
                {
                    // Allocate us in the XBAT
                    for (int i = 0; i < bigBlockSize.GetXBATEntriesPerBlock(); i++)
                    {
                        if (xbat.GetValueAt(i) == POIFSConstants.UNUSED_BLOCK)
                        {
                            xbat.SetValueAt(i, offset);
                            break;
                        }
                    }
                }
            }
            else
            {
                // Store us in the header
                int[] newBATs = new int[_header.BATCount + 1];
                Array.Copy(_header.BATArray, 0, newBATs, 0, newBATs.Length - 1);
                newBATs[newBATs.Length - 1] = offset;
                _header.BATArray            = newBATs;
            }
            _header.BATCount = _bat_blocks.Count;

            // The current offset stores us, but the next one is free
            return(offset + 1);
        }
Пример #17
0
 /**
  * Returns the BATBlock that handles the specified offset,
  *  and the relative index within it
  */
 public override BATBlockAndIndex GetBATBlockAndIndex(int offset)
 {
     return(BATBlock.GetBATBlockAndIndex(offset, _header, _bat_blocks));
 }
Пример #18
0
        public void TestWriteThenReplace()
        {
            NPOIFSFileSystem fs = new NPOIFSFileSystem();

            // Starts empty
            BATBlock bat = fs.GetBATBlockAndIndex(0).Block;

            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(2));

            // Write something that uses a main stream
            byte[] main4106 = new byte[4106];
            main4106[0]    = unchecked ((byte)-10);
            main4106[4105] = unchecked ((byte)-11);
            DocumentEntry normal = fs.Root.CreateDocument(
                "Normal", new MemoryStream(main4106));

            // Should have used 9 blocks
            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(3, bat.GetValueAt(2));
            Assert.AreEqual(4, bat.GetValueAt(3));
            Assert.AreEqual(5, bat.GetValueAt(4));
            Assert.AreEqual(6, bat.GetValueAt(5));
            Assert.AreEqual(7, bat.GetValueAt(6));
            Assert.AreEqual(8, bat.GetValueAt(7));
            Assert.AreEqual(9, bat.GetValueAt(8));
            Assert.AreEqual(10, bat.GetValueAt(9));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(10));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(11));

            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            Assert.AreEqual(4106, normal.Size);
            Assert.AreEqual(4106, ((DocumentNode)normal).Property.Size);


            // Replace with one still big enough for a main stream, but one block smaller
            byte[] main4096 = new byte[4096];
            main4096[0]    = unchecked ((byte)-10);
            main4096[4095] = unchecked ((byte)-11);

            NDocumentOutputStream nout = new NDocumentOutputStream(normal);

            nout.Write(main4096);
            nout.Close();

            // Will have dropped to 8
            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(3, bat.GetValueAt(2));
            Assert.AreEqual(4, bat.GetValueAt(3));
            Assert.AreEqual(5, bat.GetValueAt(4));
            Assert.AreEqual(6, bat.GetValueAt(5));
            Assert.AreEqual(7, bat.GetValueAt(6));
            Assert.AreEqual(8, bat.GetValueAt(7));
            Assert.AreEqual(9, bat.GetValueAt(8));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(9));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(10));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(11));

            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            Assert.AreEqual(4096, normal.Size);
            Assert.AreEqual(4096, ((DocumentNode)normal).Property.Size);


            // Write and check
            fs  = TestNPOIFSFileSystem.WriteOutAndReadBack(fs);
            bat = fs.GetBATBlockAndIndex(0).Block;

            // Will have properties, but otherwise the same
            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(3, bat.GetValueAt(2));
            Assert.AreEqual(4, bat.GetValueAt(3));
            Assert.AreEqual(5, bat.GetValueAt(4));
            Assert.AreEqual(6, bat.GetValueAt(5));
            Assert.AreEqual(7, bat.GetValueAt(6));
            Assert.AreEqual(8, bat.GetValueAt(7));
            Assert.AreEqual(9, bat.GetValueAt(8));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(9));  // End of Normal
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(10)); // Props
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(11));

            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            Assert.AreEqual(4096, normal.Size);
            Assert.AreEqual(4096, ((DocumentNode)normal).Property.Size);


            // Make longer, take 1 block After the properties too
            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            nout   = new NDocumentOutputStream(normal);
            nout.Write(main4106);
            nout.Close();

            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(3, bat.GetValueAt(2));
            Assert.AreEqual(4, bat.GetValueAt(3));
            Assert.AreEqual(5, bat.GetValueAt(4));
            Assert.AreEqual(6, bat.GetValueAt(5));
            Assert.AreEqual(7, bat.GetValueAt(6));
            Assert.AreEqual(8, bat.GetValueAt(7));
            Assert.AreEqual(9, bat.GetValueAt(8));
            Assert.AreEqual(11, bat.GetValueAt(9));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(10)); // Props
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(11)); // Normal
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(12));

            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            Assert.AreEqual(4106, normal.Size);
            Assert.AreEqual(4106, ((DocumentNode)normal).Property.Size);


            // Make it small, will trigger the SBAT stream and free lots up
            byte[] mini = new byte[] { 42, 0, 1, 2, 3, 4, 42 };
            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            nout   = new NDocumentOutputStream(normal);
            nout.Write(mini);
            nout.Close();

            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(2)); // SBAT
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(3)); // Mini Stream
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(4));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(5));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(6));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(7));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(8));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(9));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(10)); // Props
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(11));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(12));

            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            Assert.AreEqual(7, normal.Size);
            Assert.AreEqual(7, ((DocumentNode)normal).Property.Size);


            // Finally back to big again
            nout = new NDocumentOutputStream(normal);
            nout.Write(main4096);
            nout.Close();

            // Will keep the mini stream, now empty
            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(2)); // SBAT
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(3)); // Mini Stream
            Assert.AreEqual(5, bat.GetValueAt(4));
            Assert.AreEqual(6, bat.GetValueAt(5));
            Assert.AreEqual(7, bat.GetValueAt(6));
            Assert.AreEqual(8, bat.GetValueAt(7));
            Assert.AreEqual(9, bat.GetValueAt(8));
            Assert.AreEqual(11, bat.GetValueAt(9));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(10)); // Props
            Assert.AreEqual(12, bat.GetValueAt(11));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(12));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(13));

            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            Assert.AreEqual(4096, normal.Size);
            Assert.AreEqual(4096, ((DocumentNode)normal).Property.Size);


            // Save, re-load, re-check
            fs  = TestNPOIFSFileSystem.WriteOutAndReadBack(fs);
            bat = fs.GetBATBlockAndIndex(0).Block;

            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(2)); // SBAT
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(3)); // Mini Stream
            Assert.AreEqual(5, bat.GetValueAt(4));
            Assert.AreEqual(6, bat.GetValueAt(5));
            Assert.AreEqual(7, bat.GetValueAt(6));
            Assert.AreEqual(8, bat.GetValueAt(7));
            Assert.AreEqual(9, bat.GetValueAt(8));
            Assert.AreEqual(11, bat.GetValueAt(9));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(10)); // Props
            Assert.AreEqual(12, bat.GetValueAt(11));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(12));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(13));

            normal = (DocumentEntry)fs.Root.GetEntry("Normal");
            Assert.AreEqual(4096, normal.Size);
            Assert.AreEqual(4096, ((DocumentNode)normal).Property.Size);

            fs.Close();
        }
Пример #19
0
        public void TestReadWriteNewStream()
        {
            NPOIFSFileSystem fs     = new NPOIFSFileSystem();
            NPOIFSStream     stream = new NPOIFSStream(fs);

            // Check our filesystem has a BAT and the Properties
            Assert.AreEqual(2, fs.GetFreeBlock());
            BATBlock bat = fs.GetBATBlockAndIndex(0).Block;

            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(2));

            // Check the stream as-is
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, stream.GetStartBlock());
            try
            {
                stream.GetBlockIterator();
                Assert.Fail("Shouldn't be able to get an iterator before writing");
            }
            catch (Exception) { }

            // Write in two blocks
            byte[] data = new byte[512 + 20];
            for (int i = 0; i < 512; i++)
            {
                data[i] = (byte)(i % 256);
            }
            for (int i = 512; i < data.Length; i++)
            {
                data[i] = (byte)(i % 256 + 100);
            }
            stream.UpdateContents(data);

            // Check now
            Assert.AreEqual(4, fs.GetFreeBlock());
            bat = fs.GetBATBlockAndIndex(0).Block;
            Assert.AreEqual(POIFSConstants.FAT_SECTOR_BLOCK, bat.GetValueAt(0));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(1));
            Assert.AreEqual(3, bat.GetValueAt(2));
            Assert.AreEqual(POIFSConstants.END_OF_CHAIN, bat.GetValueAt(3));
            Assert.AreEqual(POIFSConstants.UNUSED_BLOCK, bat.GetValueAt(4));


            IEnumerator <ByteBuffer> it = stream.GetBlockIterator();

            Assert.AreEqual(true, it.MoveNext());
            ByteBuffer b = it.Current;

            byte[] read = new byte[512];
            //b.get(read);
            // Array.Copy(b, 0, read, 0, b.Length);
            b.Read(read);
            for (int i = 0; i < read.Length; i++)
            {
                //Assert.AreEqual("Wrong value at " + i, data[i], read[i]);
                Assert.AreEqual(data[i], read[i], "Wrong value at " + i);
            }

            Assert.AreEqual(true, it.MoveNext());
            b = it.Current;

            read = new byte[512];
            //b.get(read);
            //Array.Copy(b, 0, read, 0, b.Length);
            b.Read(read);
            for (int i = 0; i < 20; i++)
            {
                Assert.AreEqual(data[i + 512], read[i]);
            }
            for (int i = 20; i < read.Length; i++)
            {
                Assert.AreEqual(0, read[i]);
            }

            Assert.AreEqual(false, it.MoveNext());

            fs.Close();
        }
Пример #20
0
        public void TestCalculateMaximumSize()
        {
            // Zero fat blocks isn't technically valid, but it'd be header only
            Assert.AreEqual(
                512,
                BATBlock.CalculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 0)
                );
            Assert.AreEqual(
                4096,
                BATBlock.CalculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 0)
                );

            // A single FAT block can address 128/1024 blocks
            Assert.AreEqual(
                512 + 512 * 128,
                BATBlock.CalculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 1)
                );
            Assert.AreEqual(
                4096 + 4096 * 1024,
                BATBlock.CalculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 1)
                );

            Assert.AreEqual(
                512 + 4 * 512 * 128,
                BATBlock.CalculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 4)
                );
            Assert.AreEqual(
                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
            Assert.AreEqual(
                512 + 109 * 512 * 128,
                BATBlock.CalculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 109)
                );
            Assert.AreEqual(
                4096 + 109 * 4096 * 1024,
                BATBlock.CalculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 109)
                );

            Assert.AreEqual(
                512 + 110 * 512 * 128,
                BATBlock.CalculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 110)
                );
            Assert.AreEqual(
                4096 + 110 * 4096 * 1024,
                BATBlock.CalculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 110)
                );

            Assert.AreEqual(
                512 + 112 * 512 * 128,
                BATBlock.CalculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 112)
                );
            Assert.AreEqual(
                4096 + 112 * 4096 * 1024,
                BATBlock.CalculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 112)
                );

            // Check for >2gb, which we only support via a File
            Assert.AreEqual(
                512 + 8030L * 512 * 128,
                BATBlock.CalculateMaximumSize(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS, 8030)
                );
            Assert.AreEqual(
                4096 + 8030L * 4096 * 1024,
                BATBlock.CalculateMaximumSize(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS, 8030)
                );
        }
Пример #21
0
        public void TestGetBATBlockAndIndex()
        {
            HeaderBlock     header = new HeaderBlock(POIFSConstants.SMALLER_BIG_BLOCK_SIZE_DETAILS);
            List <BATBlock> blocks = new List <BATBlock>();
            int             offset;


            // First, try a one BAT block file
            header.BATCount = (1);
            blocks.Add(
                BATBlock.CreateBATBlock(header.BigBlockSize, new ByteBuffer(512, 512))
                );

            offset = 0;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 1;
            Assert.AreEqual(1, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 127;
            Assert.AreEqual(127, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));


            // Now go for one with multiple BAT blocks
            header.BATCount = (2);
            blocks.Add(
                BATBlock.CreateBATBlock(header.BigBlockSize, new ByteBuffer(512, 512))
                );

            offset = 0;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 127;
            Assert.AreEqual(127, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 128;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(1, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 129;
            Assert.AreEqual(1, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(1, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));


            // The XBAT count makes no difference, as we flatten in memory
            header.BATCount  = (1);
            header.XBATCount = (1);
            offset           = 0;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 126;
            Assert.AreEqual(126, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 127;
            Assert.AreEqual(127, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 128;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(1, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 129;
            Assert.AreEqual(1, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(1, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));


            // Check with the bigger block size too
            header = new HeaderBlock(POIFSConstants.LARGER_BIG_BLOCK_SIZE_DETAILS);

            offset = 0;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 1022;
            Assert.AreEqual(1022, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 1023;
            Assert.AreEqual(1023, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 1024;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(1, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            // Biggr block size, back to real BATs
            header.BATCount = (2);

            offset = 0;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 1022;
            Assert.AreEqual(1022, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 1023;
            Assert.AreEqual(1023, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(0, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));

            offset = 1024;
            Assert.AreEqual(0, BATBlock.GetBATBlockAndIndex(offset, header, blocks).Index);
            Assert.AreEqual(1, blocks.IndexOf(BATBlock.GetBATBlockAndIndex(offset, header, blocks).Block));
        }