[Oracle] Parse sets lazily to slash importer peak memory (#7217)

* [Oracle] Parse sets lazily to slash importer peak memory

- Add a raw JSON scanner that splits the document into per-set byte ranges
  without materializing the JSON tree
- Keep only the raw document bytes and parse one set at a time in startImport
- Take readSetsFromByteArray by value so the wizard's buffer is moved, not copied
- Clear the retained raw data in releaseSetData()/clear()
- Cover the scanner and lazy parsing with tests

Took 2 minutes

* [Oracle] Fix nesting-depth cap, tolerate unescaped control chars, lazy-parse review fixes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-09-05 20:35:28 +02:00 committed by GitHub
parent 1dc54617ba
commit c011ea7ceb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 992 additions and 45 deletions

View file

@ -1,6 +1,9 @@
#ifndef ORACLEIMPORTER_H
#define ORACLEIMPORTER_H
#include "raw_json_scanner.h"
#include <QByteArray>
#include <QJsonArray>
#include <QJsonObject>
#include <QMap>
@ -46,10 +49,12 @@ class SetToDownload
{
private:
QString shortName, longName;
QJsonArray cards;
QDate releaseDate;
QString setType;
CardSet::Priority priority;
// Byte range of this set's object within the importer's raw JSON text. Parsing
// one set at a time keeps peak memory low instead of holding the whole document.
RawJson::SetDataRange rawRange;
public:
const QString &getShortName() const
@ -60,10 +65,6 @@ public:
{
return longName;
}
const QJsonArray &getCards() const
{
return cards;
}
const QString &getSetType() const
{
return setType;
@ -76,16 +77,23 @@ public:
{
return priority;
}
const RawJson::SetDataRange &getRawRange() const
{
return rawRange;
}
SetToDownload(QString _shortName,
QString _longName,
QJsonArray _cards,
CardSet::Priority _priority,
QString _setType = QString(),
const QDate &_releaseDate = QDate())
: shortName(std::move(_shortName)), longName(std::move(_longName)), cards(std::move(_cards)),
releaseDate(_releaseDate), setType(std::move(_setType)), priority(_priority)
: shortName(std::move(_shortName)), longName(std::move(_longName)), releaseDate(_releaseDate),
setType(std::move(_setType)), priority(_priority)
{
}
void setRawRange(const RawJson::SetDataRange &_rawRange)
{
rawRange = _rawRange;
}
bool operator<(const SetToDownload &set) const
{
return longName.compare(set.longName, Qt::CaseInsensitive) < 0;
@ -141,6 +149,12 @@ private:
QList<SetToDownload> allSets;
/**
* The raw JSON text of the source document, retained for lazy per-set
* parsing during startImport(). Frees the card data as each set is imported.
*/
QByteArray rawSetsData;
CardInfoPtr addCard(QString name,
const QString &text,
bool isToken,
@ -153,7 +167,11 @@ signals:
public:
explicit OracleImporter(QObject *parent = nullptr);
bool readSetsFromByteArray(const QByteArray &data);
/**
* Scans the given JSON document for set metadata. Takes the data by value so
* the wizard can hand over its decompressed buffer without copying it.
*/
bool readSetsFromByteArray(QByteArray data);
int startImport();
bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion);
int importCardsFromSet(const CardSetPtr &currentSet, const QJsonArray &cardsList);
@ -169,6 +187,10 @@ public:
{
return allSets;
}
const QByteArray &getRawSetsData() const
{
return rawSetsData;
}
void releaseSetData();
void clear();
};