Faster JSON entities parser

This commit is contained in:
Simon Walton 2018-10-16 18:08:07 -07:00
parent f5f34e8e7d
commit c031769c67
4 changed files with 67 additions and 15 deletions

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#include "OctreeDataUtils.h" #include "OctreeDataUtils.h"
#include "OctreeEntitiesFileParser.h"
#include <Gzip.h> #include <Gzip.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
@ -55,19 +56,30 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) {
return true; return true;
} }
bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(QVariantMap map) {
if (map.contains("Id") && map.contains("DataVersion") && map.contains("Version")) {
id = map["Id"].toUuid();
dataVersion = map["DataVersion"].toInt();
version = map["Version"].toInt();
}
return true;
}
bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromData(QByteArray data) { bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromData(QByteArray data) {
QByteArray jsonData; QByteArray jsonData;
if (gunzip(data, jsonData)) { if (gunzip(data, jsonData)) {
data = jsonData; data = jsonData;
} }
auto doc = QJsonDocument::fromJson(data); OctreeEntitiesFileParser jsonParser;
if (doc.isNull()) { jsonParser.setEntitiesString(data);
QVariantMap entitiesMap;
if (!jsonParser.parseEntities(entitiesMap)) {
qCritical() << "Can't parse Entities JSON: " << jsonParser.getErrorString().c_str();
return false; return false;
} }
auto root = doc.object(); return readOctreeDataInfoFromMap(entitiesMap);
return readOctreeDataInfoFromJSON(root);
} }
// Reads octree file and parses it into a RawOctreeData object. // Reads octree file and parses it into a RawOctreeData object.

View file

@ -43,6 +43,7 @@ public:
bool readOctreeDataInfoFromData(QByteArray data); bool readOctreeDataInfoFromData(QByteArray data);
bool readOctreeDataInfoFromFile(QString path); bool readOctreeDataInfoFromFile(QString path);
bool readOctreeDataInfoFromJSON(QJsonObject root); bool readOctreeDataInfoFromJSON(QJsonObject root);
bool readOctreeDataInfoFromMap(QVariantMap map);
}; };
class RawEntityData : public RawOctreeData { class RawEntityData : public RawOctreeData {

View file

@ -189,23 +189,25 @@ bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) {
} }
while (true) { while (true) {
QJsonParseError parseError; if (nextToken() != '{') {
QByteArray entitiesJson(_entitiesContents.right(_entitiesLength - _position)); _errorString = "Entity array item is not an object";
QJsonDocument entity = QJsonDocument::fromJson(entitiesJson, &parseError); return false;
if (parseError.error != QJsonParseError::GarbageAtEnd) { }
_errorString = "Ill-formed entity array"; int matchingBrace = findMatchingBrace();
if (matchingBrace < 0) {
_errorString = "Unterminated entity object";
return false; return false;
} }
int entityLength = parseError.offset;
entitiesJson.truncate(entityLength);
_position += entityLength;
entity = QJsonDocument::fromJson(entitiesJson, &parseError); QByteArray jsonEntity = _entitiesContents.mid(_position - 1, matchingBrace - _position + 1);
if (parseError.error != QJsonParseError::NoError) { QJsonDocument entity = QJsonDocument::fromJson(jsonEntity);
_errorString = "Entity item parse error"; if (entity.isNull()) {
_errorString = "Ill-formed entity";
return false; return false;
} }
entitiesArray.append(entity.object()); entitiesArray.append(entity.object());
_position = matchingBrace;
char c = nextToken(); char c = nextToken();
if (c == ']') { if (c == ']') {
return true; return true;
@ -216,3 +218,37 @@ bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) {
} }
return true; return true;
} }
int OctreeEntitiesFileParser::findMatchingBrace() const {
int index = _position;
int nestCount = 1;
while (index < _entitiesLength && nestCount != 0) {
switch (_entitiesContents[index++]) {
case '{':
++nestCount;
break;
case '}':
--nestCount;
break;
case '"':
// Skip string
while (index < _entitiesLength) {
if (_entitiesContents[index] == '"') {
++index;
break;
} else if (_entitiesContents[index] == '\\' && _entitiesContents[++index] == 'u') {
index += 4;
}
++index;
}
break;
default:
break;
}
}
return nestCount == 0 ? index : -1;
}

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
// Parse the top-level of the Models object ourselves - use QJsonDocument for each Entity object.
#ifndef hifi_OctreeEntitiesFileParser_h #ifndef hifi_OctreeEntitiesFileParser_h
#define hifi_OctreeEntitiesFileParser_h #define hifi_OctreeEntitiesFileParser_h
@ -27,6 +29,7 @@ private:
std::string readString(); std::string readString();
int readInteger(); int readInteger();
bool readEntitiesArray(QVariantList& entitiesArray); bool readEntitiesArray(QVariantList& entitiesArray);
int findMatchingBrace() const;
QByteArray _entitiesContents; QByteArray _entitiesContents;
int _position { 0 }; int _position { 0 };