Cockatrice/libcockatrice_card/libcockatrice/card/database/card_database_loader.h
BruebachL c72192fd81
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / vcpkg baseline (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
[Format] Small fixup (#7768)
* [Format] Small fixup.

* [CardDB] Stop leaking the loader's mutexes.

Both mutexes were heap-allocated through default member initializers and
never freed, so every CardDatabase destruction leaked 16 bytes. LeakSanitizer
flags this while discovering tests that create the database in main.

Hold them by value instead, as the loader is a QObject and therefore neither
copyable nor movable.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-11-03 15:59:32 +01:00

184 lines
6.6 KiB
C++

#ifndef COCKATRICE_CARD_DATABASE_LOADER_H
#define COCKATRICE_CARD_DATABASE_LOADER_H
#include <QBasicMutex>
#include <QByteArray>
#include <QList>
#include <QLoggingCategory>
#include <QObject>
#include <QString>
#include <QStringList>
#include <qtmetamacros.h>
class ICardDatabasePathProvider;
class ICardSetPriorityController;
// IWYU pragma: keep
// CardDatabaseData is passed by value to the databaseDataReady() signal, so the
// moc-generated code needs the complete type.
#include "card_database_data.h"
inline Q_LOGGING_CATEGORY(CardDatabaseLoadingLog, "card_database.loading");
inline Q_LOGGING_CATEGORY(CardDatabaseLoadingSuccessOrFailureLog, "card_database.loading.success_or_failure");
class CardDatabase;
class ICardDatabaseParser;
/**
* @enum LoadStatus
* @brief Represents the result of attempting to load a card database.
*/
enum LoadStatus
{
Ok, /**< Database loaded successfully. */
VersionTooOld, /**< Database version is too old to load. */
Invalid, /**< Database is invalid or unparsable. */
NotLoaded, /**< Database has not been loaded. */
FileError, /**< Error opening or reading the file. */
NoCards /**< Database contains no cards. */
};
/**
* @class CardDatabaseLoader
* @ingroup CardDatabase
* @brief Handles loading card databases from disk and saving custom tokens.
*
* This class is responsible for:
* - Discovering configured card database paths.
* - Loading main, token, spoiler, and custom databases.
* - Populating a CardDatabase instance using connected parsers.
* - Emitting signals about loading progress and new sets.
*/
class CardDatabaseLoader : public QObject
{
Q_OBJECT
public:
/**
* @brief Constructs a CardDatabaseLoader.
* @param parent QObject parent.
* @param db Pointer to the CardDatabase to populate (non-owning).
* @param pathProvider Provider for card database file paths.
*/
explicit CardDatabaseLoader(QObject *parent,
CardDatabase *db,
ICardDatabasePathProvider *pathProvider,
ICardSetPriorityController *_priorityController);
/** @brief Destructor cleans up allocated parsers. */
~CardDatabaseLoader() override;
public slots:
/**
* @brief Loads all configured card databases.
*
* Runs synchronously on the calling thread. The caller should ensure
* that any signal receivers are already connected before invoking this.
* @param ignoreCache Skip the binary cache lookup and re-parse the source
* files from disk, refreshing the cache afterwards. Used for
* explicit reloads so that edits the source hash cannot see are
* still picked up.
* @return Status of the main database load.
*/
LoadStatus loadCardDatabases(bool ignoreCache = false);
/**
* @brief Loads a single card database file into the given snapshot.
* @param path Path to the database file.
* @param data Snapshot to populate.
* @return LoadStatus indicating success or failure.
*/
LoadStatus loadCardDatabase(const QString &path, CardDatabaseData &data);
/**
* @brief Saves custom tokens to the user-defined custom database path.
* @return True if the save was successful.
*/
bool saveCustomTokensToFile();
signals:
/** @brief Emitted when loading starts. */
void loadingStarted();
/** @brief Emitted when loading finishes successfully. */
void loadingFinished();
/** @brief Emitted when loading fails. */
void loadingFailed();
/**
* @brief Emitted with the fully-built snapshot once background loading
* completes. The receiver swaps it into the live database.
*/
void databaseDataReady(CardDatabaseData data);
/**
* @brief Emitted when new sets are discovered during loading.
* @param numSets Number of new sets.
* @param setNames Names of the discovered sets.
*/
void newSetsFound(int numSets, const QStringList &setNames);
/** @brief Emitted when all newly discovered sets have been enabled. */
void allNewSetsEnabled();
private:
/**
* @brief Parses a single file into the given snapshot using the available parsers.
* @param fileName Path to the database file.
* @param data Snapshot to populate.
* @return LoadStatus indicating success or failure.
*/
LoadStatus loadFromFile(const QString &fileName, CardDatabaseData &data);
/**
* @brief Performs the actual load work synchronously on the calling thread.
* @param ignoreCache Skip the binary cache lookup and re-parse from disk.
* @return Status of the main database load.
*/
LoadStatus doLoadCardDatabases(bool ignoreCache);
/**
* @brief Collects custom card database paths recursively.
* @return Sorted list of file paths to custom databases.
*/
[[nodiscard]] QStringList collectCustomDatabasePaths() const;
/**
* @brief Computes a hash identifying the current set of input database files.
* @return Hash over the paths, sizes and modification times of all inputs.
*/
[[nodiscard]] QByteArray computeSourceHash(const QStringList &customPaths) const;
/**
* @brief Path of the binary cache file derived from the main card database path.
* @return Cache file path.
*/
[[nodiscard]] QString cachePath() const;
/**
* @brief Attempts to populate the snapshot from the binary cache.
* @param data Snapshot to populate.
* @param sourceHash Pre-computed hash identifying the current input files.
* @return True if a valid, up-to-date cache was read.
*/
bool loadFromCache(CardDatabaseData &data, const QByteArray &sourceHash);
/**
* @brief Writes the snapshot to the binary cache for the current inputs.
* @param data Snapshot to serialize.
* @param sourceHash Pre-computed hash identifying the current input files.
* @return True if the cache was written successfully.
*/
bool saveToCache(const CardDatabaseData &data, const QByteArray &sourceHash);
private:
CardDatabase *database; /**< Non-owning pointer to the target CardDatabase. */
ICardDatabasePathProvider *pathProvider; /**< Pointer to the path provider. */
ICardSetPriorityController *priorityController; /**< Controller for reconstructing set state. */
QList<ICardDatabaseParser *> availableParsers; /**< List of available parsers for different formats. */
QBasicMutex loadFromFileMutex; /**< Mutex for single-file loading. */
QBasicMutex reloadDatabaseMutex; /**< Mutex for reloading entire database. */
};
#endif // COCKATRICE_CARD_DATABASE_LOADER_H