Initial commit with recently added functions to work with the database

This commit is contained in:
MarStr 2024-07-05 15:28:42 +02:00
commit 914da69a0b
6 changed files with 691 additions and 0 deletions

512
database.cpp Normal file
View File

@ -0,0 +1,512 @@
#include <database/database.h>
// Set up DB
void marstr::database::setUpDatabase(string dbname, vector<string> fieldnames, vector<int> datatypes)
{
// Set the name
DatabaseName = dbname;
// Acquire the field names
int i;
for (i=1; i<=fieldnames.size(); i++)
{ FieldNames.push_back(fieldnames[i-1]); }
// Acquire the field data types
for (i=1; i<=datatypes.size(); i++)
{ RowDatatypes.push_back(datatypes[i-1]); }
}
// Adds a pre-created row
void marstr::database::addRow(databaserow row)
{
// Check if the row has the same amount of fields that we expect there to be
if (row.RowFields.size() == FieldNames.size())
{ Rows.push_back(row); }
}
// Deletes the specified row
void marstr::database::deleteRow(int rowNumber)
{
// Create a new vector with the specified row omitted
vector<databaserow> rows;
// Go through existing rows and add them to the above vector
// (except rowNumber)
int i;
for (i=1; i<=Rows.size(); i++)
{
if (i!=rowNumber)
{ rows.push_back(Rows[i-1]); }
}
// Now clear the real vector
Rows.clear();
// And add the rows we got to the real rows
for (i=1; i<=rows.size(); i++)
{ Rows.push_back(rows[i-1]); }
// Clear the tmp vector
rows.clear();
}
// Updates the content of a specific field in a specific row
void marstr::database::updateFieldInRow(int rowNumber, int fieldNumber, string content)
{
// Make sure the row and field exist
if (rowNumber <= Rows.size() && fieldNumber <= FieldNames.size())
{
// When updating a field, it is probably not a null field
Rows[rowNumber].RowFields[fieldNumber].NullField = false;
// It can be either one of these... so make sure they exist
int newInt = 0;
short newShort = 0;
float newFloat = 0.0f;
double newDouble = 0.0f;
string newString = content;
// Find the datatype for the field
int datatype = RowDatatypes[fieldNumber];
// Convert the field
if (datatype == MARSTR_DB_INT)
{
newInt = atoi(content.c_str());
Rows[rowNumber].RowFields[fieldNumber].fieldInt = newInt;
}
if (datatype == MARSTR_DB_SHORT)
{
newShort = atoi(content.c_str());
Rows[rowNumber].RowFields[fieldNumber].fieldShort = newShort;
}
if (datatype == MARSTR_DB_FLOAT)
{
newFloat = atof(content.c_str());
Rows[rowNumber].RowFields[fieldNumber].fieldFloat = newFloat;
}
if (datatype == MARSTR_DB_DOUBLE)
{
newDouble = atof(content.c_str());
Rows[rowNumber].RowFields[fieldNumber].fieldDouble = newDouble;
}
if (datatype == MARSTR_DB_STRING)
{ Rows[rowNumber].RowFields[fieldNumber].fieldString = content; }
}
}
// Defines a field to be a null field or not
void marstr::database::fieldIsNullField(int rowNumber, int fieldNumber, bool isNull)
{
// Make sure the row and field exist
if (rowNumber <= Rows.size() && fieldNumber <= FieldNames.size())
{ Rows[rowNumber].RowFields[fieldNumber].NullField = isNull; }
}
// Creates an empty db to be filled later
void marstr::database::createDatabase(string filename, string dbname, vector<string> fieldnames, vector<int> datatypes)
{
setUpDatabase(dbname, fieldnames, datatypes);
}
// Load database, but only the header - no content
void marstr::database::openDatabaseLink(string filename)
{
// Remember file name
DatabaseFileName = filename;
// File IO object
_linkIO = new fileio();
// Open DB
_linkIO->openFileForReading(filename);
// Read header and version
string hdrString = _linkIO->readString();
float hdrVersion = _linkIO->readFloat();
// Check if the header works out
string hdrStringCheck = "MARSTRDB";
if (hdrVersion == 1) // String check to be implemented
{
// Then this is a valid database
DatabaseFileName = filename;
// Read database name
DatabaseName = _linkIO->readString();
// Get number of field names
int amountOfFields = _linkIO->readInt();
// Read in the field names themselves
int i, j;
for (i=1; i<=amountOfFields; i++)
{ FieldNames.push_back(_linkIO->readString()); }
// Acquire the data types of each field
for (i=1; i<=amountOfFields; i++)
{ RowDatatypes.push_back(_linkIO->readInt()); }
// Number of rows in total
int numberOfRows = _linkIO->readInt();
}
}
databaserow marstr::database::nextDatabaseRow()
{
// The row to be constructed
databaserow row;
for (int j=1; j<=FieldNames.size(); j++)
{
// The field to be added to the row
databasefield fld;
// OK find out if the field has content (null field)
int isNullField = _linkIO->readInt();
// If this is a null field, set the field to be so and add it
// to the row
if (isNullField == 1)
{ fld.NullField = true; row.RowFields.push_back(fld); }
// If we have data, read according to the data type, set the
// value into the field, and add the field to the row
if (isNullField == 0)
{
// Not a null field
fld.NullField = false;
if (RowDatatypes[j-1] == MARSTR_DB_INT)
{ int fldval = _linkIO->readInt(); fld.fieldInt = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_UINT)
{ unsigned int fldval = _linkIO->readUInt(); fld.fieldUInt = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_SHORT)
{ short fldval = _linkIO->readInt(); fld.fieldShort = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_USHORT)
{ unsigned short fldval = _linkIO->readUShort(); fld.fieldUShort = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_LONG)
{ long fldval = _linkIO->readLong(); fld.fieldLong = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_ULONG)
{ unsigned long fldval = _linkIO->readULong(); fld.fieldULong = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_FLOAT)
{ float fldval = _linkIO->readFloat(); fld.fieldFloat = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_DOUBLE)
{ double fldval = _linkIO->readDouble(); fld.fieldDouble = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_STRING)
{ string fldval = _linkIO->readString(); fld.fieldString = fldval; }
// Add the field
row.RowFields.push_back(fld);
}
}
return row;
}
void marstr::database::goToFirstRow()
{
_linkIO->closeFileForReading();
openDatabaseLink(DatabaseFileName);
}
// Loads a database from file
void marstr::database::loadDatabase(string filename)
{
// File IO object
fileio *FileIO = new fileio();
// Remember file name
DatabaseFileName = filename;
// Open DB
FileIO->openFileForReading(filename);
// Read header and version
string hdrString = FileIO->readString();
float hdrVersion = FileIO->readFloat();
// Check if the header works out
string hdrStringCheck = "MARSTRDB";
if (hdrVersion == 1) // String check to be implemented
{
// Then this is a valid database
DatabaseFileName = filename;
// Read database name
DatabaseName = FileIO->readString();
// Get number of field names
int amountOfFields = FileIO->readInt();
// Read in the field names themselves
int i, j;
for (i=1; i<=amountOfFields; i++)
{ FieldNames.push_back(FileIO->readString()); }
// Acquire the data types of each field
for (i=1; i<=amountOfFields; i++)
{ RowDatatypes.push_back(FileIO->readInt()); }
// Number of rows in total
int numberOfRows = FileIO->readInt();
// Go through the remainder of the file to acquire the content
// of each row and their fields
for (i=1; i<=numberOfRows; i++)
{
// The row to be constructed
databaserow row;
for (j=1; j<=FieldNames.size(); j++)
{
// The field to be added to the row
databasefield fld;
// OK find out if the field has content (null field)
int isNullField = FileIO->readInt();
// If this is a null field, set the field to be so and add it
// to the row
if (isNullField == 1)
{ fld.NullField = true; row.RowFields.push_back(fld); }
// If we have data, read according to the data type, set the
// value into the field, and add the field to the row
if (isNullField == 0)
{
// Not a null field
fld.NullField = false;
if (RowDatatypes[j-1] == MARSTR_DB_INT)
{ int fldval = FileIO->readInt(); fld.fieldInt = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_UINT)
{ unsigned int fldval = FileIO->readUInt(); fld.fieldUInt = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_SHORT)
{ short fldval = FileIO->readInt(); fld.fieldShort = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_USHORT)
{ unsigned short fldval = FileIO->readUShort(); fld.fieldUShort = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_LONG)
{ long fldval = FileIO->readLong(); fld.fieldLong = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_ULONG)
{ unsigned long fldval = FileIO->readULong(); fld.fieldULong = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_FLOAT)
{ float fldval = FileIO->readFloat(); fld.fieldFloat = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_DOUBLE)
{ double fldval = FileIO->readDouble(); fld.fieldDouble = fldval; }
if (RowDatatypes[j-1] == MARSTR_DB_STRING)
{ string fldval = FileIO->readString(); fld.fieldString = fldval; }
// Add the field
row.RowFields.push_back(fld);
}
}
// Add the completed row
Rows.push_back(row);
}
// Magic number check
int Magic = (Rows.size() * RowDatatypes.size());
int MagicInFile = FileIO->readInt();
// If the magic does not stack up, clear all data
if (Magic != MagicInFile)
{ Rows.clear(); }
}
// Close the pipe
FileIO->closeFileForReading();
// That's it
}
// Saves the database to the specified file. Use when everything is done!
void marstr::database::saveDatabase()
{
// File IO object// File IO object
fileio *FileIO = new fileio();
// Open the DB
FileIO->openFileForWriting(DatabaseFileName);
// Write a header
FileIO->writeString("MARSTRDB");
// Write a version
FileIO->writeFloat(1.0);
// Write database name
FileIO->writeString(DatabaseName);
// Write number of field names
FileIO->writeInt(FieldNames.size());
// The field names themselves
int i, j;
for (i=1; i<=FieldNames.size(); i++)
{ FileIO->writeString(FieldNames[i-1]); }
// The datatypes in each field
for (i=1; i<=RowDatatypes.size(); i++)
{ FileIO->writeInt(RowDatatypes[i-1]); }
// Write number of rows
FileIO->writeInt(Rows.size());
// Go through the rows and write the content, depending on if the field is
// a null field or not
for (i=1; i<=Rows.size(); i++)
{
for (j=1; j<=Rows[i-1].RowFields.size(); j++)
{
// Write down an int to signalize if the field has data or not
int isNull = 0;
if (Rows[i-1].RowFields[j-1].NullField == true) { isNull = 1; }
if (Rows[i-1].RowFields[j-1].NullField == false) { isNull = 0; }
FileIO->writeInt(isNull);
// Write actual data if field is not null
if (isNull == 0)
{
if (RowDatatypes[j-1] == MARSTR_DB_SHORT) // short
{ FileIO->writeInt(Rows[i-1].RowFields[j-1].fieldShort); }
if (RowDatatypes[j-1] == MARSTR_DB_INT) // int
{ FileIO->writeInt(Rows[i-1].RowFields[j-1].fieldInt); }
if (RowDatatypes[j-1] == MARSTR_DB_UINT) // uint
{ FileIO->writeUInt(Rows[i-1].RowFields[j-1].fieldUInt); }
if (RowDatatypes[j-1] == MARSTR_DB_LONG) // long
{ FileIO->writeLong(Rows[i-1].RowFields[j-1].fieldLong); }
if (RowDatatypes[j-1] == MARSTR_DB_ULONG) // ulong
{ FileIO->writeULong(Rows[i-1].RowFields[j-1].fieldULong); }
if (RowDatatypes[j-1] == MARSTR_DB_FLOAT) // float
{ FileIO->writeFloat(Rows[i-1].RowFields[j-1].fieldFloat); }
if (RowDatatypes[j-1] == MARSTR_DB_DOUBLE) // double
{ FileIO->writeDouble(Rows[i-1].RowFields[j-1].fieldDouble); }
if (RowDatatypes[j-1] == MARSTR_DB_STRING) // string
{ FileIO->writeString(Rows[i-1].RowFields[j-1].fieldString); }
}
}
}
// Write a magic number for verification
int Magic = (Rows.size() * RowDatatypes.size());
FileIO->writeInt(Magic);
// Close DB
FileIO->closeFileForWriting();
// That's it
}
void marstr::database::closeDatabase(bool save)
{
if (save == true)
{ saveDatabase(); }
Rows.clear();
}
vector<int> marstr::database::findRowsWithValueInColumn(string value, string colname)
{
// The result with the index IDs
vector<int> rows;
// The column index to be searched
int colindex = -1;
for (int i=0; i<FieldNames.size(); i++)
{
if (FieldNames[i].compare(colname) == 0)
{ colindex = i; break; }
}
// Proceed if we have a column
if (colindex != -1)
{
// Look for results depending on datatype
for (int i=0; i<Rows.size(); i++)
{
bool push=false;
// The field to check
databasefield fld = Rows[i].RowFields[colindex];
if (RowDatatypes[colindex] == MARSTR_DB_INT)
{
int needle = stoi(value);
if (fld.NullField == false && fld.fieldInt == needle)
{ push = true; }
}
if (RowDatatypes[colindex] == MARSTR_DB_UINT)
{
unsigned int needle = stoi(value);
if (fld.NullField == false && fld.fieldUInt == needle)
{ push = true; }
}
if (RowDatatypes[colindex] == MARSTR_DB_SHORT)
{
short needle = stoi(value);
if (fld.NullField == false && fld.fieldShort == needle)
{ push = true; }
}
if (RowDatatypes[colindex] == MARSTR_DB_USHORT)
{
unsigned short needle = stoi(value);
if (fld.NullField == false && fld.fieldUShort == needle)
{ push = true; }
}
if (RowDatatypes[colindex] == MARSTR_DB_LONG)
{
long needle = stol(value);
if (fld.NullField == false && fld.fieldLong == needle)
{ push = true; }
}
if (RowDatatypes[colindex] == MARSTR_DB_ULONG)
{
long needle = stol(value);
if (fld.NullField == false && fld.fieldULong == needle)
{ push = true; }
}
if (RowDatatypes[colindex] == MARSTR_DB_FLOAT)
{
float needle = stof(value);
if (fld.NullField == false && fld.fieldFloat == needle)
{ push = true; }
}
if (RowDatatypes[colindex] == MARSTR_DB_DOUBLE)
{
double needle = stod(value);
if (fld.NullField == false && fld.fieldDouble == needle)
{ push = true; }
}
if (RowDatatypes[colindex] == MARSTR_DB_STRING)
{
if (fld.NullField == false && fld.fieldString.compare(value) == 0)
{ push = true; }
}
if (push == true) { rows.push_back(i); }
}
}
// Return list
return rows;
}
int marstr::database::numberOfRows() { return Rows.size(); }

