/** * 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); } }
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); }
private void free(ChainLoopDetector loopDetector) { int nextBlock = startBlock; while (nextBlock != POIFSConstants.END_OF_CHAIN) { int thisBlock = nextBlock; loopDetector.Claim(thisBlock); nextBlock = blockStore.GetNextBlock(thisBlock); blockStore.SetNextBlock(thisBlock, POIFSConstants.UNUSED_BLOCK); } this.startBlock = POIFSConstants.END_OF_CHAIN; }
public StreamBlockByteBufferIterator(BlockStore blockStore, int firstBlock) { this.blockStore = blockStore; this.nextBlock = firstBlock; try { this.loopDetector = blockStore.GetChainLoopDetector(); } catch (IOException e) { //throw new System.RuntimeException(e); throw new Exception(e.Message); } }
/** * Load the block, extending the underlying stream if needed */ public override ByteBuffer CreateBlockIfNeeded(int offset) { // Try to Get it without extending the stream try { return(GetBlockAt(offset)); } catch (IndexOutOfRangeException) { // Need to extend the stream // TODO Replace this with proper append support // For now, do the extending by hand... // Ask for another block int newBigBlock = _filesystem.GetFreeBlock(); _filesystem.CreateBlockIfNeeded(newBigBlock); // Tack it onto the end of our chain ChainLoopDetector loopDetector = _filesystem.GetChainLoopDetector(); int block = _mini_stream.GetStartBlock(); while (true) { loopDetector.Claim(block); int next = _filesystem.GetNextBlock(block); if (next == POIFSConstants.END_OF_CHAIN) { break; } block = next; } _filesystem.SetNextBlock(block, newBigBlock); _filesystem.SetNextBlock(newBigBlock, POIFSConstants.END_OF_CHAIN); // Now try again to Get it return(CreateBlockIfNeeded(offset)); } }
// TODO Streaming write support // TODO then convert fixed sized write to use streaming internally // TODO Append write support (probably streaming) /** * Frees all blocks in the stream */ public void free() { ChainLoopDetector loopDetector = blockStore.GetChainLoopDetector(); free(loopDetector); }
/** * Updates the contents of the stream to the new * Set of bytes. * Note - if this is property based, you'll still * need to update the size in the property yourself */ public void UpdateContents(byte[] contents) { // How many blocks are we going to need? int blockSize = blockStore.GetBlockStoreBlockSize(); int blocks = (int)Math.Ceiling(((double)contents.Length) / blockSize); // Make sure we don't encounter a loop whilst overwriting // the existing blocks ChainLoopDetector loopDetector = blockStore.GetChainLoopDetector(); // Start writing int prevBlock = POIFSConstants.END_OF_CHAIN; int nextBlock = startBlock; for (int i = 0; i < blocks; i++) { int thisBlock = nextBlock; // Allocate a block if needed, otherwise figure // out what the next block will be if (thisBlock == POIFSConstants.END_OF_CHAIN) { thisBlock = blockStore.GetFreeBlock(); loopDetector.Claim(thisBlock); // We're on the end of the chain nextBlock = POIFSConstants.END_OF_CHAIN; // Mark the previous block as carrying on to us if needed if (prevBlock != POIFSConstants.END_OF_CHAIN) { blockStore.SetNextBlock(prevBlock, thisBlock); } blockStore.SetNextBlock(thisBlock, POIFSConstants.END_OF_CHAIN); // If we've just written the first block on a // new stream, save the start block offset if (this.startBlock == POIFSConstants.END_OF_CHAIN) { this.startBlock = thisBlock; } } else { loopDetector.Claim(thisBlock); nextBlock = blockStore.GetNextBlock(thisBlock); } // Write it //byte[] buffer = blockStore.CreateBlockIfNeeded(thisBlock); ByteBuffer buffer = blockStore.CreateBlockIfNeeded(thisBlock); int startAt = i * blockSize; int endAt = Math.Min(contents.Length - startAt, blockSize); buffer.Write(contents, startAt, endAt); //for (int index = startAt, j = 0; index < endAt; index++, j++) // buffer[j] = contents[index]; // Update pointers prevBlock = thisBlock; } int lastBlock = prevBlock; // If we're overwriting, free any remaining blocks NPOIFSStream toFree = new NPOIFSStream(blockStore, nextBlock); toFree.free(loopDetector); // Mark the end of the stream blockStore.SetNextBlock(lastBlock, POIFSConstants.END_OF_CHAIN); }
/** * 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); }