First implementation of "flat files" for cache.

This commit is contained in:
gordon%netscape.com 2001-04-17 03:54:48 +00:00
Родитель 00a7ccbba6
Коммит 5781ea5a43
2 изменённых файлов: 520 добавлений и 0 удалений

437
netwerk/cache/src/nsDiskCacheBlockFile.cpp поставляемый Normal file
Просмотреть файл

@ -0,0 +1,437 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is nsDiskCacheBlockFile.cpp, released April 12, 2001.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 2001 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Gordon Sheridan <gordon@netscape.com>
*/
#include "nsCRT.h"
#include "nsDiskCacheBlockFile.h"
/******************************************************************************
* nsDiskCacheBlockFile -
*****************************************************************************/
/******************************************************************************
* Open
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::Open( nsILocalFile * blockFile, PRUint32 blockSize)
{
// create the stream
mBlockSize = blockSize;
mStream = new nsANSIFileStream;
if (!mStream) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(mStream);
// open the stream
nsresult rv = mStream->Open(blockFile);
if (NS_FAILED(rv)) {
NS_RELEASE(mStream);
mStream = nsnull;
return rv;
}
// allocate bit map buffer
mBitMap = new PRUint8[kBitMapBytes];
if (!mBitMap) {
rv = NS_ERROR_OUT_OF_MEMORY;
goto error_exit;
}
// check if we just creating the file
rv = mStream->Available(&mEndOfFile);
if (mEndOfFile == 0) {
// initialize bit map and write it
nsCRT::zero(mBitMap, kBitMapBytes);
PRUint32 bytesWritten = 0;
rv = mStream->Write((char *)mBitMap, kBitMapBytes, &bytesWritten);
if (NS_FAILED(rv)) goto error_exit;
if (kBitMapBytes != bytesWritten) {
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
mEndOfFile = kBitMapBytes;
} else if (mEndOfFile < kBitMapBytes) {
rv = NS_ERROR_UNEXPECTED; // XXX NS_ERROR_CACHE_INVALID;
goto error_exit;
} else {
// read the bit map
PRUint32 bytesRead = 0;
rv = mStream->Read((char *)mBitMap, kBitMapBytes, &bytesRead);
if (NS_FAILED(rv)) goto error_exit;
if (kBitMapBytes != bytesRead) {
rv = NS_ERROR_UNEXPECTED;
goto error_exit;
}
// validate block file
rv = ValidateFile();
if (NS_FAILED(rv)) goto error_exit;
}
return NS_OK;
error_exit:
if (mStream) {
(void) mStream->Close();
mStream = nsnull;
}
if (mBitMap) {
delete [] mBitMap;
mBitMap = nsnull;
}
return rv;
}
/******************************************************************************
* Close
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::Close()
{
if (!mStream) return NS_OK;
nsresult rv = FlushBitMap();
nsresult rv2 = mStream->Close();
NS_RELEASE(mStream);
mStream = nsnull;
if (mBitMap) {
delete [] mBitMap;
mBitMap = nsnull;
}
return rv ? rv : rv2;
}
/******************************************************************************
* Trim
*
* Truncate the block file to the end of the last allocated block.
*
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::Trim()
{
return NS_OK;
}
/******************************************************************************
* AllocateBlocks
*
* Allocates 1-4 blocks, using a first fit strategy,
* so that no group of blocks spans a quad block boundary.
*
* Returns block number of first block allocated or -1 on failure.
*
*****************************************************************************/
PRInt32
nsDiskCacheBlockFile::AllocateBlocks(PRInt32 numBlocks)
{
if (!mStream) return -1; // NS_ERROR_NOT_AVAILABLE;
// return -1 if unable to allocate blocks
// PRUint8 mask = (0x01 << numBlocks) - 1;
int i = 0;
PRUint8 mapByte;
PRUint8 mask;
// presume allocation will succeed
PRBool oldValue = mBitMapDirty;
mBitMapDirty = PR_TRUE;
while ((mBitMap[i] == 0xFF) && (i < kBitMapBytes)) ++i; // find first block with a free bit
if (numBlocks == 1) {
if (i < kBitMapBytes) {
// don't need a while loop, because we know there's at least 1 free bit in this byte
mapByte = ~mBitMap[i]; // flip bits so free bits are 1
/*
* // Linear search for first free bit in byte
* mask = 0x01;
* for (int j=0; j<8; ++j, mask <<= 1)
* if (mask & mapByte) {mBitMap[i] |= mask; return (i * 8 + j); }
*/
// Binary search for first free bit in byte
PRUint8 bit = 0;
if ((mapByte & 0x0F) == 0) { bit |= 4; mapByte >>= 4; }
if ((mapByte & 0x03) == 0) { bit |= 2; mapByte >>= 2; }
if ((mapByte & 0x01) == 0) { bit |= 1; mapByte >>= 1; }
mBitMap[i] |= (PRUint8)1 << bit;
return i * 8 + bit;
}
} else if (numBlocks == 2) {
while (i < kBitMapBytes) {
mapByte = ~mBitMap[i]; // flip bits so free bits are 1
mask = 0x03;
// check for fit in lower quad bits
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8); } mask <<= 1;
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 1); } mask <<= 1;
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 2); } mask <<= 2;
// check for fit in upper quad bits
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 4); } mask <<= 1;
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 5); } mask <<= 1;
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 6); }
++i;
}
} else if (numBlocks == 3) {
while (i < kBitMapBytes) {
mapByte = ~mBitMap[i]; // flip bits so free bits are 1
mask = 0x07;
// check for fit in lower quad bits
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8); } mask <<= 1;
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 1); } mask <<= 3;
// check for fit in upper quad bits
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 4); } mask <<= 1;
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 5); }
++i;
}
} else if (numBlocks == 4) {
while (i < kBitMapBytes) {
mapByte = ~mBitMap[i]; // flip bits so free bits are 1
mask = 0x0F;
// check for fit in lower quad bits
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8); } mask <<= 4;
// check for fit in upper quad bits
if ((mask & mapByte) == mask) { mBitMap[i] |= mask; return (i * 8 + 4); }
++i;
}
}
mBitMapDirty = oldValue;
return -1;
}
/******************************************************************************
* DeallocateBlocks
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::DeallocateBlocks( PRInt32 startBlock, PRInt32 numBlocks)
{
if (!mStream) return NS_ERROR_NOT_AVAILABLE;
if ((startBlock < 0) || (startBlock > kBitMapBytes * 8 - 1) ||
(numBlocks < 1) || (numBlocks > 4))
return NS_ERROR_ILLEGAL_VALUE;
PRInt32 startByte = startBlock / 8;
PRUint8 startBit = startBlock % 8;
// make sure requested deallocation doesn't span a byte boundary
if ((startBlock + numBlocks - 1) / 8 != startByte) return NS_ERROR_UNEXPECTED;
PRUint8 mask = ((0x01 << numBlocks) - 1) << startBit;
PRUint8 mapByte = ~mBitMap[startByte]; // flip so allocated bits are zero
// make sure requested deallocation is currently allocated
if (mapByte & mask) return NS_ERROR_ABORT;
mBitMap[startByte] ^= mask; // flips the bits off;
mBitMapDirty = PR_TRUE;
// XXX rv = FlushBitMap(); // coherency vs. performance
return NS_OK;
}
/******************************************************************************
* WriteBlocks
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::WriteBlocks( char * buffer,
PRInt32 startBlock,
PRInt32 numBlocks)
{
// presume buffer != nsnull
if (!mStream) return NS_ERROR_NOT_AVAILABLE;
nsresult rv = VerifyAllocation(startBlock, numBlocks);
if (NS_FAILED(rv)) return rv;
// seek to block position
PRInt32 blockPos = kBitMapBytes + startBlock * mBlockSize;
rv = mStream->Seek(nsISeekableStream::NS_SEEK_SET, blockPos);
if (NS_FAILED(rv)) return rv;
if (mEndOfFile < (blockPos + numBlocks * mBlockSize))
mEndOfFile = (blockPos + numBlocks * mBlockSize);
// write the blocks
PRUint32 bytesToWrite = numBlocks * mBlockSize;
PRUint32 bytesWritten = 0;
rv = mStream->Write(buffer, bytesToWrite, &bytesWritten);
if (NS_FAILED(rv)) return rv;
if (bytesWritten != bytesToWrite) return NS_ERROR_UNEXPECTED;
// write the bit map and flush the file
rv = FlushBitMap();
return rv;
}
/******************************************************************************
* ReadBlocks
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::ReadBlocks( char * buffer,
PRInt32 startBlock,
PRInt32 numBlocks)
{
// presume buffer != nsnull
if (!mStream) return NS_ERROR_NOT_AVAILABLE;
nsresult rv = VerifyAllocation(startBlock, numBlocks);
if (NS_FAILED(rv)) return rv;
// seek to block position
PRInt32 blockPos = kBitMapBytes + startBlock * mBlockSize;
rv = mStream->Seek(nsISeekableStream::NS_SEEK_SET, blockPos);
if (NS_FAILED(rv)) return rv;
// read the blocks
PRUint32 bytesToRead = numBlocks * mBlockSize;
PRUint32 bytesRead = 0;
rv = mStream->Read(buffer, bytesToRead, &bytesRead);
if (NS_FAILED(rv)) return rv;
if (bytesRead != bytesToRead) return NS_ERROR_UNEXPECTED;
return rv;
}
/******************************************************************************
* FlushBitMap
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::FlushBitMap()
{
if (!mBitMapDirty) return NS_OK;
// seek to bitmap
nsresult rv = mStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
if (NS_FAILED(rv)) return rv;
// write bitmap
PRUint32 bytesWritten = 0;
rv = mStream->Write((char *)mBitMap, kBitMapBytes, &bytesWritten);
if (NS_FAILED(rv)) return rv;
if (kBitMapBytes != bytesWritten) {
return NS_ERROR_UNEXPECTED;
}
rv = mStream->Flush();
if (NS_FAILED(rv)) return rv;
mBitMapDirty = PR_FALSE;
return NS_OK;
}
/******************************************************************************
* ValidateFile
*
* Check size of file against last bit allocated for mBlockSize.
*
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::ValidateFile()
{
PRUint32 estimatedSize = kBitMapBytes;
PRInt32 lastBlock = LastBlock();
if (lastBlock >= 0)
estimatedSize += (lastBlock + 1) * mBlockSize;
PRUint32 fileSize = 0;
// seek to beginning
nsresult rv = mStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
if (NS_FAILED(rv)) return rv;
rv = mStream->Available(&fileSize);
if (NS_FAILED(rv)) return rv;
if (estimatedSize > fileSize)
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
/******************************************************************************
* VerfiyAllocation
*
* Return values:
* NS_OK if all bits are marked allocated
* NS_ERROR_ILLEGAL_VALUE if parameters don't obey constraints
* NS_ERROR_FAILURE if some or all the bits are marked unallocated
*
*****************************************************************************/
nsresult
nsDiskCacheBlockFile::VerifyAllocation( PRInt32 startBlock, PRInt32 numBlocks)
{
if ((startBlock < 0) || (startBlock > kBitMapBytes * 8 - 1) ||
(numBlocks < 1) || (numBlocks > 4))
return NS_ERROR_ILLEGAL_VALUE;
PRInt32 startByte = startBlock / 8;
PRUint8 startBit = startBlock % 8;
// make sure requested deallocation doesn't span a byte boundary
if ((startBlock + numBlocks - 1) / 8 != startByte) return NS_ERROR_ILLEGAL_VALUE;
PRUint8 mask = ((0x01 << numBlocks) - 1) << startBit;
// check if all specified blocks are currently allocated
if ((mBitMap[startByte] & mask) != mask) return NS_ERROR_FAILURE;
return NS_OK;
}
/******************************************************************************
* LastBlock
*
* Return last block allocated or -1 if no blocks are allocated.
*
*****************************************************************************/
PRInt32
nsDiskCacheBlockFile::LastBlock()
{
// search for last byte in mBitMap with allocated bits
PRInt32 i = kBitMapBytes;
while (--i >= 0) {
if (mBitMap[i]) break;
}
if (i >= 0) {
// binary search to find last allocated bit in byte
PRUint8 mapByte = mBitMap[i];
PRUint8 lastBit = 7;
if ((mapByte & 0xF0) == 0) { lastBit ^= 4; mapByte <<= 4; }
if ((mapByte & 0xC0) == 0) { lastBit ^= 2; mapByte <<= 2; }
if ((mapByte & 0x80) == 0) { lastBit ^= 1; mapByte <<= 1; }
return i * 8 + lastBit;
}
return -1;
}