92
database.h Normal file
View File

@ -0,0 +1,92 @@
// This class represents a custom database format used in the Reason Engine. It
// is small and quite simple to use. This format is used for a number of things,
// mostly for models.
#ifndef MSTR_DATABASE
#define MSTR_DATABASE
#include <cstdlib>
#include <string>
#include <vector>
#include <fileio/fileio.h>
#include <database/databaserow.h>
#include <database/databasedefines.h>
using namespace std;
using namespace marstr;
namespace marstr
{
class database
{
public:
// Name of the database
string DatabaseName;
// Name of all fields in this database
vector<string> FieldNames;
// The database rows
vector<databaserow> Rows;
// Definitions of the single datatypes in each field
vector<int> RowDatatypes;
// The filename from which this database came from, if opened from file
string DatabaseFileName;
// Sets up this database with a name, the field names, and the data
// types each row should hold
void setUpDatabase(string dbname, vector<string> fieldnames, vector<int> datatypes);
// Adds a row to this database
void addRow(databaserow row);
// Deletes the row of the specified index
void deleteRow(int rowNumber);
// Updates the content of a specific field, in the specified row. For
// simplycity, the content is a string which we process to the correct
// field type for you.
void updateFieldInRow(int rowNumber, int fieldNumber, string content);
// Defines if a field has data or not
void fieldIsNullField(int rowNumber, int fieldNumber, bool isNull);
// Loads a database entirely from a file into RAM
void loadDatabase(string filename);
// Loads a database, but only reads the header info - you will need to scan
// rows of each DB separately. This is useful for larger databases and low amounts
// of RAM.
void openDatabaseLink(string filename);
// Returns the next row from the database link for evaluation
databaserow nextDatabaseRow();
// Goes back to the beginning of the database in the opened link
void goToFirstRow();
// Creates an empty database
void createDatabase(string filename, string dbname, vector<string> fieldnames, vector<int> datatypes);
// Saves the database to a file
void saveDatabase();
// Clears the database in RAM, basically frees up used space and lets
// you load another database with the same object
void closeDatabase(bool save);
// Acquires the amount of rows in DB
int numberOfRows();
// Find something in all rows, in the specified column. Looks for the
// EXACT value match. The value can also be an int, float or double, but
// must be encoded as a string, like "30.99", "64", or "19.645773373"
vector<int> findRowsWithValueInColumn(string value, string colname);
private:
// The link that remains open to the database file should it be requested
// to only load the header
fileio *_linkIO;
};
}
#endif

