aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/common/Csv.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2019-06-06 16:48:21 +0200
committerShauren <shauren.trinity@gmail.com>2019-06-08 17:09:24 +0200
commitfc330fd8ff0115804d9c4b53a1f810c00dd63de9 (patch)
treecfa10998fed66779834bf0b7a9b8b799d33d91d4 /dep/CascLib/src/common/Csv.cpp
parent82c7b6c5688495d90c4ee5995a4ff74039348296 (diff)
Dep/CascLib: Update to ladislav-zezula/CascLib@a1197edf0b3bd4d52c3f39be7fa7b44bb0b98012
Diffstat (limited to 'dep/CascLib/src/common/Csv.cpp')
-rw-r--r--dep/CascLib/src/common/Csv.cpp370
1 files changed, 370 insertions, 0 deletions
diff --git a/dep/CascLib/src/common/Csv.cpp b/dep/CascLib/src/common/Csv.cpp
new file mode 100644
index 00000000000..24b654daa86
--- /dev/null
+++ b/dep/CascLib/src/common/Csv.cpp
@@ -0,0 +1,370 @@
+/*****************************************************************************/
+/* Csv.cpp Copyright (c) Ladislav Zezula 2019 */
+/*---------------------------------------------------------------------------*/
+/* Implementation for the CSV handler class */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 24.05.19 1.00 Lad Created */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "../CascLib.h"
+#include "../CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local variables
+
+static const CASC_CSV_LINE NullLine;
+#define NullColumn NullLine.Columns[0];
+
+//-----------------------------------------------------------------------------
+// Local functions
+
+static char * NextLine(char * szLine)
+{
+ // Find the end of the line
+ while(szLine[0] != 0 && szLine[0] != 0x0A && szLine[0] != 0x0D)
+ szLine++;
+
+ // Terminate the line
+ while(szLine[0] == 0x0A || szLine[0] == 0x0D)
+ *szLine++ = 0;
+
+ // If there's an end-of-string, it's over
+ return (szLine[0] != 0) ? szLine : NULL;
+}
+
+static char * NextColumn(char * szColumn)
+{
+ // Find the end of the column
+ while(szColumn[0] != 0 && szColumn[0] != '|')
+ szColumn++;
+
+ // Terminate the column
+ if (szColumn[0] == '|')
+ {
+ *szColumn++ = 0;
+ return szColumn;
+ }
+
+ // If there's an end-of-string, it's over
+ return NULL;
+}
+
+static size_t CalcHashValue(const char * szString)
+{
+ size_t dwHash = 0x7EEE7EEE;
+
+ // Hash the string itself
+ while(szString[0] != 0)
+ {
+ dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[0];
+ szString++;
+ }
+
+ // Return the hash limited by the table size
+ return dwHash;
+}
+
+static BYTE HashToIndex(size_t dwHashValue)
+{
+ return (BYTE)(dwHashValue & (CSV_HASH_TABLE_SIZE - 1));
+}
+
+//-----------------------------------------------------------------------------
+// Class for CSV line
+
+CASC_CSV_LINE::CASC_CSV_LINE()
+{
+ m_pParent = NULL;
+ m_nColumns = 0;
+}
+
+CASC_CSV_LINE::~CASC_CSV_LINE()
+{}
+
+bool CASC_CSV_LINE::SetLine(CASC_CSV * pParent, char * szCsvLine)
+{
+ char * szCsvColumn;
+ size_t nHdrColumns = 0;
+ size_t nColumns = 0;
+
+ // Reset the number of column to zero
+ m_pParent = pParent;
+ m_nColumns = 0;
+
+ // Parse each column
+ while(szCsvLine != NULL && nColumns < CSV_MAX_COLUMNS)
+ {
+ // Get current line and the next one
+ szCsvColumn = szCsvLine;
+ szCsvLine = NextColumn(szCsvLine);
+
+ // Save the current line
+ Columns[nColumns].szValue = szCsvColumn;
+ Columns[nColumns].nLength = strlen(szCsvColumn);
+ nColumns++;
+ }
+
+ // Columns overflow
+ if(nColumns >= CSV_MAX_COLUMNS)
+ {
+ assert(false);
+ return false;
+ }
+
+ // If the parent has header lines, then the number of columns must match
+ // In the case of mismatched column count, ignore the line
+ nHdrColumns = pParent->GetHeaderColumns();
+ if(nHdrColumns != 0 && nColumns != nHdrColumns)
+ return false;
+
+ // All OK
+ m_nColumns = nColumns;
+ return true;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](const char * szColumnName) const
+{
+ size_t nIndex;
+
+ // The column must have a hash table
+ if(m_pParent != NULL)
+ {
+ nIndex = m_pParent->GetColumnIndex(szColumnName);
+ if(nIndex != CSV_INVALID_INDEX && nIndex < m_nColumns)
+ {
+ return Columns[nIndex];
+ }
+ }
+
+ return NullColumn;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](size_t nIndex) const
+{
+ return (nIndex < m_nColumns) ? Columns[nIndex] : NullColumn;
+}
+
+//-----------------------------------------------------------------------------
+// Class for CSV object
+
+CASC_CSV::CASC_CSV(size_t nLinesMax, bool bHasHeader)
+{
+ // Initialize the class variables
+ memset(HashTable, 0xFF, sizeof(HashTable));
+ m_szCsvFile = NULL;
+ m_szCsvPtr = NULL;
+ m_nCsvFile = 0;
+ m_nLines = 0;
+ m_bHasHeader = bHasHeader;
+
+ // Nonzero number of lines means a finite CSV handler. The CSV will be loaded at once.
+ // Zero means that the user needs to call LoadNextLine() and then the line data
+ if(nLinesMax != 0)
+ {
+ m_pLines = new CASC_CSV_LINE[nLinesMax];
+ m_nLinesMax = nLinesMax;
+ m_bHasAllLines = true;
+ }
+ else
+ {
+ m_pLines = new CASC_CSV_LINE[1];
+ m_nLinesMax = 1;
+ m_bHasAllLines = false;
+ }
+}
+
+CASC_CSV::~CASC_CSV()
+{
+ if(m_pLines != NULL)
+ delete[] m_pLines;
+ if(m_szCsvFile != NULL)
+ delete [] m_szCsvFile;
+ m_szCsvFile = NULL;
+}
+
+int CASC_CSV::Load(const TCHAR * szFileName)
+{
+ DWORD cbFileData = 0;
+ int nError = ERROR_SUCCESS;
+
+ m_szCsvFile = (char *)LoadFileToMemory(szFileName, &cbFileData);
+ if (m_szCsvFile != NULL)
+ {
+ // There is one extra byte reserved by LoadFileToMemory, so we can put zero there
+ m_szCsvFile[cbFileData] = 0;
+ m_szCsvPtr = m_szCsvFile;
+ m_nCsvFile = cbFileData;
+
+ // Parse the data
+ nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
+ else
+ {
+ nError = GetLastError();
+ }
+
+ return nError;
+}
+
+int CASC_CSV::Load(LPBYTE pbData, size_t cbData)
+{
+ int nError = ERROR_NOT_ENOUGH_MEMORY;
+
+ m_szCsvFile = new char[cbData + 1];
+ if (m_szCsvFile != NULL)
+ {
+ // Copy the entire data and terminate them with zero
+ memcpy(m_szCsvFile, pbData, cbData);
+ m_szCsvFile[cbData] = 0;
+ m_szCsvPtr = m_szCsvFile;
+ m_nCsvFile = cbData;
+
+ // Parse the data
+ nError = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
+
+ return nError;
+}
+
+bool CASC_CSV::LoadNextLine()
+{
+ bool bResult = false;
+
+ // Only endless CSV handler can do this
+ if(m_bHasAllLines == false)
+ {
+ // A few checks
+ assert(m_pLines != NULL);
+ assert(m_nLinesMax == 1);
+
+ // Reset the current line and load it
+ bResult = LoadNextLine(m_pLines[0]);
+ m_nLines = (bResult) ? 1 : 0;
+ }
+
+ return bResult;
+}
+
+bool CASC_CSV::InitializeHashTable()
+{
+ // Create the hash table of HeaderText -> ColumnIndex
+ for(size_t i = 0; i < Header.GetColumnCount(); i++)
+ {
+ // Calculate the start slot and the current slot
+ size_t nStartIndex = HashToIndex(CalcHashValue(Header[i].szValue));
+ size_t nHashIndex = nStartIndex;
+
+ // Go as long as there is not a free space
+ while(HashTable[nHashIndex] != 0xFF)
+ {
+ nHashIndex = HashToIndex(nHashIndex + 1);
+ }
+
+ // Set the slot
+ HashTable[nHashIndex] = (BYTE)i;
+ }
+
+ return true;
+}
+
+bool CASC_CSV::LoadNextLine(CASC_CSV_LINE & Line)
+{
+ // Overwatch ROOT's header begins with "#"
+ if(m_szCsvPtr == m_szCsvFile && m_szCsvPtr[0] == '#')
+ m_szCsvPtr++;
+
+ // Parse the entire line
+ while(m_szCsvPtr != NULL && m_szCsvPtr[0] != 0)
+ {
+ char * szCsvLine = m_szCsvPtr;
+
+ // Get the next line
+ m_szCsvPtr = NextLine(m_szCsvPtr);
+
+ // Initialize the line
+ if (Line.SetLine(this, szCsvLine))
+ return true;
+ }
+
+ // End-of-file found
+ return false;
+}
+
+bool CASC_CSV::ParseCsvData()
+{
+ // Sanity checks
+ assert(m_nLines == 0);
+
+ // If we have header, then parse it and set the pointer to the next line
+ if(m_bHasHeader)
+ {
+ // Load the current line to the header
+ if (!LoadNextLine(Header))
+ return false;
+
+ // Initialize the hash table
+ if(!InitializeHashTable())
+ return false;
+ }
+
+ // Are we supposed to load all lines?
+ if(m_bHasAllLines)
+ {
+ // Parse each line
+ for(size_t i = 0; i < m_nLinesMax; i++)
+ {
+ if(!LoadNextLine(m_pLines[i]))
+ break;
+ m_nLines++;
+ }
+ }
+
+ return true;
+}
+
+const CASC_CSV_COLUMN & CASC_CSV::operator[](const char * szColumnName) const
+{
+ if (m_pLines == NULL || m_nLines == 0)
+ return NullColumn;
+ return m_pLines[0][GetColumnIndex(szColumnName)];
+}
+
+const CASC_CSV_LINE & CASC_CSV::operator[](size_t nIndex) const
+{
+ if (m_pLines == NULL || nIndex >= m_nLines)
+ return NullLine;
+ return m_pLines[nIndex];
+}
+
+size_t CASC_CSV::GetHeaderColumns() const
+{
+ return (m_bHasHeader) ? Header.GetColumnCount() : 0;
+}
+
+size_t CASC_CSV::GetColumnIndex(const char * szColumnName) const
+{
+ if(m_bHasHeader)
+ {
+ // Calculate the start slot and the current slot
+ size_t nStartIndex = HashToIndex(CalcHashValue(szColumnName));
+ size_t nHashIndex = nStartIndex;
+ size_t nIndex;
+
+ // Go as long as there is not a free space
+ while((nIndex = HashTable[nHashIndex]) != 0xFF)
+ {
+ // Compare the string
+ if(!strcmp(Header[nIndex].szValue, szColumnName))
+ return nIndex;
+
+ // Move to the next column
+ nHashIndex = HashToIndex(nHashIndex + 1);
+ }
+ }
+
+ return CSV_INVALID_INDEX;
+}
+