/*
	File:		CBaseDatabase.10.1.cpp

	Copyright:	© 2003 by Apple Computer, Inc., all rights reserved.

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:
*/

#include <ctype.h>
#include <string.h>
#include <time.h>

#include "CFile.h"
#include "Database.10.1.h"
#include "CBaseDatabase.10.1.h"
#include "UException.h"


Boolean gBaseDatabaseAssert_10_1 = true;

CBaseDatabase_10_1::CBaseDatabase_10_1 ( const uInt32 inMinFreeDiskSpace,
										 CDBBaseObjectCache_10_1 *inBaseObjectCachePtr,
										 uInt32 inPageCacheCount ) : fStore()
{
	// *********************************************************************************
	// Warning: DO NOT CHANGE/ADD/REMOVE PUBLIC/PRIVATE DATA FIELDS TO THIS CLASS/STRUCT
	// WITHOUT CONSULTING WITH A APPLESHARE IP MAIL TEAM MEMBER.  CHANGES TO
	// THIS STRUCTURE __WILL__ CAUSE FILE FORMAT COMPATIBILITY ISSUES FOR APPLESHARE IP 5.0
	// AND 6.0 MAIL DATABASES.
	//
	// FAILURE TO HEED THIS WARNING __WILL__ LEAD TO DATA LOSS
	// AND/OR CRASHING BUGS WHEN CUSTOMERS ATTEMPT TO USE YOUR SIMPLE MODIFICATIONS WITH
	// A PRE-EXISTING MAIL DATABASE.
	//
	// SINCE THIS FILE IS SHARED BY MORE COMPONENTS THAN JUST YOURS, PLEASE BE AWARE OF THE
	// C++ FRAGILE BASE CLASS PROBLEM AND DON'T MAKE CHANGES IN THE BASE CLASS SOURCE CODE
	// WITH OUT CONSULTING OTHER SOURCES THAT ARE DEPENDANT ON THIS STURUCTURE.
	// *********************************************************************************

	uInt32	index = 0;

//	fFileRef	 = 0;
	fFileType	 = 0;
	fFileCreator = 0;
	fOldPageCount = 0;
	fHeaderDirty = false;
	fObjectCachePtr = inBaseObjectCachePtr;

//	::memset( &fFSSpec, 0, sizeof( FSSpec ) );
	::memset( &fDatabaseHeader, 0, sizeof( SDatabaseHeaderData_10_1 ) );
	fMinFreeDiskSpace = inMinFreeDiskSpace;

	fNextFreeSpaceCheck		= 0;
	fPageCountTillFreeCheck	= 0;

	::memcpy( fDatabaseHeader.fDatabaseTag, kDBTag_10_1, sizeof( kDBTag_10_1 ) );
	fDatabaseHeader.fDatabaseVersion = kCurrentDBVersion_10_1;
	fDatabaseHeader.fPageSize		 = kStandardPageSize;
	fDatabaseHeader.f1stPageOffset	 = ( ( sizeof( SDatabaseHeaderData_10_1 ) / this->GetPageSize() ) + 1 ) * this->GetPageSize();
	fDatabaseHeader.fSmallObjectSize = kSmallDBObjSize;
	fDatabaseHeader.fMaxObjectSize	 = kMaxDBObjSize;
	fDatabaseHeader.fMaxObjTypeCount = kMaxObjTypeCount;
	fDatabaseHeader.fDBState		 = kNoDBState_10_1;
	fDatabaseHeader.fDBReOpenState	 = kNoDBState_10_1;

	if ( inPageCacheCount > kMaxPageCacheEntries_10_1 )
	{
		inPageCacheCount = kMaxPageCacheEntries_10_1;
	}

	fPageCache.fCount = 0;
	fPageCache.fEntryCount = inPageCacheCount;
	fPageCache.fLRUEntryCount = 0;
	fPageCache.fDirtyCount = 0;
	fPageCache.fPageCacheFreeListHead = kNULLIndex_10_1;

	fPageCache.fLRUListHead = kNULLIndex_10_1;
	fPageCache.fLRUListTail = kNULLIndex_10_1;

	for ( index = 0; index < fPageCache.fEntryCount; index++ )
	{
//		fPageCache.fPageCacheEntryList[index] = new PageCacheEntry_10_1;
		fPageCache.fPageCacheEntryList[index] = new PageCacheEntry_10_1;
		if ( fPageCache.fPageCacheEntryList[index] == NULL )
		{
			fPageCache.fEntryCount = index + 1;
			break;
		}

		::memset( fPageCache.fPageCacheEntryList[index], 0, sizeof( PageCacheEntry_10_1 ) );

		fPageCache.fPageCacheEntryList[index]->fNextPageIndex = fPageCache.fPageCacheFreeListHead;
		fPageCache.fPageCacheFreeListHead = index;

		fPageCache.fPageCacheEntryList[index]->fLRUPrevIndex = kNULLIndex_10_1;
		fPageCache.fPageCacheEntryList[index]->fLRUNextIndex = kNULLIndex_10_1;
	}
	BaseDBAssert( index == fPageCache.fEntryCount );

	for ( index = 0; index < kMaxHashTableSize_10_1; index++ )
	{
		fPageCache.fPageCacheHashTable[index] = kNULLIndex_10_1;
	}

//	::memset( &fFileIOPB, 0, sizeof( ParamBlockRec ) );

	CBaseDatabase_10_1::CheckBackwardCompatibility();

} // CBaseDatabase_10_1


CBaseDatabase_10_1::~CBaseDatabase_10_1 ( void )
{
	uInt32	index = 0;
	BaseDBAssert( fStore.is_open() == false );

	if ( fStore.is_open() == true )
	{
		this->CloseDatabaseFile();
	}
	fHeaderDirty = false;

	::memset( &fDatabaseHeader, 0, sizeof( SDatabaseHeaderData_10_1 ) );

	for ( index = 0; index < fPageCache.fEntryCount; index++ )
	{
		if ( fPageCache.fPageCacheEntryList[index] != NULL )
		{
			delete fPageCache.fPageCacheEntryList[index];
			fPageCache.fPageCacheEntryList[index] = NULL;
		}
	}

	fPageCache.fCount = 0;
	fPageCache.fEntryCount = 0;
	fPageCache.fDirtyCount = 0;
	fObjectCachePtr = NULL;
}

CDBBaseObjectCache_10_1  *CBaseDatabase_10_1::GetBaseObjectCachePtr ( void )
{
	if ( this != NULL )
	{
		return ( this->fObjectCachePtr );
	}

	return NULL;
}

void CBaseDatabase_10_1::CheckBackwardCompatibility ( void )
{
	//*************************************************************************************************
	// if any of these checks cause a debugger break, then some change has caused the binary size of
	// these data types to be different than what was shipped with AppleShare IP 5.0 & 6.0....
	// this means that there will be data-loss or crashing bugs with this version of CIDTable_10_1 since
	// portions of CIDTable_10_1 have been written to disk in their native binary format....
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( ObjID ),				4 );		// NOTE: this is hard coded for a reason!!
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( PageID ),				4 );		// NOTE: this is hard coded for a reason!!
//
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( SObjTypeInfo_10_1 ),		8 );		// NOTE: this is hard coded for a reason!!
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( SObjList_10_1 ),			28 );	// NOTE: this is hard coded for a reason!!
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( SPrivObjTypeInfo_10_1 ),	128 );	// NOTE: this is hard coded for a reason!!
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( SOldFreeList ),		8 );		// NOTE: this is hard coded for a reason!!
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( SNewFreeListEntry ),	12 );	// NOTE: this is hard coded for a reason!!
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( SDatabaseHeaderData_10_1 ),	4196 );	// NOTE: this is hard coded for a reason!!
//
//	CBaseDatabase_10_1::ReportBackwardCompatibility( kStandardPageSize,			8192 );	// NOTE: this is hard coded for a reason!!
//	CBaseDatabase_10_1::ReportBackwardCompatibility( sizeof( SStr2IDTable ),		8192 );	// NOTE: this is hard coded for a reason!!
}

void CBaseDatabase_10_1::ReportBackwardCompatibility ( const uInt32 curSize, const uInt32 correctSize )
{
	uInt32	aSize = curSize;
	if ( aSize != correctSize )
	{
//		DebugStr( "\pWARNING: CBaseDatabase_10_1 Base Class data element size problem.  Potential File format compatibility problem detected!" );
	}
}

#pragma mark -

OSStatus CBaseDatabase_10_1::OpenDatabaseFile (	const CFileSpecPtr	inFileSpecPtr,
											const OSType		inFileType,
											const OSType		inFileCreator,
											const Boolean		inCreateFlag,
											const uInt32		inDBVersion )
{
#pragma unused ( inFileType, inFileCreator )
	OSErr	fileErr = kNoErr;
	short	aFileRef = 0;

	this->DoLockDB();

	try
	{
		fStore.open( inFileSpecPtr, inCreateFlag );
		fStore.seekg( 0, ios::end );
		if ( fStore.tellg() )
		{
			fileErr = this->ReadDatabaseHeader( inDBVersion );
		}
		else
		{
			fileErr = this->SetupDatabase( inDBVersion );
		}
	}

	catch ( OSErr thrownErr )
	{
		fileErr = thrownErr;
	}

	catch ( ... )
	{
		// Clients aren't expecting the object to throw, so
	}


	this->DoReleaseDB();

	return( fileErr );

} // OpenDatabaseFile


OSStatus CBaseDatabase_10_1::CloseDatabaseFile ( void )
{
	OSStatus	result = kDBNoErr;
	OSErr		anAsyncErr = kNoErr;

	if ( fStore.is_open() == false )
	{
		return kDBNotOpen;
	}

	this->DoLockDB();

	try
	{
		this->FlushDatabaseFile( kDBSafe_10_1 );

		fDatabaseHeader.fDBState		= kDBClosed_10_1;
		fDatabaseHeader.fDBReOpenState	= kDBSafe_10_1;
		this->WriteDatabaseHeader( true );

		fStore.close ();
	}

	catch ( OSErr thrownErr )
	{
		if ( thrownErr == kFnOpnErr )
		{
			result = kDBNotOpen;
		}
		else
		{
			result = thrownErr;
		}
	}

	catch ( ... )
	{
		// Clients aren't expecting the object to throw, so
		result = kDBNotOpen;
	}

	this->DoReleaseDB();

	return( result );

} // CloseDatabaseFile


OSStatus CBaseDatabase_10_1::ReadDatabaseHeader ( const uInt32 inDBVersion )
{
	OSErr				aFileErr	= kNoErr;
	long				byteCount	= sizeof( SDatabaseHeaderData_10_1 );
	SPrivObjTypeInfoPtr_10_1	anObjInfo;
	uInt32				i = 0;

	if ( fStore.is_open() == false )
	{
		return( kDBNotOpen );
	}

	this->DoLockDB();

	try
	{
		fStore.seekg( 0 );
		fStore.Read( &fDatabaseHeader, byteCount );

		if ( ( fDatabaseHeader.fDatabaseVersion != inDBVersion ) &&
			  ( fDatabaseHeader.fDatabaseVersion != kCurrentDBVersion_10_1 ) )
		{
			aFileErr = kDBWrongVersion;
		}
		else
		{
			switch ( fDatabaseHeader.fDBState )
			{
				case kDBClosed_10_1:
					// All is right with the world
					aFileErr = kDBNoErr;
					break;

				case kDBOpen_10_1:
					if ( fDatabaseHeader.fDBReOpenState == kDBSafe_10_1 )
					{
						// DB was open but most likley in a safe state, lets open it but
						//	warn the user
						aFileErr = kDBNotClosedProperly;
					}
					else
					{
						aFileErr = kDBCanNotOpen;
					}
					break;

				default:
				case kNoDBState_10_1:
				case kDBFlushing_10_1:
					// In the realm of good and bad, this clearly falls into bad!
					//	We were in the middle of a flush when this happened and 
					//	we are not sure what state the database is in.  Don't open it.
					//	*** Warn The User ***
					aFileErr = kDBCanNotOpen;
					break;
			}
		}

		if ( ( aFileErr == kDBNoErr ) || ( aFileErr == kDBNotClosedProperly ) )
		{
			for ( i = 0; i < this->GetObjTypeCount(); i++ )
			{
				this->GetObjTypeEntry( i, anObjInfo );
				if ( anObjInfo != NULL )
				{
					anObjInfo->fObjDataFieldProc = NULL;
					anObjInfo->fIteratorList = NULL;
				}
			}

			// And how is this different from the previous test, exactly??
			if ( ( fDatabaseHeader.fDatabaseVersion != kCurrentDBVersion_10_1 ) &&
				 ( fDatabaseHeader.fDatabaseVersion == k60DataBaseVersion_10_1 ) )
			{
				uInt32	index = 0;

				for ( index = 0; index < kFreeMapPageCount_10_1; index++ )
				{
					this->fDatabaseHeader.fNewFreeList[index].fEntryCount	= 0;
					this->fDatabaseHeader.fNewFreeList[index].f1stEntry		= 0;
					this->fDatabaseHeader.fNewFreeList[index].fFreeListPage	= 0;
				}

				::memcpy( this->fDatabaseHeader.fDatabaseTag, kDBTag_10_1, sizeof( kDBTag_10_1 ) );
				fDatabaseHeader.fDatabaseVersion = kCurrentDBVersion_10_1;
			}
			this->WriteDatabaseHeader( true );
		}
	}

	catch ( OSErr thrownErr )
	{
		aFileErr = thrownErr;
	}
	catch ( ... )
	{
		// Clients aren't expecting the object to throw, so
	}

	this->DoReleaseDB();

	return( aFileErr );

} // ReadDatabaseHeader


OSStatus CBaseDatabase_10_1::WriteDatabaseHeader ( Boolean inTrueWriteFlag )
{
	OSStatus	result		= kDBNoErr;
	long		byteCount	= sizeof( SDatabaseHeaderData_10_1 );

	if ( fStore.is_open() == false )
	{
		return( kDBNotOpen );
	}

	this->DoLockDB();

	try
	{
		if ( inTrueWriteFlag == true )
		{
			fStore.seekp( 0 );
			fStore.write( &fDatabaseHeader, byteCount );

			fHeaderDirty = false;
		}
		else
		{
			fHeaderDirty = true;
		}
	}

	catch ( ... )
	{
		// Clients aren't expecting the object to throw, so
		result = kDBNotOpen;
	}

	this->DoReleaseDB();

	return( result );
} // WriteDatabaseHeader


OSStatus CBaseDatabase_10_1::SetupDatabase ( const uInt32 inDBVersion )
{
	OSStatus	aFileErr = kNoErr;
	sInt64		sdPageCount		= 0;
	sInt64		sdPageSize		= 0;
	sInt64		sd1stPageOffset	= 0;
	sInt64		sdNewEOF		= 0;

	if ( fStore.is_open() == false )
	{
		return kDBNotOpen;
	}

	this->DoLockDB();

	try
	{
		fDatabaseHeader.fDatabaseVersion = inDBVersion;
		fDatabaseHeader.fDBState		 = kDBOpen_10_1;
		fDatabaseHeader.fDBReOpenState	 = kDBNotSafe_10_1;

		aFileErr = this->WriteDatabaseHeader( true );
		if ( aFileErr == kDBNoErr )
		{
			sdPageSize		= this->GetPageSize();
			sdPageCount		= this->GetPageCount();
			sd1stPageOffset	= this->Get1stPageOffset();

			sdNewEOF = sd1stPageOffset + (sdPageCount * sdPageSize);
			fStore.seekp( sdNewEOF );
		}
	}

	catch ( OSErr thrownErr )
	{
		if ( thrownErr == kFnOpnErr )
		{
			aFileErr = kDBNotOpen;
		}
		else
		{
			aFileErr = thrownErr;
		}
	}

	catch ( ... )
	{
		// Clients aren't expecting the object to throw, so
		aFileErr = kDBNotOpen;
	}

	this->DoReleaseDB();

	return( aFileErr );

} // SetupDatabase


OSStatus CBaseDatabase_10_1::ReadPage ( const PageID inPageNum, char *outDataPtr )
{
	OSStatus	result		= kDBNoErr;
	sInt64		sdPageCount		= 0;
	sInt64		sdPageSize		= 0;
	sInt64		sdPageOffset	= 0;
	sInt64		sd1stPageOffset	= 0;
	sInt64		sdPageID		= inPageNum - 1;
	sInt32		siByteCount		= 0;

	if ( fStore.is_open() == false )
	{
		return( kDBNotOpen );
	}

	this->DoLockDB();

	try
	{
		sdPageCount = this->GetPageCount();

		BaseDBAssert( inPageNum != 0 );
		BaseDBAssert_if( sdPageID < sdPageCount )
		{
			sdPageSize		= this->GetPageSize();
			sd1stPageOffset	= this->Get1stPageOffset();

			sdPageOffset = sdPageID * sdPageSize;
			sdPageOffset = sdPageOffset + sd1stPageOffset;
			siByteCount	 = this->GetPageSize();

			fStore.seekg( sdPageOffset );
			fStore.read( outDataPtr, siByteCount );
		}
		else
		{
			result = kDBNoSuchPage;
		}
	}

	catch ( ... )
	{
		// Clients aren't expecting the object to throw, so
		result = kDBNotOpen;
	}

	this->DoReleaseDB();

	return( result );

} // ReadPage


//--------------------------------------------------------------------------------------------------
//	* WritePage ()
//
//--------------------------------------------------------------------------------------------------

OSStatus CBaseDatabase_10_1::WritePage ( const PageID inPageNum, char *inDataPtr )
{
	OSStatus	result			= kDBNoErr;
	sInt32		siByteCount		= 0;
	sInt64		sd1stPageOffset	= 0;
	sInt64		sdPageCount		= 0;
	sInt64		sdPageOffset	= 0;
	sInt64		sdPageSize		= 0;
	sInt64		sdPageID		= inPageNum - 1;

	if ( fStore.is_open() == false )
	{
		return( kDBNotOpen );
	}

	this->DoLockDB();

	try
	{
		sdPageCount	= this->GetPageCount();

		BaseDBAssert( inPageNum != 0 );
		BaseDBAssert( inDataPtr != NULL );
		BaseDBAssert_if( sdPageID < sdPageCount )
		{
			sdPageSize		= this->GetPageSize();
			siByteCount		= this->GetPageSize();
			sd1stPageOffset	= this->Get1stPageOffset();

			sdPageOffset = sdPageID * sdPageSize;
			sdPageOffset = sdPageOffset + sd1stPageOffset;

			fStore.seekp( sdPageOffset );
			BaseDBAssert( fStore.tellp() == sdPageOffset );
			fStore.write( inDataPtr, siByteCount );
		}
		else
		{
			result = kDBNoSuchPage;
		}
	}

	catch ( ... )
	{
		result = kDBNotOpen;
	}

	this->DoReleaseDB();

	return( result );

} // WritePage


//--------------------------------------------------------------------------------------------------
//	* AddPage ()
//
//--------------------------------------------------------------------------------------------------

OSStatus CBaseDatabase_10_1::AddPage	( uInt32 &outPageNum )
{
	OSStatus	result			= kDBNoErr;
	sInt64		sdBase			= (512 * 1024);
	sInt64		sdNewEOF		= 0;
	sInt64		sdPageCount		= 0;
	uInt32		uiIndex			= 0;
	sInt64		sd1stPageOffset	= 0;
	sInt64		sdPageSize		= 0;	// Needs to be an int, could be less than zero
	sInt64		sdMinFreeSpace	= 0;	// Needs to be an int, could be less than zero
	sInt64		sdVolFreeSpace	= 0;	// Needs to be an int, could be less than zero

	if ( fStore.is_open() == false )
	{
		return( kDBNotOpen );
	}

	this->DoLockDB();

	try
	{
		outPageNum = 0;
		if ( this->fDatabaseHeader.fPageCount >= kMaxDBPageCount_10_1 )
		{
			result = kDBDiskFull;
		}
		else if ( ((uTime_t)::time( nil ) > fNextFreeSpaceCheck) || (fPageCountTillFreeCheck == 0) )
		{
			sdPageSize		= this->GetPageSize();
			sdVolFreeSpace	= fStore.freespace();
			sdMinFreeSpace	= this->GetMinFreeDiskSpace();

			fNextFreeSpaceCheck = ::time( nil ) + kFreeSpaceCheckDelta_10_1;
			fPageCountTillFreeCheck = (sdVolFreeSpace / sdPageSize) / 4;

			// Will this page fit in the free space less the min?
			if ( ((sdVolFreeSpace - sdPageSize) < sdMinFreeSpace) || (sdVolFreeSpace <= sdBase) )
			{
				result = kDBDiskFull;

				fNextFreeSpaceCheck		= 0;
				fPageCountTillFreeCheck	= 0;
			}
			else
			{
				result = kDBNoErr;
			}
		}
		else
		{
			sdPageSize = this->GetPageSize();

			sdPageCount = (sdBase / sdPageSize) + 1;
			fPageCountTillFreeCheck -= sdPageCount;
		}

		if ( result == kDBNoErr )
		{
			sdPageSize		= this->GetPageSize();
			sdPageCount		= this->GetPageCount();
			sd1stPageOffset	= this->Get1stPageOffset();

			sdPageCount	= (sdBase / sdPageSize) + 1;
			sdNewEOF	= sdPageCount;
			sdNewEOF	= (sdNewEOF + sdPageCount) * sdPageSize;
			sdNewEOF	= sdNewEOF + sd1stPageOffset;

			fStore.seekp( sdNewEOF );

			outPageNum = this->GetPageCount() + 1;
			this->SetPageCount( this->GetPageCount() + sdPageCount );

			for ( uiIndex = outPageNum + 1; uiIndex <= this->GetPageCount(); uiIndex++ )
			{
				this->AddPage2FreeList( uiIndex, false );
			}
		}
		else
		{
			switch ( result )
			{
				case kDskFulErr:
				case kWPrErr:
				case kFLckdErr:
				case kVLckdErr:
				case kWrPermErr:
				case kFileBoundsErr:
					fNextFreeSpaceCheck = 0;
					fPageCountTillFreeCheck = 0;
					result = kDBDiskFull;
					break;

				default:
					result = kDBBadFile;
					break;
			}
		}
	}

	catch ( ... )
	{
		// Clients aren't expecting the object to throw, so
	}

	this->DoReleaseDB();

	return( result );

} // AddPage


OSStatus CBaseDatabase_10_1::FlushDatabaseFile ( eDBState_10_1 inReOpenState )
{
	OSStatus	result = kDBNoErr;
	OSErr		anAsyncErr = kNoErr;

	if ( fStore.is_open() == false )
	{
		return kDBNotOpen;
	}

	this->DoLockDB();

	try
	{
		fDatabaseHeader.fDBState		= kDBFlushing_10_1;
		fDatabaseHeader.fDBReOpenState	= kDBNotSafe_10_1;
		this->WriteDatabaseHeader( true );

		if ( inReOpenState == kDBSafe_10_1 )
		{
			fStore.flush();
		}

		result = this->FlushPageCache();

		fDatabaseHeader.fDBState		= kDBOpen_10_1;
		fDatabaseHeader.fDBReOpenState	= inReOpenState;
		result = this->WriteDatabaseHeader( true );

		if ( inReOpenState == kDBSafe_10_1 )
		{
			// If the file size hasn't changed, flush the file.
			if ( fOldPageCount == ( sInt32 )fDatabaseHeader.fPageCount )
			{
				fStore.flush();
			}
			else
			{
				// If it has, flush the disk structures as well.
				fStore.syncdisk();
			}
		}
		fOldPageCount = fDatabaseHeader.fPageCount;
	}

	catch ( ExceptionCode err )
	{
	}

	this->DoReleaseDB();

	return( result );

} // FlushDatabaseFile