16
databasedefines.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef MARSTR_DATABASEDEFINES
#define MARSTR_DATABASEDEFINES
#define MARSTR_DB_INT 101
#define MARSTR_DB_UINT 102
#define MARSTR_DB_SHORT 103
#define MARSTR_DB_USHORT 104
#define MARSTR_DB_LONG 105
#define MARSTR_DB_ULONG 106
#define MARSTR_DB_FLOAT 107
#define MARSTR_DB_DOUBLE 108
#define MARSTR_DB_STRING 109
#endif

36
databasefield.h Normal file
View File

@ -0,0 +1,36 @@
// Describes one single database field of our own custom format.
#ifndef MARSTR_DATABASEFIELD
#define MARSTR_DATABASEFIELD
#include <cstdlib>
#include <string>
using namespace std;
namespace marstr
{
class databasefield
{
public:
// Whether or not this is a null field
bool NullField;
// The content type
int ContentType;
// The single content types
int fieldInt;
unsigned int fieldUInt;
short fieldShort;
unsigned short fieldUShort;
long fieldLong;
unsigned long fieldULong;
float fieldFloat;
double fieldDouble;
string fieldString;
};
}
#endif

5
databaserow.cpp Normal file
View File

@ -0,0 +1,5 @@
#include <database/databaserow.h>
void marstr::databaserow::AddField(marstr::databasefield fld)
{ RowFields.push_back(fld); }

30
databaserow.h Normal file
View File

@ -0,0 +1,30 @@
// Describes one single row of our database format.
#ifndef MARSTR_DATABASEROW
#define MARSTR_DATABASEROW
#include <cstdlib>
#include <vector>
#include <database/databasefield.h>
using namespace std;
using namespace marstr;
namespace marstr
{
class databaserow
{
public:
// The single fields in this row
vector <databasefield> RowFields;
// Defines a specific field to be null or not
void FieldIsNullField(int fld, bool isNull);
// Adds a field
void AddField(databasefield fld);
};
}
#endif