int VSICachedFile::LoadBlocks( size_t nStartBlock, size_t nBlockCount, void *pBuffer, size_t nBufferSize ) { if( nBlockCount == 0 ) return 1; if( apoCache.size() < nStartBlock + nBlockCount ) apoCache.resize( nStartBlock + nBlockCount ); /* -------------------------------------------------------------------- */ /* When we want to load only one block, we can directly load it */ /* into the target buffer with no concern about intermediaries. */ /* -------------------------------------------------------------------- */ if( nBlockCount == 1 ) { poBase->Seek( nStartBlock * CHUNK_SIZE, SEEK_SET ); apoCache[nStartBlock] = new VSICacheChunk(); VSICacheChunk *poBlock = apoCache[nStartBlock]; poBlock->iBlock = nStartBlock; poBlock->nDataFilled = poBase->Read( poBlock->abyData, 1, CHUNK_SIZE ); nCacheUsed += poBlock->nDataFilled; // Merges into the LRU list. Demote( poBlock ); return 1; } /* -------------------------------------------------------------------- */ /* If the buffer is quite large but not quite large enough to */ /* hold all the blocks we will take the pain of splitting the */ /* io request in two in order to avoid allocating a large */ /* temporary buffer. */ /* -------------------------------------------------------------------- */ if( nBufferSize > CHUNK_SIZE * 20 && nBufferSize < nBlockCount * CHUNK_SIZE ) { if( !LoadBlocks( nStartBlock, 2, pBuffer, nBufferSize ) ) return 0; return LoadBlocks( nStartBlock+2, nBlockCount-2, pBuffer, nBufferSize ); } /* -------------------------------------------------------------------- */ /* Do we need to allocate our own buffer? */ /* -------------------------------------------------------------------- */ GByte *pabyWorkBuffer = (GByte *) pBuffer; if( nBufferSize < CHUNK_SIZE * nBlockCount ) pabyWorkBuffer = (GByte *) CPLMalloc(CHUNK_SIZE * nBlockCount); /* -------------------------------------------------------------------- */ /* Read the whole request into the working buffer. */ /* -------------------------------------------------------------------- */ if( poBase->Seek( nStartBlock * CHUNK_SIZE, SEEK_SET ) != 0 ) return 0; size_t nDataRead = poBase->Read( pabyWorkBuffer, 1, nBlockCount*CHUNK_SIZE); if( nBlockCount * CHUNK_SIZE > nDataRead + CHUNK_SIZE - 1 ) nBlockCount = (nDataRead + CHUNK_SIZE - 1) / CHUNK_SIZE; for( size_t i = 0; i < nBlockCount; i++ ) { VSICacheChunk *poBlock = new VSICacheChunk(); poBlock->iBlock = nStartBlock + i; CPLAssert( apoCache[i+nStartBlock] == NULL ); apoCache[i + nStartBlock] = poBlock; if( nDataRead >= (i+1) * CHUNK_SIZE ) poBlock->nDataFilled = CHUNK_SIZE; else poBlock->nDataFilled = nDataRead - i*CHUNK_SIZE; memcpy( poBlock->abyData, pabyWorkBuffer + i*CHUNK_SIZE, (size_t) poBlock->nDataFilled ); nCacheUsed += poBlock->nDataFilled; // Merges into the LRU list. Demote( poBlock ); } if( pabyWorkBuffer != pBuffer ) CPLFree( pabyWorkBuffer ); return 1; }
size_t VSICachedFile::Read( void * pBuffer, size_t nSize, size_t nCount ) { if( nOffset >= nFileSize ) return 0; /* ==================================================================== */ /* Make sure the cache is loaded for the whole request region. */ /* ==================================================================== */ size_t nStartBlock = (size_t) (nOffset / CHUNK_SIZE); size_t nEndBlock = (size_t) ((nOffset + nSize * nCount - 1) / CHUNK_SIZE); for( size_t iBlock = nStartBlock; iBlock <= nEndBlock; iBlock++ ) { if( apoCache.size() <= iBlock || apoCache[iBlock] == NULL ) { size_t nBlocksToLoad = 1; while( iBlock + nBlocksToLoad <= nEndBlock && (apoCache.size() <= iBlock+nBlocksToLoad || apoCache[iBlock+nBlocksToLoad] == NULL) ) nBlocksToLoad++; LoadBlocks( iBlock, nBlocksToLoad, pBuffer, nSize * nCount ); } } /* ==================================================================== */ /* Copy data into the target buffer to the extent possible. */ /* ==================================================================== */ size_t nAmountCopied = 0; while( nAmountCopied < nSize * nCount ) { size_t iBlock = (size_t) ((nOffset + nAmountCopied) / CHUNK_SIZE); size_t nThisCopy; VSICacheChunk *poBlock = apoCache[iBlock]; nThisCopy = (size_t) ((iBlock * CHUNK_SIZE + poBlock->nDataFilled) - nAmountCopied - nOffset); if( nThisCopy > nSize * nCount - nAmountCopied ) nThisCopy = nSize * nCount - nAmountCopied; if( nThisCopy == 0 ) break; memcpy( ((GByte *) pBuffer) + nAmountCopied, poBlock->abyData + (nOffset + nAmountCopied) - iBlock * CHUNK_SIZE, nThisCopy ); nAmountCopied += nThisCopy; } nOffset += nAmountCopied; /* -------------------------------------------------------------------- */ /* Ensure the cache is reduced to our limit. */ /* -------------------------------------------------------------------- */ while( nCacheUsed > nCacheMax ) FlushLRU(); return nAmountCopied / nSize; }
size_t VSICachedFile::Read( void * pBuffer, size_t nSize, size_t nCount ) { if( nOffset >= nFileSize ) { bEOF = TRUE; return 0; } /* ==================================================================== */ /* Make sure the cache is loaded for the whole request region. */ /* ==================================================================== */ vsi_l_offset nStartBlock = nOffset / nChunkSize; vsi_l_offset nEndBlock = (nOffset + nSize * nCount - 1) / nChunkSize; for( vsi_l_offset iBlock = nStartBlock; iBlock <= nEndBlock; iBlock++ ) { if( apoCache.size() <= iBlock || apoCache[iBlock] == NULL ) { size_t nBlocksToLoad = 1; while( iBlock + nBlocksToLoad <= nEndBlock && (apoCache.size() <= iBlock+nBlocksToLoad || apoCache[iBlock+nBlocksToLoad] == NULL) ) nBlocksToLoad++; LoadBlocks( iBlock, nBlocksToLoad, pBuffer, nSize * nCount ); } } /* ==================================================================== */ /* Copy data into the target buffer to the extent possible. */ /* ==================================================================== */ size_t nAmountCopied = 0; while( nAmountCopied < nSize * nCount ) { vsi_l_offset iBlock = (nOffset + nAmountCopied) / nChunkSize; size_t nThisCopy; VSICacheChunk *poBlock = apoCache[iBlock]; if( poBlock == NULL ) { /* We can reach that point when the amount to read exceeds */ /* the cache size */ LoadBlocks( iBlock, 1, ((GByte *) pBuffer) + nAmountCopied, MIN(nSize * nCount - nAmountCopied, nChunkSize) ); poBlock = apoCache[iBlock]; CPLAssert(poBlock != NULL); } vsi_l_offset nStartOffset = (vsi_l_offset)iBlock * nChunkSize; nThisCopy = (size_t) ((nStartOffset + poBlock->nDataFilled) - nAmountCopied - nOffset); if( nThisCopy > nSize * nCount - nAmountCopied ) nThisCopy = nSize * nCount - nAmountCopied; if( nThisCopy == 0 ) break; memcpy( ((GByte *) pBuffer) + nAmountCopied, poBlock->pabyData + (nOffset + nAmountCopied) - nStartOffset, nThisCopy ); nAmountCopied += nThisCopy; } nOffset += nAmountCopied; /* -------------------------------------------------------------------- */ /* Ensure the cache is reduced to our limit. */ /* -------------------------------------------------------------------- */ while( nCacheUsed > nCacheMax ) FlushLRU(); size_t nRet = nAmountCopied / nSize; if (nRet != nCount) bEOF = TRUE; return nRet; }