www.gusucode.com > VC++编写的SQL服务端和客户端源码程序 > VC++编写的SQL服务端和客户端源码程序\code\Server\Cache.cpp

    //Download by http://www.NewXing.com
// cache.cpp
//

#include "stdafx.h"
#include "miniSQL.h"
#include "Cache.h"

extern CMiniSQLApp theApp;

////////////////////////////////////////////////////////////////////
// CBLOCK

CBlock::CBlock()
{
	Clear();
}

CBlock::~CBlock()
{
}

void CBlock::Clear()
{
	status.dirty = status.lock = false;
	status.level = 0;
	status.pos = 0;
	status.pFile = NULL;
}

Status* CBlock::GetStatus()
{
	return &status;
}

char* CBlock::GetBlock()
{
	status.level++;
	return Block;
}

UINT CBlock::Read(CFile *pFile, long pos)
{
	if( !pFile )
		throw Error( FILE_OPEN_ERROR, 0, pFile->GetFileName() );

//	status.dirty = status.lock = false;	//just no change
	status.level = 1;
	status.pos = pos;
	status.pFile = pFile;
	
	long lPos = status.pos << BLOCK_OFFSET;
	status.pFile->Seek(lPos, CFile::begin);
	return status.pFile->Read(Block, sizeof(Block));
}

bool CBlock::Write()
{
	if( status.pFile && status.dirty ){
		long lPos = status.pos << BLOCK_OFFSET;
		status.pFile->Seek(lPos, CFile::begin);
		status.pFile->Write(Block, sizeof(Block));
	}

	return true;
}

/////////////////////////////////////////////////////////////////////
// CCACHE

int CCache::Next()
{
	//Find the next Block which can be used
	//using Clockwise algorithm
	int i = (last + 1) % CACHE_SIZE;
	
	Status* status = Cache[i].GetStatus();
	while( status->level ){
		if( !status->lock )
			status->level--;
		i = (i + 1) % CACHE_SIZE;
		status = Cache[i].GetStatus();
	}
	return i;
}

int CCache::Find(POSITION fPos, long pos)
{
	//Find the specified Block
	Status *status;
	for(int i = CACHE_SIZE - 1; i >= 0; i--){
		status = Cache[i].GetStatus();
		if( status->pFile == FileList.GetAt(fPos)
			&& status->pos == pos )
			return i;
	}
	return -1;
}

POSITION CCache::Find(CFile* pFile)
{
	//Find the position in the list corresponding to pFile
	POSITION pos = FileList.GetHeadPosition();

	while( pos ){
		POSITION old = pos;
		if( FileList.GetNext(pos) == pFile )
			return old;
	}
	return NULL;
}

POSITION CCache::Open( const char* fname, const char* fsuf )
{
	//open a file;
	char file_name[ FILE_NAME_LEN + 1 ];
	::strncpy( file_name, fname, MAIN_NAME_LEN );
	file_name[ MAIN_NAME_LEN ] = 0;
	::strncat( file_name, fsuf, FILE_NAME_LEN - MAIN_NAME_LEN );
	file_name[ FILE_NAME_LEN ] = 0;

	POSITION pos = FileList.GetHeadPosition();

	while( pos ){
		POSITION old = pos;
		if( FileList.GetNext(pos)->GetFileName() == file_name )
			return old;
	}

	//check if the file exists
	CFileStatus status;
	CFile* pFile;
	CString path = theApp.dir;
	path = path + "\\" + file_name;
	if( !CFile::GetStatus( path, status ) )
	{
		if( !( pFile = new CFile( path, 
			CFile::modeCreate | CFile::modeReadWrite) ) )
			throw Error( FILE_CREATE_ERROR, 0, path );
	}
	else
	{
		if( !( pFile = new CFile( path, CFile::modeReadWrite ) ) )
			throw Error( FILE_OPEN_ERROR, 0, path );
	}
	
	//add to list
	FileList.AddTail(pFile);
	return FileList.GetTailPosition();
}

void CCache::Close(POSITION fPos, bool reserve)
{
	//check if fPos exist
	bool flag = false;
	for(int i = 0; i < FileList.GetCount(); i++)
		if( FileList.FindIndex( i ) == fPos ){
			flag = true;
			break ;
		}
	if( !flag )return ;

	//close the cache specified by fPos
	CFile* pFile = FileList.GetAt ( fPos );
	if( pFile == NULL ) return ;

	for(i = 0; i < CACHE_SIZE; i++)
		if( Cache[i].status.pFile == pFile ){
			if( reserve )Cache[i].Write();
			Cache[i].Clear();
		}

	CString fPath = pFile->GetFilePath();
	pFile->Close();
	if( !reserve ){		
		TRY{
			CFile::Remove( fPath );
		}
		CATCH( CFileException, e ){
			#ifdef _DEBUG
				Message( "File \' " + fPath + " \' can't be removed.\n" );
			#endif
		}
		END_CATCH
	}

	FileList.RemoveAt( fPos );
	pFile = NULL;
}

void CCache::Clear()
{
	//clear the cache
	for( int i = 0; i < CACHE_SIZE; i++ ){
		Cache[i].Write();
		Cache[i].Clear();
	}

	POSITION pos = FileList.GetHeadPosition();
	while( pos )
		FileList.GetNext( pos )->Close();
	FileList.RemoveAll();

	last = -1;
}

CBlock* CCache::FetchBlock(POSITION fPos, long pos)
{
	//check if fPos exist
	bool flag = false;
	for(int i = 0; i < FileList.GetCount(); i++)
		if( FileList.FindIndex( i ) == fPos ){
			flag = true;
			break ;
		}
	if( !flag )return NULL;

	//fetch a specified data into memory
	if( ( i = Find( fPos, pos ) ) == -1 ){
		i = Next();
		Cache[i].Write();
		Cache[i].Read( FileList.GetAt( fPos ), pos );
	}
	last = i;

	return &Cache[i];
}

