Important Update: (incomplete)

- Implemented JSON type NULL
- Implement dynamic buffer
- Split parsing int separate re-usable methods
- Implement basic parsing of an object
- Add GetError() method
- Add SkipWhiteSpace as inline method
- Make parsing variable private attributes
- Improve checking when getting parameters
This commit is contained in:
Charl Wentzel
2017-03-15 04:29:01 +02:00
parent 9b180c77f5
commit 508cf0fb0d
2 changed files with 404 additions and 166 deletions

View File

@@ -13,13 +13,26 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <ctype.h>
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
CConfigCore::CConfigCore() CConfigCore::CConfigCore()
{ {
// Parameter tree
FirstParam = NULL; FirstParam = NULL;
// File Operation
InputFile = NULL;
BufLen = 500;
Buffer = NULL;
BufEnd = NULL;
// Parsing operation
BufPos = NULL;
LineMark = NULL;
LineNo = 0;
Error = false;
ErrorText[0] = 0;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@@ -82,21 +95,23 @@ bool CConfigCore::DeleteAll()
bool CConfigCore::SetParam( TConfigParam * Param, EJSONtype Type, const char * Value, const int Len ) bool CConfigCore::SetParam( TConfigParam * Param, EJSONtype Type, const char * Value, const int Len )
{ {
// Clear previous value // Clear previous value
if (Param->Value != NULL) if (Param->Value != NULL) {
free( Param->Value ); free( Param->Value );
}
// Set type // Set type
Param->Type = Type; Param->Type = Type;
// Set new value // Set new value
if (Value == NULL) { if (Type == jtNull) {
Param->Len = 0; Param->Len = 0;
Param->Value = NULL; Param->Value = NULL;
} }
else { else {
Param->Len = (Len == -1)? strlen(Value) : Len; Param->Len = (Len == -1)? strlen(Value) : Len;
Param->Value = (char *)calloc( 1, sizeof(Param->Len+1) ); Param->Value = (char *)malloc( sizeof(Param->Len+1) );
memcpy( Param->Value, Value, Param->Len+1 ); memcpy( Param->Value, Value, Param->Len+1 );
Param->Value[Param->Len] = 0;
} }
return true; return true;
@@ -153,6 +168,17 @@ bool CConfigCore::SetParamBool( const char * Name, const bool Value )
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CConfigCore::SetParamNull( const char * Name )
{
TConfigParam * Param = NULL;
// Create new param if it doesn't exist
Param = FindCreateParam( Name );
SetParam( Param, jtNull, NULL, -1 );
return false;
}
//---------------------------------------------------------------------------
const EJSONtype CConfigCore::GetParamType( const char * Name ) const EJSONtype CConfigCore::GetParamType( const char * Name )
{ {
TConfigParam * Param; TConfigParam * Param;
@@ -161,7 +187,7 @@ const EJSONtype CConfigCore::GetParamType( const char * Name )
return Param->Type; return Param->Type;
} }
else { else {
return jtNone; return jtNull;
} }
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@@ -170,7 +196,7 @@ const char * CConfigCore::GetParamStr( const char * Name, const char * Default )
{ {
TConfigParam * Param; TConfigParam * Param;
if ((Param = GetParam( Name ))) { if ((Param = GetParam( Name )) && (Param->Type == jtString)) {
return Param->Value; return Param->Value;
} }
else { else {
@@ -183,7 +209,7 @@ const char * CConfigCore::GetParamStr( const char * Name, int &Len, const char *
{ {
TConfigParam * Param; TConfigParam * Param;
if ((Param = GetParam( Name ))) { if ((Param = GetParam( Name )) && (Param->Type == jtString)) {
Len = Param->Len; Len = Param->Len;
return Param->Value; return Param->Value;
} }
@@ -198,7 +224,7 @@ const long CConfigCore::GetParamInt( const char * Name, long Default )
{ {
TConfigParam * Param; TConfigParam * Param;
if ((Param = GetParam( Name ))) { if ((Param = GetParam( Name )) && (Param->Type == jtInt)) {
return strtol( Param->Value, NULL, 10 ); return strtol( Param->Value, NULL, 10 );
} }
else { else {
@@ -211,7 +237,7 @@ const double CConfigCore::GetParamFloat( const char * Name, double Default )
{ {
TConfigParam * Param; TConfigParam * Param;
if ((Param = GetParam( Name ))) { if ((Param = GetParam( Name )) && (Param->Type == jtFloat)) {
return strtod( Param->Value, NULL ); return strtod( Param->Value, NULL );
} }
else { else {
@@ -224,7 +250,7 @@ const bool CConfigCore::GetParamBool( const char * Name, bool Default )
{ {
TConfigParam * Param; TConfigParam * Param;
if ((Param = GetParam( Name ))) { if ((Param = GetParam( Name )) && (Param->Type == jtBool)) {
return ((!strcasecmp( Param->Value, "true" ))? true : false ); return ((!strcasecmp( Param->Value, "true" ))? true : false );
} }
else { else {
@@ -233,21 +259,9 @@ const bool CConfigCore::GetParamBool( const char * Name, bool Default )
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CConfigCore::LoadFile( const char * FilePath ) bool CConfigCore::LoadFile( const char * FilePath, int pBufLen )
{ {
FILE * InputFile = NULL; TConfigParam * Object = NULL;
char InputStr[500];
char * StrPos;
char * Mark = NULL;
char * EndMark = NULL;
char * LineMark = NULL;
int LineNo = 1;
char ParamName[50] = "";
long IntVal = 0;
double FloatVal = 0.0;
bool Error = false;
char ErrorText[50] = "";
int BufCount = 0;
// Validate // Validate
if (!FilePath || !FilePath[0]) if (!FilePath || !FilePath[0])
@@ -257,167 +271,343 @@ bool CConfigCore::LoadFile( const char * FilePath )
if (!(InputFile = fopen( FilePath, "r" ))) if (!(InputFile = fopen( FilePath, "r" )))
return false; return false;
while (!Error && (BufCount = fread( InputStr, 1, 500, InputFile ))) // Load Buffer
CreateBuffer( pBufLen );
if (!FillBuffer()) {
Error = true;
sprintf( ErrorText, "Could not read from file" );
FreeBuffer();
return false;
}
// Reset values
LineNo = 1;
Error = false;
// Create Root object
DeleteAll();
Object = FindCreateParam( "root" );
// Parse Root Object
SkipWhiteSpace();
if (!ParseObject( Object )) {
if (!Error) {
Error = true;
sprintf( ErrorText, "First entry in file must be an Object on line %d:%ld", LineNo, BufPos-LineMark );
}
FreeBuffer();
return false;
}
// Ensure remainder of file is empty
SkipWhiteSpace();
if (*BufPos != 0) {
Error = true;
sprintf( ErrorText, "No content expected after Root object on line %d:%ld", LineNo, BufPos-LineMark );
FreeBuffer();
return false;
}
// Success
FreeBuffer();
return true;
}
//---------------------------------------------------------------------------
bool CConfigCore::CreateBuffer( int pBufLen )
{
// Validate
if (!pBufLen)
return false;
// Create buffer
BufLen = pBufLen;
Buffer = (char*)malloc( BufLen+1 );
// Reset markers
BufEnd = Buffer;
BufPos = Buffer;
LineMark = Buffer;
return true;
}
//---------------------------------------------------------------------------
bool CConfigCore::FillBuffer()
{
int BufCount = 0;
// Read from file
BufCount = fread( Buffer, 1, 500, InputFile );
if (BufCount < 1) {
return false;
}
// Update Markers
BufEnd += BufCount;
BufPos = Buffer;
LineMark = Buffer;
return true;
}
//---------------------------------------------------------------------------
void CConfigCore::FreeBuffer()
{
// Destroy buffer
if (Buffer) {
free( Buffer );
Buffer = NULL;
}
BufLen = 0;
}
//---------------------------------------------------------------------------
bool CConfigCore::ParseObject( TConfigParam * Object )
{
TConfigParam * Param = NULL;
char * ParamName = NULL;
int Len = 0;
// Clear Values
Error = false;
// Check for start of Object
if (*BufPos != '{') {
return false;
}
BufPos++;
while (true)
{ {
// Read Param Name // Look for Param Name
StrPos = InputStr; SkipWhiteSpace();
LineMark = StrPos-1; if (*BufPos == 0) {
Error = true;
// Process string sprintf( ErrorText, "Expect quoted key name on line %d:%ld", LineNo, BufPos-LineMark );
while (!Error && (StrPos < InputStr + BufCount)) }
{ else if (!ParseString( &ParamName, &Len )) {
// Skip white space if (!Error) {
while (isspace(*StrPos)) {
if (*StrPos == '\n') {
LineMark = StrPos;
LineNo++;
}
StrPos++;
}
if (*StrPos == 0) {
break;
}
// Check for opening quote
if (*StrPos != '"') {
Error = true; Error = true;
sprintf( ErrorText, "Expect opening '\"' for param name on line %d [%ld]", LineNo, StrPos-LineMark ); sprintf( ErrorText, "Expect quoted key name on line %d:%ld", LineNo, BufPos-LineMark );
break;
} }
return false;
}
else if (!Len) {
Error = true;
sprintf( ErrorText, "Empty parameter name on line %d:%ld", LineNo, BufPos-LineMark );
return false;
}
// Mark start of param name // Create parameter
StrPos++; Param = FindCreateParam( ParamName );
Mark = StrPos;
// Check for closing quote // Check for delimiter
if (!(StrPos = strchr( StrPos, '"' ))) { SkipWhiteSpace();
Error = true; if (*BufPos != ':') {
sprintf( ErrorText, "Expect closing '\"' for param name on line %d [%ld]", LineNo, Mark-LineMark ); Error = true;
break; sprintf( ErrorText, "Expected ':' delimiter on line %d:%ld", LineNo, BufPos-LineMark );
} return false;
}
BufPos++;
// Validate size of Param name // Get Value
if (StrPos - Mark == 0) { SkipWhiteSpace();
Error = true; if (!ParseObject( Param ) &&
sprintf( ErrorText, "Empty parameter name on line %d [%ld]", LineNo, StrPos-LineMark ); !Error &&
break; !ParseString( &Param->Value, &Param->Len, &Param->Type ) &&
} !Error &&
!ParsePrimitive( &Param->Value, &Param->Len, &Param->Type )) {
return false;
}
// Copy Parameter name // Check if more parameters to follow
memcpy( ParamName, Mark, (StrPos-Mark) ); SkipWhiteSpace();
ParamName[(StrPos-Mark)] = 0; if (*BufPos != ',') {
StrPos++; // No more parameters
break;
}
BufPos++;
}
// Skip white space // Expect end of object
while (isspace(*StrPos)) { if (*BufPos != '}') {
if (*StrPos == '\n') { Error = true;
LineMark = StrPos; sprintf( ErrorText, "Closing brace for object '}' expected on line %d:%ld", LineNo, BufPos-LineMark );
LineNo++; return false;
} }
StrPos++; BufPos++;
}
// Check for delimiter // success
if (*StrPos != ':') { return true;
Error = true; }
sprintf( ErrorText, "Expected ':' delimiter on line %d [%ld]", LineNo, StrPos-LineMark ); //---------------------------------------------------------------------------
break;
}
StrPos++;
// Skip white space bool CConfigCore::ParseString( char ** Value, int *pLen, EJSONtype *pType )
while (isspace(*StrPos)) { {
if (*StrPos == '\n') { char * Mark;
LineMark = StrPos; char * EndMark;
LineNo++; int Len;
}
StrPos++;
}
// Mark start value // Clear value
Mark = StrPos; Error = false;
*Value = NULL;
Len = 0;
// Get Value // Check for opening quote
if (*StrPos == '"') if (*BufPos != '"') {
{ return false;
// Quoted values }
// Check for closing quote
StrPos++;
if (!(StrPos = strchr( StrPos, '"' ))) {
Error = true;
sprintf( ErrorText, "Expect closing '\"' for param value on line %d [%ld]", LineNo, Mark-LineMark );
break;
}
// Create new string Parameter // Mark start of quote
SetParamStr( ParamName, Mark+1, StrPos-Mark-2 ); Mark = BufPos;
StrPos++; BufPos++;
}
else
{
// Primitive Values
// Get end of value
while ((*StrPos != 0) && !isspace(*StrPos) && (*StrPos != ',')) {
StrPos++;
}
// Analyze value // Check for closing quote
if (StrPos - Mark == 0) { while ((BufPos = strpbrk( BufPos, "\"/\\\n\t\b\f\n\r" )))
Error = true; {
sprintf( ErrorText, "Missing param value on line %d [%ld]", LineNo, Mark-LineMark ); if (*BufPos == '"') {
break; // End of string found
} BufPos++;
else if ((StrPos-Mark == 4) && !strncasecmp( Mark, "true", 4 )) { break;
// Create new boolean Parameter }
SetParamBool( ParamName, true ); else if (!*BufPos) {
} Error = true;
else if ((StrPos-Mark == 5) && !strncasecmp( Mark, "false", 4 )) { sprintf( ErrorText, "Expect closing '\"' for string on line %d:%ld", LineNo, Mark-LineMark );
// Create new boolean Parameter return false;
SetParamBool( ParamName, false ); }
} else if (*BufPos == '\\') {
else { if (*(BufPos+1) == 'u') {
// Try conversion to int for (EndMark = BufPos+2; EndMark < BufPos+6; EndMark++) {
IntVal = strtol( Mark, &EndMark, 10 ); if (!isxdigit( *EndMark )) {
if (EndMark == StrPos) { Error = true;
// Create new integer Parameter sprintf( ErrorText, "Expect 4-digit hex value for escape value on line %d:%ld", LineNo, Mark-LineMark );
SetParamInt( ParamName, IntVal ); return false;
}
else {
// Try conversion to float
FloatVal = strtod( Mark, &EndMark );
if (EndMark == StrPos) {
// Create new float Parameter
SetParamFloat( ParamName, FloatVal );
}
else {
Error = true;
sprintf( ErrorText, "Invalid primitive param value on line %d [%ld]", LineNo, Mark-LineMark );
break;
}
} }
} }
BufPos += 6;
} }
else if (strchr( "bfnrt/\\\"", *(BufPos+1) )) {
// Skip white space BufPos += 2;
while (isspace(*StrPos)) {
if (*StrPos == '\n') {
LineMark = StrPos;
LineNo++;
}
StrPos++;
} }
else {
// Check if more parameters to follow
if ((*StrPos != ',') && (*StrPos != 0)) {
Error = true; Error = true;
sprintf( ErrorText, "Parameter separator ',' expected on line %d [%ld]", LineNo, StrPos-LineMark ); sprintf( ErrorText, "Invalid escape sequence on line %d:%ld", LineNo, Mark-LineMark );
break; return false;
} }
StrPos++; }
else {
Error = true;
sprintf( ErrorText, "Un-escaped special character in string on line %d:%ld", LineNo, BufPos-LineMark );
return false;
} }
} }
return ((Error)? false : true); // Validate size of Param name
Len = BufPos-Mark-2;
// Create Return value pointer
*Value = (char*)malloc( Len+1 );
memcpy( *Value, Mark+1, Len );
(*Value)[Len] = 0;
// Set other parameters
if (pType)
*pType = jtString;
if (pLen)
*pLen = Len;
// Success
return true;
}
//---------------------------------------------------------------------------
bool CConfigCore::ParsePrimitive( char ** Value, int * pLen, EJSONtype * pType )
{
char * Mark;
char * EndMark;
int Len;
// Clear value
Error = false;
*Value = NULL;
Len = 0;
// Mark start of value
Mark = BufPos;
// Get end of value
while ((*BufPos != 0) && !isspace(*BufPos) && (*BufPos != ',')) {
BufPos++;
}
// Check length of value
Len = BufPos - Mark;
if (!Len) {
Error = true;
sprintf( ErrorText, "Missing param value on line %d:%ld", LineNo, Mark-LineMark );
return false;
}
// Check for primitive values
if ((Len == 4) && !strncasecmp( Mark, "null", 4 )) {
if (pType)
*pType = jtNull;
if (pLen)
*pLen = Len;
*Value = (char*)malloc( 5 );
strcpy( *Value, "null" );
}
else if ((Len == 4) && !strncasecmp( Mark, "true", 4 )) {
if (pType)
*pType = jtBool;
if (pLen)
*pLen = Len;
*Value = (char*)malloc( 5 );
strcpy( *Value, "true" );
}
else if ((Len == 5) && !strncasecmp( Mark, "false", 5 )) {
if (pType)
*pType = jtBool;
if (pLen)
*pLen = Len;
*Value = (char*)malloc( 6 );
strcpy( *Value, "false" );
}
else {
// Try conversion to int
strtol( Mark, &EndMark, 10 );
if (EndMark == BufPos) {
if (pType)
*pType = jtInt;
if (pLen)
*pLen = Len;
*Value = (char*)malloc( Len+1 );
memcpy( *Value, Mark, Len );
*Value[Len] = 0;
}
else {
// Try conversion to float
strtod( Mark, &EndMark );
if (EndMark == BufPos) {
if (pType)
*pType = jtFloat;
if (pLen)
*pLen = Len;
*Value = (char*)malloc( Len+1 );
memcpy( *Value, Mark, Len );
*Value[Len] = 0;
}
else {
Error = true;
sprintf( ErrorText, "Invalid primitive param value on line %d:%ld", LineNo, Mark-LineMark );
return false;
}
}
}
// Success
return true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@@ -452,6 +642,10 @@ bool CConfigCore::SaveFile( const char * FilePath, const int ValueTab )
switch (Param->Type) switch (Param->Type)
{ {
case jtNull :
fprintf( OutputFile, "%s,\n", "null" );
break;
case jtBool : case jtBool :
case jtInt : case jtInt :
case jtFloat : case jtFloat :
@@ -462,7 +656,12 @@ bool CConfigCore::SaveFile( const char * FilePath, const int ValueTab )
fprintf( OutputFile, "\"%s\",\n", Param->Value ); fprintf( OutputFile, "\"%s\",\n", Param->Value );
break; break;
default : case jtArray :
fprintf( OutputFile, "[],\n" );
break;
case jtObject :
fprintf( OutputFile, "{},\n" );
break; break;
} }
} }

View File

@@ -14,10 +14,12 @@
// Standard C/C++ Libraries // Standard C/C++ Libraries
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <stdio.h>
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
typedef enum { jtNone = 0, jtBool = 1, jtInt = 2, jtFloat = 3, jtString = 4, jtArray = 5, jtObject = 6 } EJSONtype; typedef enum { jtNull = 0, jtBool = 1, jtInt = 2, jtFloat = 3, jtString = 4, jtArray = 5, jtObject = 6 } EJSONtype;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@@ -41,6 +43,40 @@ class CConfigCore
private: private:
TConfigParam * FirstParam; TConfigParam * FirstParam;
// File operation
FILE * InputFile;
char * Buffer;
int BufLen;
// Parsing operation
char * BufEnd;
char * BufPos;
char * LineMark;
int LineNo;
bool Error;
char ErrorText[100];
// File Buffer operation
bool CreateBuffer( int pBufLen );
bool FillBuffer();
void FreeBuffer();
// Parsing functions
inline void SkipWhiteSpace() {
while (isspace(*BufPos)) {
if (*BufPos == '\n') {
LineMark = BufPos;
LineNo++;
}
BufPos++;
}
}
bool ParseObject( TConfigParam * Object );
bool ParseString( char ** Value, int * pLen = NULL, EJSONtype * pType = NULL );
bool ParsePrimitive( char ** Value, int * pLen = NULL, EJSONtype * pType = NULL );
// Find Param // Find Param
inline TConfigParam * GetParam( const char * Name ) { inline TConfigParam * GetParam( const char * Name ) {
TConfigParam * Param = FirstParam; TConfigParam * Param = FirstParam;
@@ -74,10 +110,11 @@ public:
CConfigCore(); CConfigCore();
~CConfigCore(); ~CConfigCore();
bool SetParamStr( const char * Name, const char * Value = NULL, const int Len = -1 ); bool SetParamStr( const char * Name, const char * Value = NULL, const int Len = -1 ); // Use Len param if Value contains NULL values
bool SetParamInt( const char * Name, const long Value ); bool SetParamInt( const char * Name, const long Value );
bool SetParamFloat( const char * Name, const double Value ); bool SetParamFloat( const char * Name, const double Value );
bool SetParamBool( const char * Name, const bool Value ); bool SetParamBool( const char * Name, const bool Value );
bool SetParamNull( const char * Name );
const EJSONtype GetParamType( const char * Name ); const EJSONtype GetParamType( const char * Name );
@@ -90,8 +127,10 @@ public:
bool DeleteParam( const char * Name ); bool DeleteParam( const char * Name );
bool DeleteAll(); bool DeleteAll();
bool LoadFile( const char * FilePath ); bool LoadFile( const char * FilePath, int pBufLen = 500 );
bool SaveFile( const char * FilePath, const int ValueTab = 20 ); bool SaveFile( const char * FilePath, const int ValueTab = 20 );
const char * GetError() { return ((Error)? ErrorText : "Success"); };
}; };
#endif /* REDACORE_CONFIGCORE_H_ */ #endif /* REDACORE_CONFIGCORE_H_ */