#pragma mark -

OSStatus CBaseDatabase_10_1::GetStr2IDPage ( const PageID inPageNum, const Boolean inSkipPageIOFlag, CStr2IDTable_10_1 &outStr2IDTable )
{
	OSStatus	result = kDBNoErr;
	char		*aPageData = NULL;
	uInt32		junkPageIndexHint;

	try
	{
		this->DoLockDB();

		BaseDBAssert( inPageNum != 0 );

		outStr2IDTable.SetTable( NULL, 0 );
		result = this->GetPage( inPageNum, aPageData, inSkipPageIOFlag, junkPageIndexHint );
		if ( ( result == kDBNoErr ) && ( aPageData != NULL ) )
		{
			outStr2IDTable.SetTable( aPageData, inPageNum );
		}
		else if ( ( result != kDBNoErr ) && ( aPageData != NULL ) )
		{
			this->ReleasePage( inPageNum, false );
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
} // GetStr2IDPage


OSStatus CBaseDatabase_10_1::GetIDPage (	const PageID	inPageNum,
									const Boolean	inSkipPageIOFlag,
									CIDTable_10_1	   &outCIDTable,
									uInt32		   &outPageIndexHint )
{
	OSStatus	result = kDBNoErr;
	char		*aPageData = NULL;

	try
	{
		this->DoLockDB();

		BaseDBAssert( inPageNum != 0 );

		outCIDTable.SetTable( NULL, 0 );
		result = this->GetPage( inPageNum, aPageData, inSkipPageIOFlag, outPageIndexHint );
		if ( ( result == kDBNoErr ) && ( aPageData != NULL ) )
		{
			outCIDTable.SetTable( aPageData, inPageNum );
		}
		else if ( ( result != kDBNoErr ) && ( aPageData != NULL ) )
		{
			this->ReleasePage( inPageNum, false );
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
} // GetIDPage


OSStatus CBaseDatabase_10_1::GetPage ( const PageID inPageNum, char * &outPageData, const Boolean inSkipPageIOFlag, uInt32 &outPageIndexHint )
{
	static uInt32	gTotalGetPageCalls		= 0;
	static uInt32	gTotalCacheHits			= 0;
	static uInt32	gTotalCacheMisses		= 0;
	static uInt32	gTotalPageIOCounts		= 0;
	static uInt32	gTotalCacheDirtyFlushes	= 0;

	OSStatus		result = kDBNoErr;
	uInt32			anIndex2Use = 0;
	Boolean			pageExists = false;
	PageCacheEntry_10_1	*aPageCacheEntry = NULL;


	try
	{
		this->DoLockDB();

		gTotalGetPageCalls += 1;

		// BaseDBAssert( fPageCache.fCount == fPageCache.fLRUEntryCount );	// the number of pages in the Index should equal # on the LRU list
		BaseDBAssert( inPageNum != 0 );

		outPageData = NULL;
		outPageIndexHint = 0;

		pageExists = this->FindPageByIndex( inPageNum, anIndex2Use );
		if ( pageExists )
		{
			gTotalCacheHits += 1;

			BaseDBAssert( fPageCache.fPageCacheEntryList[anIndex2Use] != NULL );
			aPageCacheEntry = fPageCache.fPageCacheEntryList[anIndex2Use];

			aPageCacheEntry->fRefCount += 1;
			outPageData = aPageCacheEntry->fPageData;
			outPageIndexHint = anIndex2Use;

			this->RemoveFromLRUList( anIndex2Use );
			this->Add2LRUList( anIndex2Use );
		}
		else
		{
			gTotalCacheMisses += 1;

			if ( fPageCache.fPageCacheFreeListHead == kNULLIndex_10_1 )
			{
				// BaseDBAssert( fPageCache.fEntryCount == fPageCache.fLRUEntryCount );	// the number of pages in the Index should equal # on the LRU list

				anIndex2Use = this->GetLRUTailPage();
				BaseDBAssert_if( anIndex2Use != kNULLIndex_10_1 )
				{
					BaseDBAssert( anIndex2Use < fPageCache.fEntryCount );
					BaseDBAssert( fPageCache.fPageCacheEntryList[anIndex2Use] != NULL );

					aPageCacheEntry = fPageCache.fPageCacheEntryList[anIndex2Use];

					if ( aPageCacheEntry->fDirty )
					{
						gTotalCacheDirtyFlushes	+= 1;

						this->FlushDatabaseFile( kDBNotSafe_10_1 );
					}

					this->ClearPageFromCache( aPageCacheEntry->fPageID );
				}
				else
				{	// hmmmm....this is a _MAJOR_ error the Page table has no unrefered entries...
					BaseDBAssert( anIndex2Use != kNULLIndex_10_1 );	// lets force a debugger event...
					result = kDBPageTableAllUsed;
					outPageData = NULL;
					outPageIndexHint = 0;
				}
			}

			BaseDBAssert_if ( fPageCache.fPageCacheFreeListHead != kNULLIndex_10_1 )
			{
				// ok the page table isn't full, lets go searching for an unused entry...
				anIndex2Use = fPageCache.fPageCacheFreeListHead;
				fPageCache.fPageCacheFreeListHead = fPageCache.fPageCacheEntryList[anIndex2Use]->fNextPageIndex;

				BaseDBAssert( anIndex2Use < fPageCache.fEntryCount );
				BaseDBAssert( fPageCache.fPageCacheEntryList[anIndex2Use]->fPageID == 0 );

				if ( inSkipPageIOFlag == false )
				{
					gTotalPageIOCounts += 1;
					result = this->ReadPage( inPageNum, fPageCache.fPageCacheEntryList[anIndex2Use]->fPageData );
				}
				else
				{
					result = kDBNoErr;
				}

				if ( result == kDBNoErr )
				{
					BaseDBAssert( fPageCache.fPageCacheEntryList[anIndex2Use] != NULL );
					aPageCacheEntry = fPageCache.fPageCacheEntryList[anIndex2Use];

					aPageCacheEntry->fPageID = inPageNum;
					aPageCacheEntry->fRefCount = 1;
					aPageCacheEntry->fDirty = false;

					fPageCache.fCount += 1;

					outPageData = aPageCacheEntry->fPageData;
					outPageIndexHint = anIndex2Use;

					this->AddPage2Index( anIndex2Use );
					this->RemoveFromLRUList( anIndex2Use );
					this->Add2LRUList( anIndex2Use );
				}
			}
			else
			{	// hmmmm....this is a _MAJOR_ error the Page table has no unrefered entries...
				BaseDBAssert( fPageCache.fPageCacheFreeListHead != kNULLIndex_10_1 );	// lets force a debugger event...
				result = kDBPageTableAllUsed;
				outPageData = NULL;
				outPageIndexHint = 0;
			}
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::ReleaseStr2IDPage ( CStr2IDTable_10_1 &outStr2IDTable, Boolean inDirtyFlag )
{
	OSStatus	result = kDBNoErr;
	PageID		aPageID = outStr2IDTable.GetTablePageID();

	try
	{
		this->DoLockDB();

		BaseDBAssert( aPageID != 0 );

		outStr2IDTable.SetTable( NULL, 0 );
		result = this->ReleasePage( aPageID, inDirtyFlag );
		BaseDBAssert( result == kDBNoErr );

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
} // ReleaseStr2IDPage


OSStatus CBaseDatabase_10_1::ReleaseIDPage ( CIDTable_10_1 &outCIDTable, Boolean inDirtyFlag )
{
	OSStatus	result = kDBNoErr;
	PageID		aPageID = outCIDTable.GetTablePageID();

	try
	{
		this->DoLockDB();

		BaseDBAssert( aPageID != 0 );

		outCIDTable.SetTable( NULL, 0 );
		result = this->ReleasePage( aPageID, inDirtyFlag );
		BaseDBAssert( result == kDBNoErr );

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}


OSStatus CBaseDatabase_10_1::ReleasePage ( const PageID inPageID, const Boolean inDirtyFlag )
{
	OSStatus		result = kDBNoErr;
	uInt32			anIndex2Use = 0;
	Boolean			pageExists = false;
	PageCacheEntry_10_1	*aPageCacheEntry = NULL;

	// 1st see if the Page is already in the table
	BaseDBAssert( inPageID != 0 );

	pageExists = this->FindPageByIndex( inPageID, anIndex2Use );
	if ( pageExists == true )
	{
		aPageCacheEntry = fPageCache.fPageCacheEntryList[anIndex2Use];
		BaseDBAssert( aPageCacheEntry->fPageID == inPageID );
		BaseDBAssert( aPageCacheEntry->fRefCount > 0 );

		if ( inDirtyFlag )
		{
			this->DirtyPage( inPageID );
		}

		if ( aPageCacheEntry->fRefCount > 0 )
		{
			aPageCacheEntry->fRefCount -= 1;
		}
		else
		{ // hmmm this is a client error, but it's no reason to screw up the table
		  // but this means there is a set of unbalanced GetPage & Release page calls...
		  // we'll return an error to let the client software know...
		  result = kDB2ManyReleases;
		}
	}
	else
	{
		result = kDBPageNotInCache;
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::ClearPageFromCache ( const PageID inPageID )
{
	OSStatus		result = kDBNoErr;
	uInt32			anIndex2Use = 0;
	Boolean			pageExists = false;
	PageCacheEntry_10_1	*aPageCacheEntry = NULL;

	// 1st see if the Page is already in the table
	BaseDBAssert( inPageID != 0 );

	pageExists = this->FindPageByIndex( inPageID, anIndex2Use );
	if ( pageExists == true )
	{
		aPageCacheEntry = fPageCache.fPageCacheEntryList[anIndex2Use];
		BaseDBAssert( aPageCacheEntry->fPageID == inPageID );
		BaseDBAssert( aPageCacheEntry->fRefCount == 0 );

		this->RemovePageFromIndex( aPageCacheEntry->fPageID );
		this->RemoveFromLRUList( anIndex2Use );

		aPageCacheEntry->fNextPageIndex = fPageCache.fPageCacheFreeListHead;
		fPageCache.fPageCacheFreeListHead = anIndex2Use;

		aPageCacheEntry->fPageID = 0;
		aPageCacheEntry->fRefCount = 0;
		if ( aPageCacheEntry->fDirty )
		{
			fPageCache.fDirtyCount -= 1;
		}
		aPageCacheEntry->fDirty = false;
		fPageCache.fCount -= 1;
	}
	else
	{
		result = kDBPageNotInCache;
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::DirtyPage ( const PageID inPageID )
{
	OSStatus		result = kDBNoErr;
	uInt32			anIndex2Use = 0;
	Boolean			pageExists = false;
	PageCacheEntry_10_1	*aPageCacheEntry = NULL;

	// 1st see if the Page is already in the table
	BaseDBAssert( inPageID != 0 );

	pageExists = this->FindPageByIndex( inPageID, anIndex2Use );
	if ( pageExists == true )
	{
		aPageCacheEntry = fPageCache.fPageCacheEntryList[anIndex2Use];
		BaseDBAssert( aPageCacheEntry->fPageID == inPageID );

		if ( aPageCacheEntry->fDirty == false )
		{
			fPageCache.fDirtyCount += 1;
			BaseDBAssert( fPageCache.fDirtyCount <= fPageCache.fCount );
		}
		aPageCacheEntry->fDirty = true;
	}
	else
	{
		result = kDBPageNotInCache;
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::FlushPageCache ( Boolean inMarkEmpty )
{
	OSStatus	result = kDBNoErr;
	uInt32		index = 0;

	try
	{
		this->DoLockDB();

		for ( index = 0; ( ( fPageCache.fDirtyCount > 0 ) || ( inMarkEmpty ) ) && ( index < fPageCache.fEntryCount ); index++ )
		{
			if ( fPageCache.fPageCacheEntryList[index]->fPageID != 0 )
			{
				if ( fPageCache.fPageCacheEntryList[index]->fDirty == true )
				{
					this->WritePage( fPageCache.fPageCacheEntryList[index]->fPageID, fPageCache.fPageCacheEntryList[index]->fPageData );
					fPageCache.fPageCacheEntryList[index]->fDirty = false;
					fPageCache.fDirtyCount -= 1;
				}
			}

			if ( inMarkEmpty )
			{
				BaseDBAssert( fPageCache.fPageCacheEntryList[index]->fRefCount == 0 );
				fPageCache.fPageCacheEntryList[index]->fPageID = 0;
				fPageCache.fPageCacheEntryList[index]->fRefCount = 0;
				fPageCache.fPageCacheEntryList[index]->fDirty = false;
			}
		}

		if ( inMarkEmpty )
		{
			fPageCache.fCount = 0;
			fPageCache.fDirtyCount = 0;
		}
		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

uInt32 CBaseDatabase_10_1::GetPageCacheCount ( void )
{
	return fPageCache.fEntryCount;
}

uInt32	CBaseDatabase_10_1::HashResult ( const PageID inPageID )
{
	return ( inPageID % kMaxHashTableSize_10_1 );
}

Boolean	CBaseDatabase_10_1::FindPageByIndex	( const PageID inPageID, uInt32 &outPageIndex )
{
	uInt32	aHashIndex = 0;
	uInt32	aPageIndex = kNULLIndex_10_1;
	Boolean	pageExists = false;

	outPageIndex = kNULLIndex_10_1;

	aHashIndex = this->HashResult( inPageID );
	aPageIndex = fPageCache.fPageCacheHashTable[aHashIndex];

	while ( ( aPageIndex != kNULLIndex_10_1 ) && ( pageExists == false ) )
	{
		if ( fPageCache.fPageCacheEntryList[aPageIndex] != NULL )
		{
			pageExists = ( fPageCache.fPageCacheEntryList[aPageIndex]->fPageID == inPageID );
			if ( pageExists == true )
			{
				outPageIndex = aPageIndex;
			}

			aPageIndex = fPageCache.fPageCacheEntryList[aPageIndex]->fNextPageIndex;
		}
		else
		{
			pageExists = false;
			outPageIndex = 0;
			aPageIndex = 0;
		}
	}

	return pageExists;
}

void CBaseDatabase_10_1::RemovePageFromIndex	( const PageID inPageID )
{
	uInt32			aHashIndex = 0;
	uInt32			aPageIndex = kNULLIndex_10_1;
	PageCacheEntry_10_1	*aPageCacheEntry = NULL;
	PageCacheEntry_10_1	*aNextPageCacheEntry = NULL;

	// Now let's remove the Page from the HashTable Index
	aHashIndex = this->HashResult( inPageID );
	aPageIndex = fPageCache.fPageCacheHashTable[aHashIndex];

	if ( aPageIndex != kNULLIndex_10_1 )
	{
		aPageCacheEntry = fPageCache.fPageCacheEntryList[aPageIndex];

		if ( aPageCacheEntry->fPageID == inPageID )
		{
			fPageCache.fPageCacheHashTable[aHashIndex] = aPageCacheEntry->fNextPageIndex;
		}
		else
		{
			aPageIndex = aPageCacheEntry->fNextPageIndex;
			while ( aPageIndex != kNULLIndex_10_1 )
			{
				aNextPageCacheEntry = fPageCache.fPageCacheEntryList[aPageIndex];

				if ( aNextPageCacheEntry->fPageID == inPageID )
				{
					aPageCacheEntry->fNextPageIndex = aNextPageCacheEntry->fNextPageIndex;
					aPageIndex = kNULLIndex_10_1;
				}
				else
				{
					aPageCacheEntry = aNextPageCacheEntry;
					aNextPageCacheEntry = NULL;
					aPageIndex = aPageCacheEntry->fNextPageIndex;
				}
			}
		}
	}

}

void	CBaseDatabase_10_1::AddPage2Index ( const uInt32 inPageIndex )
{
	uInt32	aHashIndex = kNULLIndex_10_1;
	uInt32	junk;
	Boolean pageExists = false;

	// Now let's remove the Page from the HashTable Index
	BaseDBAssert_if( fPageCache.fPageCacheEntryList[inPageIndex]->fPageID != 0 )
	{
		pageExists = this->FindPageByIndex( fPageCache.fPageCacheEntryList[inPageIndex]->fPageID, junk );
		BaseDBAssert_if ( pageExists == false )
		{
			aHashIndex = this->HashResult( fPageCache.fPageCacheEntryList[inPageIndex]->fPageID );

			fPageCache.fPageCacheEntryList[inPageIndex]->fNextPageIndex = fPageCache.fPageCacheHashTable[aHashIndex];
			fPageCache.fPageCacheHashTable[aHashIndex] = inPageIndex;
		}
	}
}

#pragma mark -

PageID CBaseDatabase_10_1::GetDBVersion		( void )
{
	return( fDatabaseHeader.fDatabaseVersion );
}

uInt32 CBaseDatabase_10_1::Get1stPageOffset ( void )
{
	return( fDatabaseHeader.f1stPageOffset );
}

uInt32 CBaseDatabase_10_1::GetPageSize ( void )
{
	return( fDatabaseHeader.fPageSize );
}

uInt32 CBaseDatabase_10_1::GetSmallObjSize ( void )
{
	return( fDatabaseHeader.fSmallObjectSize );
}

uInt32 CBaseDatabase_10_1::GetMaxObjSize ( void )
{
	return( fDatabaseHeader.fMaxObjectSize );
}

uInt32 CBaseDatabase_10_1::GetMaxObjTypeCount ( void )
{
	return( fDatabaseHeader.fMaxObjTypeCount );
}

OSStatus CBaseDatabase_10_1::GetObjectCount ( const OSType inObjType, uInt32 &outObjectCount )
{
	OSStatus			result = kDBNoErr;
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	uInt32				anIndex = 0;

	try
	{
		this->DoLockDB();

		outObjectCount = 0;
		result = this->FindObjTypeEntry( inObjType, anIndex );
		if ( result == kDBNoErr )
		{
			result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );
			outObjectCount = anObjTypeEntry->fDataList.fEntryCount;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::GetObjectStingNameEntryCount ( const OSType inObjType, uInt32 &outNameEntryCount )
{
	OSStatus			result = kDBNoErr;
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	uInt32				anIndex = 0;

	try
	{
		this->DoLockDB();

		outNameEntryCount = 0;
		result = this->FindObjTypeEntry( inObjType, anIndex );
		if ( result == kDBNoErr )
		{
			result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );
			outNameEntryCount = anObjTypeEntry->fStringList.fEntryCount;

			if ( anObjTypeEntry->fStringList.fEntryCount != anObjTypeEntry->fDataList.fEntryCount )
			{
				result = kDBObjStringNameListNeedsUpdate;
			}
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}


uInt32 CBaseDatabase_10_1::GetPageCount ( void )
{
	return fDatabaseHeader.fPageCount;
}

uInt32 CBaseDatabase_10_1::GetMinFreeDiskSpace ( void )
{
	return( fMinFreeDiskSpace );
}

ObjID CBaseDatabase_10_1::GetNextObjID ( void )
{
	if ( fDatabaseHeader.fNextObjID == 0 )
	{
		fDatabaseHeader.fNextObjID = kStartingObjID_10_1;
	}

	return fDatabaseHeader.fNextObjID++;
}


ObjID CBaseDatabase_10_1::GetCurrentObjID ( void )
{
	ObjID	outID	= kStartingObjID_10_1;

	if ( fDatabaseHeader.fNextObjID != 0 )
	{
		outID = fDatabaseHeader.fNextObjID;
	}

	return( outID );
}


void CBaseDatabase_10_1::SetMinFreeDiskSpace ( const uInt32 inMinFreeDiskSpace )
{
	fMinFreeDiskSpace = inMinFreeDiskSpace;
}

void CBaseDatabase_10_1::SetDBOpenState ( const eDBState_10_1 inReOpenState )
{
	if ( this->fDatabaseHeader.fDBReOpenState != inReOpenState )
	{
		this->fDatabaseHeader.fDBReOpenState = inReOpenState;
		this->WriteDatabaseHeader();
	}
}

eDBState_10_1 CBaseDatabase_10_1::GetDBOpenState ( void )
{
	return this->fDatabaseHeader.fDBReOpenState;
}

OSStatus CBaseDatabase_10_1::SetPageCount ( uInt32 inPageCount )
{
	OSStatus result = kDBNoErr;

	try
	{
		this->DoLockDB();

		if ( fDatabaseHeader.fPageCount != inPageCount )
		{
			fDatabaseHeader.fPageCount = inPageCount;
			this->WriteDatabaseHeader();
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

#pragma mark -

void CBaseDatabase_10_1::Add2LRUList ( const uInt32 inPageIndex )
{
	PageCacheEntry_10_1	*aPageCacheEntry = NULL;
	PageCacheEntry_10_1	*aLRUHeadCacheEntry = NULL;

	BaseDBAssert( inPageIndex != kNULLIndex_10_1 );
	aPageCacheEntry = fPageCache.fPageCacheEntryList[inPageIndex];

	BaseDBAssert( aPageCacheEntry->fLRUNextIndex == kNULLIndex_10_1 );
	BaseDBAssert( aPageCacheEntry->fLRUPrevIndex == kNULLIndex_10_1 );

	if ( fPageCache.fLRUListHead != kNULLIndex_10_1 )
	{
		aLRUHeadCacheEntry = fPageCache.fPageCacheEntryList[fPageCache.fLRUListHead];
		BaseDBAssert( aLRUHeadCacheEntry->fLRUPrevIndex == kNULLIndex_10_1 );
		aLRUHeadCacheEntry->fLRUPrevIndex = inPageIndex;
	}

	aPageCacheEntry->fLRUNextIndex = fPageCache.fLRUListHead;
	fPageCache.fLRUListHead = inPageIndex;
	fPageCache.fLRUEntryCount += 1;

	if ( fPageCache.fLRUListTail == kNULLIndex_10_1 )
	{
		fPageCache.fLRUListTail = inPageIndex;
	}
}

uInt32 CBaseDatabase_10_1::GetLRUTailPage ( void )
{
	PageCacheEntry_10_1	*aLRUTailCacheEntry = NULL;
	uInt32			aDirtyPageIndex = kNULLIndex_10_1;
	uInt32			aCleanPageIndex = kNULLIndex_10_1;
	uInt32			outPageCacheIndex = kNULLIndex_10_1;

	if ( ( fPageCache.fLRUListHead == kNULLIndex_10_1 ) || ( fPageCache.fLRUListTail == kNULLIndex_10_1 ) )
	{	// LRU List is empty this is wrong!!!!
		BaseDBAssert( false );
	}
	else
	{
		outPageCacheIndex = fPageCache.fLRUListTail;
		while ( ( outPageCacheIndex != kNULLIndex_10_1 ) && ( aCleanPageIndex == kNULLIndex_10_1 ) )
		{
			aLRUTailCacheEntry = fPageCache.fPageCacheEntryList[outPageCacheIndex];

			if ( aLRUTailCacheEntry->fRefCount == 0 )
			{
				if ( aLRUTailCacheEntry->fDirty == true )
				{
					if ( aDirtyPageIndex == kNULLIndex_10_1 )
					{
						aDirtyPageIndex = outPageCacheIndex;
					}
				}
				else
				{
					aCleanPageIndex = outPageCacheIndex;
				}
			}

			outPageCacheIndex = aLRUTailCacheEntry->fLRUPrevIndex;
		}

		outPageCacheIndex = aCleanPageIndex;
		if ( outPageCacheIndex == kNULLIndex_10_1 )
		{
			outPageCacheIndex = aDirtyPageIndex;
		}
	}

	return outPageCacheIndex;
}

void CBaseDatabase_10_1::RemoveFromLRUList ( const uInt32 inPageIndex )
{
	PageCacheEntry_10_1	*aPageCacheEntry = NULL;
	PageCacheEntry_10_1	*aPrevCacheEntry = NULL;
	PageCacheEntry_10_1	*aNextCacheEntry = NULL;

	BaseDBAssert( inPageIndex != kNULLIndex_10_1 );
	aPageCacheEntry = fPageCache.fPageCacheEntryList[inPageIndex];

	// 1st remove the "current" item from the double-linked list....
	if ( aPageCacheEntry->fLRUPrevIndex != kNULLIndex_10_1 )
	{
		aPrevCacheEntry = fPageCache.fPageCacheEntryList[aPageCacheEntry->fLRUPrevIndex];
		BaseDBAssert( aPrevCacheEntry->fLRUNextIndex == inPageIndex );
		aPrevCacheEntry->fLRUNextIndex = aPageCacheEntry->fLRUNextIndex;
	}
	else
	{
		if ( fPageCache.fLRUListHead == inPageIndex )
		{
			fPageCache.fLRUListHead = aPageCacheEntry->fLRUNextIndex;
		}
	}

	if ( aPageCacheEntry->fLRUNextIndex != kNULLIndex_10_1 )
	{
		aNextCacheEntry = fPageCache.fPageCacheEntryList[aPageCacheEntry->fLRUNextIndex];
		BaseDBAssert( aNextCacheEntry->fLRUPrevIndex == inPageIndex );
		aNextCacheEntry->fLRUPrevIndex = aPageCacheEntry->fLRUPrevIndex;
	}
	else
	{
		if ( fPageCache.fLRUListTail == inPageIndex )
		{
			fPageCache.fLRUListTail = aPageCacheEntry->fLRUPrevIndex;
		}
	}

	if ( ( aPageCacheEntry->fLRUPrevIndex != kNULLIndex_10_1 ) || ( aPageCacheEntry->fLRUNextIndex != kNULLIndex_10_1 ) )
	{
		fPageCache.fLRUEntryCount -= 1;
		aPageCacheEntry->fLRUNextIndex = kNULLIndex_10_1;
		aPageCacheEntry->fLRUPrevIndex = kNULLIndex_10_1;
	}
}

OSStatus CBaseDatabase_10_1::AddPage2FreeList	( const PageID inPageNum, const Boolean inAllowFileShrink )
{
	OSStatus	result = kDBNoErr;
	PageID		aFreePage = 0;
	PageID		aNextPageID = 0;
	CIDTable_10_1	aFreeList( kFreeListType, 0 );
	PageID		aPageIndexHint = 0;

	try
	{
		this->DoLockDB();

		if ( fStore.is_open() == true )
		{
			if ( ( inPageNum == this->GetPageCount() ) && ( inAllowFileShrink ) )
			{
				this->SetPageCount( this->GetPageCount() - 1 );
				this->ClearPageFromCache( inPageNum );
				this->SetupDatabase( this->GetDBVersion() );
			}
			else
			{
				uInt32	aFreeListPage			= inPageNum / kFreeCountPerPage_10_1;
				uInt32	aFreeListPageBit		= inPageNum - ( kFreeCountPerPage_10_1 * aFreeListPage );
				uInt32	aFreeListPageIndex		= aFreeListPageBit / kBitsPerByte_10_1;
				uInt32	aFreeListBitPosition	= aFreeListPageBit % kBitsPerByte_10_1;
				char	aBitField				= 0x01 << aFreeListBitPosition;
				char	*aFreeListMapPagePtr	= NULL;

				if ( this->fDatabaseHeader.fNewFreeList[aFreeListPage].fFreeListPage == 0 )
				{
					this->fDatabaseHeader.fNewFreeList[aFreeListPage].fFreeListPage = inPageNum;
					result = this->GetPage( inPageNum, aFreeListMapPagePtr, true, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					BaseDBAssert_if( aFreeListMapPagePtr != NULL )
					{
						::memset( aFreeListMapPagePtr, 0, kStandardPageSize );
						this->fDatabaseHeader.fNewFreeList[aFreeListPage].f1stEntry = 0xffffffff;
						this->fDatabaseHeader.fNewFreeList[aFreeListPage].fEntryCount = 0;

						this->ReleasePage( inPageNum, true );
					}
				}
				else
				{
					// in a sad attempt at pre-optimization...we will "move" the current free page to
					// a "lower" page number - MED -
					// not clear this is required...
					if ( inPageNum < this->fDatabaseHeader.fNewFreeList[aFreeListPage].fFreeListPage )
					{
						char	*aNewFreePagePtr = NULL;
						char	*aOldFreePagePtr = NULL;
						PageID	aNewPageID = 0;
						PageID	anOldPageID = 0;

						aNewPageID = inPageNum;
						anOldPageID = this->fDatabaseHeader.fNewFreeList[aFreeListPage].fFreeListPage;

						this->GetPage( aNewPageID, aNewFreePagePtr, true, aPageIndexHint );
						BaseDBAssert( aNewFreePagePtr != NULL );
						this->GetPage( anOldPageID, aOldFreePagePtr, false, aPageIndexHint );
						BaseDBAssert( aOldFreePagePtr != NULL );

						// copy the old freeList bitmap to the new page...
						::memcpy( aNewFreePagePtr, aOldFreePagePtr, this->GetPageSize() );
						this->fDatabaseHeader.fNewFreeList[aFreeListPage].fFreeListPage = aNewPageID;

						// release the old FreeList page and and then clear it from the Cache...
						this->ReleasePage( anOldPageID, true );
						this->ClearPageFromCache( anOldPageID );

						// release the new Page...
						this->ReleasePage( aNewPageID, true );

						// recalculate the bit settings to add the old free list page to the free list...
						BaseDBAssert( ( aNewPageID / kFreeCountPerPage_10_1 ) == aFreeListPage );
						aFreeListPageBit		= anOldPageID - ( kFreeCountPerPage_10_1 * aFreeListPage );
						aFreeListPageIndex		= aFreeListPageBit / kBitsPerByte_10_1;
						aFreeListBitPosition	= aFreeListPageBit % kBitsPerByte_10_1;
						aBitField				= 0x01 << aFreeListBitPosition;
					}
					else
					{
						this->ClearPageFromCache( inPageNum );
					}

					result = this->GetPage( this->fDatabaseHeader.fNewFreeList[aFreeListPage].fFreeListPage, aFreeListMapPagePtr, false, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					BaseDBAssert_if( aFreeListMapPagePtr != NULL )
					{
						BaseDBAssert( ( ( aFreeListMapPagePtr[aFreeListPageIndex] & ~aBitField ) & aBitField ) == 0 );
						aFreeListMapPagePtr[aFreeListPageIndex] |= aBitField;

						this->fDatabaseHeader.fNewFreeList[aFreeListPage].fEntryCount += 1;
						if ( aFreeListPageIndex < this->fDatabaseHeader.fNewFreeList[aFreeListPage].f1stEntry )
						{
							this->fDatabaseHeader.fNewFreeList[aFreeListPage].f1stEntry = aFreeListPageIndex;
						}
						result = this->ReleasePage( this->fDatabaseHeader.fNewFreeList[aFreeListPage].fFreeListPage, true );
					}
				}

				this->WriteDatabaseHeader();
			}
		}
		else
		{
			result = kDBNotOpen;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::RemovePageFromList ( SObjList_10_1 &inObjTypeInfo, const PageID inPageID, SPrivObjTypeInfoPtr_10_1 inObjTypeInfoPtr )
{
	OSStatus			result = kDBNoErr;
	CIDTable_10_1			aPageTable( inObjTypeInfo.fListType, inObjTypeInfo.fEntryDataSize );
	PageID				prevPageID;
	PageID				nextPageID;
	uInt32				aPageIndexHint = 0;
	SDBIterator_10_1 		*anSBIterPtr	= NULL;

	try
	{
		this->DoLockDB();

		if ( ( inObjTypeInfo.fListType != kListNotEnabled_10_1 ) && ( inObjTypeInfo.fPageCount != 0 ) )
		{
			result = GetIDPage( inPageID, false, aPageTable, aPageIndexHint );
			BaseDBAssert( result == kDBNoErr );
			BaseDBAssert( aPageTable.GetTableType() == inObjTypeInfo.fListType );

			prevPageID = aPageTable.GetPrevID();
			nextPageID = aPageTable.GetNextID();

			BaseDBAssert( nextPageID != inObjTypeInfo.fHeadPageID );

			this->ReleaseIDPage( aPageTable, false );
			this->AddPage2FreeList( inPageID, true );

			if ( inObjTypeInfoPtr != NULL )
			{
				anSBIterPtr = ( SDBIterator_10_1 * ) inObjTypeInfoPtr->fIteratorList;
				while ( anSBIterPtr != NULL )
				{
					if ( anSBIterPtr->fCurrentPageID == inPageID )
					{
						anSBIterPtr->fCurrentPageID = nextPageID;
						anSBIterPtr->fCurrentObjID = 0;
						if ( nextPageID == 0 )
						{
							anSBIterPtr->fIteratorDone = true;
						}
					}
					anSBIterPtr = ( SDBIterator_10_1 * ) anSBIterPtr->fNextIterator;
				}
			}

			inObjTypeInfo.fPageCount -= 1;
			if ( inObjTypeInfo.fPageCount == 1 )
			{
				inObjTypeInfo.fListSorted = true;
			}

			if ( inObjTypeInfo.fPageCount == 0 )
			{
				inObjTypeInfo.fEntryCount = 0;
			}

			if ( inObjTypeInfo.fHeadPageID == inPageID )
			{
				BaseDBAssert( prevPageID == 0 );	// if we're the head of the list, there should be no Previous Page...
				inObjTypeInfo.fHeadPageID = nextPageID;
			}

			if ( inObjTypeInfo.fLastPageUsed == inPageID )
			{
				inObjTypeInfo.fLastPageUsed = 0;
			}

			if ( nextPageID != 0 )
			{
				result = GetIDPage( nextPageID, false, aPageTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aPageTable.GetTableType() == inObjTypeInfo.fListType );
				aPageTable.SetPrevID( prevPageID );	// the next page's prevID to point to the same PrevID we used to point to..
				this->ReleaseIDPage( aPageTable, true );
			}

			if ( prevPageID != 0 )
			{
				result = GetIDPage( prevPageID, false, aPageTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aPageTable.GetTableType() == inObjTypeInfo.fListType );
				aPageTable.SetNextID( nextPageID );	// set the previous Page's nextID to point to what we used to point to..
				this->ReleaseIDPage( aPageTable, true );
			}
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::GetNextFreePage ( PageID &outPageNum )
{
	OSStatus	result			= kDBNoErr;
	ObjID		aEntryID		= 0;
	PageID		aFreeListPage	= 0;
	uInt32		pageIndex		= 0;
	uInt32		startOffSet		= 0;
	uInt32		offSet			= 0;
	char		*aFreePagePtr	= NULL;
	char		freeEntry		= 0x00;
	uInt32		bitNumber		= 0;
	uInt32		aPageIndexHint	= 0;
	uInt32		tryCount		= 0;
	CIDTable_10_1	aFreeList( kFreeListType, 0 );
	Boolean		tryAgain		= true;

	try
	{
		this->DoLockDB();

		outPageNum = 0;

		if ( fStore.is_open() == true )
		{
			while ( tryAgain == true )
			{
				tryAgain = false;

				for ( pageIndex = 0; ( aFreeListPage == 0 ) && ( pageIndex < kFreeMapPageCount_10_1 ); pageIndex++ )
				{
					if ( ( this->fDatabaseHeader.fNewFreeList[pageIndex].fFreeListPage != 0 ) &&
						( this->fDatabaseHeader.fNewFreeList[pageIndex].fEntryCount != 0 ) )
					{
						result = this->GetPage( this->fDatabaseHeader.fNewFreeList[ pageIndex ].fFreeListPage, aFreePagePtr, false, aPageIndexHint );
						BaseDBAssert( result == kDBNoErr );
						BaseDBAssert_if( aFreePagePtr != NULL )
						{
							startOffSet = this->fDatabaseHeader.fNewFreeList[ pageIndex ].f1stEntry;
							if ( ( startOffSet == 0xffffffff ) || ( aFreePagePtr[ startOffSet ] == 0 ) )
							{
								startOffSet = 0;
								this->fDatabaseHeader.fNewFreeList[ pageIndex ].f1stEntry = 0xffffffff;
							}

							for ( offSet = startOffSet; ( aFreeListPage == 0 ) && ( offSet < kStandardPageSize ); offSet++ )
							{
								if ( aFreePagePtr[ offSet ] != 0 )
								{
									freeEntry = aFreePagePtr[ offSet ];
									bitNumber = 0;
									while ( ( ( freeEntry & ( 0x01 << bitNumber ) ) == 0 ) && ( bitNumber < kBitsPerByte_10_1 ) )
									{
										bitNumber += 1;
									}

									BaseDBAssert( bitNumber < kBitsPerByte_10_1 );

									aFreeListPage = ( pageIndex ) * kFreeCountPerPage_10_1;
									aFreeListPage += ( offSet * kBitsPerByte_10_1 );
									aFreeListPage += bitNumber;
									BaseDBAssert( aFreeListPage != 0 );

									freeEntry &= ~( 0x01 << bitNumber );
									aFreePagePtr[ offSet ] = freeEntry;

									if ( freeEntry == 0 )
									{
										this->fDatabaseHeader.fNewFreeList[ pageIndex ].f1stEntry = 0xffffffff;
									}
									else
									{
										if ( offSet < this->fDatabaseHeader.fNewFreeList[ pageIndex ].f1stEntry )
										{
											this->fDatabaseHeader.fNewFreeList[ pageIndex ].f1stEntry = offSet;
										}
									}

									this->fDatabaseHeader.fNewFreeList[pageIndex].fEntryCount -= 1;
									if ( this->fDatabaseHeader.fNewFreeList[pageIndex].fEntryCount == 0 )
									{
										this->fDatabaseHeader.fNewFreeList[pageIndex].f1stEntry = 0xffffffff;
									}
								}
							}

							this->ReleasePage( this->fDatabaseHeader.fNewFreeList[pageIndex].fFreeListPage, ( aFreeListPage != 0 ) );
						}

					}
				}

				if ( aFreeListPage == 0 )
				{
					result = this->AddPage( aFreeListPage );
					BaseDBAssert( ( result == kDBNoErr ) || ( result == kDBDiskFull ) );

					if ( ( result == kDBDiskFull ) && ( tryCount == 0 ) )
					{
						tryCount += 1;
						//this->ConsolodatePages();		// these lines should be restored
						//tryAgain = true;				// if ConsolodatePages ever works
						tryAgain = false;
					}
				}

				this->WriteDatabaseHeader();
				outPageNum = aFreeListPage;
			}
		}
		else
		{
			result = kDBNotOpen;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

#pragma mark -

OSStatus CBaseDatabase_10_1::AddObject2List	( SObjList_10_1 &inObjTypeInfo, const ObjID inObjID, void *inData, PageID &outPageID )
{
	CIDTable_10_1	aTable( inObjTypeInfo.fListType, inObjTypeInfo.fEntryDataSize );
	OSStatus	result = kDBNoErr;
	PageID		aNewPage = 0;
	PageID		aScratchPage = 0;
	PageID		oldHeadPage = 0;
	uInt32		aPageIndexHint = 0;

	try
	{
		this->DoLockDB();

		outPageID = 0;
		if ( inObjTypeInfo.fListType != kListNotEnabled_10_1 )
		{
			if ( inObjTypeInfo.fHeadPageID != 0 )
			{
				result = GetIDPage( inObjTypeInfo.fHeadPageID, false, aTable, aPageIndexHint );
				BaseDBAssert( aTable.GetTableType() == inObjTypeInfo.fListType );
				BaseDBAssert( aTable.GetPrevID() == 0 );
				BaseDBAssert( result == kDBNoErr );

				result = aTable.AddID2List( inObjID, inData );
				outPageID = aTable.GetTablePageID();
				if ( result == CIDTable_10_1::kIDNewMinNotAllowed )
				{
					result = aTable.AddID2List( inObjID, inData, true, true );
					BaseDBAssert( result == CIDTable_10_1::kIDTableNoErr );
					outPageID = aTable.GetTablePageID();

					if ( inObjTypeInfo.fPageCount > 1 )
					{
						aScratchPage = aTable.GetNextID();
						if ( aScratchPage != 0 )
						{
							this->ReleaseIDPage( aTable, true );
							result = GetIDPage( aScratchPage, false, aTable, aPageIndexHint );
							BaseDBAssert( aTable.GetTableType() == inObjTypeInfo.fListType );
							if ( inObjID < aTable.GetEntryIDMax() )
							{
								inObjTypeInfo.fListSorted = false;
							}

							BaseDBAssert( inObjID != aTable.GetEntryIDMax() );
							aScratchPage = aTable.GetPrevID();
							if ( aScratchPage != 0 )
							{
								result = this->ReleaseIDPage( aTable, false );
								BaseDBAssert( result == kDBNoErr );
								result = this->GetIDPage( aScratchPage, false, aTable, aPageIndexHint );	// have to do this so there is a matching release
								BaseDBAssert( result == kDBNoErr );
								BaseDBAssert( aTable.GetTableType() == inObjTypeInfo.fListType );
							}
						}
					}
				}
				else if ( result == CIDTable_10_1::kIDTableFull )
				{
					if ( inObjID <= aTable.GetEntryIDMax() )
					{
						inObjTypeInfo.fListSorted = false;
					}
					this->ReleaseIDPage( aTable, false );
				}
				else
				{
					BaseDBAssert( result == kDBNoErr );
				}
			}
			else
			{
				result = CIDTable_10_1::kIDTableFull;
			}

			if ( result == CIDTable_10_1::kIDTableFull )
			{
				result = this->GetNextFreePage( aNewPage );
				BaseDBAssert( ( result == kDBNoErr ) || ( result == kDBDiskFull ) );

				if ( result == kDBNoErr )
				{
					result = GetIDPage( aNewPage, true, aTable, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					aTable.ClearIDTable();
					BaseDBAssert( aTable.GetNextID() == 0 );
					BaseDBAssert( aTable.GetPrevID() == 0 );

					oldHeadPage = inObjTypeInfo.fHeadPageID;
					aTable.SetNextID( inObjTypeInfo.fHeadPageID );
					BaseDBAssert( aTable.GetNextID() == inObjTypeInfo.fHeadPageID );

					BaseDBAssert( aNewPage != inObjTypeInfo.fHeadPageID );
					inObjTypeInfo.fHeadPageID = aNewPage;
					inObjTypeInfo.fPageCount += 1;

					result = aTable.AddID2List( inObjID, inData );
					BaseDBAssert( result == CIDTable_10_1::kIDTableNoErr );
					outPageID = aTable.GetTablePageID();
					BaseDBAssert( outPageID == aNewPage );
				}
			}

			if ( result == CIDTable_10_1::kIDTableNoErr )
			{
				inObjTypeInfo.fEntryCount += 1;
				inObjTypeInfo.fLastPageUsed = aTable.GetTablePageID();
			}
			else
			{
				outPageID = 0;
				if ( result != kDBDiskFull )
				{
					result = kDBNoAddObject;
				}
			}

			// This could happen if disk is full ( this should be OK )
			if ( aTable.GetTablePageID() != 0 )
			{
				this->ReleaseIDPage( aTable, true );
			}

			if ( oldHeadPage != 0 )
			{	// hmmm, oldHead page is not 0, that means we've added a new Page, must set the
				// the old head page to point back to the new head page...
				result = GetIDPage( oldHeadPage, false, aTable, aPageIndexHint );
				BaseDBAssert( aTable.GetPrevID() == 0 );
				BaseDBAssert( aTable.GetNextID() != inObjTypeInfo.fHeadPageID );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aTable.GetTableType() == inObjTypeInfo.fListType );

				aTable.SetPrevID( inObjTypeInfo.fHeadPageID );
				this->ReleaseIDPage( aTable, true );
			}
		}
		else
		{
			result = kDBListNotEnabled;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::AddObjectTo2ndIDList( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo, const ObjID inObjID, void *inObjData, PageID inObjDataPageID )
{
	OSStatus			result			= kDBNoErr;
	PageID				aJunkPageID		= 0;
	PageID				aListPage		= 0;
	Boolean				weHaveData		= false;
	ObjID				a2ndObjID		= 0;
	uInt32				anIndex			= 0;
	SObjList_10_1			a2ndIDList;


	if ( inObjTypeInfo->fObjDataFieldProc != NULL )
	{
		try
		{
			this->DoLockDB();

			weHaveData = inObjTypeInfo->fObjDataFieldProc( k2ndIDRequest_10_1, inObjData, &a2ndObjID );
			if ( ( weHaveData ) && ( a2ndObjID != 0 ) )
			{
				result = this->MakeNew2ndIDObject( a2ndObjID, &a2ndIDList );
				if ( result == kDBNoErr )
				{
					result = this->AddObject2List( a2ndIDList, inObjID, &inObjDataPageID, aJunkPageID );
					BaseDBAssert( result == kDBNoErr );

					if ( result == kDBNoErr )
					{
						result = this->SetObjData( k2ndIDListMap_10_1, a2ndObjID, &a2ndIDList );
						BaseDBAssert( result == kDBNoErr );
					}
				}
			}
			this->DoReleaseDB();
		}

		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::AddObject2DataList	( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo, const ObjID inObjID, void *inData, PageID &outPageID )
{
	OSStatus	result = kDBNoErr;

	result = AddObject2List( inObjTypeInfo->fDataList, inObjID, inData, outPageID );

	return( result );
}

OSStatus CBaseDatabase_10_1::AddObject2IDList ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo, const ObjID inObjID, void *inData, PageID &outPageID )
{
	OSStatus	result = kDBNoErr;
	PageID		oldHeadPage = 0;
	PageID		aJunkPageID = 0;
	uInt32		aMin = 0;
	uInt32		aPageIndexHint = 0;

	try
	{
		this->DoLockDB();

		oldHeadPage = inObjTypeInfo->fIDList.fHeadPageID;
		result = AddObject2List( inObjTypeInfo->fIDList, inObjID, inData, outPageID );
		BaseDBAssert( result == kDBNoErr );
		if ( result == kDBNoErr )
		{
			if ( ( inObjTypeInfo->fIDList.fHeadPageID != oldHeadPage ) && ( inObjTypeInfo->fIDList.fPageCount > 1 ) )
			{
				if ( inObjTypeInfo->fIDList.fPageCount == 2 )
				{	// ok. we've got ourselves the very first two entires for the fIDRangeList
					CIDTable_10_1 anIndexTable( inObjTypeInfo->fIDList.fListType, inObjTypeInfo->fIDList.fEntryDataSize );

					result = GetIDPage( oldHeadPage, false, anIndexTable, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					BaseDBAssert( anIndexTable.GetTableType() == inObjTypeInfo->fIDList.fListType );

					aMin = anIndexTable.GetEntryIDMin();

					result = ReleaseIDPage( anIndexTable, false );
					BaseDBAssert( result == kDBNoErr );

					result = AddObject2List( inObjTypeInfo->fIDRangeList, aMin, &oldHeadPage, aJunkPageID );
					BaseDBAssert( result == kDBNoErr );
				}

				if ( result == kDBNoErr )
				{
					result = AddObject2List( inObjTypeInfo->fIDRangeList, inObjID, &outPageID, aJunkPageID );
					BaseDBAssert( result == kDBNoErr );
				}
			}
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::AddObject2StringList ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo, const ObjID inObjID, const void *inObjData, const PageID inObjPageID )
{
	OSStatus		result = kDBNoErr;
	Boolean			weHaveData = false;
	char			*aString = NULL;
	PageID			aNewPage = 0;
	PageID			aRoot = 0;
	PageID			junkPageID = 0;
	ObjID			junkObjID = 0;
	long			aCmpResult = 0;

	if ( inObjTypeInfo->fObjDataFieldProc != NULL )
	{
		try
		{
			this->DoLockDB();

			aString = new char[kStr2IDMaxStringSize_10_1];
			BaseDBAssert( aString != NULL );

			weHaveData = inObjTypeInfo->fObjDataFieldProc( kStringNameRequest_10_1, inObjData, aString );
			if ( weHaveData )
			{
				CStr2IDTable_10_1 aStringTable( inObjTypeInfo->fObjStrCaseSensitive );

				aRoot = inObjTypeInfo->fStringList.fHeadPageID;

				if ( aRoot != 0 )
				{
					result = this->FindObjectInStrList( inObjTypeInfo, aString, junkObjID, junkPageID, junkPageID );
					if ( result == kDBObjNotFound )
					{
						while ( aRoot != 0 )
						{
							result = GetStr2IDPage( aRoot, false, aStringTable );
							BaseDBAssert( result == kDBNoErr );
							result = aStringTable.AddStr2List( aString, inObjID, inObjPageID, ( ( inObjTypeInfo->fStringList.fPageCount == 1 ) || ( aStringTable.GetPrevID() == 0 ) ) );
							switch ( result )
							{
								case CStr2IDTable_10_1::kStr2IDTableNoErr:
									result = ReleaseStr2IDPage( aStringTable, true );
									BaseDBAssert( result == kDBNoErr );
									inObjTypeInfo->fStringList.fEntryCount += 1;
									aRoot = 0;
									break;

								case CStr2IDTable_10_1::kStr2IDNewMinNotAllowed:
									// NOTE: this code was written to fall through the "case CStr2IDTable_10_1::kStr2IDTableFull:"
									// in the event we weren't able to adding the item to the current Page...

								case CStr2IDTable_10_1::kStr2IDTableFull:
									aCmpResult = strcmp( aString, aStringTable.GetMinCharStar() );
									if ( aCmpResult < 0 )
									{
										aNewPage = aStringTable.GetPrevID();
										if ( aNewPage == 0 )
										{
											result = this->GetNextFreePage( aNewPage );
											BaseDBAssert_if( result == kDBNoErr )
											{
												aStringTable.SetPrevID( aNewPage );
												result = ReleaseStr2IDPage( aStringTable, true );
												BaseDBAssert( result == kDBNoErr );

												result = GetStr2IDPage( aNewPage, true, aStringTable );
												BaseDBAssert( result == kDBNoErr );
												aStringTable.ClearStr2IDTable( inObjTypeInfo->fObjStrCaseSensitive );
												inObjTypeInfo->fStringList.fPageCount += 1;
												inObjTypeInfo->fStringList.fEntryCount += 1;

												result = aStringTable.SetParentID( aRoot );
												result = aStringTable.AddStr2List( aString, inObjID, inObjPageID, true );
												BaseDBAssert( result == kDBNoErr );
												result = ReleaseStr2IDPage( aStringTable, true );
												aRoot = 0;
											}
										}
										else
										{
											result = ReleaseStr2IDPage( aStringTable );
											BaseDBAssert( result == kDBNoErr );
											aRoot = aNewPage;
											aNewPage = 0;
										}
									}
									else if ( aCmpResult > 0 )
									{
										aNewPage = aStringTable.GetNextID();
										if ( aNewPage == 0 )
										{
											result = this->GetNextFreePage( aNewPage );
											BaseDBAssert( result == kDBNoErr );

											aStringTable.SetNextID( aNewPage );
											result = ReleaseStr2IDPage( aStringTable, true );
											BaseDBAssert( result == kDBNoErr );

											result = GetStr2IDPage( aNewPage, true, aStringTable );
											BaseDBAssert( result == kDBNoErr );
											aStringTable.ClearStr2IDTable( inObjTypeInfo->fObjStrCaseSensitive );
											inObjTypeInfo->fStringList.fPageCount += 1;
											inObjTypeInfo->fStringList.fEntryCount += 1;

											result = aStringTable.SetParentID( aRoot );
											result = aStringTable.AddStr2List( aString, inObjID, inObjPageID, true );
											BaseDBAssert( result == kDBNoErr );
											result = ReleaseStr2IDPage( aStringTable, true );
											aRoot = 0;
										}
										else
										{
											result = ReleaseStr2IDPage( aStringTable );
											BaseDBAssert( result == kDBNoErr );
											aRoot = aNewPage;
											aNewPage = 0;
										}
									}
									else
									{
										result = ReleaseStr2IDPage( aStringTable );
										BaseDBAssert( result == kDBNoErr );
										aRoot = aNewPage;
										result = kDBNoAddObject;
										aRoot = 0;
									}
									break;

								default:
									result = ReleaseStr2IDPage( aStringTable );
									BaseDBAssert( result == kDBNoErr );
									aRoot = aNewPage;
									result = kDBNoAddObject;
									break;
							}

							if ( aRoot != 0 )
							{
								this->DoYield( false );
							}
						}
					}
					else
					{
						result = kDBDuplicateObjNamesNotAllowed;
					}
				}
				else
				{
					result = this->GetNextFreePage( aNewPage );
					BaseDBAssert( result == kDBNoErr );

					result = GetStr2IDPage( aNewPage, true, aStringTable );
					BaseDBAssert( result == kDBNoErr );
					aStringTable.ClearStr2IDTable( inObjTypeInfo->fObjStrCaseSensitive );

					BaseDBAssert( aStringTable.GetParentID() == 0 );
					BaseDBAssert( aStringTable.GetNextID() == 0 );
					BaseDBAssert( aStringTable.GetPrevID() == 0 );

					inObjTypeInfo->fStringList.fHeadPageID = aNewPage;
					inObjTypeInfo->fStringList.fPageCount += 1;
					inObjTypeInfo->fStringList.fEntryCount += 1;

					result = aStringTable.AddStr2List( aString, inObjID, inObjPageID, true );
					BaseDBAssert( result == CIDTable_10_1::kIDTableNoErr );

					result = ReleaseStr2IDPage( aStringTable, true );
					BaseDBAssert( result == kDBNoErr );
				}
			}

			if ( aString != NULL )
			{
				delete aString;
				aString = NULL;
			}

			this->DoReleaseDB();
		}

		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::SetObjectDataInList ( SObjList_10_1 &inObjTypeInfo, const ObjID inObjID, void *inData, PageID inHintPage )
{
	OSStatus	result = kDBNoErr;
	PageID		aSearchPage = 0;
	PageID		aLastSearchPage = 0;
	CIDTable_10_1	aTable( inObjTypeInfo.fListType, inObjTypeInfo.fEntryDataSize );
	uInt32		aPageIndexHint = 0;

	try
	{
		this->DoLockDB();

		if ( ( inObjTypeInfo.fListType != kListNotEnabled_10_1 ) && ( inObjTypeInfo.fPageCount != 0 ) )
		{
			result = FindObjectInList( inObjTypeInfo, inObjID, NULL, aSearchPage, inHintPage );
			if ( result == kDBNoErr )
			{
				result = this->GetIDPage( aSearchPage, false, aTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aTable.GetTableType() == inObjTypeInfo.fListType );
				result = aTable.SetEntryData( inObjID, inData );
				BaseDBAssert( result == kDBNoErr );
				this->ReleaseIDPage( aTable, true );
			}

			if ( result != CIDTable_10_1::kIDTableNoErr )
			{
				result = kDBObjNotFound;
			}
		}
		else
		{
			result = kDBObjNotFound;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::Delete2ndIDList ( const ObjID inObjID )
{
	OSStatus	result		= kDBNoErr;
	SObjList_10_1	a2ndIDList;

	try
	{
		if ( ( inObjID < kStartingObjID_10_1 ) || ( inObjID > fDatabaseHeader.fNextObjID ) )
		{
			return kDBObjNotFound;
		}

		this->DoLockDB();

		result = this->GetObjData( k2ndIDListMap_10_1, inObjID, &a2ndIDList );
		BaseDBAssert( ( result == kDBNoErr ) || ( result == kDBNoSuchObjType ) || ( result == kDBObjNotFound ) );
		if ( result == kDBNoErr )
		{

			while ( a2ndIDList.fHeadPageID != 0 )
			{
				result = this->RemovePageFromList( a2ndIDList, a2ndIDList.fHeadPageID, NULL );
				BaseDBAssert( result == kDBNoErr );
				this->DoYield( false );
			}

			if ( result == kDBNoErr )
			{
				result = this->RemoveObj( k2ndIDListMap_10_1, inObjID );
				BaseDBAssert( result == kDBNoErr );
			}
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::RemoveObjectFrom2ndIDList ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo, const ObjID inObjID, char *inObjDataPtr )
{
	OSStatus	result		= kDBNoErr;
	PageID		aJunkPageID	= 0;
	PageID		aListPage	= 0;
	Boolean		weHaveData	= false;
	ObjID		a2ndObjID	= 0;
	char		*anObjData	= inObjDataPtr;
	SObjList_10_1	a2ndIDList;

	try
	{
		if ( ( inObjID < kStartingObjID_10_1 ) || ( inObjID > fDatabaseHeader.fNextObjID ) )
		{
			return kDBObjNotFound;
		}

		this->DoLockDB();

		if ( inObjTypeInfo->fObjDataFieldProc != NULL )
		{
			if ( anObjData == NULL )
			{
				anObjData = new char[inObjTypeInfo->fDataList.fEntryDataSize];
				BaseDBAssert( anObjData != NULL );

				result = this->GetObjData( inObjTypeInfo->fObjType, inObjID, anObjData );
			}

			if ( result == kDBNoErr )
			{
				weHaveData = inObjTypeInfo->fObjDataFieldProc( k2ndIDRequest_10_1, anObjData, &a2ndObjID );
				if ( ( weHaveData ) && ( a2ndObjID != 0 ) )
				{
					result = this->GetObjData( k2ndIDListMap_10_1, a2ndObjID, &a2ndIDList );
					BaseDBAssert( ( result == kDBNoErr ) || ( result == kDBNoSuchObjType ) || ( result == kDBObjNotFound ) );
					if ( result == kDBNoErr )
					{
						this->FixIteratorList( inObjTypeInfo, inObjID, a2ndObjID );

						result = this->RemoveObjFromList( a2ndIDList, inObjID, kNoHintPage, NULL );
						BaseDBAssert_if( result == kDBNoErr )
						{	// Warning !!!! Danger!!! Danger!!!!
							// don't actually delete this item...even if the page count goes to zero...
							// we have to hold this place in line, so that when we re-add a New 2IDList
							// it get's properly sorted into the linked list...
							// if you don't understand why this is important, then don't modify this code!!!
							result = this->SetObjData( k2ndIDListMap_10_1, a2ndObjID, &a2ndIDList );
							BaseDBAssert( result == kDBNoErr );
						}
					}
				}
			}

			if ( ( inObjDataPtr == NULL ) && ( anObjData != NULL ) )
			{
				delete anObjData;
				anObjData = NULL;
			}
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
} // 


OSStatus CBaseDatabase_10_1::RemovePageFromStrList( SObjList_10_1 &inObjTypeInfo, const PageID inPageID, const Boolean inCaseFlag )
{
	OSStatus		result = kDBNoErr;
	CStr2IDTable_10_1	aStringTable( inCaseFlag );
	CStr2IDTable_10_1	aSubTreeTable( inCaseFlag );
	PageID			prevPageID = 0;
	PageID			nextPageID = 0;
	PageID			parentPageID = 0;
	PageID			curPageID = 0;
	PageID			aSubTreePageID = 0;
	PageID			aPage2Fetch = 0;
	long			aCmpResult = 0;

	if ( ( inObjTypeInfo.fListType != kListNotEnabled_10_1 ) && ( inObjTypeInfo.fPageCount != 0 ) )
	{
		try
		{
			this->DoLockDB();

			result = GetStr2IDPage( inPageID, false, aStringTable );
			BaseDBAssert( result == kDBNoErr );

			prevPageID = aStringTable.GetPrevID();
			nextPageID = aStringTable.GetNextID();
			parentPageID = aStringTable.GetParentID();

			result = this->ReleaseStr2IDPage( aStringTable, false );
			BaseDBAssert( result == kDBNoErr );
			result = this->AddPage2FreeList( inPageID, true );
			BaseDBAssert( result == kDBNoErr );
			inObjTypeInfo.fPageCount -= 1;

			if ( inObjTypeInfo.fPageCount == 0 )
			{
				inObjTypeInfo.fEntryCount = 0;
				inObjTypeInfo.fHeadPageID = 0;
			}

			if ( ( prevPageID == 0 ) && ( nextPageID == 0 ) )
			{	// take care of the easy case, we're removing a node with no children...
				// we just find the node ID in the parent node and set it to zero...
				if ( parentPageID != 0 )
				{
					result = GetStr2IDPage( parentPageID, false, aStringTable );
					BaseDBAssert( result == kDBNoErr );

					if ( aStringTable.GetPrevID() == inPageID )
					{
						aStringTable.SetPrevID( prevPageID );
					}
					else if ( aStringTable.GetNextID() == inPageID )
					{
						aStringTable.SetNextID( nextPageID );
					}
					else
					{	// hmmm we shouldn't be here...
						BaseDBAssert( inPageID == 0 );		// force the assert to fire...
					}

					result = this->ReleaseStr2IDPage( aStringTable, true );
				}
			}
			else if ( ( prevPageID != 0 ) || ( nextPageID != 0 ) )
			{	// this is the slightly more complex case...we're removing a Node from the list that has
				// children on one or both sides of the fence...
				if ( parentPageID == 0 )
				{ // oh...this is just great, we're removing the root node from the tree...
					if ( prevPageID != 0 )
					{
						parentPageID = prevPageID;
						inObjTypeInfo.fHeadPageID = prevPageID;
						aSubTreePageID = nextPageID;
					}
					else
					{
						parentPageID = nextPageID;
						inObjTypeInfo.fHeadPageID = nextPageID;
						aSubTreePageID = prevPageID;
					}

					result = GetStr2IDPage( inObjTypeInfo.fHeadPageID, false, aStringTable );
					BaseDBAssert( result == kDBNoErr );
					result = aStringTable.SetParentID( 0 );
					BaseDBAssert( result == kDBNoErr );
					result = this->ReleaseStr2IDPage( aStringTable, true );
					BaseDBAssert( result == kDBNoErr );
				}
				else
				{
					result = GetStr2IDPage( parentPageID, false, aStringTable );
					BaseDBAssert( result == kDBNoErr );

					if ( aStringTable.GetPrevID() == inPageID )
					{
						if ( prevPageID != 0 )
						{
							result = aStringTable.SetPrevID( prevPageID );
							aSubTreePageID = nextPageID;
							aPage2Fetch = prevPageID;
						}
						else
						{
							BaseDBAssert( nextPageID != 0 );
							result = aStringTable.SetPrevID( nextPageID );

							BaseDBAssert( prevPageID == 0 );
							aSubTreePageID = prevPageID;	// prevPageID is zero in this case...therefore there is no subtree..

							aPage2Fetch = nextPageID;
						}

						BaseDBAssert( result == kDBNoErr );

						result = this->ReleaseStr2IDPage( aStringTable, true );
						BaseDBAssert( result == kDBNoErr );
					}
					else if ( aStringTable.GetNextID() == inPageID )
					{
						if ( nextPageID != 0 )
						{
							result = aStringTable.SetNextID( nextPageID );
							aSubTreePageID = prevPageID;
							aPage2Fetch = nextPageID;
						}
						else
						{
							BaseDBAssert( prevPageID != 0 );
							result = aStringTable.SetNextID( prevPageID );
							BaseDBAssert( nextPageID == 0 );
							aSubTreePageID = nextPageID;	// nextPageID is zero in this case...therefore there is no subtree..
							aPage2Fetch = prevPageID;
						}
						BaseDBAssert( result == kDBNoErr );

						result = this->ReleaseStr2IDPage( aStringTable, true );
						BaseDBAssert( result == kDBNoErr );
					}
					else
					{	// hmmm we shouldn't be here...
						BaseDBAssert( inPageID == 0 );		// force the assert to fire...
					}

					if ( aPage2Fetch != 0 )
					{
						result = GetStr2IDPage( aPage2Fetch, false, aStringTable );
						BaseDBAssert( result == kDBNoErr );

						result = aStringTable.SetParentID( parentPageID );
						BaseDBAssert( result == kDBNoErr );
						result = this->ReleaseStr2IDPage( aStringTable, true );
						BaseDBAssert( result == kDBNoErr );
					}
					else
					{
						BaseDBAssert( false );	// this is bad!!!!
					}
				}


				if ( aSubTreePageID != 0 )
				{
					result = GetStr2IDPage( aSubTreePageID, false, aSubTreeTable );
					BaseDBAssert( result == kDBNoErr );

					curPageID = parentPageID;
					while ( curPageID != 0 )
					{
						result = GetStr2IDPage( curPageID, false, aStringTable );
						BaseDBAssert( result == kDBNoErr );

						// xxx what about case sensitivity here???
						aCmpResult = ::strcmp( aSubTreeTable.GetMinCharStar(), aStringTable.GetMinCharStar() );
						if ( aCmpResult < 0 )
						{
							curPageID = aStringTable.GetPrevID();
							if ( curPageID == 0 )
							{
								result = aStringTable.SetPrevID( aSubTreePageID );
								BaseDBAssert( result == kDBNoErr );
								result = aSubTreeTable.SetParentID( aStringTable.GetTablePageID() );
								BaseDBAssert( result == kDBNoErr );
							}
						}
						else if ( aCmpResult > 0 )
						{
							curPageID = aStringTable.GetNextID();
							if ( curPageID == 0 )
							{
								result = aStringTable.SetNextID( aSubTreePageID );
								BaseDBAssert( result == kDBNoErr );
								result = aSubTreeTable.SetParentID( aStringTable.GetTablePageID() );
								BaseDBAssert( result == kDBNoErr );
							}
						}
						else
						{	// hmmm we shouldn't be here...
							BaseDBAssert( inPageID == 0 );		// force the assert to fire...
						}

						result = ReleaseStr2IDPage( aStringTable, ( curPageID == 0 ) );
						BaseDBAssert( result == kDBNoErr );

						if ( curPageID != 0 )
						{
							this->DoYield( false );
						}
					}

					result = ReleaseStr2IDPage( aSubTreeTable, true );
					BaseDBAssert( result == kDBNoErr );
				}
			}

			this->DoReleaseDB();
		}

		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::RemoveObjectFromStringList ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo, const ObjID inObjID, char *inObjDataPtr )
{
	OSStatus	result				= kDBNoErr;
	ObjID		aJunkObjID			= 0;
	PageID		aStr2IDTablePage	= 0;
	PageID		aJunkObjPageID		= 0;
	uInt32		aCount				= 0;
	Boolean		weHaveData			= false;
	char		*anObjData			= inObjDataPtr;
	char		*aString			= NULL;

	if ( ( inObjID < kStartingObjID_10_1 ) || ( inObjID > fDatabaseHeader.fNextObjID ) )
	{
		return kDBObjNotFound;
	}

	if ( inObjTypeInfo->fObjDataFieldProc != NULL )
	{
		try
		{
			this->DoLockDB();

			if ( anObjData == NULL )
			{
				anObjData = new char[inObjTypeInfo->fDataList.fEntryDataSize];
				BaseDBAssert( anObjData != NULL );

				result = this->GetObjData( inObjTypeInfo->fObjType, inObjID, anObjData );
			}

			if ( result == kDBNoErr )
			{
				aString = new char[kStr2IDMaxStringSize_10_1];
				BaseDBAssert( aString != NULL );

				weHaveData = inObjTypeInfo->fObjDataFieldProc( kStringNameRequest_10_1, anObjData, aString );
				if ( ( weHaveData ) && ( strlen( aString ) != 0 ) )
				{
					result = this->FindObjectInStrList( inObjTypeInfo, aString, aJunkObjID, aJunkObjPageID, aStr2IDTablePage );
					if ( result == kDBNoErr )
					{
						CStr2IDTable_10_1 aStringTable( inObjTypeInfo->fObjStrCaseSensitive );

						if ( aStr2IDTablePage != 0 )
						{
							result = GetStr2IDPage( aStr2IDTablePage, false, aStringTable );
							BaseDBAssert( result == kDBNoErr );

							result = aStringTable.RemoveStrFromList( aString );
							BaseDBAssert( result == kDBNoErr );

							inObjTypeInfo->fStringList.fEntryCount -= 1;

							aCount = aStringTable.GetEntryCount();

							result = this->ReleaseStr2IDPage( aStringTable, true );
							BaseDBAssert( result == kDBNoErr );

							if ( aCount == 0 )
							{
								result = this->RemovePageFromStrList( inObjTypeInfo->fStringList, aStr2IDTablePage, inObjTypeInfo->fObjStrCaseSensitive );
								BaseDBAssert( result == kDBNoErr );
							}
						}
					}
				}

				if ( aString != NULL )
				{
					delete aString;
					aString = NULL;
				}
			}

			if ( ( inObjDataPtr == NULL ) && ( anObjData != NULL ) )
			{
				delete anObjData;
				anObjData = NULL;
			}

			this->DoReleaseDB();
		}

		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return( result );
}


OSStatus CBaseDatabase_10_1::RemoveObjFromList ( SObjList_10_1			&inObjTypeInfo,
											const ObjID			inObjID,
											PageID				inHintPage,
											SPrivObjTypeInfoPtr_10_1 inObjTypeInfoPtr )
{
	OSStatus	result = kDBNoErr;
	CIDTable_10_1	aTable( inObjTypeInfo.fListType, inObjTypeInfo.fEntryDataSize );
	PageID		aSearchPage = 0;
	PageID		aTablePageID = 0;
	uInt32		aPageIndexHint = 0;

	try
	{
		this->DoLockDB();

		if ( ( inObjTypeInfo.fListType != kListNotEnabled_10_1 ) && ( inObjTypeInfo.fPageCount != 0 ) )
		{
			result = FindObjectInList( inObjTypeInfo, inObjID, NULL, aSearchPage, inHintPage );
			if ( result == kDBNoErr )
			{
				result = this->GetIDPage( aSearchPage, false, aTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aTable.GetTableType() == inObjTypeInfo.fListType );
				result = aTable.RemoveIDFromList( inObjID );

				BaseDBAssert_if( result == CIDTable_10_1::kIDTableNoErr )
				{
					inObjTypeInfo.fEntryCount -= 1;
					inObjTypeInfo.fLastPageUsed = aTable.GetTablePageID();

					aTablePageID = aTable.GetTablePageID();
					BaseDBAssert( aTablePageID == aSearchPage );
					if ( aTable.GetEntryCount() == 0 )
					{
						this->ReleaseIDPage( aTable, false );
						this->RemovePageFromList( inObjTypeInfo, aTablePageID, inObjTypeInfoPtr );
					}
					else
					{
						this->ReleaseIDPage( aTable, true );
					}
				}
				else
				{
					this->ReleaseIDPage( aTable, false );
				}
			}
			else
			{
				result = kDBObjNotFound;
			}
		}
		else
		{
			result = kDBObjNotFound;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::FuzzyFind ( SObjList_10_1 &inObjTypeInfo, const ObjID inObjID, void *outData, PageID inHintPage )
{
	CIDTable_10_1	aTable( inObjTypeInfo.fListType, inObjTypeInfo.fEntryDataSize );
	CIDTable_10_1	a2ndTable( inObjTypeInfo.fListType, inObjTypeInfo.fEntryDataSize );
	OSStatus	result			= kDBNoErr;
	OSStatus	aSearchResult	= kDBObjNotFound;
	uInt32		anIndex			= kCIDNullIndex_10_1;
	PageID		aPageIndexHint	= 0;
	PageID		aSearchPage		= 0;
	ObjID		anObjID			= 0;

	if ( ( inObjID < kStartingObjID_10_1 ) || ( inObjID > fDatabaseHeader.fNextObjID ) )
	{
		return kDBObjNotFound;
	}

	if ( ( inObjTypeInfo.fListType != kListNotEnabled_10_1 ) && ( inObjTypeInfo.fPageCount != 0 ) )
	{
		try
		{
			this->DoLockDB();

			if ( inHintPage == kNoHintPage )
			{	// we'll make up our own hint...
				inHintPage = inObjTypeInfo.fLastPageUsed;
				BaseDBAssert( inObjTypeInfo.fListSorted == true );
			}

			if ( inHintPage != kNoHintPage )
			{
				aSearchPage = inHintPage;
			}
			else
			{
				aSearchPage = inObjTypeInfo.fHeadPageID;
			}

			while ( aSearchPage != 0 )
			{
				anIndex = kCIDNullIndex_10_1;
				result = this->GetIDPage( aSearchPage, false, aTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aTable.GetTableType() == inObjTypeInfo.fListType );
				if ( inObjID < aTable.GetEntryIDMin() )
				{
					aSearchPage = aTable.GetNextID();
					result = this->ReleaseIDPage( aTable, false );
					BaseDBAssert( result == kDBNoErr );
				}
				else if ( inObjID > aTable.GetEntryIDMax() )
				{
					aSearchPage = aTable.GetPrevID();
					if ( aSearchPage != 0 )
					{
						result = this->GetIDPage( aSearchPage, false, a2ndTable, aPageIndexHint );
						BaseDBAssert( result == kDBNoErr );

						if ( inObjID < a2ndTable.GetEntryIDMin() )	// well we're greater than the max-value on the current page
																	// and less than the min-value on the next page
																	// so well stick with the current page...
						{
							aSearchResult = aTable.FindEntry( inObjID, anIndex );
							BaseDBAssert( anIndex != kCIDNullIndex_10_1 );

							result = this->ReleaseIDPage( a2ndTable, false );
							BaseDBAssert( result == kDBNoErr );

							aSearchPage = 0;	// force an exit of the while loop..
							break;
						}
						else
						{
							result = this->ReleaseIDPage( aTable, false );
							BaseDBAssert( result == kDBNoErr );
						}

						result = this->ReleaseIDPage( a2ndTable, false );
						BaseDBAssert( result == kDBNoErr );
					}
					else
					{	// there is no next page, so this is the best we can do...we're at the head
						// of the list, and there is no more items...we return this match..
						aSearchResult = aTable.FindEntry( inObjID, anIndex );
						BaseDBAssert( anIndex != kCIDNullIndex_10_1 );
						break;
					}
				}
				else
				{
					aSearchResult = aTable.FindEntry( inObjID, anIndex );
					BaseDBAssert( anIndex != kCIDNullIndex_10_1 );
					aSearchPage = 0;	// force an exit of the while loop..
					break;
				}

				if ( aSearchPage != 0 )
				{
					this->DoYield( false );
				}
			}

			if ( anIndex != kCIDNullIndex_10_1 )
			{
				aSearchResult = aTable.GetEntry( anIndex, anObjID );
				if ( anObjID > inObjID )
				{	// we might be one off, so lets search around the index returned to see if we have a match
					if ( anIndex > 0 )
					{
						aSearchResult = aTable.GetEntry( anIndex - 1, anObjID );
						BaseDBAssert_if( anObjID <= inObjID )
						{
							aSearchResult = aTable.GetEntry( anIndex - 1, anObjID, ( char * ) outData );
						}
					}
					else
					{
						result = kDBObjNotFound;
					}
				}
				else if ( anObjID < inObjID )
				{	// we might be one off, so lets search around the index returned to see if we have a match
					if ( anIndex < ( aTable.GetEntryCount() - 1 ) )
					{
						aSearchResult = aTable.GetEntry( anIndex + 1, anObjID );
						if ( anObjID <= inObjID )
						{
							aSearchResult = aTable.GetEntry( anIndex + 1, anObjID, ( char * ) outData );
							BaseDBAssert( anObjID <= inObjID );
						}
						else
						{
							aSearchResult = aTable.GetEntry( anIndex, anObjID, ( char * ) outData );
						}
					}
					else
					{
						aSearchResult = aTable.GetEntry( anIndex, anObjID, ( char * ) outData );
						BaseDBAssert( anObjID < inObjID );
					}
				}
				else if ( anObjID == inObjID )
				{
					aSearchResult = aTable.GetEntry( anIndex, anObjID, ( char * ) outData );
				}

				BaseDBAssert( aSearchResult == CIDTable_10_1::kIDTableNoErr );
				inObjTypeInfo.fLastPageUsed = aTable.GetTablePageID();
			}
			else
			{
				result = kDBObjNotFound;
			}

			if ( aTable.GetTablePageID() != 0 )
			{
				this->ReleaseIDPage( aTable, false );
			}

			this->DoReleaseDB();
		}

		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}
	else
	{
		result = kDBObjNotFound;
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::FindObjectInList (	SObjList_10_1	   &inObjTypeInfo,
											const ObjID		inObjID,
											void		   *outData,
											PageID		   &outPageID,
											PageID			inHintPage )
{
					// does this routine "work" (and is it even called) for the top level "partial" index...
					// - MED -
					
	OSStatus	result			= kDBNoErr;
	OSStatus	aSearchResult	= kDBObjNotFound;
	CIDTable_10_1	aTable( inObjTypeInfo.fListType, inObjTypeInfo.fEntryDataSize );
	uInt32		anIndex			= 0;
	ObjID		anObjID			= 0;
	PageID		aSearchPage 	= 0;
	PageID		aNewSearchPage	= 0;
	PageID		aLastSearchPage	= 0;
	uInt32		aPageIndexHint	= 0;
	Boolean		startAtHeadPage	= true;

	try
	{
		this->DoLockDB();

		outPageID = 0;
		if ( ( inObjTypeInfo.fListType != kListNotEnabled_10_1 ) && ( inObjTypeInfo.fPageCount != 0 ) )
		{
			if ( inHintPage == kNoHintPage )
			{	// we'll make up our own hint...
				if ( inObjTypeInfo.fListSorted )
				{
					inHintPage = inObjTypeInfo.fLastPageUsed;
				}
				else
				{
					inHintPage = inObjTypeInfo.fHeadPageID;
				}
			}

			if ( inHintPage != kNoHintPage )
			{
				aSearchPage = inHintPage;
			}
			else
			{
				aSearchPage = inObjTypeInfo.fHeadPageID;
			}

			while ( aSearchPage != 0 )
			{
				result = this->GetIDPage( aSearchPage, false, aTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aTable.GetTableType() == inObjTypeInfo.fListType );
				aSearchResult = aTable.FindEntry( inObjID, anIndex );
				if ( aSearchResult == CIDTable_10_1::kIDTableNoErr )
				{
					aSearchPage = 0;	// ok we're done, let's exit this loop...
				}
				else
				{
					if ( inObjTypeInfo.fListSorted )
					{
						if ( inObjID < aTable.GetEntryIDMin() )
						{
							aNewSearchPage = aTable.GetNextID();
						}
						else
						{
							aNewSearchPage = aTable.GetPrevID();
						}
					}
					else
					{
						if ( ( startAtHeadPage == true ) && ( aSearchPage != inObjTypeInfo.fHeadPageID ) )
						{
							aNewSearchPage = inObjTypeInfo.fHeadPageID;
						}
						else
						{
							aNewSearchPage = aTable.GetNextID();
						}

						startAtHeadPage = false;
					}

					result = this->ReleaseIDPage( aTable, false );
					BaseDBAssert( result == kDBNoErr );

					if ( aNewSearchPage != aLastSearchPage )
					{
						aLastSearchPage = aSearchPage;
						aSearchPage = aNewSearchPage;
						aNewSearchPage = 0;
					}
					else
					{
						aSearchPage = 0;
					}
				}

				if ( aSearchPage != 0 )
				{
					this->DoYield( false );
				}
			}

			if ( aSearchResult == CIDTable_10_1::kIDTableNoErr )
			{
				aSearchResult = aTable.GetEntry( anIndex, anObjID, ( char * ) outData );
				BaseDBAssert( anObjID == inObjID );
				BaseDBAssert( aSearchResult == CIDTable_10_1::kIDTableNoErr );
				inObjTypeInfo.fLastPageUsed = aTable.GetTablePageID();
				outPageID = aTable.GetTablePageID();
				this->ReleaseIDPage( aTable, false );
			}
			else
			{
				result = kDBObjNotFound;
			}
		}
		else
		{
			result = kDBObjNotFound;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::FindObjectInStrList ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo, const char *inSearchString, ObjID &outObjID, PageID &outObjPageID, PageID &outStrIndexPageID )
{
	OSStatus		result = kDBObjNotFound;
	OSStatus		searchResult = kDBObjNotFound;
	PageID			aRoot = 0;
	SStr2IDEntry_10_1	aTempEntry;
	long			aCmpResult = 0;
	uInt32			anIndex = 0;

	try
	{
		this->DoLockDB();

		CStr2IDTable_10_1 aStringTable( inObjTypeInfo->fObjStrCaseSensitive );

		aRoot = inObjTypeInfo->fStringList.fHeadPageID;
		outObjID = 0;
		outObjPageID = 0;
		outStrIndexPageID = 0;

		while ( aRoot != 0 )
		{
			result = GetStr2IDPage( aRoot, false, aStringTable );
			BaseDBAssert( result == kDBNoErr );

			searchResult = aStringTable.FindEntry( inSearchString, anIndex );
			switch ( searchResult )
			{
				case CStr2IDTable_10_1::kStr2IDTableNoErr:
					result = aStringTable.GetEntry( anIndex, aTempEntry );
					outStrIndexPageID = aRoot;
					outObjID = aTempEntry.fObjID;
					outObjPageID = aTempEntry.fObjPageID;
					searchResult = kDBNoErr;
					aRoot = 0;
					break;

				case CStr2IDTable_10_1::kStr2IDTableNoSuchObject:
					aCmpResult = strcmp( inSearchString, aStringTable.GetMinCharStar() );
					if ( aCmpResult < 0 )
					{
						aRoot = aStringTable.GetPrevID();
					}
					else if ( aCmpResult > 0 )
					{
						aRoot = aStringTable.GetNextID();
					}
					else
					{
						// Set root to 0 to avoid a potential infinite loop
						aRoot = 0;
					}

					searchResult = kDBObjNotFound;
					break;

				default:
					aRoot = 0;
					searchResult = kDBObjNotFound;
					break;
			}

			result = ReleaseStr2IDPage( aStringTable );
			BaseDBAssert( result == kDBNoErr );

			if ( aRoot != 0 )
			{
				this->DoYield( false );
			}
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return searchResult;
}

#pragma mark -

OSStatus CBaseDatabase_10_1::AddObjType ( const OSType inObjType, const uInt32 inObjectSize, const Boolean inCaseSensitiveFlag, const DataFieldProc_10_1 inFieldCallBackProc )
{
	OSStatus			result = kDBNoErr;
	SPrivObjTypeInfo_10_1	aTempObjInfo;
	SPrivObjTypeInfoPtr_10_1	aTempObjInfoPtr;
	uInt32				anIndex = 0;

	try
	{
		this->DoLockDB();

		BaseDBAssert_if( inObjType != kListNotEnabled_10_1 )
		{
			if ( inObjectSize > CIDTable_10_1::GetEntryMaxByteSize() )
			{
				result = kDBObjTypeSize2Big2Fit;
			}
			else
			{
				result = FindObjTypeEntry( inObjType, anIndex );
				if ( result == kDBNoSuchObjType )
				{
					result = kDBNoErr;
					if ( this->GetObjTypeCount() < this->GetMaxObjTypeCount() )
					{
						::memset( &aTempObjInfo, 0, sizeof( SPrivObjTypeInfo_10_1 ) );
						aTempObjInfo.fObjType = inObjType;
						aTempObjInfo.fObjStrCaseSensitive = inCaseSensitiveFlag;
						aTempObjInfo.fObjDataFieldProc = inFieldCallBackProc;
						aTempObjInfo.fIteratorList = NULL;

						aTempObjInfo.fDataList.fListType = kID2Data_10_1;
						aTempObjInfo.fDataList.fEntryDataSize = inObjectSize;
						aTempObjInfo.fDataList.fListSorted = true;

						aTempObjInfo.fIDList.fListType = kID2PageMap_10_1;
						aTempObjInfo.fIDList.fEntryDataSize = sizeof( PageID );
						aTempObjInfo.fIDList.fListSorted = true;

						aTempObjInfo.fIDRangeList.fListType = kIDRangeMap_10_1;
						aTempObjInfo.fIDRangeList.fEntryDataSize = sizeof( PageID );
						aTempObjInfo.fIDRangeList.fListSorted = true;

						aTempObjInfo.fStringList.fListType = kStr2ID_10_1;
						aTempObjInfo.fStringList.fEntryDataSize = 0;
						aTempObjInfo.fStringList.fListSorted = true;

						this->SetObjTypeEntry( this->GetObjTypeCount(), aTempObjInfo );
						result = this->SetObjTypeCount( this->GetObjTypeCount() + 1 );
						BaseDBAssert( result == kDBNoErr );

						this->WriteDatabaseHeader();

						if ( aTempObjInfo.fObjDataFieldProc != NULL )
						{
							aTempObjInfo.fObjDataFieldProc( kObjectPINGRequest_10_1, NULL, NULL );
						}
					}
					else
					{
						result = kDBTypeListFull;
					}
				}
				else
				{
					if ( inFieldCallBackProc != NULL )
					{
						inFieldCallBackProc( kObjectPINGRequest_10_1, NULL, NULL );
						result = this->GetObjTypeEntry( anIndex, aTempObjInfoPtr );
						if ( aTempObjInfoPtr->fDataList.fEntryDataSize != inObjectSize )
						{
							result = kDBDataTypeSizesNoMatch;
						}
						else
						{
							if ( aTempObjInfoPtr->fObjDataFieldProc == NULL )
							{
								aTempObjInfoPtr->fObjDataFieldProc = inFieldCallBackProc;
								this->WriteDatabaseHeader();
								result = kDBNoErr;
							}
							else
							{
								result = kDBDataTypeHasFieldProc;
							}
						}
					}
					else
					{
						result = kDBObjTypeExists;
					}
				}
			}
		}
		else
		{
			result = kDBObjTypeNotAllowedDBInteral;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}


OSStatus CBaseDatabase_10_1::SetObjTypeCount ( uInt32 inObjTypeCount )
{
	OSStatus result = kDBNoErr;

	try
	{
		this->DoLockDB();

		if ( inObjTypeCount < this->GetMaxObjTypeCount() )
		{
			fDatabaseHeader.fObjTypeCount = inObjTypeCount;
			this->WriteDatabaseHeader();
		}
		else
		{
			result = kDBObjTypeCount2Big;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}


uInt32 CBaseDatabase_10_1::GetObjTypeCount ( void )
{
	uInt32 result = 0;

	result = fDatabaseHeader.fObjTypeCount;
	return( result );
}

OSStatus CBaseDatabase_10_1::GetObjTypeInfo ( const uInt32 inObjTypeIndex, SObjTypeInfo_10_1 &outObjTypeInfo )
{
	OSStatus result = kDBNoErr;

	if ( inObjTypeIndex < this->GetMaxObjTypeCount() )
	{
		outObjTypeInfo.fObjType = this->fDatabaseHeader.fObjTypeList[inObjTypeIndex].fObjType;
		outObjTypeInfo.fObjSize = this->fDatabaseHeader.fObjTypeList[inObjTypeIndex].fDataList.fEntryDataSize;
	}
	else
	{
		result = kDBObjTypeIndexBad;
	}
	return( result );
}

OSStatus CBaseDatabase_10_1::SetObjTypeEntry ( const uInt32 inIndex, const SPrivObjTypeInfo_10_1 &inObjTypeInfo )
{
	OSStatus result = kDBNoErr;

	try
	{
		this->DoLockDB();

		if ( inIndex < this->GetMaxObjTypeCount() )
		{
			this->fDatabaseHeader.fObjTypeList[inIndex] = inObjTypeInfo;
			this->WriteDatabaseHeader();
		}
		else
		{
			result = kDBObjTypeIndexBad;
		}
		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus	CBaseDatabase_10_1::GetObjTypeEntry		( const uInt32 inIndex, SPrivObjTypeInfoPtr_10_1 &inObjTypeInfoPtr )
{
	OSStatus result = kDBNoErr;

	inObjTypeInfoPtr = NULL;
	if ( inIndex < this->GetMaxObjTypeCount() )
	{
		inObjTypeInfoPtr = &( this->fDatabaseHeader.fObjTypeList[inIndex] );
	}
	else
	{
		result = kDBObjTypeIndexBad;
	}

	return( result );
}

OSStatus	CBaseDatabase_10_1::FindObjTypeEntry	( const OSType inObjType, uInt32 &outIndex )
{
	OSStatus			result = kDBNoSuchObjType;
	uInt32				i;
	SPrivObjTypeInfoPtr_10_1	aTempObjEntry;

	try
	{
		this->DoLockDB();

		outIndex = 0;
		for ( i=0; i<this->GetObjTypeCount(); i++ )
		{
			this->GetObjTypeEntry( i, aTempObjEntry );
			if ( ( aTempObjEntry != NULL ) && ( aTempObjEntry->fObjType == inObjType ) )
			{
				outIndex = i;
				result = kDBNoErr;
				break;
			}
		}
		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

#pragma mark -

void CBaseDatabase_10_1::DBIdle ( void )
{
	uInt32	index = 0;
	try
	{
		this->DoLockDB();

		#ifdef DEBUG
			for ( index = 0; index < this->fPageCache.fEntryCount; index++ )
			{
				BaseDBAssert( this->fPageCache.fPageCacheEntryList[index]->fRefCount == 0 );
			}
		#endif

		if ( ( this != NULL ) && ( this->fStore.is_open() == true ) )
		{
			if ( this->GetDBOpenState() == kDBSafe_10_1 )
			{
				// this->ConsolodatePages();
			}

			if ( this->GetDBOpenState() != kDBSafe_10_1 )
			{
				this->FlushDatabaseFile( kDBSafe_10_1 );
			}
		}

		this->DoReleaseDB();
	}
	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}
}

#pragma mark -

uInt32	CBaseDatabase_10_1::CalcPageSavings	( const SObjList_10_1	*inList )
{
	uInt32	aPageSavings	= 0;
	uInt32	anItemCapacity	= 0;
	uInt32	unUsedItemCount	= 0;

	if ( inList != NULL )
	{
		anItemCapacity = inList->fPageCount * this->CalcMaxItems( inList );
		unUsedItemCount = anItemCapacity - inList->fEntryCount;
		aPageSavings = unUsedItemCount / this->CalcMaxItems( inList );
	}

	return ( aPageSavings );
}

uInt32	CBaseDatabase_10_1::CalcMaxItems ( const SObjList_10_1 *inList )
{
	if ( inList != 0 )
	{
		return ( kMaxIDTableSize_10_1 / ( sizeof( ObjID ) + inList->fEntryDataSize ) );
	}

	return ( 0 );
}

void CBaseDatabase_10_1::ConsolodatePages ( void )
{
	uInt32				anObjectTypeIndex	= 0;
	SPrivObjTypeInfoPtr_10_1	anObjTypeInfo		= NULL;

	if ( ( this != NULL ) && ( this->fStore.is_open() == true ) )
	{
		try
		{
			this->DoLockDB();

			for ( anObjectTypeIndex = 0; anObjectTypeIndex < this->GetObjTypeCount(); anObjectTypeIndex++ )
			{
				this->GetObjTypeEntry( anObjectTypeIndex, anObjTypeInfo );
				if ( ( this->MakeFreeSpace( anObjTypeInfo ) == true ) || ( this->IsThreadQuitting() == true ) )
				{
					this->FlushDatabaseFile( kDBNotSafe_10_1 );
					break;	// we got some free space, so let's return early and let
							// someone else run...
				}
			}

			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}
}

Boolean CBaseDatabase_10_1::MakeFreeSpace ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo )
{
	Boolean	result			= false;

	if ( ( this != NULL ) && ( this->fStore.is_open() == true ) && ( inObjTypeInfo != NULL ) )
	{
		if ( inObjTypeInfo->fIteratorList == NULL )
		{
			try
			{
				this->DoLockDB();

				result = this->CheckDataPages4FreeSpace( inObjTypeInfo );
				result = result || this->CheckID2PageIndex4FreeSpace( inObjTypeInfo );
				result = result || this->CheckGenericList4Space( &inObjTypeInfo->fIDRangeList );

				this->DoReleaseDB();
			}
			catch ( ExceptionCode err )
			{
				this->DoReleaseDB();
			}
		}
	}

	return ( result );
}

Boolean CBaseDatabase_10_1::CheckGenericList4Space ( SObjList_10_1 *inList )
{
	uInt32		aPageSavings	= 0;
	uInt32		oldPageSavings	= 0;
	uInt32		aMaxItemCount	= 0;
	uInt32		maxLoopCount	= 0;
	uInt32		aPageIndexHint	= 0;
	uInt32		objectMoveCount	= 0;

	Boolean		resultFlag		= false;
	Boolean		dirtyFlag		= false;
	Boolean		pageWasDeleted	= false;

	PageID		aTargetPageID	= 0;
	OSErr		result			= kNoErr;


	if ( ( this != NULL ) && ( this->fStore.is_open() == true ) && ( inList != NULL ) )
	{
		try
		{
			this->DoLockDB();
			aPageSavings = this->CalcPageSavings( inList );
			aMaxItemCount = this->CalcMaxItems( inList );
			oldPageSavings = aPageSavings;

			maxLoopCount = aMaxItemCount * 2;	// based on the shuffle nature of the compression
												// the worst case O-notation behavior of this routine
												// is that we'd have to iterate ( aMaxItemCount - 1 ) times
												// in order to pick up a single free page ( if there is one to
												// be had ).  But I dislike code that could run for an infinite amount
												// of time, and I also rarely trust myself when it comes to +/-
												// one off issues, therefore, we'll set an upper loop execution bound
												// and let the code run for a maximum of twice as long as it theoretically needs
												// to...if we ever encounter this boundary, the fact that we let it
												// run twice as long, rather than just as long as necessary, probably
												// will make _NO_ difference, because we're screwed for some other
												// reason..

			while (	( aPageSavings > 0 )					&&	// is there any joy to be had by doing this work???
					( maxLoopCount > 0 )					&&	// have we been running too long??
					( oldPageSavings == aPageSavings )	&&	// have we already made progress, and gotten at least one page??
					( this->IsThreadQuitting() == false ) )	// have we been told to stop by the mythical "user" from TRON??
			{
				resultFlag = true;

				CIDTable_10_1	aTargetDataTable( inList->fListType, inList->fEntryDataSize );
				aTargetPageID = inList->fHeadPageID;
				dirtyFlag = false;

				while (	( aTargetPageID != 0 )				&&
						( this->IsThreadQuitting() == false ) &&
						( dirtyFlag == false ) )
				{
					objectMoveCount = 0;
					pageWasDeleted = false;

					result = GetIDPage( aTargetPageID, false, aTargetDataTable, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					BaseDBAssert( aTargetDataTable.GetTableType() == inList->fListType );

					if ( aTargetDataTable.GetEntryCount() < aMaxItemCount )
					{
						this->MoveDataFromNextPage( inList, aTargetPageID, ( aMaxItemCount - aTargetDataTable.GetEntryCount() ),  objectMoveCount, pageWasDeleted );
					}

					resultFlag = pageWasDeleted;
					if ( objectMoveCount != 0 )
					{
						dirtyFlag = true;
					}

					aTargetPageID = aTargetDataTable.GetNextID();
					this->ReleaseIDPage( aTargetDataTable, dirtyFlag );

					this->DoYield( false );
				}

				aPageSavings = this->CalcPageSavings( inList );

				this->DoYield( false );
				maxLoopCount -= 1;
			}

			BaseDBAssert( maxLoopCount > 0 );	// if this is zero, then our safety code kicked in
											// we should know about this...

			result = ( oldPageSavings != aPageSavings );
			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return ( resultFlag );
}

Boolean CBaseDatabase_10_1::CheckID2PageIndex4FreeSpace ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo )
{
	uInt32	aPageSavings	= 0;
	uInt32	oldPageSavings	= 0;
	uInt32	aMaxItemCount	= 0;
	uInt32	maxLoopCount	= 0;
	Boolean	result			= false;

	if ( ( this != NULL ) && ( this->fStore.is_open() == true ) && ( inObjTypeInfo != NULL ) )
	{
		try
		{
			this->DoLockDB();
			aPageSavings = this->CalcPageSavings( &inObjTypeInfo->fIDList );
			aMaxItemCount = this->CalcMaxItems( &inObjTypeInfo->fIDList );
			oldPageSavings = aPageSavings;

			maxLoopCount = aMaxItemCount * 2;	// based on the shuffle nature of the compression
												// the worst case O-notation behavior of this routine
												// is that we'd have to iterate ( aMaxItemCount - 1 ) times
												// in order to pick up a single free page ( if there is one to
												// be had ).  But I dislike code that could run for an infinite amount
												// of time, and I also rarely trust myself when it comes to +/-
												// one off issues, therefore, we'll set an upper loop execution bound
												// and let the code run for a maximum of twice as long as it theoretically needs
												// to...if we ever encounter this boundary, the fact that we let it
												// run twice as long, rather than just as long as necessary, probably
												// will make _NO_ difference, because we're screwed for some other
												// reason..

			while (	( aPageSavings > 0 )					&&						// is there any joy to be had by doing this work???
					( maxLoopCount > 0 )					&&						// have we been running too long??
					( oldPageSavings == aPageSavings )	&&						// have we already made progress, and gotten at least one page??
					( this->IsThreadQuitting() == false ) )	// have we been told to stop by the mythical "user" from TRON??
			{
				result = true;
				this->ConsolodateIndexPages( inObjTypeInfo, aMaxItemCount );

				aPageSavings = this->CalcPageSavings( &inObjTypeInfo->fIDList );

				this->DoYield( false );
				maxLoopCount -= 1;
			}

			BaseDBAssert( maxLoopCount > 0 );	// if this is zero, then our safety code kicked in
											// we should know about this...

			result = ( oldPageSavings != aPageSavings );
			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return ( result );
}

Boolean CBaseDatabase_10_1::CheckDataPages4FreeSpace ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo )
{

	uInt32	aPageSavings	= 0;
	uInt32	oldPageSavings	= 0;
	uInt32	aMaxItemCount	= 0;
	uInt32	anItemCapacity	= 0;
	uInt32	unUsedItemCount	= 0;
	uInt32	maxLoopCount	= 0;
	Boolean	result			= false;

	if ( ( this != NULL ) && ( this->fStore.is_open() == true ) && ( inObjTypeInfo != NULL ) )
	{
		try
		{
			this->DoLockDB();

			aPageSavings = this->CalcPageSavings( &inObjTypeInfo->fDataList );
			aMaxItemCount = this->CalcMaxItems( &inObjTypeInfo->fDataList );
			oldPageSavings = aPageSavings;

			maxLoopCount = aMaxItemCount;	// based on the shuffle nature of the compression
											// the worst case O-notation behavior of this routine
											// is that we'd have to iterate ( aMaxItemCount - 1 ) * ( currentPageCount ) times
											// in order to pick up a single free page ( if there is one to
											// be had ).  But I dislike code that could run for an infinite amount
											// of time, and I also rarely trust myself when it comes to +/-
											// one off issues, therefore, we'll set an upper loop execution bound
											// and let the code run for a maximum amount of time, and if we win, we win, if we don't 
											// we'll back later to finish....

			while (	( aPageSavings > 0 )					&&						// is there any joy to be had by doing this work???
					( maxLoopCount > 0 )					&&						// have we been running too long??
					( oldPageSavings == aPageSavings )	&&						// have we already made progress, and gotten at least one page??
					( this->IsThreadQuitting() == false ) )	// have we been told to stop by the mythical "user" from TRON??
			{
				result = true;
				this->ConsolodateDataPages( inObjTypeInfo, aMaxItemCount );

				aPageSavings = this->CalcPageSavings( &inObjTypeInfo->fDataList );

				maxLoopCount -= 1;
				this->DoYield( false );
			}

			result = ( oldPageSavings != aPageSavings );
			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return ( result );
}

Boolean	CBaseDatabase_10_1::ConsolodateDataPages ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo,
										  const uInt32		  inMaxDataItemCount )
{
	SObjList_10_1	*anID2PageList	= NULL;
	OSErr		result			= kNoErr;

	PageID		lastPageSeen	= 0;
	PageID		anIndexPageID	= 0;
	PageID		aTempPageID		= 0;
	ObjID		aTempObjID		= 0;

	uInt32		aPageIndexHint	= 0;
	uInt32		anEntryCount	= 0;
	uInt32		freeCount		= 0;
	sInt32		i				= 0;
	uInt32		itemsMoved		= 0;

	Boolean		keepGoing		= true;
	Boolean		pageWasDeleted	= false;
	Boolean		resultFlag		= false;

	BaseDBAssert_if ( ( this != NULL ) && ( inObjTypeInfo != NULL ) )
	{
		try
		{
			this->DoLockDB();

			anID2PageList = &( inObjTypeInfo->fIDList );

			CIDTable_10_1	anIndexTable( anID2PageList->fListType, anID2PageList->fEntryDataSize );

			// First let's search the Index List for a page with some space...
			anIndexPageID = anID2PageList->fHeadPageID;
			while ( ( anIndexPageID != 0 ) && ( keepGoing == true ) )
			{
				this->DoYield( false );
				if ( this->IsThreadQuitting() == true )
				{
					break;
				}

				result = GetIDPage( anIndexPageID, false, anIndexTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( anIndexTable.GetTableType() == anID2PageList->fListType );

				for ( i = anIndexTable.GetEntryCount() - 1; i >= 0; i-- )
				{
					this->DoYield( false );
					if ( this->IsThreadQuitting() == true )
					{
						break;
					}

					result = anIndexTable.GetEntry( i, aTempObjID, ( Ptr ) &( aTempPageID ) );
					if ( aTempPageID != lastPageSeen )
					{
						if ( anEntryCount != 0 )
						{
							freeCount = inMaxDataItemCount - anEntryCount;
							if ( freeCount != 0 )
							{
								anID2PageList->fLastPageUsed = anIndexPageID;	// help the cache...
								this->MoveData( inObjTypeInfo, lastPageSeen, freeCount, itemsMoved, pageWasDeleted );

								if ( itemsMoved > 0 )
								{
									resultFlag = true;
									keepGoing = false;
									break;
								}
							}
						}

						lastPageSeen = aTempPageID;
						freeCount = 0;
						anEntryCount = 1;
					}
					else
					{
						anEntryCount += 1;
					}
				}

				anIndexPageID = anIndexTable.GetNextID();
				this->ReleaseIDPage( anIndexTable, false );
			}

			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return ( resultFlag );
}

Boolean	CBaseDatabase_10_1::ConsolodateIndexPages ( SPrivObjTypeInfoPtr_10_1 inObjTypeInfo,
										  const uInt32		  inMaxDataItemCount )
{
	SObjList_10_1	*anID2PageList	= NULL;
	OSErr		result			= kNoErr;

	PageID		anIndexPageID	= 0;

	uInt32		aPageIndexHint	= 0;
	uInt32		anEntryCount	= 0;
	uInt32		freeCount		= 0;
	uInt32		itemsMoved		= 0;

	Boolean		keepGoing		= true;
	Boolean		pageWasDeleted	= false;
	Boolean		resultFlag		= false;

	BaseDBAssert_if ( ( this != NULL ) && ( inObjTypeInfo != NULL ) )
	{
		try
		{
			this->DoLockDB();

			anID2PageList = &( inObjTypeInfo->fIDList );
			CIDTable_10_1	anIndexTable( anID2PageList->fListType, anID2PageList->fEntryDataSize );

			// First let's search the Index List for a page with some space...
			anIndexPageID = anID2PageList->fHeadPageID;
			while ( ( anIndexPageID != 0 ) && ( keepGoing == true ) )
			{
				this->DoYield( false );
				if ( this->IsThreadQuitting() == true )
				{
					break;
				}

				result = GetIDPage( anIndexPageID, false, anIndexTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( anIndexTable.GetTableType() == anID2PageList->fListType );

				if ( anIndexTable.GetEntryCount() < inMaxDataItemCount )
				{
					freeCount = inMaxDataItemCount - anIndexTable.GetEntryCount();
					this->MoveIndexData( inObjTypeInfo, anIndexPageID, freeCount, itemsMoved, pageWasDeleted );

					if ( itemsMoved > 0 )
					{
						resultFlag = true;
						keepGoing = false;
					}
				}

				anIndexPageID = anIndexTable.GetNextID();
				this->ReleaseIDPage( anIndexTable, false );
			}

			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}

	return ( resultFlag );
}

void CBaseDatabase_10_1::MoveData (	SPrivObjTypeInfoPtr_10_1 inObjTypeInfo,
							const PageID		inTargetDataPageID,
							const uInt32		inObjectCount,
							uInt32 				&outObjectMoved,
							Boolean				&outPageDeleteFlag	)
{
	SObjList_10_1	*aDataList		= NULL;
	SObjList_10_1	*anID2PageList	= NULL;
	uInt32		aPageIndexHint	= 0;
	uInt32		index			= 0;
	ObjID		tempObjID		= 0;
	Boolean		dirtyFlag		= false;
	OSErr		result			= kNoErr;

	outObjectMoved = 0;
	outPageDeleteFlag = false;

	if ( ( this != NULL ) && ( inObjTypeInfo != NULL ) && ( inTargetDataPageID != 0 ) )
	{
		try
		{
			this->DoLockDB();

			aDataList = &( inObjTypeInfo->fDataList );
			anID2PageList = &( inObjTypeInfo->fIDList );

			CIDTable_10_1	aTargetDataTable( aDataList->fListType, aDataList->fEntryDataSize );

			result = GetIDPage( inTargetDataPageID, false, aTargetDataTable, aPageIndexHint );
			BaseDBAssert( result == kDBNoErr );
			BaseDBAssert( aTargetDataTable.GetTableType() == aDataList->fListType );

			this->MoveDataFromNextPage( aDataList, inTargetDataPageID, inObjectCount,  outObjectMoved, outPageDeleteFlag );

			if ( outObjectMoved != 0 )
			{
				dirtyFlag = true;
				for ( index = 0; index < aTargetDataTable.GetEntryCount(); index++ )
				{
					result = aTargetDataTable.GetEntry( index, tempObjID, NULL );
					BaseDBAssert( result == CIDTable_10_1::kIDTableNoErr );

					result = this->SetObjectDataInList( *anID2PageList, tempObjID, ( Ptr ) &inTargetDataPageID, anID2PageList->fLastPageUsed );
					BaseDBAssert( result == kDBNoErr );
				}
			}

			this->ReleaseIDPage( aTargetDataTable, dirtyFlag );
			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}
}

void CBaseDatabase_10_1::MoveIndexData (	SPrivObjTypeInfoPtr_10_1 inObjTypeInfo,
								const PageID		inTargetDataPageID,
								const uInt32		inObjectCount,
								uInt32 				&outObjectMoved,
								Boolean				&outPageDeleteFlag	)
{
	SObjList_10_1	*anID2PageList	= NULL;
	SObjList_10_1	*anIDRangeList	= NULL;

	PageID	aSourcePageID		= 0;
	PageID	aTargetPageID		= inTargetDataPageID;
	PageID	aPageIndexHint		= 0;
	PageID	aJunkPageID			= 0;

	ObjID	oldSourceMin		= 0;
	ObjID	oldTargetMin		= 0;

	OSErr	result				= kNoErr;

	uInt32	anEntryIndex		= 0;

	outObjectMoved = 0;
	outPageDeleteFlag = false;

	if ( ( this != NULL ) && ( inObjTypeInfo != NULL ) && ( inTargetDataPageID != 0 ) )
	{
		try
		{
			this->DoLockDB();

			anID2PageList = &( inObjTypeInfo->fIDList );
			anIDRangeList = &( inObjTypeInfo->fIDRangeList );

			CIDTable_10_1	aTargetDataTable( anID2PageList->fListType, anID2PageList->fEntryDataSize );
			CIDTable_10_1	aSourceDataTable( anID2PageList->fListType, anID2PageList->fEntryDataSize );
			CIDTable_10_1	anIDRangeTable( anIDRangeList->fListType, anIDRangeList->fEntryDataSize );

			result = GetIDPage( aTargetPageID, false, aTargetDataTable, aPageIndexHint );
			BaseDBAssert( result == kDBNoErr );
			BaseDBAssert( aTargetDataTable.GetTableType() == anID2PageList->fListType );

			aSourcePageID = aTargetDataTable.GetNextID();
			if ( aSourcePageID != 0 )
			{
				result = GetIDPage( aSourcePageID, false, aSourceDataTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aSourceDataTable.GetTableType() == anID2PageList->fListType );

				oldSourceMin = aSourceDataTable.GetEntryIDMin();
				oldTargetMin = aTargetDataTable.GetEntryIDMin();

				this->ReleaseIDPage( aSourceDataTable, false );
				this->MoveDataFromNextPage( anID2PageList, inTargetDataPageID, inObjectCount,  outObjectMoved, outPageDeleteFlag );

				if ( outPageDeleteFlag == true )
				{
					// ok we just removed a page from somewhere in the list....
					if ( anIDRangeList->fEntryCount != 0 )
					{
						result = this->RemoveObjFromList( *anIDRangeList, oldSourceMin, kNoHintPage, NULL );
						BaseDBAssert( result == kDBNoErr );
					}

					if ( anIDRangeList->fEntryCount == 1 )
					{	// there is really no point to having only one entry in an IDRangeList
						result = RemovePageFromList( *anIDRangeList, anIDRangeList->fHeadPageID, NULL );
						BaseDBAssert( result == kDBNoErr );
					}
				}
				else
				{
					result = GetIDPage( aSourcePageID, false, aSourceDataTable, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					BaseDBAssert( aSourceDataTable.GetTableType() == anID2PageList->fListType );

					if ( oldSourceMin != aSourceDataTable.GetEntryIDMin() )
					{
						result = this->FindObjectInList( *anIDRangeList, oldSourceMin, &aJunkPageID, anIDRangeList->fLastPageUsed );
						if ( result == kDBNoErr )
						{
							result = GetIDPage( anIDRangeList->fLastPageUsed, false, anIDRangeTable, aPageIndexHint );
							BaseDBAssert( result == kDBNoErr );

							result = anIDRangeTable.FindEntry( oldSourceMin, anEntryIndex );
							BaseDBAssert( result == kDBNoErr );
							if ( result == kDBNoErr )
							{
								result = anIDRangeTable.ResetEntryID( oldSourceMin, aSourceDataTable.GetEntryIDMin() );
								BaseDBAssert( result == kDBNoErr );
							}

							result = this->ReleaseIDPage( anIDRangeTable, true );
							BaseDBAssert( result == kDBNoErr );
						}
					}

					this->ReleaseIDPage( aSourceDataTable, true );
				}
			}

			if ( oldTargetMin != aTargetDataTable.GetEntryIDMin() )
			{
				result = this->FindObjectInList( *anIDRangeList, oldTargetMin, &aJunkPageID, anIDRangeList->fLastPageUsed );
				if ( result == kDBNoErr )
				{
					result = GetIDPage( anIDRangeList->fLastPageUsed, false, anIDRangeTable, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );

					result = anIDRangeTable.FindEntry( oldTargetMin, anEntryIndex );
					BaseDBAssert_if ( result == kDBNoErr )
					{
						result = anIDRangeTable.ResetEntryID( oldTargetMin, aTargetDataTable.GetEntryIDMin() );
						BaseDBAssert( result == kDBNoErr );
					}

					result = this->ReleaseIDPage( anIDRangeTable, true );
					BaseDBAssert( result == kDBNoErr );
				}
			}

			this->ReleaseIDPage( aTargetDataTable, true );
			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}
	}
}

void CBaseDatabase_10_1::MoveDataFromNextPage (	SObjList_10_1			*inList,
										const PageID		inTargetPageID,
										const uInt32		inObjectCount,
										uInt32 				&outObjectMoved,
										Boolean				&outPageDeleteFlag	)
{
	PageID	aSourcePageID		= 0;
	PageID	aTargetPageID		= inTargetPageID;

	uInt32	aPageIndexHint		= 0;
	uInt32	aSourceItemCount	= 0;
	uInt32	itemsMoved			= 0;
	sInt32	aMoveCount			= 0;

	Ptr		tempData			= NULL;
	ObjID	tempObjID			= 0;

	Boolean	dirtyFlag			= false;

	OSErr	result				= kNoErr;
	OSErr	searchResult		= kNoErr;

	outObjectMoved = 0;
	outPageDeleteFlag = false;

	if ( ( this != NULL ) && ( inList != NULL ) && ( aTargetPageID != 0 ) )
	{
		try
		{
			this->DoLockDB();

			CIDTable_10_1	aTargetDataTable( inList->fListType, inList->fEntryDataSize );
			CIDTable_10_1	aSourceDataTable( inList->fListType, inList->fEntryDataSize );

			result = GetIDPage( aTargetPageID, false, aTargetDataTable, aPageIndexHint );
			BaseDBAssert( result == kDBNoErr );
			BaseDBAssert( aTargetDataTable.GetTableType() == inList->fListType );

			aSourcePageID = aTargetDataTable.GetNextID();
			if ( aSourcePageID != 0 )
			{
				result = GetIDPage( aSourcePageID, false, aSourceDataTable, aPageIndexHint );
				BaseDBAssert( result == kDBNoErr );
				BaseDBAssert( aSourceDataTable.GetTableType() == inList->fListType );
				BaseDBAssert( aSourceDataTable.GetEntryDataSize() == aTargetDataTable.GetEntryDataSize() );
				BaseDBAssert( aSourceDataTable.GetPrevID() == aTargetPageID );

				if ( ( result == kDBNoErr ) &&
					( aSourceDataTable.GetTableType() == inList->fListType ) &&
					( aSourceDataTable.GetPrevID() == aTargetPageID ) && 
					( aSourceDataTable.GetEntryDataSize() == aTargetDataTable.GetEntryDataSize() ) )
				{
					tempData	= new char[aSourceDataTable.GetEntryDataSize()];

					BaseDBAssert_if ( tempData != NULL )
					{
						aSourceItemCount = aSourceDataTable.GetEntryCount();
						if ( aSourceItemCount < inObjectCount )
						{
							aMoveCount = aSourceItemCount;
						}
						else
						{
							aMoveCount = inObjectCount;
						}

						itemsMoved = 0;
						for ( ; aMoveCount > 0; aMoveCount-- )
						{
							result = aSourceDataTable.GetEntry( aSourceDataTable.GetEntryCount() - 1, tempObjID, tempData );
							BaseDBAssert( result == CIDTable_10_1::kIDTableNoErr );

							result = aTargetDataTable.AddID2List( tempObjID, tempData, true, true );
							BaseDBAssert( result == kNoErr );

							result = aSourceDataTable.RemoveIDFromList( tempObjID );
							BaseDBAssert( result == kNoErr );

							dirtyFlag = true;
							itemsMoved += 1;
						}
					}
				}

				if ( aSourceDataTable.GetEntryCount() == 0 )
				{
					this->ReleaseIDPage( aSourceDataTable, false );
					this->RemovePageFromList( *inList, aSourcePageID, NULL );
					outPageDeleteFlag = true;
				}
				else
				{
					this->ReleaseIDPage( aSourceDataTable, dirtyFlag );
					outPageDeleteFlag = false;
				}
			}

			this->ReleaseIDPage( aTargetDataTable, dirtyFlag );
			this->DoReleaseDB();
		}
		catch ( ExceptionCode err )
		{
			this->DoReleaseDB();
		}

		if ( tempData != NULL )
		{
			delete tempData;
			tempData = NULL;
		}
	}

	outObjectMoved = itemsMoved;
}

#pragma mark -

OSStatus CBaseDatabase_10_1::CreateObj ( const OSType inObjType, ObjID &outNewObjID, void *inObjData )
{
	OSStatus	result = kNoErr;

	this->DoLockDB();

	outNewObjID = this->GetNextObjID();
	result = this->CreateObjWithID( inObjType, outNewObjID, inObjData );
	if ( result != kDBNoErr )
	{
		outNewObjID = 0;
	}

	this->DoReleaseDB();

	return ( result );
}

OSStatus CBaseDatabase_10_1::MakeNew2ndIDObject ( const ObjID inObjID, const SObjList_10_1 *outObjListPtr )
{
	OSStatus	result = kDBNoErr;
	uInt32		anIndex = 0;
	SObjList_10_1	a2ndIDList;
	OSStatus	addTypeResult = kNoErr;

	try
	{
		this->DoLockDB();

		// 1st do we have a 2nd ID Type List, if we do use it!!, if we don't create it!!
		addTypeResult = this->FindObjTypeEntry( k2ndIDListMap_10_1, anIndex );
		if ( addTypeResult == kDBNoSuchObjType )
		{
			addTypeResult = this->AddObjType( k2ndIDListMap_10_1, sizeof( SObjList_10_1 ), false, NULL );
			BaseDBAssert( result == kDBNoErr );
		}

		// Now make sure we're not creating a duplicate....
		if ( outObjListPtr == NULL )
		{
			result = this->GetObjData( k2ndIDListMap_10_1, inObjID, &a2ndIDList );
		}
		else
		{
			result = this->GetObjData( k2ndIDListMap_10_1, inObjID, ( void * ) outObjListPtr );
		}

		if ( result == kDBObjNotFound )
		{
			a2ndIDList.fListType		= kID2PageMap_10_1;
			a2ndIDList.fPageCount		= 0;
			a2ndIDList.fEntryCount		= 0;
			a2ndIDList.fEntryDataSize	= sizeof( PageID );
			a2ndIDList.fHeadPageID		= 0;
			a2ndIDList.fLastPageUsed	= 0;
			a2ndIDList.fListSorted		= true;

			result = this->CreateObjWithID( k2ndIDListMap_10_1, inObjID, &a2ndIDList );
			BaseDBAssert( result == kDBNoErr );

			if( outObjListPtr != NULL )
			{
				::memcpy( ( void * ) outObjListPtr, &a2ndIDList, sizeof( SObjList_10_1 ) );
			}
		}
	}
	catch ( ExceptionCode err )
	{
	}

	this->DoReleaseDB();

	return ( result );
}

OSStatus CBaseDatabase_10_1::CreateObjWithID ( const OSType inObjType, const ObjID inObjID, void *inObjData )
{
	OSStatus			result = kDBNoErr;
	PageID				anObjPageID;
	PageID				junk;
	uInt32				anIndex;
	SPrivObjTypeInfoPtr_10_1	anObjTypeInfo;
	Boolean				isObja2ndIDTarget = false;

	try
	{
		this->DoLockDB();

		if ( fStore.is_open() == true )
		{
			result = this->FindObjTypeEntry( inObjType, anIndex );
			if ( result == kDBNoErr )
			{
				this->GetObjTypeEntry( anIndex, anObjTypeInfo );

				if ( anObjTypeInfo->fObjDataFieldProc != NULL )
				{
					ObjID	tempObjID = inObjID;
					anObjTypeInfo->fObjDataFieldProc( kSetObjID_10_1, inObjData, &tempObjID );

					isObja2ndIDTarget = anObjTypeInfo->fObjDataFieldProc( k2ndIDTargetChk_10_1, NULL, NULL );
				}

				result = this->AddObject2DataList( anObjTypeInfo, inObjID, inObjData, anObjPageID );
				BaseDBAssert( ( result == kDBNoErr ) || ( result == kDBDiskFull ) );

				// Ok here is a dataspace optimization, if the objects DataSize is bigger than kSmallDBObjSize
				// then we don't have a 2nd ID to Page entry table, we'll just look the data up directly in the
				// normal list of objects...if the object is bigger than kSmallDBObjSize, then we also store an
				// index of what page an particular ObjID happens to be stored on.
				if ( ( result == kDBNoErr ) && ( anObjTypeInfo->fDataList.fEntryDataSize > this->GetSmallObjSize() ) )
				{
					result = this->AddObject2IDList( anObjTypeInfo, inObjID, &anObjPageID, junk );
					BaseDBAssert( result == kDBNoErr );
				}

				if ( ( result == kDBNoErr ) && ( inObjData != NULL ) )
				{
					result = this->AddObjectTo2ndIDList( anObjTypeInfo, inObjID, inObjData, anObjPageID );
					BaseDBAssert( result == kDBNoErr );
				}

				if ( ( result == kDBNoErr ) && ( inObjData != NULL ) )
				{
					result = this->AddObject2StringList( anObjTypeInfo, inObjID, inObjData, anObjPageID );
					BaseDBAssert( result == kDBNoErr );

				}

				if ( ( result == kDBNoErr ) && ( isObja2ndIDTarget == true ) )
				{
					result = this->MakeNew2ndIDObject( inObjID, NULL );
					BaseDBAssert( result == kDBNoErr );
				}

				if ( result == kDBDiskFull )
				{
					result = this->RemoveObj( inObjType, inObjID );
					result = kDBDiskFull;
				}

				this->WriteDatabaseHeader();
			}
			else
			{
				result = kDBNoSuchObjType;
			}
		}
		else
		{
			result = kDBNotOpen;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::GetObjData ( const OSType inObjType, const ObjID inObjID, void *outObjData )
{
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	OSStatus			result = kDBNoErr;
	uInt32				anIndex = 0;
	PageID				aDataPageHint = 0;
	PageID				anIndexPageHint = 0;
	PageID				aJunkPageID = 0;


	if ( ( inObjID < kStartingObjID_10_1 ) || ( inObjID > fDatabaseHeader.fNextObjID ) )
	{
		return( kDBObjNotFound );
	}

	try
	{
		this->DoLockDB();

		result = this->FindObjTypeEntry( inObjType, anIndex );
		if ( result == kDBNoErr )
		{
			result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );

			// Ok here is a dataspace optimization, if the objects DataSize is bigger than kSmallDBObjSize
			// then we don't have a 2nd ID to Page entry table, we'll just look the data up directly in the
			// normal list of objects...if the object is bigger than kSmallDBObjSize, then we also store an
			// index of what page an particular ObjID happens to be stored on.
			if ( anObjTypeEntry->fDataList.fEntryDataSize > this->GetSmallObjSize() )
			{
				if ( anObjTypeEntry->fIDList.fPageCount > 1 )
				{
					result = this->FuzzyFind( anObjTypeEntry->fIDRangeList, inObjID, &anIndexPageHint, aJunkPageID );
					if ( result != kDBNoErr )
					{
						anIndexPageHint = 0;
					}
				}

				if ( anObjTypeEntry->fDataList.fPageCount > 1 )
				{
					result = this->FindObjectInList( anObjTypeEntry->fIDList, inObjID, &aDataPageHint, aJunkPageID, anIndexPageHint );
				}
			}

			if ( result == kDBNoErr )
			{
				result = this->FindObjectInList( anObjTypeEntry->fDataList, inObjID, outObjData, aJunkPageID, aDataPageHint );
			}
		}

		if ( result != kDBNoErr )
		{
			result = kDBObjNotFound;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::GetObjDataByString	( const OSType inObjType, const char *inSearchString, ObjID &outObjID, void *outObjData )
{
	OSStatus			result = kDBNoErr;
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	uInt32				anIndex = 0;
	PageID				aDataPageHint = 0;
	PageID				aJunkPageID = 0;

	try
	{
		this->DoLockDB();

		if ( inSearchString == NULL )
		{
			result = kDBObjNotFound;
		}
		else
		{
			result = this->FindObjTypeEntry( inObjType, anIndex );
			if ( result == kDBNoErr )
			{
				result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );

				BaseDBAssert_if ( result == kDBNoErr )
				{
					result = this->FindObjectInStrList( anObjTypeEntry, inSearchString, outObjID, aDataPageHint, aJunkPageID );
					if ( ( result == kDBNoErr ) && ( outObjData != NULL ) )
					{
						result = this->FindObjectInList( anObjTypeEntry->fDataList, outObjID, outObjData, aJunkPageID, aDataPageHint );
						BaseDBAssert( result == kDBNoErr );
					}
				}
			}

			if ( result != kDBNoErr )
			{
				result = kDBObjNotFound;
			}
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::SetObjData ( const OSType inObjType, const ObjID inObjID, void *inObjData )
{
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	OSStatus			result = kDBNoErr;
	uInt32				anIndex = 0;
	PageID				aDataPageHint = 0;
	PageID				aJunkPageID = 0;
	PageID				anIndexPageHint = 0;
	ObjID				aNew2ndID = 0;
	ObjID				anOld2ndID = 0;
	char				*anOldString = NULL;
	char				*aNewString = NULL;
	Boolean				weHaveOldData = false;
	Boolean				weHaveNewData = false;
	Boolean				weHaveSomeData = false;

	try
	{
		if ( ( inObjID < kStartingObjID_10_1 ) || ( inObjID > fDatabaseHeader.fNextObjID ) )
		{
			return kDBObjNotFound;
		}

		this->DoLockDB();

		result = this->FindObjTypeEntry( inObjType, anIndex );
		if ( result == kDBNoErr )
		{
			this->GetObjTypeEntry( anIndex, anObjTypeEntry );

			// Ok here is a dataspace optimization, if the objects DataSize is bigger than kSmallDBObjSize
			// then we don't have a 2nd ID to Page entry table, we'll just look the data up directly in the
			// normal list of objects...if the object is bigger than kSmallDBObjSize, then we also store an
			// index of what page an particular ObjID happens to be stored on.
			if ( anObjTypeEntry->fDataList.fEntryDataSize > this->GetSmallObjSize() )
			{
				if ( anObjTypeEntry->fIDList.fPageCount > 1 )
				{	// calling this routine is at least one page hit, if there isn't more than
					// a single page in the IDList, there really is no point in taking this extra hit...
					result = this->FuzzyFind( anObjTypeEntry->fIDRangeList, inObjID, &anIndexPageHint, aJunkPageID );
					if ( result != kDBNoErr )
					{
						anIndexPageHint = 0;
					}
				}

				if ( anObjTypeEntry->fDataList.fPageCount > 1 )
				{	// calling this rotine is at least one page hit, if there isn't more than
					// a single page in the data list there really is no point in making this call...
					result = this->FindObjectInList( anObjTypeEntry->fIDList, inObjID, &aDataPageHint, aJunkPageID, anIndexPageHint );
				}
			}

			if ( result == kDBNoErr )
			{
				if ( anObjTypeEntry->fObjDataFieldProc != NULL )
				{
					char *aTempData = new char[anObjTypeEntry->fDataList.fEntryDataSize];
					BaseDBAssert( aTempData != NULL );
					::memset( aTempData, 0, anObjTypeEntry->fDataList.fEntryDataSize );

					result = this->GetObjData( inObjType, inObjID, aTempData );
					if ( result == kDBNoErr )
					{
						// 1st check to see if the 2nd ID has changed...
						weHaveOldData = anObjTypeEntry->fObjDataFieldProc( k2ndIDRequest_10_1, aTempData, &anOld2ndID );
						weHaveNewData = anObjTypeEntry->fObjDataFieldProc( k2ndIDRequest_10_1, inObjData, &aNew2ndID );
						weHaveSomeData = ( weHaveOldData || weHaveNewData );

						if ( weHaveOldData == false )
						{
							// if the client returned false, make sure we don't
							// inadvertantly use any values they may have put into the parameter
							anOld2ndID = 0;
						}

						if ( weHaveNewData == false )
						{
							// if the client returned false, make sure we don't
							// inadvertantly use any values they may have put into the parameter
							aNew2ndID = 0;
						}

						if ( ( anOld2ndID != aNew2ndID ) && ( weHaveSomeData ) )
						{
							if ( aDataPageHint == 0 )
							{	// we have to do this lookup so that we have the "aDataPageHint" setup....
								// for the 2ndID management...
								result = this->FindObjectInList( anObjTypeEntry->fIDList, inObjID, &aDataPageHint, aJunkPageID, anIndexPageHint );
							}

							if ( weHaveOldData )
							{
								result = this->RemoveObjectFrom2ndIDList( anObjTypeEntry, inObjID, NULL );
							}

							if ( weHaveNewData )
							{
								result = this->AddObjectTo2ndIDList( anObjTypeEntry, inObjID, inObjData, aDataPageHint );
								BaseDBAssert( result == kDBNoErr );
							}
						}

						// next check to see if the string name has changed...
						anOldString = new char[kStr2IDMaxStringSize_10_1];
						BaseDBAssert ( anOldString != NULL );
						::memset( anOldString, 0, kStr2IDMaxStringSize_10_1 );
						aNewString = new char[kStr2IDMaxStringSize_10_1];
						BaseDBAssert ( aNewString != NULL );
						::memset( aNewString, 0, kStr2IDMaxStringSize_10_1 );

						weHaveOldData = anObjTypeEntry->fObjDataFieldProc( kStringNameRequest_10_1, aTempData, anOldString );
						weHaveNewData = anObjTypeEntry->fObjDataFieldProc( kStringNameRequest_10_1, inObjData, aNewString );
						weHaveSomeData = ( weHaveOldData || weHaveNewData );

						if ( weHaveOldData == false )
						{
							// if the client returned false, make sure we don't
							// inadvertantly use any values they may have put into the parameter
							::memset( anOldString, 0, kStr2IDMaxStringSize_10_1 );
						}

						if ( weHaveNewData == false )
						{
							// if the client returned false, make sure we don't
							// inadvertantly use any values they may have put into the parameter
							::memset( aNewString, 0, kStr2IDMaxStringSize_10_1 );
						}

						if ( ( weHaveSomeData ) && ( anObjTypeEntry->fObjStrCaseSensitive == false ) )
						{
							uInt32	index = 0;
							uInt32	strSize = 0;

							strSize = strlen( anOldString );
							for ( index = 0; index < strSize; index++ )
							{
								anOldString[index] = tolower( anOldString[index] );
							}

							strSize = strlen( aNewString );
							for ( index = 0; index < strSize; index++ )
							{
								aNewString[index] = tolower( aNewString[index] );
							}
						}

						if ( ( weHaveSomeData ) && ( ::strcmp( anOldString, aNewString ) != 0 ) )
						{
							if ( aDataPageHint == 0 )
							{	// we have to do this lookup so that we have the "aDataPageHint" setup....
								// for the 2ndID management...
								result = this->FindObjectInList( anObjTypeEntry->fIDList, inObjID, &aDataPageHint, aJunkPageID, anIndexPageHint );
							}

							if ( weHaveOldData )
							{
								result = this->RemoveObjectFromStringList( anObjTypeEntry, inObjID, NULL );
								BaseDBAssert( result == kDBNoErr );
							}

							if ( weHaveNewData )
							{	// disk full can happen here, what to do about it is left as an excercise for the reader...
								// xxxx
								result = this->AddObject2StringList( anObjTypeEntry, inObjID, inObjData, aDataPageHint );
								BaseDBAssert( result == kDBNoErr );
							}
						}

						if ( anOldString != NULL )
						{
							delete anOldString;
							anOldString = NULL;
						}

						if ( aNewString != NULL )
						{
							delete aNewString;
							aNewString = NULL;
						}
					}


					if ( aTempData != NULL )
					{
						delete aTempData;
						aTempData = NULL;
					}
				}

				result = this->SetObjectDataInList( anObjTypeEntry->fDataList, inObjID, inObjData, aDataPageHint );
				BaseDBAssert( result == kDBNoErr );
			}
			else
			{
				result = kDBObjNotFound;
			}

			this->WriteDatabaseHeader();
		}
		else
		{
			result = kDBNoSuchObjType;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}


OSStatus CBaseDatabase_10_1::RemoveObj ( const OSType inObjType, ObjID inObjID )
{
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	OSStatus			result = kDBNoErr;
	OSStatus			searchResult = kDBNoErr;
	uInt32				anIndex = 0;
	PageID				aDataHintPage = 0;
	PageID				anIndexHint = 0;
	PageID				theRealIndexPage = 0;
	PageID				anIndexRangeHint = 0;
	PageID				oldIDListPageCount = 0;
	ObjID				anOldObjID = 0;
	SDBIterator_10_1 		*anSBIterPtr = NULL;
	char				*anObjData = NULL;
	uInt32				aPageIndexHint = 0;
	Boolean				isObja2ndIDTarget = false;

	try
	{
		this->DoLockDB();

		result = this->FindObjTypeEntry( inObjType, anIndex );
		if ( result == kDBNoErr )
		{
			result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );
			if ( result == kDBNoErr )
			{
				if ( anObjTypeEntry->fObjDataFieldProc != NULL )
				{
					isObja2ndIDTarget = anObjTypeEntry->fObjDataFieldProc( k2ndIDTargetChk_10_1, NULL, NULL );
				}

				this->FixIteratorList( anObjTypeEntry, inObjID, 0 );

				anObjData = new char[anObjTypeEntry->fDataList.fEntryDataSize];

				BaseDBAssert_if ( anObjData != NULL )
				{
					result = this->GetObjData( anObjTypeEntry->fObjType, inObjID, anObjData );
					BaseDBAssert( result == kDBNoErr );

					if ( result != kDBNoErr )
					{
						delete anObjData;
						anObjData = NULL;
					}
				}

				// remove the object from the string index...if necessary
				result = this->RemoveObjectFromStringList( anObjTypeEntry, inObjID, anObjData );
				BaseDBAssert( result == kDBNoErr );

				// remove the object's 2nd ID from the 2nd ID list...if necessary
				result = this->RemoveObjectFrom2ndIDList( anObjTypeEntry, inObjID, anObjData );
				BaseDBAssert( result == kDBNoErr );

				if ( anObjData != NULL )
				{
					delete anObjData;
					anObjData = NULL;
				}

				if ( isObja2ndIDTarget == true )
				{
					result = this->Delete2ndIDList( inObjID );
					BaseDBAssert( ( result == kDBNoErr ) || ( result == kDBNoSuchObjType ) || ( result == kDBObjNotFound ) );
				}

				// Ok here is a dataspace optimization, if the objects DataSize is bigger than kSmallDBObjSize
				// then we don't have a 2nd ID to Page entry table, we'll just look the data up directly in the
				// normal list of objects...if the object is bigger than kSmallDBObjSize, then we also store an
				// index of what page an particular ObjID happens to be stored on.
				if ( anObjTypeEntry->fDataList.fEntryDataSize > this->GetSmallObjSize() )
				{
					this->FuzzyFind( anObjTypeEntry->fIDRangeList, inObjID, &anIndexHint );
					result = this->FindObjectInList( anObjTypeEntry->fIDList, inObjID, &aDataHintPage, theRealIndexPage, anIndexHint );
					BaseDBAssert( result == kDBNoErr );

					oldIDListPageCount = anObjTypeEntry->fIDList.fPageCount;
					searchResult = this->RemoveObjFromList( anObjTypeEntry->fIDList, inObjID, theRealIndexPage, anObjTypeEntry );
					BaseDBAssert( searchResult == kDBNoErr );

					if ( oldIDListPageCount != anObjTypeEntry->fIDList.fPageCount )
					{	// ok we just removed a page from somewhere in the list....
						if ( anObjTypeEntry->fIDRangeList.fEntryCount != 0 )
						{
							result = this->RemoveObjFromList( anObjTypeEntry->fIDRangeList, inObjID, kNoHintPage, anObjTypeEntry );
							BaseDBAssert( result == kDBNoErr );
						}

						if ( anObjTypeEntry->fIDRangeList.fEntryCount == 1 )
						{	// there is really no point to having only one entry in an IDRangeList
							result = RemovePageFromList( anObjTypeEntry->fIDRangeList, anObjTypeEntry->fIDRangeList.fHeadPageID, NULL );
							BaseDBAssert( result == kDBNoErr );
						}
					}
					else
					{	// ok we didn't just delete the last item from the list, that means there may be
						// an new "entry" that represents the new min....
						result = this->RemoveObjFromList( anObjTypeEntry->fIDRangeList, inObjID, kNoHintPage, anObjTypeEntry );
						if ( ( result == kDBNoErr ) && ( anObjTypeEntry->fIDList.fPageCount ==  oldIDListPageCount ) )
						{  // ok that means the object we just delete actually existed, and that means it
						   // was the min. ObjID in the fIDList, and since the page count didn't change
						   // that means we have a new min entry that we have to update in the ObjectTable...
						   CIDTable_10_1	anIndexTable( anObjTypeEntry->fIDList.fListType, anObjTypeEntry->fIDList.fEntryDataSize );
						   ObjID	aNewMin;
						   
						   result = this->GetIDPage( theRealIndexPage, false, anIndexTable, aPageIndexHint );
						   BaseDBAssert( result == kNoErr );
						   BaseDBAssert( anIndexTable.GetTableType() == anObjTypeEntry->fIDList.fListType );
						   
						   aNewMin = anIndexTable.GetEntryIDMin();
						   this->ReleaseIDPage( anIndexTable, false );
						   
						   result = this->AddObject2List( anObjTypeEntry->fIDRangeList, aNewMin, &theRealIndexPage, anIndexRangeHint );
						   BaseDBAssert( anIndexRangeHint != 0 );
						   BaseDBAssert( aNewMin > inObjID );
						   BaseDBAssert( aNewMin != 0 );
						}
					}

					searchResult = kDBNoErr;
				}

				if ( searchResult == kDBNoErr )
				{
					searchResult = this->RemoveObjFromList( anObjTypeEntry->fDataList, inObjID, aDataHintPage, anObjTypeEntry );
					BaseDBAssert( searchResult == kDBNoErr );
				}

				if ( searchResult != CIDTable_10_1::kIDTableNoErr )
				{
					result = kDBObjNotFound;
				}
			}

			this->WriteDatabaseHeader();
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return searchResult;
}

#pragma mark -

OSStatus CBaseDatabase_10_1::CreateIterator ( const OSType inObjType, SDBIterator_10_1 *outIterator, const ObjID in2ndID2Match )
{
	OSStatus			result = kDBNoErr;
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	uInt32				anIndex;

	try
	{
		this->DoLockDB();

		BaseDBAssert_if ( outIterator != NULL )
		{
			outIterator->fIteratorDataType = kListNotEnabled_10_1;
			outIterator->fCurrentObjID = 0;
			outIterator->fCurrentPageID = 0;
			outIterator->fNextIterator = NULL;
			outIterator->fIteratorDone = true;
			outIterator->f2ndIDMatch = 0;

			result = this->FindObjTypeEntry( inObjType, anIndex );
			if ( result == kDBNoErr )
			{
				result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );
				outIterator->fIteratorDataType = inObjType;
				outIterator->fCurrentObjID = 0;
				outIterator->fCurrentPageID = 0;
				outIterator->fIteratorDone = false;
				outIterator->f2ndIDMatch = in2ndID2Match;

				outIterator->fNextIterator = anObjTypeEntry->fIteratorList;
				anObjTypeEntry->fIteratorList = outIterator;
			}
		}
		else
		{
			result = kDBBadIterator;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}


OSStatus CBaseDatabase_10_1::GetIteratorItemCnt ( SDBIteratorPtr_10_1 inOutIterator, uInt32 &outCount )
{
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	SObjList_10_1			a2ndIDList;
	OSStatus			result = kDBBadIterator;
	OSStatus			aSearchResult = kDBNoErr;
	uInt32				anIndex = 0;

	try
	{
		this->DoLockDB();

		outCount = 0;

		BaseDBAssert_if ( inOutIterator != NULL )
		{
			result = this->FindObjTypeEntry( inOutIterator->fIteratorDataType, anIndex );
			if ( ( result == kDBNoErr ) && ( inOutIterator->fIteratorDataType != kListNotEnabled_10_1 ) )
			{
				result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );

				BaseDBAssert_if ( result == kDBNoErr )
				{
					if ( inOutIterator->f2ndIDMatch == 0 )
					{
						outCount = anObjTypeEntry->fDataList.fEntryCount;
					}
					else
					{
						aSearchResult = this->GetObjData( k2ndIDListMap_10_1, inOutIterator->f2ndIDMatch, &a2ndIDList );
						if ( ( aSearchResult == kDBNoErr ) && ( a2ndIDList.fEntryCount != 0 ) )
						{
							outCount = a2ndIDList.fEntryCount;
						}
					}
				}
				else
				{
					result = kDBNoSuchObjType;
				}
			}
		}
		else
		{
			result = kDBBadIterator;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}


OSStatus CBaseDatabase_10_1::ReleaseIterator ( SDBIteratorPtr_10_1 inOutIterator )
{
	OSStatus			result = kDBNoErr;
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	uInt32				anIndex = 0;
	SDBIteratorPtr_10_1		anSBIterPtr = NULL;
	SDBIteratorPtr_10_1		aNextSBIterPtr = NULL;

	try
	{
		this->DoLockDB();

		BaseDBAssert_if ( inOutIterator != NULL )
		{
			result = this->FindObjTypeEntry( inOutIterator->fIteratorDataType, anIndex );
			BaseDBAssert_if ( result == kDBNoErr )
			{
				result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );
				BaseDBAssert( result == kDBNoErr );

				anSBIterPtr = anObjTypeEntry->fIteratorList;
				if ( anSBIterPtr == inOutIterator )
				{	// ok we are the head of the list...
					anObjTypeEntry->fIteratorList = inOutIterator->fNextIterator;
				}
				else
				{	// else we go searching for the iterator we're screwing with..
					while ( anSBIterPtr != NULL )
					{
						aNextSBIterPtr = anSBIterPtr->fNextIterator;
						if ( aNextSBIterPtr == inOutIterator )
						{
							anSBIterPtr->fNextIterator = aNextSBIterPtr->fNextIterator;
							break;
						}
						anSBIterPtr = aNextSBIterPtr;
					}
				}
			}
			else
			{
				BaseDBAssert( false );
			}

			inOutIterator->fIteratorDataType = kListNotEnabled_10_1;
			inOutIterator->fCurrentObjID = 0;
			inOutIterator->fCurrentPageID = 0;
			inOutIterator->fNextIterator = NULL;
			inOutIterator->fIteratorDone = true;
			inOutIterator->f2ndIDMatch = 0;
		}
		else
		{
			result = kDBBadIterator;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::ResetIterator ( SDBIteratorPtr_10_1 inOutIterator, ObjID &outObjID, void *outObjData )
{
	OSStatus			result = kDBBadIterator;
	uInt32				anIndex;

	try
	{
		this->DoLockDB();

		result = this->FindObjTypeEntry( inOutIterator->fIteratorDataType, anIndex );
		if ( ( result == kDBNoErr ) && ( inOutIterator->fIteratorDataType != kListNotEnabled_10_1 ) )
		{
			inOutIterator->fCurrentObjID = 0;
			inOutIterator->fCurrentPageID = 0;
			inOutIterator->fIteratorDone = false;

			result = this->GetCurObject( inOutIterator, outObjID, outObjData );
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::GetCurObject ( SDBIteratorPtr_10_1 inOutIterator, ObjID &outObjID, void *outObjData )
{
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	SObjList_10_1			a2ndIDList;
	OSStatus			result = kDBBadIterator;
	OSStatus			aSearchResult = kDBNoSuchObjType;
	uInt32				anIndex = 0;
	PageID				aDataPageHint = 0;
	PageID				aListPage = 0;
	PageID				aJunkPage = 0;
	ObjID				aTempObjID = 0;
	uInt32				aPageIndexHint = 0;

	try
	{
		this->DoLockDB();

		outObjID = 0;
		result = this->FindObjTypeEntry( inOutIterator->fIteratorDataType, anIndex );
		if ( ( result == kDBNoErr ) && ( inOutIterator->fIteratorDone == false ) && ( inOutIterator->fIteratorDataType != kListNotEnabled_10_1 ) )
		{
			result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );
			BaseDBAssert( result == kDBNoErr );

			CIDTable_10_1 anIDIndexTable( anObjTypeEntry->fIDList.fListType, anObjTypeEntry->fIDList.fEntryDataSize );

			if ( anObjTypeEntry->fDataList.fEntryCount != 0 )
			{
				if ( inOutIterator->fCurrentPageID == 0 )
				{
					if ( inOutIterator->f2ndIDMatch == 0 )
					{
						inOutIterator->fCurrentPageID = anObjTypeEntry->fIDList.fHeadPageID;
					}
					else
					{
						aSearchResult = this->GetObjData( k2ndIDListMap_10_1, inOutIterator->f2ndIDMatch, &a2ndIDList );
						if ( ( aSearchResult == kDBNoErr ) && ( a2ndIDList.fEntryCount != 0 ) )
						{
							inOutIterator->fCurrentPageID = a2ndIDList.fHeadPageID;
						}
					}
				}

				if ( inOutIterator->fCurrentPageID != 0 )
				{
					result = GetIDPage( inOutIterator->fCurrentPageID, false, anIDIndexTable, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					BaseDBAssert( anIDIndexTable.GetTableType() == anObjTypeEntry->fIDList.fListType );

					if ( inOutIterator->fCurrentObjID == 0 )
					{
						aSearchResult = anIDIndexTable.GetEntry( anIDIndexTable.GetEntryCount()-1, inOutIterator->fCurrentObjID, ( char * ) &aDataPageHint );
					}
					else
					{
						aTempObjID = inOutIterator->fCurrentObjID;
						aSearchResult = anIDIndexTable.FindEntry( aTempObjID, anIndex );
						aSearchResult = anIDIndexTable.GetEntry( anIndex, aTempObjID, ( char * ) &aDataPageHint );

						BaseDBAssert( aTempObjID == inOutIterator->fCurrentObjID );
					}

					this->ReleaseIDPage( anIDIndexTable, false );
					if ( ( outObjData != NULL ) && ( aSearchResult == kDBNoErr ) )
					{
						// **********************************************************************
						// this was changed on 1/2/99 ( ASIP 6.2a1 ) because the Consolodate Data code as a serious bug...
						// this list for iteration stores both the ObjID & the PageID where the "raw" data is stored....
						// but do to DaveO's stupidity when he wrote the consolodate data code, he didn't check with 2nd ID
						// list entries and "update" the page number to reflect where the data has moved...that leaves a dangeling
						// PageID pointer in this list...this is _VERY_ bad....
						// fortunately _NO_ iteration code in the ASIP Mail Server ever caused this code path to execute...
						// outObjData == NULL has always been true.....
						// but if anyone ever did pass in a data buffer the code as written could potentially retrieve the wrong data
						// or cause a crash, bascially it's unknown how FindObjectInList will behave when given a bad-pageID hint that
						// may not even be part of the right set of pages for this data type...
						// The fix is to do a slightly more expensive but fully resolved Object fetch, where the page information is
						// guarenteed to be correct...We're going to call GetObjData which will do a full & reliable look up of the data
						// this means that the stored PageID is now no longer used and therefore wasted space.  However there are
						// existing 6.0 & 6.1 ASIP Mail Databases with this information in them, therefore to remove this now useless data
						// will cause a file format compatibility problem with pre-existing Mail Databases...therefore we'll simply waste the
						// space, but avoid the potential error due to Consolodate data...

						// Legacy line of code commented out...
						// aSearchResult = this->FindObjectInList( anObjTypeEntry->fDataList, inOutIterator->fCurrentObjID, outObjData, aJunkPage, aDataPageHint );

						aSearchResult = this->GetObjData( inOutIterator->fIteratorDataType, inOutIterator->fCurrentObjID, outObjData );
					}
				}
				else
				{
					aSearchResult = kDBNoSuchObjType;
				}
			}
		}
		else
		{
			inOutIterator->fIteratorDone = true;
		}

		if ( aSearchResult != kDBNoErr )
		{
			inOutIterator->fCurrentObjID = 0;
			inOutIterator->fCurrentPageID = 0;
			inOutIterator->fIteratorDone = true;
			result = kDBObjNotFound;
		}
		outObjID = inOutIterator->fCurrentObjID;

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

void CBaseDatabase_10_1::FixIteratorList	( SPrivObjTypeInfoPtr_10_1 inObjTypeInfoPtr, const ObjID inObjID, const ObjID in2ndID )
{
	SDBIteratorPtr_10_1	anSBIterPtr = NULL;
	ObjID			aJunkObjID = 0;

	anSBIterPtr = ( SDBIterator_10_1 * ) inObjTypeInfoPtr->fIteratorList;
	while ( anSBIterPtr != NULL )
	{
		if ( ( ( in2ndID == 0 ) || ( anSBIterPtr->f2ndIDMatch == in2ndID ) ) && ( anSBIterPtr->fCurrentObjID == inObjID ) )
		{
			this->NextObject( anSBIterPtr, aJunkObjID );
		}

		anSBIterPtr = ( SDBIterator_10_1 * ) anSBIterPtr->fNextIterator;
	}
}

OSStatus CBaseDatabase_10_1::FixAndRemoveCurObject ( SDBIteratorPtr_10_1 inOutIterator )
{

	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	SObjList_10_1			a2ndIDList;
	OSStatus			result = kDBBadIterator;
	OSStatus			aSearchResult = kDBNoSuchObjType;
	uInt32				anIndex = 0;
	PageID				tempPageID = 0;
	ObjID				tempObjID = 0;
	uInt32				aPageIndexHint = 0;

	try
	{
		this->DoLockDB();

		result = this->FindObjTypeEntry( inOutIterator->fIteratorDataType, anIndex );
		if ( ( result == kDBNoErr ) && ( inOutIterator->fIteratorDone == false ) && ( inOutIterator->fIteratorDataType != kListNotEnabled_10_1 ) )
		{
			result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );
			BaseDBAssert( result == kDBNoErr );

			CIDTable_10_1 anIDIndexTable( anObjTypeEntry->fIDList.fListType, anObjTypeEntry->fIDList.fEntryDataSize );

			if ( (inOutIterator->f2ndIDMatch    != 0)	&&
				 (inOutIterator->fCurrentPageID != 0)	&&
				 (inOutIterator->fCurrentObjID  != 0) )
			{
				aSearchResult = this->GetObjData( k2ndIDListMap_10_1, inOutIterator->f2ndIDMatch, &a2ndIDList );
				BaseDBAssert( result == aSearchResult );
				if ( aSearchResult == kDBNoErr )
				{
					result = GetIDPage( inOutIterator->fCurrentPageID, false, anIDIndexTable, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					BaseDBAssert( anIDIndexTable.GetTableType() == anObjTypeEntry->fIDList.fListType );

					aSearchResult = anIDIndexTable.FindEntry( inOutIterator->fCurrentObjID, anIndex );
					BaseDBAssert( aSearchResult == kDBNoErr );
					this->ReleaseIDPage( anIDIndexTable, false );

					if ( aSearchResult == kDBNoErr )
					{
						// move the Iterator off the current object since we're about to delete it..
						tempPageID = inOutIterator->fCurrentPageID;
						tempObjID = inOutIterator->fCurrentObjID;

						this->FixIteratorList( anObjTypeEntry, tempObjID, inOutIterator->f2ndIDMatch );

						aSearchResult = this->RemoveObjFromList( a2ndIDList, tempObjID, tempPageID, anObjTypeEntry );
						if ( aSearchResult == kDBNoErr )
						{
							aSearchResult = this->SetObjData( k2ndIDListMap_10_1, inOutIterator->f2ndIDMatch, &a2ndIDList );
							BaseDBAssert( aSearchResult == kDBNoErr );
						}
					}
				}
				else
				{
					aSearchResult = kDBNoSuchObjType;
				}
			}
		}
		else
		{
			inOutIterator->fIteratorDone = true;
		}

		if ( aSearchResult != kDBNoErr )
		{
			inOutIterator->fCurrentObjID = 0;
			inOutIterator->fCurrentPageID = 0;
			inOutIterator->fIteratorDone = true;
			result = kDBObjNotFound;
		}

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}


OSStatus CBaseDatabase_10_1::PrivGetNextObject ( SDBIteratorPtr_10_1 inOutIterator, ObjID &outObjID, Boolean inForwardFlag, void *outObjData )
{
	SPrivObjTypeInfoPtr_10_1	anObjTypeEntry;
	SObjList_10_1			a2ndIDList;
	OSStatus			result			= kDBBadIterator;
	OSStatus			aSearchResult	= kDBNoSuchObjType;
	uInt32				anIndex			= 0;
	uInt32				listBeginning	= 0;
	uInt32				endOfList		= 0;
	sInt32				indexDirection	= 0;
	PageID				aDataPageHint	= 0;
	PageID				aNewPage		= 0;
	PageID				aListPage		= 0;
	PageID				aJunkPageID		= 0;
	ObjID				aTempObjID		= 0;
	uInt32				aPageIndexHint	= 0;

	try
	{
		this->DoLockDB();

		outObjID = 0;
		result = this->FindObjTypeEntry( inOutIterator->fIteratorDataType, anIndex );
		if ( ( result == kDBNoErr ) && ( inOutIterator->fIteratorDone == false ) && ( inOutIterator->fIteratorDataType != kListNotEnabled_10_1 ) )
		{
			result = this->GetObjTypeEntry( anIndex, anObjTypeEntry );
			BaseDBAssert( result == kDBNoErr );

			CIDTable_10_1 anIDIndexTable( anObjTypeEntry->fIDList.fListType, anObjTypeEntry->fIDList.fEntryDataSize );

			if ( anObjTypeEntry->fDataList.fEntryCount != 0 )
			{
				if ( inOutIterator->fCurrentPageID == 0 )
				{
					if ( inOutIterator->f2ndIDMatch == 0 )
					{
						inOutIterator->fCurrentPageID = anObjTypeEntry->fIDList.fHeadPageID;
					}
					else
					{
						aSearchResult = this->GetObjData( k2ndIDListMap_10_1, inOutIterator->f2ndIDMatch, &a2ndIDList );
						if ( ( aSearchResult == kDBNoErr ) && ( a2ndIDList.fEntryCount != 0 ) )
						{
							inOutIterator->fCurrentPageID = a2ndIDList.fHeadPageID;
						}
					}
				}

				if ( inOutIterator->fCurrentPageID != 0 )
				{
					result = GetIDPage( inOutIterator->fCurrentPageID, false, anIDIndexTable, aPageIndexHint );
					BaseDBAssert( result == kDBNoErr );
					BaseDBAssert( anIDIndexTable.GetTableType() == anObjTypeEntry->fIDList.fListType );

					if ( inForwardFlag )
					{
						listBeginning = anIDIndexTable.GetEntryCount()-1;
						endOfList = 0;
						indexDirection = -1;
						aNewPage = anIDIndexTable.GetNextID();
					}
					else
					{
						listBeginning = 0;
						indexDirection = 1;
						endOfList = anIDIndexTable.GetEntryCount()-1;
						aNewPage = anIDIndexTable.GetPrevID();
					}

					if ( inOutIterator->fCurrentObjID == 0 )
					{
						aSearchResult = anIDIndexTable.GetEntry( listBeginning, inOutIterator->fCurrentObjID, ( char * ) &aDataPageHint );
						BaseDBAssert( aSearchResult == kDBNoErr );
						this->ReleaseIDPage( anIDIndexTable, false );
					}
					else
					{
						aTempObjID = inOutIterator->fCurrentObjID;
						aSearchResult = anIDIndexTable.FindEntry( aTempObjID, anIndex );
						BaseDBAssert( aSearchResult == kDBNoErr );

						if ( anIndex != endOfList )
						{
							aSearchResult = anIDIndexTable.GetEntry( anIndex+indexDirection, aTempObjID, ( char * ) &aDataPageHint );
							this->ReleaseIDPage( anIDIndexTable, false );
							inOutIterator->fCurrentObjID = aTempObjID;
						}
						else
						{
							this->ReleaseIDPage( anIDIndexTable, false );
							if ( aNewPage != 0 )
							{
								result = GetIDPage( aNewPage, false, anIDIndexTable, aPageIndexHint );
								BaseDBAssert( result == kDBNoErr );
								BaseDBAssert( anIDIndexTable.GetTableType() == anObjTypeEntry->fIDList.fListType );

								if ( inForwardFlag )
								{
									listBeginning = anIDIndexTable.GetEntryCount()-1;
								}
								else
								{
									endOfList = anIDIndexTable.GetEntryCount()-1;
								}

								aSearchResult = anIDIndexTable.GetEntry( listBeginning, aTempObjID, ( char * ) &aDataPageHint );
								BaseDBAssert( aSearchResult == kDBNoErr );
								inOutIterator->fCurrentObjID = aTempObjID;
								inOutIterator->fCurrentPageID = aNewPage;
								this->ReleaseIDPage( anIDIndexTable, false );
							}
							else
							{
								aSearchResult = kDBObjNotFound;
							}
						}

					}

					if ( ( outObjData != NULL ) && ( aSearchResult == kDBNoErr ) )
					{
						// **********************************************************************
						// this was changed on 1/2/99 ( ASIP 6.2a1 ) because the Consolodate Data code as a serious bug...
						// this list for iteration stores both the ObjID & the PageID where the "raw" data is stored....
						// but do to DaveO's stupidity when he wrote the consolodate data code, he didn't check with 2nd ID
						// list entries and "update" the page number to reflect where the data has moved...that leaves a dangeling
						// PageID pointer in this list...this is _VERY_ bad....
						// fortunately _NO_ iteration code in the ASIP Mail Server ever caused this code path to execute...
						// outObjData == NULL has always been true.....
						// but if anyone ever did pass in a data buffer the code as written could potentially retrieve the wrong data
						// or cause a crash, bascially it's unknown how FindObjectInList will behave when given a bad-pageID hint that
						// may not even be part of the right set of pages for this data type...
						// The fix is to do a slightly more expensive but fully resolved Object fetch, where the page information is
						// guarenteed to be correct...We're going to call GetObjData which will do a full & reliable look up of the data
						// this means that the stored PageID is now no longer used and therefore wasted space.  However there are
						// existing 6.0 & 6.1 ASIP Mail Databases with this information in them, therefore to remove this now useless data
						// will cause a file format compatibility problem with pre-existing Mail Databases...therefore we'll simply waste the
						// space, but avoid the potential error due to Consolodate data...

						// Legacy line of code commented out...
						// aSearchResult = this->FindObjectInList( anObjTypeEntry->fDataList, inOutIterator->fCurrentObjID, outObjData, aJunkPageID, aDataPageHint );

						aSearchResult = this->GetObjData( inOutIterator->fIteratorDataType, inOutIterator->fCurrentObjID, outObjData );
					}
				}
				else
				{
					aSearchResult = kDBNoSuchObjType;
				}
			}
		}

		if ( aSearchResult != kDBNoErr )
		{
			inOutIterator->fCurrentObjID = 0;
			inOutIterator->fCurrentPageID = 0;
			inOutIterator->fIteratorDone = true;
			result = kDBObjNotFound;
		}
		outObjID = inOutIterator->fCurrentObjID;

		this->DoReleaseDB();
	}

	catch ( ExceptionCode err )
	{
		this->DoReleaseDB();
	}

	return( result );
}

OSStatus CBaseDatabase_10_1::NextObject ( SDBIteratorPtr_10_1 inOutIterator, ObjID &outObjID, void *outObjData )
{
	return this->PrivGetNextObject( inOutIterator, outObjID, true, outObjData );
}

OSStatus CBaseDatabase_10_1::PrevObject ( SDBIteratorPtr_10_1 inOutIterator, ObjID &outObjID, void *outObjData )
{
	return this->PrivGetNextObject( inOutIterator, outObjID, false, outObjData );
}

#pragma mark -

void CBaseDatabase_10_1::DoYield ( const Boolean inForceYieldFlag )
{
	// this function must be provided by the derived class
	#pragma unused ( inForceYieldFlag )

	BaseDBAssert( false );
}

Boolean CBaseDatabase_10_1::IsThreadQuitting ( void )
{
	BaseDBAssert( false );

	return ( false );
}

void CBaseDatabase_10_1::DoLockDB ( void )
{
	// this function must be provided by the derived class
	BaseDBAssert( false );
}

void CBaseDatabase_10_1::DoReleaseDB ( void )
{
	// this function must be provided by the derived class
	BaseDBAssert( false );
}

#pragma mark -

SDBIterator_10_1::SDBIterator_10_1 ( CBaseDatabase_10_1 *inBaseDBPtr )
{
	fIteratorDataType	= kListNotEnabled_10_1;
	fCurrentObjID		= 0;
	fCurrentPageID		= 0;
	fNextIterator		= NULL;
	fIteratorDone		= true;
	f2ndIDMatch			= 0;
	fParentDatabase		= inBaseDBPtr;
}

SDBIterator_10_1::~SDBIterator_10_1 ( void )
{
	if ( fParentDatabase != NULL )
	{
		BaseDBAssert( this != nil );
		BaseDBAssert( fIteratorDataType == kListNotEnabled_10_1 );
		BaseDBAssert( fCurrentObjID == 0 );
		BaseDBAssert( fCurrentPageID == 0 );
		BaseDBAssert( fNextIterator == NULL );
		BaseDBAssert( fIteratorDone == true );
		BaseDBAssert( f2ndIDMatch == 0 );
	}
}
