]> marstr Code Repo - Database/commitdiff
Initial commit with recently added functions to work with the database master
authorMarStr <marcus@marstr.online>
Fri, 5 Jul 2024 13:28:42 +0000 (15:28 +0200)
committerMarStr <marcus@marstr.online>
Fri, 5 Jul 2024 13:28:42 +0000 (15:28 +0200)
database.cpp [new file with mode: 0644]
database.h [new file with mode: 0644]
databasedefines.h [new file with mode: 0644]
databasefield.h [new file with mode: 0644]
databaserow.cpp [new file with mode: 0644]
databaserow.h [new file with mode: 0644]

diff --git a/database.cpp b/database.cpp
new file mode 100644 (file)
index 0000000..b9c17c3
--- /dev/null
@@ -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(); }
diff --git a/database.h b/database.h
new file mode 100644 (file)
index 0000000..b8cdb3a
--- /dev/null
@@ -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
diff --git a/databasedefines.h b/databasedefines.h
new file mode 100644 (file)
index 0000000..071a5af
--- /dev/null
@@ -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 (file)
index 0000000..2ac7cbb
--- /dev/null
@@ -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
diff --git a/databaserow.cpp b/databaserow.cpp
new file mode 100644 (file)
index 0000000..fcf4b93
--- /dev/null
@@ -0,0 +1,5 @@
+
+#include <database/databaserow.h>
+
+void marstr::databaserow::AddField(marstr::databasefield fld)
+{ RowFields.push_back(fld); }
diff --git a/databaserow.h b/databaserow.h
new file mode 100644 (file)
index 0000000..5e62003
--- /dev/null
@@ -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