From 2e2ba113f15326a3fefc064264cec3580ed6118a Mon Sep 17 00:00:00 2001 From: Charl Wentzel Date: Sun, 2 Apr 2017 19:44:44 +0200 Subject: [PATCH] Major Update: - Update printing: Do not use '\n' if indent = 0 - Implement refilling of buffer (from fd) while parsing - No longer use LineMark, but keep track of char position with CharNo - Use Mark to reference point for shifting buffer on refill - SkipWhiteSpace no longer inline method - Refill buffer if required, change Mark & CharNo on line break - CreateBuffer(), RefillBuffer() & FreeBuffer() now public methods - Make Loading and Saving more flexible: - Refactor from LoadFromFile(): ReadFromFile(), ReadFromHandle(), ReadFromBuffer() - Refactor from SaveToFile() & PrintToScreen: WriteToFile(), WriteToScreen, WriteToHandle() --- BufferCore.cpp | 4 +- JSONparseCore.cpp | 367 +++++++++++++++++++++++++++++++--------------- JSONparseCore.h | 38 ++--- 3 files changed, 268 insertions(+), 141 deletions(-) diff --git a/BufferCore.cpp b/BufferCore.cpp index 827264a..723eee4 100644 --- a/BufferCore.cpp +++ b/BufferCore.cpp @@ -688,16 +688,16 @@ int CShiftBuffer::Clear( int ClearLen ) if ((ClearLen == -1) || (ClearLen >= BufLen)) { // Clear all data - BytesCleared = BufLen; BufLen = 0; Buffer[0] = 0; + BytesCleared = BufLen; } else { // Shift remaining data - BytesCleared = ClearLen; memmove( Buffer, &Buffer[ClearLen], BufLen-ClearLen ); // Shift remaining old data left BufLen -= ClearLen; Buffer[BufLen] = 0; + BytesCleared = ClearLen; } return BytesCleared; diff --git a/JSONparseCore.cpp b/JSONparseCore.cpp index 2122044..e499124 100644 --- a/JSONparseCore.cpp +++ b/JSONparseCore.cpp @@ -27,12 +27,13 @@ CJSONparse::CJSONparse( CDataTree * pDataTree ) OutputHandle = -1; Buffer = NULL; - BufEnd = NULL; // Parsing operation BufPos = NULL; - LineMark = NULL; + Mark = NULL; LineNo = 0; + CharNo = 0; + RefillBuffer = false; // Printing operation Spacer[0] = 0; @@ -53,53 +54,31 @@ CJSONparse::~CJSONparse() } //--------------------------------------------------------------------------- -bool CJSONparse::PrintToScreen( const char * RootPath, const int Indent ) +bool CJSONparse::WriteToScreen( const char * RootPath, const int Indent ) { - TDataMember * RootObject; - - // Validate - if (!DataTree) { - return false; - } - - // Set to StdOut - OutputHandle = 1; - - // Get Root object - if (!RootPath || !*RootPath) { - RootObject = DataTree->GetRootMember(); - } - else if (!(RootObject = DataTree->GetMember( NULL, RootPath ))) { - Error = true; - sprintf( ErrorText, "Invalid root object path" ); - return false; - } - - // Print key name - if (RootObject->Name) { - write( OutputHandle, "\"", 1 ); - write( OutputHandle, RootObject->Name, strlen(RootObject->Name) ); - write( OutputHandle, "\" : ", 4 ); - } - // Print to screen - PrintObject( RootObject, Indent ); + WriteToHandle( RootPath, 1, Indent ); - // Close file - close( OutputHandle ); - OutputHandle = -1; return true; } //--------------------------------------------------------------------------- -bool CJSONparse::SaveToFile( const char * RootPath, const char * FilePath, const int Indent ) +bool CJSONparse::WriteToFile( const char * RootPath, const char * Path, const char * FileName, const int Indent ) { - TDataMember * RootObject; + char FilePath[250] = ""; - // Validate - if (!DataTree) { - return false; - } + // Build file name + strcpy( FilePath, Path ); + strcat( FilePath, FileName ); + + // Read file + return WriteToFile( RootPath, FilePath, Indent ); +} +//--------------------------------------------------------------------------- + +bool CJSONparse::WriteToFile( const char * RootPath, const char * FilePath, const int Indent ) +{ + int Handle = -1; // Clear Error Error = false; @@ -112,12 +91,38 @@ bool CJSONparse::SaveToFile( const char * RootPath, const char * FilePath, const } // Open file - if (!(OutputHandle = open( FilePath, O_CREAT|O_WRONLY|O_TRUNC ))) { + if ((Handle = open( FilePath, O_CREAT|O_WRONLY|O_TRUNC )) < 0) { Error = true; sprintf( ErrorText, "Could not open file" ); return false; } + // Save to file + WriteToHandle( RootPath, Handle, Indent ); + + // Close file + close( Handle ); + return true; +} +//--------------------------------------------------------------------------- + +bool CJSONparse::WriteToHandle( const char * RootPath, const int Handle, const int Indent ) +{ + TDataMember * RootObject; + + // Validate + if (!DataTree) { + return false; + } + + // Open file + if (Handle < 0) { + Error = true; + sprintf( ErrorText, "Could not write to invalid handle" ); + return false; + } + OutputHandle = Handle; + // Get Root object if (!RootPath || !*RootPath) { RootObject = DataTree->GetRootMember(); @@ -130,17 +135,29 @@ bool CJSONparse::SaveToFile( const char * RootPath, const char * FilePath, const // Print to file PrintObject( RootObject, Indent ); + write( OutputHandle, "\n", 1 ); - // Close file - close( OutputHandle ); OutputHandle = -1; return true; } //--------------------------------------------------------------------------- -bool CJSONparse::LoadFromFile( const char * RootPath, const char * FilePath, int pBufLen ) +bool CJSONparse::ReadFromFile( const char * RootPath, const char * Path, const char * FileName ) { - TDataMember * RootObject = NULL; + char FilePath[250] = ""; + + // Build file name + strcpy( FilePath, Path ); + strcat( FilePath, FileName ); + + // Read file + return ReadFromFile( RootPath, FilePath ); +} +//--------------------------------------------------------------------------- + +bool CJSONparse::ReadFromFile( const char * RootPath, const char * FilePath ) +{ + int Handle = -1; // Validate if (!DataTree) { @@ -158,14 +175,36 @@ bool CJSONparse::LoadFromFile( const char * RootPath, const char * FilePath, int } // Open file - if (!(InputHandle = open( FilePath, O_RDONLY ))) { + if ((Handle = open( FilePath, O_RDONLY )) < 0) { Error = true; sprintf( ErrorText, "Could not open file" ); return false; } + // Continuously refill buffer while loading + ReadFromHandle( RootPath, Handle, true ); + + // Close File + close( Handle ); + return true; +} +//--------------------------------------------------------------------------- + +bool CJSONparse::ReadFromHandle( const char * RootPath, int Handle, bool pRefillBuffer ) +{ + // Clear Error + Error = false; + + // Validate + if (Handle < 0) { + Error = true; + sprintf( ErrorText, "Cannot read from invalid handle" ); + return false; + } + InputHandle = Handle; + // Load Buffer - CreateBuffer( pBufLen ); + CreateBuffer( 50 ); if (!FillBuffer()) { Error = true; sprintf( ErrorText, "Could not read from file" ); @@ -173,6 +212,31 @@ bool CJSONparse::LoadFromFile( const char * RootPath, const char * FilePath, int return false; } + // Continuously refill buffer while loading + RefillBuffer = pRefillBuffer; + ReadFromBuffer( RootPath ); + RefillBuffer = false; + + // Destroy buffer + FreeBuffer(); + + InputHandle = -1; + return true; +} +//--------------------------------------------------------------------------- + +bool CJSONparse::ReadFromBuffer( const char * RootPath ) +{ + TDataMember * RootObject = NULL; + + // Validate + if (!DataTree || !Buffer) { + return false; + } + + // Clear Error + Error = false; + // Get/Create Root object if (!RootPath || !*RootPath) { RootObject = DataTree->GetRootMember(); @@ -183,16 +247,20 @@ bool CJSONparse::LoadFromFile( const char * RootPath, const char * FilePath, int return false; } - // Reset values - LineNo = 1; + // Delete existing object contents DataTree->Delete( RootObject, NULL ); + // Position Counters + LineNo = 1; + CharNo = 0; + // Parse Root Object SkipWhiteSpace(); if (!ParseObject( RootObject )) { if (!Error) { Error = true; - sprintf( ErrorText, "First entry in file must be an Object on line %d:%ld", LineNo, BufPos-LineMark ); + CharNo += BufPos-Mark; + sprintf( ErrorText, "First entry in file must be an Object on line %d:%d", LineNo, CharNo ); } FreeBuffer(); return false; @@ -202,15 +270,13 @@ bool CJSONparse::LoadFromFile( const char * RootPath, const char * FilePath, int SkipWhiteSpace(); if (*BufPos != 0) { Error = true; - sprintf( ErrorText, "No content expected after Root object on line %d:%ld", LineNo, BufPos-LineMark ); + CharNo += BufPos-Mark; + sprintf( ErrorText, "No content expected after Root object on line %d:%d", LineNo, CharNo ); FreeBuffer(); return false; } // Success - close( InputHandle ); - InputHandle = -1; - FreeBuffer(); return true; } //--------------------------------------------------------------------------- @@ -226,8 +292,7 @@ bool CJSONparse::CreateBuffer( int pBufLen ) // Reset markers Buffer->PeekDirect( &BufPos, 0 ); - BufEnd = BufPos; - LineMark = BufPos; + Mark = BufPos; return true; } @@ -235,13 +300,19 @@ bool CJSONparse::CreateBuffer( int pBufLen ) bool CJSONparse::FillBuffer() { + char * BufStart; + int PosShift = BufPos - Mark; + + // Remove all bytes up to Mark + Buffer->PeekDirect( &BufStart, 0 ); + Buffer->Clear( Mark-BufStart ); + // Read from file Buffer->ReadFromFD( InputHandle ); - // Update Markers - Buffer->PeekDirect( &BufEnd, Buffer->Len() ); - Buffer->PeekDirect( &BufPos, 0 ); - LineMark = BufPos; + // Reset Mark to start of buffer + Mark = BufStart; + BufPos = Mark + PosShift; return true; } @@ -255,18 +326,40 @@ void CJSONparse::FreeBuffer() Buffer = NULL; // Update Markers - BufEnd = NULL; BufPos = NULL; - LineMark = NULL; + Mark = NULL; } } //--------------------------------------------------------------------------- -bool CJSONparse::ParseString( char ** Value, int &pLen ) +void CJSONparse::SkipWhiteSpace() +{ + while (true) + { + // Append buffer if required + if (!*BufPos && RefillBuffer) + FillBuffer(); + + // Skip whitespace + if (!isspace(*BufPos)) { + CharNo += BufPos-Mark; + Mark = BufPos; + break; + } + + if (*BufPos == '\n') { + Mark = BufPos; + LineNo++; + CharNo = 0; + } + BufPos++; + } +} +//--------------------------------------------------------------------------- + +bool CJSONparse::ParseString( char ** Value, int &Len ) { - char * Mark; char * EndMark; - int Len; // Check for opening quote if (*BufPos != '"') { @@ -278,12 +371,19 @@ bool CJSONparse::ParseString( char ** Value, int &pLen ) Len = 0; // Mark start of quote - Mark = BufPos; BufPos++; // Check for closing quote - while ((BufPos = strpbrk( BufPos, "\"/\\\n\t\b\f\n\r" ))) + while (true) { + // Check for special characters + if ((EndMark = strpbrk( BufPos, "\"/\\\n\t\b\f\n\r" ))) { + BufPos = EndMark; + } else if (RefillBuffer) { + FillBuffer(); + continue; + } + if (*BufPos == '"') { // End of string found BufPos++; @@ -291,15 +391,25 @@ bool CJSONparse::ParseString( char ** Value, int &pLen ) } else if (!*BufPos) { Error = true; - sprintf( ErrorText, "Expect closing '\"' for string on line %d:%ld", LineNo, Mark-LineMark ); + sprintf( ErrorText, "Expect closing '\"' for string on line %d:%d", LineNo, CharNo+1 ); return false; } else if (*BufPos == '\\') { + if (!*(BufPos+1) && RefillBuffer) { + if (FillBuffer()) { + continue; + } + } if (*(BufPos+1) == 'u') { for (EndMark = BufPos+2; EndMark < BufPos+6; EndMark++) { + if (!*BufPos && RefillBuffer) { + FillBuffer(); + continue; + } if (!isxdigit( *EndMark )) { Error = true; - sprintf( ErrorText, "Expect 4-digit hex value for escape value on line %d:%ld", LineNo, Mark-LineMark ); + CharNo += BufPos-EndMark; + sprintf( ErrorText, "Expect 4-digit hex value for escape value on line %d:%d", LineNo, CharNo ); return false; } } @@ -310,18 +420,20 @@ bool CJSONparse::ParseString( char ** Value, int &pLen ) } else { Error = true; - sprintf( ErrorText, "Invalid escape sequence on line %d:%ld", LineNo, Mark-LineMark ); + CharNo += BufPos-Mark+1; + sprintf( ErrorText, "Invalid escape sequence on line %d:%d", LineNo, CharNo ); return false; } } else { Error = true; - sprintf( ErrorText, "Un-escaped special character in string on line %d:%ld", LineNo, BufPos-LineMark ); + CharNo += BufPos-Mark+1; + sprintf( ErrorText, "Un-escaped special character in string on line %d:%d", LineNo, CharNo ); return false; } } - // Validate size of name + // Get size of name Len = BufPos-Mark-2; // Create Return value pointer @@ -329,9 +441,6 @@ bool CJSONparse::ParseString( char ** Value, int &pLen ) memcpy( *Value, Mark+1, Len ); (*Value)[Len] = 0; - // Set other parameters - pLen = Len; - // Success return true; } @@ -354,21 +463,22 @@ bool CJSONparse::ParseObject( TDataMember * Object ) while (true) { - // Look for Member Name + // Evaluate key name SkipWhiteSpace(); if (*BufPos == '}') { + // End of Object break; } else if (!ParseString( &MemberName, Len )) { if (!Error) { Error = true; - sprintf( ErrorText, "Expect quoted key name on line %d:%ld", LineNo, BufPos-LineMark ); + sprintf( ErrorText, "Expect quoted key name on line %d:%d", LineNo, CharNo ); } return false; } else if (!Len) { Error = true; - sprintf( ErrorText, "Empty parameter name on line %d:%ld", LineNo, BufPos-LineMark ); + sprintf( ErrorText, "Empty parameter name on line %d:%d", LineNo, CharNo ); return false; } @@ -379,7 +489,7 @@ bool CJSONparse::ParseObject( TDataMember * Object ) SkipWhiteSpace(); if (*BufPos != ':') { Error = true; - sprintf( ErrorText, "Expected ':' delimiter on line %d:%ld", LineNo, BufPos-LineMark ); + sprintf( ErrorText, "Expected ':' delimiter on line %d:%d", LineNo, CharNo ); return false; } BufPos++; @@ -394,6 +504,7 @@ bool CJSONparse::ParseObject( TDataMember * Object ) !Error && !ParsePrimitive( Member )) { + // Destroy member DataTree->Delete( Object, MemberName ); return false; } @@ -413,7 +524,7 @@ bool CJSONparse::ParseObject( TDataMember * Object ) // Expect end of object if (*BufPos != '}') { Error = true; - sprintf( ErrorText, "Closing brace for object '}' expected on line %d:%ld", LineNo, BufPos-LineMark ); + sprintf( ErrorText, "Closing brace for object '}' expected on line %d:%d", LineNo, CharNo ); return false; } BufPos++; @@ -476,7 +587,7 @@ bool CJSONparse::ParseArray( TDataMember * Array ) // Expect end of object if (*BufPos != ']') { Error = true; - sprintf( ErrorText, "Closing brace for array ']' expected on line %d:%ld", LineNo, BufPos-LineMark ); + sprintf( ErrorText, "Closing brace for array ']' expected on line %d:%d", LineNo, CharNo ); return false; } BufPos++; @@ -507,14 +618,14 @@ bool CJSONparse::ParsePrimitive( TDataMember * Member ) { char * Value = NULL; int Len = 0; - char * Mark; char * EndMark; - // Mark start of value - Mark = BufPos; - // Get end of value - while ((*BufPos != 0) && !isspace(*BufPos) && (*BufPos != ',')) { + while (true) { + if (!*BufPos && RefillBuffer) + FillBuffer(); + if (!*BufPos || isspace(*BufPos) || (*BufPos == ',') || (*BufPos == '}') || (*BufPos == ']')) + break; BufPos++; } @@ -522,7 +633,7 @@ bool CJSONparse::ParsePrimitive( TDataMember * Member ) Len = BufPos - Mark; if (!Len) { Error = true; - sprintf( ErrorText, "Missing param value on line %d:%ld", LineNo, Mark-LineMark ); + sprintf( ErrorText, "Missing param value on line %d:%d", LineNo, CharNo ); return false; } @@ -556,7 +667,7 @@ bool CJSONparse::ParsePrimitive( TDataMember * Member ) } else { Error = true; - sprintf( ErrorText, "Invalid primitive param value on line %d:%ld", LineNo, Mark-LineMark ); + sprintf( ErrorText, "Invalid primitive param value on line %d:%d", LineNo, CharNo ); return false; } } @@ -578,32 +689,40 @@ bool CJSONparse::PrintObject( TDataMember * Object, const int Indent ) write( OutputHandle, "{", 1 ); // Extend spacer - memset( &Spacer[SpacerLen], ' ', 2 ); - SpacerLen += 2; - Spacer[SpacerLen] = 0; + if (Indent) { + memset( &Spacer[SpacerLen], ' ', 2 ); + SpacerLen += 2; + Spacer[SpacerLen] = 0; + } // Save parameters for (Member = Object->FirstChild; Member != NULL; (Member = Member->Next)) { - // Write parameter name - if (First) { - First = false; - if (Object->Name) { - write( OutputHandle, "\n", 1 ); - write( OutputHandle, Spacer, SpacerLen ); + // Whitespace around first bracket + if (Indent) { + if (First) { + First = false; + if (Object->Name) { + write( OutputHandle, "\n", 1 ); + write( OutputHandle, Spacer, SpacerLen ); + } + else { + write( OutputHandle, " ", 1 ); + } } else { - write( OutputHandle, " ", 1 ); + write( OutputHandle, Spacer, SpacerLen ); } } - else { - write( OutputHandle, Spacer, SpacerLen ); - } // Print key name write( OutputHandle, "\"", 1 ); write( OutputHandle, Member->Name, strlen(Member->Name) ); - write( OutputHandle, "\" : ", 4 ); + if (Indent) { + write( OutputHandle,"\" : ", 4 ); + } else { + write( OutputHandle, "\":", 2 ); + } // Print value Last = (++Count >= Object->Len); @@ -633,15 +752,17 @@ bool CJSONparse::PrintObject( TDataMember * Object, const int Indent ) PrintObject( Member, Indent ); break; } - if (!Last) { + if (!Last) write( OutputHandle, ",", 1 ); - } - write( OutputHandle, "\n", 1 ); + if (Indent) + write( OutputHandle, "\n", 1 ); } // Shorten spacer - SpacerLen -= 2; - Spacer[SpacerLen] = 0; + if (Indent) { + SpacerLen -= 2; + Spacer[SpacerLen] = 0; + } // Closing brace if (Object->Len) { @@ -663,19 +784,23 @@ bool CJSONparse::PrintArray( TDataMember * Array, const int Indent ) write( OutputHandle, "[", 1 ); // Extend spacer - memset( &Spacer[SpacerLen], ' ', 2 ); - SpacerLen += 2; - Spacer[SpacerLen] = 0; + if (Indent) { + memset( &Spacer[SpacerLen], ' ', 2 ); + SpacerLen += 2; + Spacer[SpacerLen] = 0; + } // Save parameters for (Member = Array->FirstChild; Member != NULL; (Member = Member->Next)) { - // Write parameter name - if (First) { - First = false; - write( OutputHandle, "\n", 1 ); + // Whitespace around brace + if (Indent) { + if (First) { + First = false; + write( OutputHandle, "\n", 1 ); + } + write( OutputHandle, Spacer, SpacerLen ); } - write( OutputHandle, Spacer, SpacerLen ); Last = (++Count >= Array->Len); switch (Member->Type) @@ -704,15 +829,17 @@ bool CJSONparse::PrintArray( TDataMember * Array, const int Indent ) PrintObject( Member, Indent ); break; } - if (!Last) { + if (!Last) write( OutputHandle, ",", 1 ); - } - write( OutputHandle, "\n", 1 ); + if (Indent) + write( OutputHandle, "\n", 1 ); } // Shorten spacer - SpacerLen -= 2; - Spacer[SpacerLen] = 0; + if (Indent) { + SpacerLen -= 2; + Spacer[SpacerLen] = 0; + } // Closing brace if (Array->Len) { diff --git a/JSONparseCore.h b/JSONparseCore.h index bbae172..0364d0e 100644 --- a/JSONparseCore.h +++ b/JSONparseCore.h @@ -32,10 +32,11 @@ private: CShiftBuffer * Buffer; // Parsing operation - char * BufEnd; char * BufPos; - char * LineMark; + char * Mark; int LineNo; + int CharNo; + bool RefillBuffer; // Printing Operation char Spacer[100]; @@ -45,21 +46,8 @@ private: 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++; - } - } + void SkipWhiteSpace(); bool ParseString( char ** Value, int &pLen ); bool ParseObject( TDataMember * Object ); bool ParseArray( TDataMember * Array ); @@ -73,10 +61,22 @@ public: CJSONparse( CDataTree * pDataTree ); ~CJSONparse(); - bool PrintToScreen( const char * Path, const int Indent ); + // Buffer operation + bool CreateBuffer( int pBufLen ); + bool FillBuffer(); + void FreeBuffer(); + bool ReadFromBuffer( const char * RootPath ); - bool LoadFromFile( const char * Path, const char * FilePath, int pBufLen = 500 ); - bool SaveToFile( const char * Path, const char * FilePath, const int Indent = 2 ); + // Input + bool ReadFromHandle( const char * RootPath, const int Handle, bool pRefillBuffer ); + bool ReadFromFile( const char * RootPath, const char * Path, const char * FileName ); + bool ReadFromFile( const char * RootPath, const char * FilePath ); + + // Output + bool WriteToHandle( const char * RootPath, const int Handle, const int Indent = 2 ); + bool WriteToScreen( const char * RootPath, const int Indent = 2 ); + bool WriteToFile( const char * RootPath, const char * Path, const char * FileName, const int Indent = 2 ); + bool WriteToFile( const char * RootPath, const char * FilePath, const int Indent = 2 ); const char * GetError() { return ((Error)? ErrorText : "Success"); }; };