From 914da69a0bb9f75284aaa074504a28870f5898ef Mon Sep 17 00:00:00 2001 From: MarStr Date: Fri, 5 Jul 2024 15:28:42 +0200 Subject: [PATCH 1/1] Initial commit with recently added functions to work with the database --- database.cpp | 512 ++++++++++++++++++++++++++++++++++++++++++++++ database.h | 92 +++++++++ databasedefines.h | 16 ++ databasefield.h | 36 ++++ databaserow.cpp | 5 + databaserow.h | 30 +++ 6 files changed, 691 insertions(+) create mode 100644 database.cpp create mode 100644 database.h create mode 100644 databasedefines.h create mode 100644 databasefield.h create mode 100644 databaserow.cpp create mode 100644 databaserow.h diff --git a/database.cpp b/database.cpp new file mode 100644 index 0000000..b9c17c3 --- /dev/null +++ b/database.cpp @@ -0,0 +1,512 @@ + +#include + +// Set up DB +void marstr::database::setUpDatabase(string dbname, vector fieldnames, vector 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 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 fieldnames, vector 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 marstr::database::findRowsWithValueInColumn(string value, string colname) +{ + // The result with the index IDs + vector rows; + + // The column index to be searched + int colindex = -1; + for (int i=0; i +#include +#include +#include +#include +#include + +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 FieldNames; + // The database rows + vector Rows; + // Definitions of the single datatypes in each field + vector 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 fieldnames, vector 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 fieldnames, vector 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 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 diff --git a/databasedefines.h b/databasedefines.h new file mode 100644 index 0000000..071a5af --- /dev/null +++ b/databasedefines.h @@ -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 diff --git a/databasefield.h b/databasefield.h new file mode 100644 index 0000000..2ac7cbb --- /dev/null +++ b/databasefield.h @@ -0,0 +1,36 @@ + +// Describes one single database field of our own custom format. + +#ifndef MARSTR_DATABASEFIELD +#define MARSTR_DATABASEFIELD + +#include +#include + +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 diff --git a/databaserow.cpp b/databaserow.cpp new file mode 100644 index 0000000..fcf4b93 --- /dev/null +++ b/databaserow.cpp @@ -0,0 +1,5 @@ + +#include + +void marstr::databaserow::AddField(marstr::databasefield fld) +{ RowFields.push_back(fld); } diff --git a/databaserow.h b/databaserow.h new file mode 100644 index 0000000..5e62003 --- /dev/null +++ b/databaserow.h @@ -0,0 +1,30 @@ + +// Describes one single row of our database format. + +#ifndef MARSTR_DATABASEROW +#define MARSTR_DATABASEROW + +#include +#include +#include + +using namespace std; +using namespace marstr; + +namespace marstr +{ + class databaserow + { + public: + // The single fields in this row + vector RowFields; + + // Defines a specific field to be null or not + void FieldIsNullField(int fld, bool isNull); + + // Adds a field + void AddField(databasefield fld); + }; +} + +#endif -- 2.30.2