#define __CASCLIB_SELF__
#include "../CascLib.h"
#include "../CascCommon.h"
static const CASC_CSV_LINE NullLine;
#define NullColumn NullLine.Columns[0];
static char * NextLine_Default(void * , char * szLine)
{
while(szLine[0] != 0 && szLine[0] != 0x0A && szLine[0] != 0x0D)
szLine++;
while(szLine[0] == 0x0A || szLine[0] == 0x0D)
*szLine++ = 0;
return (szLine[0] != 0) ? szLine : NULL;
}
static char * NextColumn_Default(void * , char * szColumn)
{
while(szColumn[0] != 0 && szColumn[0] != '|')
szColumn++;
if(szColumn[0] == '|')
{
*szColumn++ = 0;
return szColumn;
}
return NULL;
}
static size_t CalcHashValue(const char * szString)
{
size_t dwHash = 0x7EEE7EEE;
while(szString[0] != 0)
{
dwHash = (dwHash >> 24) ^ (dwHash << 5) ^ dwHash ^ szString[0];
szString++;
}
return dwHash;
}
static BYTE HashToIndex(size_t dwHashValue)
{
return (BYTE)(dwHashValue & (CSV_HASH_TABLE_SIZE - 1));
}
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)
{
CASC_CSV_NEXTPROC PfnNextColumn = pParent->GetNextColumnProc();
char * szCsvColumn;
size_t nHdrColumns = 0;
size_t nColumns = 0;
m_pParent = pParent;
m_nColumns = 0;
while(szCsvLine != NULL && nColumns < CSV_MAX_COLUMNS)
{
szCsvColumn = szCsvLine;
szCsvLine = PfnNextColumn(pParent->GetUserData(), szCsvLine);
Columns[nColumns].szValue = szCsvColumn;
Columns[nColumns].nLength = strlen(szCsvColumn);
nColumns++;
}
if(nColumns >= CSV_MAX_COLUMNS)
{
assert(false);
return false;
}
nHdrColumns = pParent->GetHeaderColumns();
if(nHdrColumns != 0 && nColumns != nHdrColumns)
return false;
m_nColumns = nColumns;
return true;
}
const CASC_CSV_COLUMN & CASC_CSV_LINE::operator[](const char * szColumnName) const
{
size_t nIndex;
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;
}
CASC_CSV::CASC_CSV(size_t nLinesMax, bool bHasHeader)
{
memset(HashTable, 0xFF, sizeof(HashTable));
m_pvUserData = NULL;
m_szCsvFile = NULL;
m_szCsvPtr = NULL;
m_nLines = 0;
m_bHasHeader = bHasHeader;
PfnNextLine = NextLine_Default;
PfnNextColumn = NextColumn_Default;
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;
}
}
DWORD CASC_CSV::SetNextLineProc(CASC_CSV_NEXTPROC PfnNextLineProc, CASC_CSV_NEXTPROC PfnNextColProc, void * pvUserData)
{
if(PfnNextLineProc != NULL)
PfnNextLine = PfnNextLineProc;
if(PfnNextColProc != NULL)
PfnNextColumn = PfnNextColProc;
m_pvUserData = pvUserData;
return ERROR_SUCCESS;
}
DWORD CASC_CSV::Load(LPCTSTR szFileName)
{
DWORD dwErrCode;
dwErrCode = LoadFileToMemory(szFileName, CsvFile);
if(dwErrCode == ERROR_SUCCESS)
{
m_szCsvFile = (char *)CsvFile.pbData;
m_szCsvPtr = m_szCsvFile;
dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
return dwErrCode;
}
DWORD CASC_CSV::Load(LPBYTE pbData, size_t cbData)
{
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
if((dwErrCode = CsvFile.SetData(pbData, cbData)) == ERROR_SUCCESS)
{
m_szCsvFile = (char *)CsvFile.pbData;
m_szCsvFile[CsvFile.cbData] = 0;
m_szCsvPtr = m_szCsvFile;
dwErrCode = ParseCsvData() ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
}
return dwErrCode;
}
bool CASC_CSV::LoadNextLine()
{
bool bResult = false;
if(m_bHasAllLines == false)
{
assert(m_pLines != NULL);
assert(m_nLinesMax == 1);
bResult = LoadNextLine(m_pLines[0]);
m_nLines = (bResult) ? 1 : 0;
}
return bResult;
}
bool CASC_CSV::InitializeHashTable()
{
for(size_t i = 0; i < Header.GetColumnCount(); i++)
{
size_t nStartIndex = HashToIndex(CalcHashValue(Header[i].szValue));
size_t nHashIndex = nStartIndex;
while(HashTable[nHashIndex] != 0xFF)
{
nHashIndex = HashToIndex(nHashIndex + 1);
}
HashTable[nHashIndex] = (BYTE)i;
}
return true;
}
bool CASC_CSV::LoadNextLine(CASC_CSV_LINE & Line)
{
if(m_szCsvPtr == m_szCsvFile && m_szCsvPtr[0] == '#')
m_szCsvPtr++;
while(m_szCsvPtr != NULL && m_szCsvPtr[0] != 0)
{
char * szCsvLine = m_szCsvPtr;
m_szCsvPtr = PfnNextLine(m_pvUserData, m_szCsvPtr);
if(Line.SetLine(this, szCsvLine))
return true;
}
return false;
}
bool CASC_CSV::ParseCsvData()
{
assert(m_nLines == 0);
if(m_bHasHeader)
{
if(!LoadNextLine(Header))
return false;
if(!InitializeHashTable())
return false;
}
if(m_bHasAllLines)
{
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)
{
size_t nStartIndex = HashToIndex(CalcHashValue(szColumnName));
size_t nHashIndex = nStartIndex;
size_t nIndex;
while((nIndex = HashTable[nHashIndex]) != 0xFF)
{
if(!strcmp(Header[nIndex].szValue, szColumnName))
return nIndex;
nHashIndex = HashToIndex(nHashIndex + 1);
}
}
return CSV_INVALID_INDEX;
}