void CCache::Read( POSITION fPos, long address, void* vdes, UINT sz)
{
	//read data from hareware to memory
	char* des = ( char* ) vdes;
	UINT m_sz;
	CBlock* pBlock;

	while( sz > 0 ){
		m_sz = BLOCK_SIZE - address % BLOCK_SIZE;
		if( m_sz > sz )
			m_sz = sz;
		pBlock = FetchBlock( fPos, address >> BLOCK_OFFSET );
		::memcpy( des, pBlock->GetBlock() + address % BLOCK_SIZE, m_sz );
		des += m_sz;
		address += m_sz;
		sz -= m_sz;
	}
}

void CCache::Write( POSITION fPos, long address, const void* vsrc, UINT sz)
{
	//write data in memory, change the cache
	const char* src = ( const char* ) vsrc;
	UINT m_sz;
	CBlock* pBlock;

	while( sz > 0 ){
		m_sz = BLOCK_SIZE - address % BLOCK_SIZE;
		if( m_sz > sz )
			m_sz = sz;
		pBlock = FetchBlock( fPos, address >> BLOCK_OFFSET );
		::memcpy( pBlock->GetBlock() + address % BLOCK_SIZE, src, m_sz );
		pBlock->status.dirty = true;
		src += m_sz;
		address += m_sz;
		sz -= m_sz;
	}
}

/////////////////////////////////////////////////////////////////////
// THE EXTERNAL OBJECT, ONE AND THE ONLY

CCache cache;

/////////////////////////////////////////////////////////////////////
// CFILEAPI

CFileAPI::CFileAPI( const char* fname, const char* fsuf )
{
	//construction
	//for read
	CFileStatus status;
	CString path = theApp.dir;
	path = path + "\\" + fname + fsuf;
	if( !CFile::GetStatus( path, status ) )
		throw Error( FILE_OPEN_ERROR, 0, path );
	fPos = cache.Open( fname, fsuf );
	LoadHead();
}

CFileAPI::CFileAPI( const char* fname, const char* fsuf, long rec_len, long attr_len, bool del_sign )
{
	//construction
	//open a new file and write
	RecCount	= AllocList = 0;
	RecLen		= rec_len;
	AttrBlocks	= ( attr_len + BLOCK_SIZE - 1 ) / BLOCK_SIZE;
	DelSign		= del_sign;
	fPos		= cache.Open( fname, fsuf );
	SaveHead();
}

CFileAPI::~CFileAPI()
{
	//destruction
	if( fPos )
		cache.Close( fPos );
}

void CFileAPI::LoadHead()
{
	//load all the variables
	cache.Read( fPos, 0, &AllocList, ( char* ) &fPos - ( char* ) &AllocList );
}

void CFileAPI::SaveHead()
{
	//save the variables
	cache.Write( fPos, 0, &AllocList, ( char* ) &fPos - ( char* ) &AllocList );
}

//IMPLEMENTATIONS
long CFileAPI::ntop( long n )
{
	//change the number of records to file position or address
	return ( DelSign ? ( sizeof( bool ) + RecLen ) * n + sizeof( bool ) : RecLen * n )
		+ BLOCK_SIZE * ( AttrBlocks + 1 );
}

void CFileAPI::ReadAttr( void* des, UINT len )
{
	cache.Read( fPos, BLOCK_SIZE, des, len );
}

void CFileAPI::WriteAttr( const void* src, UINT len )
{
	cache.Write( fPos, BLOCK_SIZE, src, len );
}

void CFileAPI::ReadRec( long n, void* des )
{
	cache.Read( fPos, ntop( n ), des, RecLen );
}

void CFileAPI::WriteRec( long n, const void* src )
{
	cache.Write( fPos, ntop( n ), src, RecLen );
}

CBlock* CFileAPI::FetchBlock( long bpos )
{
	return cache.FetchBlock( fPos, ++bpos + AttrBlocks );
}

long CFileAPI::AllocRec()
{
	//allocate a new record
    long NewAlloc = AllocList;
    if( AllocList == RecCount )
        AllocList = ++RecCount ;
    else{
        cache.Read( fPos, ntop( NewAlloc ), &AllocList, sizeof( long ) );
        if( DelSign ){
            bool sign = false;
            cache.Write( fPos, ntop( NewAlloc ) - sizeof( bool ), &sign, sizeof( bool ) );
        }
    }
    SaveHead();
    return NewAlloc;
}

void CFileAPI::RemoveRec( long n )
{
	//remove a record
    if( DelSign )
    {
        bool sign = true;
        cache.Write( fPos, ntop( n ) - sizeof( bool ), &sign, sizeof( bool ) );
    }
    cache.Write( fPos, ntop( n ), &AllocList, sizeof( long ) );
    AllocList = n;
    SaveHead();
}

long CFileAPI::NextRec( long n )
{
	//find the next non-empty record
	if( n >= RecCount - 1 )
        return -1;
    if( !DelSign )
        return ++n;
    bool sign;
    do 
		cache.Read( fPos, ntop( ++n ) - sizeof( bool ), &sign, sizeof( bool ) );
    while( sign && n < RecCount - 1 );
    return sign ? -1 : n;
}

long CFileAPI::FirstRec()
{
	//return the first non-empty record
	return NextRec( -1 );
}

void CFileAPI::Close()
{
	cache.Close( fPos );
	fPos = NULL;
}

void CFileAPI::Drop()
{
	//drop the file API for certain reasons
	cache.Close( fPos, false );
	fPos = NULL;
}