/********************************************************************** * * CacheMan.cpp * Copyright (C) 2003 UNESCO * * A component of the Greenstone digital library software * from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *********************************************************************/ #include "stdafx.h" #include "File.h" #include "CacheMan.h" CacheManager::CacheManager() { maxblocks_ = 0; } CacheManager::CacheManager(unsigned blocksz, unsigned mxblks) { PRECONDITION(maxblocks_ == 0); maxblocks_ = mxblks; nused_ = 0; blocksize_ = blocksz; buff_ = new char[blocksize_*maxblocks_]; diskAddrs_ = new FileSystem::FAU_t[maxblocks_]; useCounts_ = new unsigned[maxblocks_]; theFile_ = NULL; } /* * Construct a cache manager for blocks of size blocksz and mxblks buffers */ void CacheManager::Create(unsigned blocksz, unsigned mxblks) { PRECONDITION(maxblocks_ == 0); maxblocks_ = mxblks; nused_ = 0; blocksize_ = blocksz; buff_ = new char[blocksize_*maxblocks_]; diskAddrs_ = new FileSystem::FAU_t[maxblocks_]; useCounts_ = new unsigned[maxblocks_]; theFile_ = NULL; } // // Connect to a CFileBase // void CacheManager::Connect(CFileBase* file) { PRECONDITION(nused_ == 0); theFile_ = file; } // // Disconnect the cache from the CFileBase // void CacheManager::Disconnect() { if (theFile_) { if (theFile_->IsOpen()) Flush(); } theFile_ = NULL; } void CacheManager::Erase() { Disconnect(); delete[] useCounts_; delete[] diskAddrs_; delete[] buff_; maxblocks_ = 0; } CacheManager::~CacheManager() { Disconnect(); delete[] useCounts_; delete[] diskAddrs_; delete[] buff_; } bool CacheManager::Flush() { PRECONDITION(theFile_ != NULL && theFile_->IsOpen()); // Because we use "write through", this is all that's necessary: if (theFile_->Flush() == FILE_NO_ERROR) return true; return false; } void CacheManager::Invalidate() { nused_ = 0; } bool CacheManager::Read(FileSystem::FAU_t locn, void* dat) { PRECONDITION(dat != NULL); size_t islot = AgeAndFindSlot(locn); if (islot == INVALID_NPOS) { // Not in buffer; we'll have to read it in from disk. // Get a free slot. if ((islot = GetFreeSlot()) == INVALID_NPOS) return false; diskAddrs_[islot] = locn; if (!(theFile_->SeekTo(locn) == locn) || !(theFile_->Fetch(buff_ + islot*blocksize_, blocksize_) == FILE_NO_ERROR)) return false; } useCounts_[islot] = 0; memcpy(dat, buff_ + islot*blocksize_, blocksize_); return true; } bool CacheManager::Write(FileSystem::FAU_t locn, void* dat) { PRECONDITION(dat != NULL); PRECONDITION(theFile_->ReadyForWriting()); size_t islot = AgeAndFindSlot(locn); if (islot == INVALID_NPOS) { // Not in buffer; find a free slot. if ((islot = GetFreeSlot()) == INVALID_NPOS) return false; diskAddrs_[islot] = locn; } useCounts_[islot] = 0; memcpy(buff_ + islot*blocksize_, dat, blocksize_); if ((theFile_->SeekTo(locn) == locn) && (theFile_->Store(buff_ + islot*blocksize_, blocksize_) == FILE_NO_ERROR)) return true; return false; } ////////////////////////////////////////////////////////////////////////////////// // Private helper functions size_t CacheManager::AgeAndFindSlot(FileSystem::FAU_t locn) { size_t islot = INVALID_NPOS; for (register unsigned i = 0; i < nused_; i++) { if (diskAddrs_[i] == locn) islot = i; useCounts_[i]++; // Age the blocks } return islot; } bool CacheManager::Flush(unsigned) { if (theFile_->Flush() == FILE_NO_ERROR) return true; return false; } size_t CacheManager::GetFreeSlot() { size_t islot; if (nused_ < maxblocks_) { islot = nused_++; // Found an unused slot. } else { // No free slots; get the Least Recently Used block islot = LRU(); if (theFile_->ReadyForWriting()) if (!Flush(islot)) islot = INVALID_NPOS; } return islot; } size_t CacheManager::LRU() const { size_t islot = 0; unsigned maxCount = useCounts_[0]; for (register unsigned i = 1; i < nused_; i++) { if (useCounts_[i] > maxCount) { maxCount = useCounts_[islot = i]; } } return islot; }