/***************************************************************************** * gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . *****************************************************************************/ #include "QJsonParser.h" #include "QJsonArray.h" #include "QJsonObject.h" #include "QJsonValue.h" #if QT_VERSION < 0x050000 #include #include #include namespace { unsigned int to_hex(int ch) { static const int hexval[256] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; if(static_cast(ch) < 256) { return hexval[static_cast(ch)]; } else { return 0; } } } //------------------------------------------------------------------------------ // Name: QJsonParser //------------------------------------------------------------------------------ QJsonParser::QJsonParser(const char *begin, const char *end) : begin_(begin), end_(end), p_(begin) { state_.error = QJsonParseError::NoError; state_.offset = 0; } //------------------------------------------------------------------------------ // Name: parse //------------------------------------------------------------------------------ QJsonRoot *QJsonParser::parse() { if(begin_ == end_) { return 0; } QJsonRoot *ret = 0; try { const char ch = peek(); switch(ch) { case ArrayBegin: ret = getArray(); break; case ObjectBegin: ret = getObject(); break; default: state_.error = QJsonParseError::IllegalValue; state_.offset = p_ - begin_; break; } } catch(const QJsonParseError &e) { state_ = e; } if(ret) { // eat up trailing white space... while(p_ != end_ && std::isspace(*p_)) { ++p_; } //detect trailing garbage if(p_ != end_) { state_.error = QJsonParseError::GarbageAtEnd; state_.offset = p_ - begin_; } } return ret; } //------------------------------------------------------------------------------ // Name: peek //------------------------------------------------------------------------------ char QJsonParser::peek() { // first eat up some whitespace while(p_ != end_ && std::isspace(*p_)) { ++p_; } return *p_; } //------------------------------------------------------------------------------ // Name: getValue //------------------------------------------------------------------------------ QJsonValue QJsonParser::getValue() { switch(peek()) { case ObjectBegin: { QScopedPointer obj(getObject()); return QJsonValue(*obj); } case ArrayBegin: { QScopedPointer arr(getArray()); return QJsonValue(*arr); } case Quote: return QJsonValue(getString()); case 't': return getTrue(); case 'f': return getFalse(); case 'n': return getNull(); default: return getNumber(); } throwError(QJsonParseError::MissingObject); return QJsonValue(); } //------------------------------------------------------------------------------ // Name: getObject //------------------------------------------------------------------------------ QJsonObject *QJsonParser::getObject() { QScopedPointer obj(new QJsonObject); char tok = peek(); if(tok != ObjectBegin) { throwError(QJsonParseError::IllegalValue); } ++p_; // handle empty object tok = peek(); if(peek() == ObjectEnd) { ++p_; } else { do { QPair p = getPair(); obj->values_.insert(p.first, p.second); tok = peek(); ++p_; } while(tok == ValueSeparator); } if(tok != ObjectEnd) { throwError(QJsonParseError::UnterminatedObject); } return obj.take(); } //------------------------------------------------------------------------------ // Name: getArray //------------------------------------------------------------------------------ QJsonArray *QJsonParser::getArray() { QScopedPointer arr(new QJsonArray); char tok = peek(); if(tok != ArrayBegin) { throwError(QJsonParseError::IllegalValue); } ++p_; // handle empty object tok = peek(); if(tok == ArrayEnd) { ++p_; } else { do { arr->values_.push_back(getValue()); tok = peek(); ++p_; } while(tok == ValueSeparator); } if(tok != ArrayEnd) { throwError(QJsonParseError::MissingValueSeparator); } return arr.take(); } //------------------------------------------------------------------------------ // Name: getPair //------------------------------------------------------------------------------ QPair QJsonParser::getPair() { QString key = getString(); if(peek() != NameSeparator) { throwError(QJsonParseError::MissingNameSeparator); } ++p_; return qMakePair(key, getValue()); } //------------------------------------------------------------------------------ // Name: getString //------------------------------------------------------------------------------ QString QJsonParser::getString() { if(peek() != Quote) { throwError(QJsonParseError::IllegalUTF8String); } ++p_; QByteArray s; while(p_ != end_ && *p_ != Quote && *p_ != '\n') { if(*p_ == '\\') { ++p_; if(p_ != end_) { switch(*p_) { case '"': s.append('"'); break; case '\\': s.append('\\'); break; case '/': s.append('/'); break; case 'b': s.append('\b'); break; case 'f': s.append('\f'); break; case 'n': s.append('\n'); break; case 'r': s.append('\r'); break; case 't': s.append('\t'); break; case 'u': { QString hexChar; // convert \uXXXX escape sequences to UTF-8 char hex[4]; if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[0] = *++p_; if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[1] = *++p_; if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[2] = *++p_; if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[3] = *++p_; if(!std::isxdigit(hex[0])) throwError(QJsonParseError::IllegalUTF8String); if(!std::isxdigit(hex[1])) throwError(QJsonParseError::IllegalUTF8String); if(!std::isxdigit(hex[2])) throwError(QJsonParseError::IllegalUTF8String); if(!std::isxdigit(hex[3])) throwError(QJsonParseError::IllegalUTF8String); quint16 w1 = 0; quint16 w2 = 0; w1 |= (to_hex(hex[0]) << 12); w1 |= (to_hex(hex[1]) << 8); w1 |= (to_hex(hex[2]) << 4); w1 |= (to_hex(hex[3])); hexChar.append(QChar(w1)); if((w1 & 0xfc00) == 0xdc00) { throwError(QJsonParseError::IllegalUTF8String); } if((w1 & 0xfc00) == 0xd800) { // part of a surrogate pair if(p_ == end_ || *++p_ != '\\') { throwError(QJsonParseError::IllegalEscapeSequence); } if(p_ == end_ || *++p_ != 'u') { throwError(QJsonParseError::IllegalEscapeSequence); } // convert \uXXXX escape sequences to UTF-8 if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[0] = *++p_; if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[1] = *++p_; if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[2] = *++p_; if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[3] = *++p_; if(!std::isxdigit(hex[0])) throwError(QJsonParseError::IllegalUTF8String); if(!std::isxdigit(hex[1])) throwError(QJsonParseError::IllegalUTF8String); if(!std::isxdigit(hex[2])) throwError(QJsonParseError::IllegalUTF8String); if(!std::isxdigit(hex[3])) throwError(QJsonParseError::IllegalUTF8String); w2 |= (to_hex(hex[0]) << 12); w2 |= (to_hex(hex[1]) << 8); w2 |= (to_hex(hex[2]) << 4); w2 |= (to_hex(hex[3])); hexChar.append(QChar(w2)); } s.append(hexChar.toUtf8()); } break; default: s.append('\\'); break; } } } else { s.append(*p_); } ++p_; } if(*p_ != Quote || p_ == end_) { throwError(QJsonParseError::UnterminatedString); } ++p_; return QString::fromUtf8(s, s.size()); } //------------------------------------------------------------------------------ // Name: getNull //------------------------------------------------------------------------------ QJsonValue QJsonParser::getNull() { if(p_ == end_ || *p_++ != 'n') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'u') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } return QJsonValue(); } //------------------------------------------------------------------------------ // Name: getTrue //------------------------------------------------------------------------------ QJsonValue QJsonParser::getTrue() { if(p_ == end_ || *p_++ != 't') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'r') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'u') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'e') { throwError(QJsonParseError::IllegalValue); } return QJsonValue(true); } //------------------------------------------------------------------------------ // Name: getFalse //------------------------------------------------------------------------------ QJsonValue QJsonParser::getFalse() { if(p_ == end_ || *p_++ != 'f') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'a') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 's') { throwError(QJsonParseError::IllegalValue); } if(p_ == end_ || *p_++ != 'e') { throwError(QJsonParseError::IllegalValue); } return QJsonValue(false); } //------------------------------------------------------------------------------ // Name: getNumber //------------------------------------------------------------------------------ QJsonValue QJsonParser::getNumber() { // JSON numbers fit the regex: -?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)? const char *const first = p_; // -? if(p_ != end_ && *p_ == '-') { ++p_; } // (0|[1-9][0-9]*) if(p_ != end_) { if(*p_ >= '1' && *p_ <= '9') { while(p_ != end_ && std::isdigit(*p_)) { ++p_; } } else if(*p_ == '0') { ++p_; } else { throwError(QJsonParseError::IllegalNumber); } } // (\.[0-9]+)? if(p_ != end_ && *p_ == '.') { ++p_; if(!std::isdigit(*p_)) { throwError(QJsonParseError::IllegalNumber); } while(p_ != end_ && std::isdigit(*p_)) { ++p_; } } // ([eE][+-]?[0-9]+)? if(p_ != end_ && (*p_ == 'e' || *p_ == 'E')) { ++p_; if(p_ != end_ && (*p_ == '+' || *p_ == '-')) { ++p_; } if(!std::isdigit(*p_)) { throwError(QJsonParseError::IllegalNumber); } while(p_ != end_ && std::isdigit(*p_)) { ++p_; } } if(p_ == end_) { throwError(QJsonParseError::TerminationByNumber); } return QJsonValue(QByteArray::fromRawData(first, p_ - first).toDouble()); } //------------------------------------------------------------------------------ // Name: state //------------------------------------------------------------------------------ QJsonParseError QJsonParser::state() const { return state_; } //------------------------------------------------------------------------------ // Name: throwError //------------------------------------------------------------------------------ void QJsonParser::throwError(QJsonParseError::ParseError e) { QJsonParseError err; err.error = e; err.offset = p_ - begin_; throw err; } #endif