83
netwerk/cache/src/nsDiskCacheBlockFile.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is nsDiskCacheBlockFile.h, released April 12, 2001.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 2001 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Gordon Sheridan <gordon@netscape.com>
*/
#ifndef _nsDiskCacheBlockFile_h_
#define _nsDiskCacheBlockFile_h_
#include "nsANSIFileStreams.h"
enum { kBitMapBytes = 4096 };
/*
typedef struct BlockFile {
PRUint32 version; // we could rely on the verion in the _CACHE_MAP_ file
PRUint32 blockSize; // caller can keep track of this
PRUint32 blockCount; // can be calculated & verified from bitmap and EOF
char bitMap[];
} BlockFile;
*/
/******************************************************************************
* nsDiskCacheBlockFile
*
* The structure of a cache block file is a 4096 bytes bit map, followed by
* some number of blocks of mBlockSize. The creator of a
* nsDiskCacheBlockFile object must provide the block size for a given file.
*
*****************************************************************************/
class nsDiskCacheBlockFile {
public:
nsDiskCacheBlockFile()
: mStream(nsnull)
, mBlockSize(0)
, mEndOfFile(0)
, mBitMap(nsnull)
, mBitMapDirty(PR_FALSE)
{}
~nsDiskCacheBlockFile() { (void) Close(); }
nsresult Open( nsILocalFile * blockFile, PRUint32 blockSize);
nsresult Close();
nsresult Trim();
PRInt32 AllocateBlocks( PRInt32 numBlocks);
nsresult DeallocateBlocks( PRInt32 startBlock, PRInt32 numBlocks);
nsresult WriteBlocks( char * buffer, PRInt32 startBlock, PRInt32 numBlocks);
nsresult ReadBlocks( char * buffer, PRInt32 startBlock, PRInt32 numBlocks);
// private:
virtual nsresult FlushBitMap();
virtual nsresult ValidateFile(); // called by Open()
virtual nsresult VerifyAllocation( PRInt32 startBlock, PRInt32 numBLocks);
virtual PRInt32 LastBlock();
/**
* Data members
*/
nsANSIFileStream * mStream;
PRUint32 mBlockSize;
PRUint32 mEndOfFile;
PRUint8 * mBitMap; // XXX future: array of bit map blocks
PRBool mBitMapDirty;
};
#endif // _nsDiskCacheBlockFile_h_