📄 database.cpp
字号:
return true;
}
record = reinterpret_cast<Record*>(record->m_nextNameRecord);
}
m_record = NULL;
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
RecordHandle::ReadAndNext()
{
Check_Object(this);
Check_Object(m_databaseHandle);
Database* db = m_databaseHandle->m_dataBase;
Check_Object(db);
Record* data;
if (!m_databaseHandle->m_currentPointer)
return false;
Check_Object(m_databaseHandle->m_currentPointer);
m_ID = m_databaseHandle->m_currentPointer->m_ID;
m_length = m_databaseHandle->m_currentPointer->m_length;
m_data = (&m_databaseHandle->m_currentPointer->m_data[m_databaseHandle->m_currentPointer->m_nameLength]);
m_name = m_databaseHandle->m_currentPointer->m_name;
m_record = m_databaseHandle->m_currentPointer;
m_timeStamp = m_databaseHandle->m_currentPointer->m_lastModified;
m_databaseHandle->m_currentPointer =
reinterpret_cast<Record*>(m_databaseHandle->m_currentPointer->m_nextIDRecord);
if (!m_databaseHandle->m_currentPointer)
{
while (++m_databaseHandle->m_currentRecord < Database::e_DataBlockSize)
{
data = reinterpret_cast<Record*>(
db->m_idOffsets[m_databaseHandle->m_currentRecord]
);
if( data )
{
m_databaseHandle->m_currentPointer =
reinterpret_cast<Record*>(
(DWORD)data + m_databaseHandle->m_baseAddress
);
return true;
}
}
m_databaseHandle->m_currentPointer = NULL;
return true;
}
m_databaseHandle->m_currentPointer =
reinterpret_cast<Record*>(
(DWORD)m_databaseHandle->m_currentPointer + m_databaseHandle->m_baseAddress
);
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RecordHandle::TestInstance() const
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
Database::FilesOpened = 0;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Database::Database()
{
m_tag = e_Tag;
m_version = e_Version;
m_nextRecordID = 1;
m_numberOfRecords = 0;
memset(m_idOffsets, 0, sizeof(m_idOffsets));
memset(m_nameOffsets, 0, sizeof(m_nameOffsets));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DatabaseHandle::DatabaseHandle(
const char* filename,
bool read_only
)
{
//
//-----------------------------------------------------
// Create the database heap if it doesn't already exist
//-----------------------------------------------------
//
m_fileName = filename;
if (!Database::FilesOpened++)
Database_Heap = gos_CreateMemoryHeap("Database", 0);
gos_PushCurrentHeap(Database_Heap);
m_currentRecord = 0;
m_currentPointer = NULL;
m_dirtyFlag = false;
//
//--------------------------------------------
// If the file is read only, our job is simple
//--------------------------------------------
//
m_readOnly = read_only;
if (m_readOnly)
{
if (gos_DoesFileExist(filename))
{
FileStream::IsRedirected = false;
DWORD size;
m_handle =
gos_OpenMemoryMappedFile(
filename,
reinterpret_cast<BYTE**>(&m_dataBase),
&size
);
if (m_dataBase->m_tag != Database::e_Tag)
STOP(("Invalid database file \"%s\"", filename));
if (m_dataBase->m_version > Database::e_Version)
STOP(("Application must be recompiled to use database \"%s\"", filename));
m_baseAddress = reinterpret_cast<DWORD>(m_dataBase);
Check_Object(m_dataBase);
}
else
STOP(("Database \"%s\" does not exist", filename));
}
//
//-------------------------------------------------------------
// The file is not read only, so see if we load it or create it
//-------------------------------------------------------------
//
else
{
m_handle = NULL;
if (gos_DoesFileExist(filename))
{
DWORD size;
gos_GetFile(
filename,
reinterpret_cast<BYTE**>(&m_dataBase),
&size
);
FileStream::IsRedirected = false;
Check_Pointer(m_dataBase);
if (m_dataBase->m_tag != Database::e_Tag || m_dataBase->m_version > Database::e_Version || m_dataBase->m_version == 1)
{
gos_Free(m_dataBase);
PAUSE(("Bad database file! Press 'Continue' to rebuild", filename));
m_dataBase = new(gos_Malloc(sizeof(Database), Database_Heap)) Database;
m_baseAddress = 0;
}
else
{
m_baseAddress = reinterpret_cast<DWORD>(m_dataBase);
Check_Object(m_dataBase);
}
}
//
//------------------------------------------
// The file doesn't exist, so create it here
//------------------------------------------
//
else
{
m_dataBase = new(gos_Malloc(sizeof(Database), Database_Heap)) Database;
m_baseAddress = 0;
}
}
gos_PopCurrentHeap();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DatabaseHandle::~DatabaseHandle()
{
Check_Object(this);
//
//---------------------------------------------------------------
// If we are closing a read only file, release the memmap handle,
// otherwise delete the database object
//---------------------------------------------------------------
//
if (m_readOnly)
gos_CloseMemoryMappedFile(m_handle);
else
{
Save();
Check_Object(m_dataBase);
//
//---------------------------------------------------------------------
// Now we need to go through and delete all the records in the database
//---------------------------------------------------------------------
//
for (DWORD i=0; i<Database::e_DataBlockSize; ++i)
{
Record* record = reinterpret_cast<Record*>(m_dataBase->m_idOffsets[i]);
if (record)
{
while (record)
{
record = reinterpret_cast<Record*>((DWORD)record + m_baseAddress);
Check_Object(record);
Record* this_record = record;
record = reinterpret_cast<Record*>(record->m_nextIDRecord);
if (this_record->m_mustFree)
delete this_record;
}
}
}
//
//--------------------------------
// Now free up the database itself
//--------------------------------
//
gos_Free(m_dataBase);
}
//
//----------------------------------------------------
// Delete the memory heap if this is the last database
//----------------------------------------------------
//
if (--Database::FilesOpened == 0)
gos_DestroyMemoryHeap(Database_Heap);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
DatabaseHandle::Save()
{
Check_Object(this);
//
//--------------------------------------
// If there were no changes, we are done
//--------------------------------------
//
if (m_readOnly || !m_dirtyFlag)
return;
//
//----------------------------------------------------------------------
// We will need to adjust pointers as we write this thing out, so make a
// copy of the database object now
//----------------------------------------------------------------------
//
Database output_db;
output_db.m_numberOfRecords = m_dataBase->m_numberOfRecords;
output_db.m_nextRecordID = m_dataBase->m_nextRecordID;
//
//----------------------------------------------------
// Build a table to deal with rethreading the database
//----------------------------------------------------
//
struct OutputRecord
{
DWORD
m_ID,
m_offset,
m_nextIDRecord,
m_nextNameRecord;
Record
*m_data;
};
OutputRecord *new_records = new OutputRecord[m_dataBase->m_numberOfRecords];
Check_Pointer(new_records);
DWORD new_id_index[Database::e_DataBlockSize];
memset(new_id_index, 0, sizeof(new_id_index));
//
//---------------------------------------------------------
// Start filling in the block information from the database
//---------------------------------------------------------
//
DWORD offset = sizeof(output_db);
OutputRecord* new_record = new_records;
for (DWORD i=0; i<Database::e_DataBlockSize; ++i)
{
Record* old_record = reinterpret_cast<Record*>(m_dataBase->m_idOffsets[i]);
if (old_record)
{
output_db.m_idOffsets[i] = offset;
new_id_index[i] = new_record - new_records;
while (old_record)
{
old_record = reinterpret_cast<Record*>((DWORD)old_record + m_baseAddress);
Check_Object(old_record);
new_record->m_data = old_record;
new_record->m_ID = old_record->m_ID;
new_record->m_offset = offset;
new_record->m_nextIDRecord = 0;
new_record->m_nextNameRecord = 0;
offset +=
sizeof(*old_record) + old_record->m_length + old_record->m_nameLength;
old_record = reinterpret_cast<Record*>(old_record->m_nextIDRecord);
if (old_record)
new_record->m_nextIDRecord = offset;
++new_record;
}
}
}
//
//-----------------------------------------
// Make sure Hash index table is up to date
//-----------------------------------------
//
for (i=0; i<Database::e_DataBlockSize; ++i)
{
Record* old_record = reinterpret_cast<Record*>(m_dataBase->m_nameOffsets[i]);
if (old_record)
{
old_record = reinterpret_cast<Record*>((DWORD)old_record + m_baseAddress);
Check_Object(old_record);
//
//---------------------------------
// Find this record in our new data
//---------------------------------
//
DWORD index = old_record->m_ID % Database::e_DataBlockSize;
int j = new_id_index[index];
OutputRecord *new_record = &new_records[j];
for (; j<m_dataBase->m_numberOfRecords; ++j, ++new_record)
{
if (new_record->m_ID == old_record->m_ID)
break;
}
Verify(j<m_dataBase->m_numberOfRecords);
//
//----------------------
// Set up the hash chain
//----------------------
//
output_db.m_nameOffsets[i] = new_record->m_offset;
while (old_record)
{
Check_Object(old_record);
//
//-----------------------------------------------------------
// Find the next record, and find where it is in our new data
//-----------------------------------------------------------
//
old_record = reinterpret_cast<Record*>(old_record->m_nextNameRecord);
Verify(j<m_dataBase->m_numberOfRecords);
if (old_record)
{
old_record = reinterpret_cast<Record*>((DWORD)old_record + m_baseAddress);
Check_Object(old_record);
index = old_record->m_ID % Database::e_DataBlockSize;
j = new_id_index[index];
OutputRecord *next_record = &new_records[j];
for (; j<m_dataBase->m_numberOfRecords; ++j, ++next_record)
{
if (next_record->m_ID == old_record->m_ID)
break;
}
Verify(j<m_dataBase->m_numberOfRecords);
new_record->m_nextNameRecord = next_record->m_offset;
new_record = next_record;
}
}
}
}
//
//------------------------------------------------
// This file was read/write, so write it out again
//------------------------------------------------
//
FileStream db_file(m_fileName, FileStream::WriteOnly);
db_file.WriteBytes(&output_db, sizeof(output_db));
//
//----------------------
// Write out each record
//----------------------
//
new_record=new_records;
for (i=0; i<m_dataBase->m_numberOfRecords; ++i, ++new_record)
{
Record* old_record = new_record->m_data;
//
//------------------------------------------------------------
// Save the old connection info, then replace it with the data
// calculated in the new records
//------------------------------------------------------------
//
bool free_block = old_record->m_mustFree;
DWORD next_id = old_record->m_nextIDRecord;
DWORD next_name = old_record->m_nextNameRecord;
old_record->m_mustFree = false;
old_record->m_nextIDRecord = new_record->m_nextIDRecord;
old_record->m_nextNameRecord = new_record->m_nextNameRecord;
//
//------------------------------------------
// Write out the info, then restore the data
//------------------------------------------
//
db_file.WriteBytes(
old_record,
sizeof(*old_record) + old_record->m_length + old_record->m_nameLength
);
old_record->m_mustFree = free_block;
old_record->m_nextIDRecord = next_id;
old_record->m_nextNameRecord = next_name;
}
//
//---------
// Clean up
//---------
//
delete[] new_records;
m_dirtyFlag = false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DWORD
DatabaseHandle::GetNumberOfRecords()
{
Check_Object(this);
return m_dataBase->m_numberOfRecords;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
DatabaseHandle::First()
{
Check_Object(this);
m_currentRecord = 0;
m_currentPointer = NULL;
for (DWORD i=0; i<Database::e_DataBlockSize; i++)
{
Record* data =
reinterpret_cast<Record*>(
m_dataBase->m_idOffsets[i] + m_baseAddress
);
if( data )
{
m_currentRecord = i;
m_currentPointer = data;
break;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
DatabaseHandle::TestInstance() const
{
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -