// BlockCache.cpp
//
// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de)
//
// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can alternatively use *this file* under the terms of the the MIT
// license included in this package.
 
#include "BlockCache.h"
 
#include <new>
 
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
#include <fs_cache.h>
 
#include "Block.h"
#include "Debug.h"
#include "reiserfs.h"
 
using std::nothrow;
 
/*!
	\class BlockCache
	\brief Implements a cache for disk blocks.
 
	The central methods are GetBlock() and PutBlock(). The former one
	requests a certain block and the latter tells the cache, that the caller
	is done with the block. When a block is unused it is either kept in
	memory until the next time it is needed or until its memory is needed
	for another block.
*/
 
// constructor
BlockCache::BlockCache()
	: fDevice(-1),
	  fBlockSize(0),
	  fBlockCount(0),
	  fLock(),
	  fBlocks(),
	  fReads(0),
	  fBlockGets(0),
	  fBlockReleases(0)
{
}
 
// destructor
BlockCache::~BlockCache()
{
	fLock.Lock();
	for (int32 i = 0; Block *block = fBlocks.ItemAt(i); i++) {
		if (block->_GetRefCount() > 0) {
			INFORM(("WARNING: block not put: %p (ref count: %ld)\n", block,
					block->_GetRefCount()));
		}
		delete block;
	}
	PRINT(("statistics: %Ld block reads\n", fReads));
	PRINT(("statistics: %Ld block gets\n", fBlockGets));
	PRINT(("statistics: %Ld block releases\n", fBlockReleases));
	if (fCacheHandle)
		block_cache_delete(fCacheHandle, false);
	fLock.Unlock();
}
 
// Init
status_t
BlockCache::Init(int device, uint64 blockCount, uint32 blockSize)
{
	if (device < 0 || blockSize <= 0)
		return B_BAD_VALUE;
 
	fDevice = device;
	fBlockSize = blockSize;
	fBlockCount = blockCount;
 
	// init block cache
	fCacheHandle = block_cache_create(fDevice, blockCount, blockSize, true);
	if (!fCacheHandle)
		return B_ERROR;
 
	return B_OK;
}
 
// GetBlock
status_t
BlockCache::GetBlock(Block *block)
{
	status_t error = (block ? B_OK : B_BAD_VALUE);
	if (error == B_OK) {
		fLock.Lock();
		if (fBlocks.HasItem(block))
			block->_Get();
		else
			error = B_BAD_VALUE;
		fLock.Unlock();
	}
	return error;
}
 
// GetBlock
status_t
BlockCache::GetBlock(uint64 number, Block **result)
{
 
	if (!result || number >= fBlockCount)
		return B_BAD_VALUE;
 
	fLock.Lock();
 
	// find the block in the cache
	status_t error = B_OK;
	Block *block = _FindBlock(number);
	if (!block) {
		// not found, read it from disk
		error = _ReadBlock(number, &block);
		if (error == B_OK)
			fBlocks.AddItem(block);
	}
 
	// increase the block's reference counter
	if (error == B_OK) {
		block->_Get();
		*result = block;
	}
 
	fLock.Unlock();
	return error;
}
 
// PutBlock
void
BlockCache::PutBlock(Block *block)
{
	fLock.Lock();
	if (block && fBlocks.HasItem(block)) {
		if (block->_Put()) {
			fBlocks.RemoveItem(block);
			delete block;
		}
	}
	fLock.Unlock();
}
 
// _FindBlock
Block *
BlockCache::_FindBlock(uint64 number)
{
	for (int32 i = 0; Block *block = fBlocks.ItemAt(i); i++) {
		if (block->GetNumber() == number)
			return block;
	}
	return NULL;
}
 
// _ReadBlock
status_t
BlockCache::_ReadBlock(uint64 number, Block **result)
{
	fReads++;	// statistics
 
	// get a free block and read the block data
	Block *block = new(nothrow) Block;
	if (!block)
		return B_NO_MEMORY;
 
	status_t error = block->_SetTo(this, number);
 
	// set the result / cleanup on error
	if (error == B_OK)
		*result = block;
	else if (block)
		delete block;
	return error;
}
 
// _GetBlock
void *
BlockCache::_GetBlock(off_t number) const
{
	fBlockGets++;	// statistics
	return const_cast<void*>(block_cache_get(fCacheHandle, number));
}
 
// _ReleaseBlock
void
BlockCache::_ReleaseBlock(off_t number, void *data) const
{
	fBlockReleases++;	// statistics
	block_cache_put(fCacheHandle, number);
}
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fCacheHandle.

V576 Incorrect format. Consider checking the third actual argument of the 'dprintf' function. The memsize type argument is expected.