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 )
{ {
// Read Param Name // Validate
StrPos = InputStr; if (!pBufLen)
LineMark = StrPos-1; return false;
// Process string // Create buffer
while (!Error && (StrPos < InputStr + BufCount)) BufLen = pBufLen;
Buffer = (char*)malloc( BufLen+1 );
// Reset markers
BufEnd = Buffer;
BufPos = Buffer;
LineMark = Buffer;
return true;
}
//---------------------------------------------------------------------------
bool CConfigCore::FillBuffer()
{ {
// Skip white space int BufCount = 0;
while (isspace(*StrPos)) {
if (*StrPos == '\n') { // Read from file
LineMark = StrPos; BufCount = fread( Buffer, 1, 500, InputFile );
LineNo++; if (BufCount < 1) {
} return false;
StrPos++;
}
if (*StrPos == 0) {
break;
} }
// Check for opening quote // Update Markers
if (*StrPos != '"') { 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)
{
// Look for Param Name
SkipWhiteSpace();
if (*BufPos == 0) {
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;
} }
else if (!ParseString( &ParamName, &Len )) {
// Mark start of param name if (!Error) {
StrPos++;
Mark = StrPos;
// Check for closing quote
if (!(StrPos = strchr( StrPos, '"' ))) {
Error = true; Error = true;
sprintf( ErrorText, "Expect closing '\"' for param name on line %d [%ld]", LineNo, Mark-LineMark ); sprintf( ErrorText, "Expect quoted key name on line %d:%ld", LineNo, BufPos-LineMark );
break;
} }
return false;
// Validate size of Param name }
if (StrPos - Mark == 0) { else if (!Len) {
Error = true; Error = true;
sprintf( ErrorText, "Empty parameter name on line %d [%ld]", LineNo, StrPos-LineMark ); sprintf( ErrorText, "Empty parameter name on line %d:%ld", LineNo, BufPos-LineMark );
break; return false;
} }
// Copy Parameter name // Create parameter
memcpy( ParamName, Mark, (StrPos-Mark) ); Param = FindCreateParam( ParamName );
ParamName[(StrPos-Mark)] = 0;
StrPos++;
// Skip white space
while (isspace(*StrPos)) {
if (*StrPos == '\n') {
LineMark = StrPos;
LineNo++;
}
StrPos++;
}
// Check for delimiter // Check for delimiter
if (*StrPos != ':') { SkipWhiteSpace();
if (*BufPos != ':') {
Error = true; Error = true;
sprintf( ErrorText, "Expected ':' delimiter on line %d [%ld]", LineNo, StrPos-LineMark ); sprintf( ErrorText, "Expected ':' delimiter on line %d:%ld", LineNo, BufPos-LineMark );
break; return false;
} }
StrPos++; BufPos++;
// Skip white space
while (isspace(*StrPos)) {
if (*StrPos == '\n') {
LineMark = StrPos;
LineNo++;
}
StrPos++;
}
// Mark start value
Mark = StrPos;
// Get Value // Get Value
if (*StrPos == '"') SkipWhiteSpace();
{ if (!ParseObject( Param ) &&
// Quoted values !Error &&
// Check for closing quote !ParseString( &Param->Value, &Param->Len, &Param->Type ) &&
StrPos++; !Error &&
if (!(StrPos = strchr( StrPos, '"' ))) { !ParsePrimitive( &Param->Value, &Param->Len, &Param->Type )) {
Error = true; return false;
sprintf( ErrorText, "Expect closing '\"' for param value on line %d [%ld]", LineNo, Mark-LineMark );
break;
}
// Create new string Parameter
SetParamStr( ParamName, Mark+1, StrPos-Mark-2 );
StrPos++;
}
else
{
// Primitive Values
// Get end of value
while ((*StrPos != 0) && !isspace(*StrPos) && (*StrPos != ',')) {
StrPos++;
}
// Analyze value
if (StrPos - Mark == 0) {
Error = true;
sprintf( ErrorText, "Missing param value on line %d [%ld]", LineNo, Mark-LineMark );
break;
}
else if ((StrPos-Mark == 4) && !strncasecmp( Mark, "true", 4 )) {
// Create new boolean Parameter
SetParamBool( ParamName, true );
}
else if ((StrPos-Mark == 5) && !strncasecmp( Mark, "false", 4 )) {
// Create new boolean Parameter
SetParamBool( ParamName, false );
}
else {
// Try conversion to int
IntVal = strtol( Mark, &EndMark, 10 );
if (EndMark == StrPos) {
// Create new integer Parameter
SetParamInt( ParamName, IntVal );
}
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;
}
}
}
}
// Skip white space
while (isspace(*StrPos)) {
if (*StrPos == '\n') {
LineMark = StrPos;
LineNo++;
}
StrPos++;
} }
// Check if more parameters to follow // Check if more parameters to follow
if ((*StrPos != ',') && (*StrPos != 0)) { SkipWhiteSpace();
Error = true; if (*BufPos != ',') {
sprintf( ErrorText, "Parameter separator ',' expected on line %d [%ld]", LineNo, StrPos-LineMark ); // No more parameters
break; break;
} }
StrPos++; BufPos++;
}
// Expect end of object
if (*BufPos != '}') {
Error = true;
sprintf( ErrorText, "Closing brace for object '}' expected on line %d:%ld", LineNo, BufPos-LineMark );
return false;
}
BufPos++;
// success
return true;
}
//---------------------------------------------------------------------------
bool CConfigCore::ParseString( char ** Value, int *pLen, EJSONtype *pType )
{
char * Mark;
char * EndMark;
int Len;
// Clear value
Error = false;
*Value = NULL;
Len = 0;
// Check for opening quote
if (*BufPos != '"') {
return false;
}
// Mark start of quote
Mark = BufPos;
BufPos++;
// Check for closing quote
while ((BufPos = strpbrk( BufPos, "\"/\\\n\t\b\f\n\r" )))
{
if (*BufPos == '"') {
// End of string found
BufPos++;
break;
}
else if (!*BufPos) {
Error = true;
sprintf( ErrorText, "Expect closing '\"' for string on line %d:%ld", LineNo, Mark-LineMark );
return false;
}
else if (*BufPos == '\\') {
if (*(BufPos+1) == 'u') {
for (EndMark = BufPos+2; EndMark < BufPos+6; EndMark++) {
if (!isxdigit( *EndMark )) {
Error = true;
sprintf( ErrorText, "Expect 4-digit hex value for escape value on line %d:%ld", LineNo, Mark-LineMark );
return false;
}
}
BufPos += 6;
}
else if (strchr( "bfnrt/\\\"", *(BufPos+1) )) {
BufPos += 2;
}
else {
Error = true;
sprintf( ErrorText, "Invalid escape sequence on line %d:%ld", LineNo, Mark-LineMark );
return false;
}
}
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_ */