From 5d0afb99a43461f5ba84ed1aa56dd6d0fd69fc64 Mon Sep 17 00:00:00 2001 From: Vincent Povirk Date: Fri, 16 Apr 2010 17:38:14 -0500 Subject: [PATCH 2/2] ole32: Cache data and block locations in BigBlockStream objects. --- dlls/ole32/storage32.c | 228 ++++++++++++++++++++++++++++++++++++++---------- dlls/ole32/storage32.h | 14 +++ 2 files changed, 196 insertions(+), 46 deletions(-) diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c index 21cac58..8b6a3af 100644 --- a/dlls/ole32/storage32.c +++ b/dlls/ole32/storage32.c @@ -2857,7 +2857,10 @@ end: *result = NULL; } else + { + StorageImpl_Flush((StorageBaseImpl*)This); *result = This; + } return hr; } @@ -2896,8 +2899,26 @@ static void StorageImpl_Destroy(StorageBaseImpl* iface) static HRESULT StorageImpl_Flush(StorageBaseImpl* iface) { StorageImpl *This = (StorageImpl*) iface; + int i; + HRESULT hr; + TRACE("(%p)\n", This); + + hr = BlockChainStream_Flush(This->smallBlockRootChain); + + if (SUCCEEDED(hr)) + hr = BlockChainStream_Flush(This->rootBlockChain); + + if (SUCCEEDED(hr)) + hr = BlockChainStream_Flush(This->smallBlockDepotChain); + + for (i=0; SUCCEEDED(hr) && iblockChainCache[i]) + hr = BlockChainStream_Flush(This->blockChainCache[i]); - return ILockBytes_Flush(This->lockBytes); + if (SUCCEEDED(hr)) + hr = ILockBytes_Flush(This->lockBytes); + + return hr; } /****************************************************************************** @@ -5820,6 +5841,53 @@ ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset) return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset; } +HRESULT BlockChainStream_GetBlockAtOffset(BlockChainStream *This, + ULONG index, BlockChainBlock **block, ULONG *sector, BOOL create) +{ + BlockChainBlock *result=NULL; + int i; + + for (i=0; i<2; i++) + if (This->cachedBlocks[i].index == index) + { + *sector = This->cachedBlocks[i].sector; + *block = &This->cachedBlocks[i]; + return S_OK; + } + + *sector = BlockChainStream_GetSectorOfOffset(This, index); + if (*sector == BLOCK_END_OF_CHAIN) + return STG_E_DOCFILECORRUPT; + + if (create) + { + if (This->cachedBlocks[0].index == 0xffffffff) + result = &This->cachedBlocks[0]; + else if (This->cachedBlocks[1].index == 0xffffffff) + result = &This->cachedBlocks[1]; + else + { + result = &This->cachedBlocks[This->blockToEvict++]; + if (This->blockToEvict == 2) + This->blockToEvict = 0; + } + + if (result->dirty) + { + if (!StorageImpl_WriteBigBlock(This->parentStorage, result->sector, result->data)) + return STG_E_WRITEFAULT; + result->dirty = 0; + } + + result->read = 0; + result->index = index; + result->sector = *sector; + } + + *block = result; + return S_OK; +} + BlockChainStream* BlockChainStream_Construct( StorageImpl* parentStorage, ULONG* headOfStreamPlaceHolder, @@ -5835,6 +5903,11 @@ BlockChainStream* BlockChainStream_Construct( newStream->indexCache = NULL; newStream->indexCacheLen = 0; newStream->indexCacheSize = 0; + newStream->cachedBlocks[0].index = 0xffffffff; + newStream->cachedBlocks[0].dirty = 0; + newStream->cachedBlocks[1].index = 0xffffffff; + newStream->cachedBlocks[1].dirty = 0; + newStream->blockToEvict = 0; if (FAILED(BlockChainStream_UpdateIndexCache(newStream))) { @@ -5846,10 +5919,30 @@ BlockChainStream* BlockChainStream_Construct( return newStream; } +HRESULT BlockChainStream_Flush(BlockChainStream* This) +{ + int i; + if (!This) return S_OK; + for (i=0; i<2; i++) + { + if (This->cachedBlocks[i].dirty) + { + if (StorageImpl_WriteBigBlock(This->parentStorage, This->cachedBlocks[i].sector, This->cachedBlocks[i].data)) + This->cachedBlocks[i].dirty = 0; + else + return STG_E_WRITEFAULT; + } + } + return S_OK; +} + void BlockChainStream_Destroy(BlockChainStream* This) { if (This) + { + BlockChainStream_Flush(This); HeapFree(GetProcessHeap(), 0, This->indexCache); + } HeapFree(GetProcessHeap(), 0, This); } @@ -5915,6 +6008,8 @@ HRESULT BlockChainStream_ReadAt(BlockChainStream* This, ULONG blockIndex; BYTE* bufferWalker; ULARGE_INTEGER stream_size; + HRESULT hr; + BlockChainBlock *cachedBlock; TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead); @@ -5936,32 +6031,50 @@ HRESULT BlockChainStream_ReadAt(BlockChainStream* This, */ bufferWalker = buffer; - while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) + while (size > 0) { ULARGE_INTEGER ulOffset; DWORD bytesReadAt; + /* * Calculate how many bytes we can copy from this big block. */ bytesToReadInBuffer = min(This->parentStorage->bigBlockSize - offsetInBlock, size); - TRACE("block %i\n",blockIndex); - ulOffset.u.HighPart = 0; - ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + - offsetInBlock; + hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToReadInBuffer); - StorageImpl_ReadAt(This->parentStorage, - ulOffset, - bufferWalker, - bytesToReadInBuffer, - &bytesReadAt); - /* - * Step to the next big block. - */ - if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex))) - return STG_E_DOCFILECORRUPT; + if (FAILED(hr)) + return hr; + + if (!cachedBlock) + { + /* Not in cache, and we're going to read past the end of the block. */ + ulOffset.u.HighPart = 0; + ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + + offsetInBlock; + + StorageImpl_ReadAt(This->parentStorage, + ulOffset, + bufferWalker, + bytesToReadInBuffer, + &bytesReadAt); + } + else + { + if (!cachedBlock->read) + { + if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data)) + return STG_E_READFAULT; + + cachedBlock->read = 1; + } + + memcpy(bufferWalker, cachedBlock->data+offsetInBlock, bytesToReadInBuffer); + bytesReadAt = bytesToReadInBuffer; + } + blockNoInSequence++; bufferWalker += bytesReadAt; size -= bytesReadAt; *bytesRead += bytesReadAt; @@ -5991,51 +6104,61 @@ HRESULT BlockChainStream_WriteAt(BlockChainStream* This, ULONG bytesToWrite; ULONG blockIndex; const BYTE* bufferWalker; - - /* - * Find the first block in the stream that contains part of the buffer. - */ - blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence); - - /* BlockChainStream_SetSize should have already been called to ensure we have - * enough blocks in the chain to write into */ - if (blockIndex == BLOCK_END_OF_CHAIN) - { - ERR("not enough blocks in chain to write data\n"); - return STG_E_DOCFILECORRUPT; - } + HRESULT hr; + BlockChainBlock *cachedBlock; *bytesWritten = 0; bufferWalker = buffer; - while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) ) + while (size > 0) { ULARGE_INTEGER ulOffset; DWORD bytesWrittenAt; + /* - * Calculate how many bytes we can copy from this big block. + * Calculate how many bytes we can copy to this big block. */ bytesToWrite = min(This->parentStorage->bigBlockSize - offsetInBlock, size); - TRACE("block %i\n",blockIndex); - ulOffset.u.HighPart = 0; - ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + - offsetInBlock; + hr = BlockChainStream_GetBlockAtOffset(This, blockNoInSequence, &cachedBlock, &blockIndex, size == bytesToWrite); - StorageImpl_WriteAt(This->parentStorage, - ulOffset, - bufferWalker, - bytesToWrite, - &bytesWrittenAt); + /* BlockChainStream_SetSize should have already been called to ensure we have + * enough blocks in the chain to write into */ + if (FAILED(hr)) + { + ERR("not enough blocks in chain to write data\n"); + return hr; + } - /* - * Step to the next big block. - */ - if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, - &blockIndex))) - return STG_E_DOCFILECORRUPT; + if (!cachedBlock) + { + /* Not in cache, and we're going to write past the end of the block. */ + ulOffset.u.HighPart = 0; + ulOffset.u.LowPart = StorageImpl_GetBigBlockOffset(This->parentStorage, blockIndex) + + offsetInBlock; + + StorageImpl_WriteAt(This->parentStorage, + ulOffset, + bufferWalker, + bytesToWrite, + &bytesWrittenAt); + } + else + { + if (!cachedBlock->read && bytesToWrite != This->parentStorage->bigBlockSize) + { + if (!StorageImpl_ReadBigBlock(This->parentStorage, cachedBlock->sector, cachedBlock->data)) + return STG_E_READFAULT; + } + memcpy(cachedBlock->data+offsetInBlock, bufferWalker, bytesToWrite); + bytesWrittenAt = bytesToWrite; + cachedBlock->read = 1; + cachedBlock->dirty = 1; + } + + blockNoInSequence++; bufferWalker += bytesWrittenAt; size -= bytesWrittenAt; *bytesWritten += bytesWrittenAt; @@ -6058,6 +6181,7 @@ static BOOL BlockChainStream_Shrink(BlockChainStream* This, { ULONG blockIndex; ULONG numBlocks; + int i; /* * Figure out how many blocks are needed to contain the new size @@ -6125,6 +6249,18 @@ static BOOL BlockChainStream_Shrink(BlockChainStream* This, last_run->lastOffset--; } + /* + * Reset the last accessed block cache. + */ + for (i=0; i<2; i++) + { + if (This->cachedBlocks[i].index >= numBlocks) + { + This->cachedBlocks[i].index = 0xffffffff; + This->cachedBlocks[i].dirty = 0; + } + } + return TRUE; } diff --git a/dlls/ole32/storage32.h b/dlls/ole32/storage32.h index 9e7fe10..36d0e2f 100644 --- a/dlls/ole32/storage32.h +++ b/dlls/ole32/storage32.h @@ -512,6 +512,15 @@ struct BlockChainRun ULONG lastOffset; }; +typedef struct BlockChainBlock +{ + ULONG index; + ULONG sector; + int read; + int dirty; + BYTE data[MAX_BIG_BLOCK_SIZE]; +} BlockChainBlock; + struct BlockChainStream { StorageImpl* parentStorage; @@ -520,6 +529,8 @@ struct BlockChainStream struct BlockChainRun* indexCache; ULONG indexCacheLen; ULONG indexCacheSize; + BlockChainBlock cachedBlocks[2]; + ULONG blockToEvict; ULONG tailIndex; ULONG numBlocks; }; @@ -553,6 +564,9 @@ BOOL BlockChainStream_SetSize( BlockChainStream* This, ULARGE_INTEGER newSize); +HRESULT BlockChainStream_Flush( + BlockChainStream* This); + /**************************************************************************** * SmallBlockChainStream definitions. * -- 1.7.0.4