Cockatrice/libcockatrice_card/libcockatrice/card/printing/exact_card.h
BruebachL c25a2c5c3b
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 / 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
[CardDB] Avoid copying printing maps and property strings (#7605)
* [CardDB] Avoid copying printing maps and property strings

Reading a card's sets was a measured hotspot: `SetToPrintingsMap setMap =
card->getSets();` copies the whole QMap, and then iterating it with a
non-const iterator detaches and deep-copies every PrintingInfo, including
its property hash, on first use. A micro-benchmark of that exact pattern
(copy plus non-const iteration) came out about 19x slower than a const
reference over the same data.

The querier, the cache writer, the XML writer, the picture loader and the
printing/set selection widgets now take `getSets()` by const reference and
only use const iterators. The tests that iterate generated data are
covered by the existing card database and picture loader suites.

String accessors were also returning by value where the value lives in a
hash that has already been materialized:
- CardInfo::getProperty() and the legacy getCardType()/getColors()/
  getManaCost()/getCmc()/getPowTough()/getLoyalty() accessors,
- PrintingInfo::getProperty(), getUuid(), getFlavorName(), getArtist(),
  and getSet(),
- ExactCard::getPrinting() and getCardPtr(),
- CardSet::getShortName(), getLongName() and getSetType().

getPrinting() in particular copied a PrintingInfo on every call, and a
copy of an unmaterialized PrintingInfo has to take the properties mutex.
Callers that need an owned copy still get one; the rest just read.

Other allocation removals in the same path:
- LazyPropertiesHash::ensureMaterialized() checks an atomic flag before
  taking the mutex, so property reads after the first one do not lock.
  The flag is release-stored after materialization and acquire-loaded by
  readers/copies, preserving the existing locking for the first read.
- CardSet::getCorrectedShortName() no longer rebuilds its QSet of
  reserved Windows file names on every call.
- CardSet::getIsKnownIgnored() no longer formats the release date into a
  throwaway string just to check its length.
- The querier builds its card-type/format count caches without
  `cards.values()` or a properties key list, reserves its result lists,
  and picks a random card without materializing `cards.keys()`.
- The querier's pinned-printing path reuses the CardInfoPtr it already
  has instead of looking the card up again by name.
- CardDatabaseCache/XML serialization copy the alt-name QSet by
  reference.
- The picture loader compiles its !prop!/!set! regular expressions once
  instead of per URL template, and walks the transform map with an
  iterator instead of keys() plus operator[].

Full build and ctest (628 tests) pass.

* [CardDB] Address review comments

PrintingInfo is a value type: it is stored in QLists and returned by value
from the querier, so a const-ref getter can hand out a reference into a
temporary. Return the QStrings and the CardSetPtr by value again.

The constFind lookup stays, so a property read still never materializes the
lazy property hash - only the last shared-data refcount is paid.

* [CardDB] Undo the const-ref QString return changes

QString is implicitly shared, so returning by value costs only a refcount
and avoids handing out references into a lazily materialized hash. The
constFind lookups stay, so a property read still never materializes the
hash.

* [CardDB] Use QMap::asKeyValueRange() in transformUrl

The range is a view over the map's own iterators and yields references to
the stored key and value, so it keeps the no-allocation property of the
manual const iteration.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-23 11:58:58 +02:00

124 lines
3.6 KiB
C++

#ifndef EXACT_CARD_H
#define EXACT_CARD_H
#include "../card_info.h"
#include "libcockatrice/card/printing/printing_info.h"
#include <QByteArray>
#include <QMetaType>
#include <QString>
/**
* @class ExactCard
* @ingroup CardPrintings
*
* @brief Represents a specific card instance, defined by its CardInfo
* and a particular printing.
*
* An ExactCard identifies a card not only by its underlying CardInfoPtr
* (which may be null), but also by its PrintingInfo, which specifies the
* exact printing/variant. This allows distinguishing between different
* printings of the same logical card (e.g., different sets, promos, foils).
*/
class ExactCard
{
CardInfoPtr card;
PrintingInfo printing;
public:
/**
* @brief Constructs an empty ExactCard.
*
* The CardInfoPtr will be null, and PrintingInfo will be default-constructed.
* An empty ExactCard represents "no card".
*/
ExactCard();
/**
* @brief Constructs an ExactCard from a card and printing.
*
* @param _card The card info pointer. May be null.
* @param _printing The printing details. Defaults to an empty PrintingInfo.
*/
explicit ExactCard(const CardInfoPtr &_card, const PrintingInfo &_printing = PrintingInfo());
/**
* @brief Returns the underlying CardInfoPtr.
*
* May be null if the ExactCard is empty.
*/
[[nodiscard]] const CardInfoPtr &getCardPtr() const
{
return card;
}
/**
* @brief Returns the printing information associated with this card.
*
* May be empty if no specific printing was assigned.
*/
[[nodiscard]] const PrintingInfo &getPrinting() const
{
return printing;
}
/**
* @brief Compares both card pointer and printing for equality.
*
* Two ExactCard objects are equal only if both their CardInfoPtr and
* PrintingInfo values are equal.
*/
bool operator==(const ExactCard &other) const;
/**
* @brief Convenience helper to get the card's display name.
*
* @return The card's name, or an empty string if the CardInfoPtr is null.
*/
[[nodiscard]] QString getName() const;
/**
* @brief Returns a reference to the underlying CardInfo object.
*
* If the CardInfoPtr is null, returns a reference to a static empty CardInfo
* instance instead. This avoids null-dereferencing but means modifications
* to the returned object do not affect the ExactCard.
*
* @return A const reference to the CardInfo object.
*/
[[nodiscard]] const CardInfo &getInfo() const;
/**
* @brief Generates a stable cache key for pixmap caching.
*
* The key includes the card's name and (if present) the printing UUID,
* allowing different printings of the same card to map to different cache entries.
*/
[[nodiscard]] QString getPixmapCacheKey() const;
/**
* @brief Indicates whether this ExactCard represents no valid card.
*
* An ExactCard is considered empty if the CardInfoPtr is null or the
* card's name is empty.
*/
[[nodiscard]] bool isEmpty() const;
/**
* @brief Boolean conversion indicating whether the card is valid (non-empty).
*
* @return true if not empty, false otherwise.
*/
explicit operator bool() const;
/**
* @brief Emits the pixmapUpdated signal on the underlying CardInfo.
*
* Assumes CardInfoPtr is non-null. If called on an empty ExactCard,
* the behavior is undefined.
*/
void emitPixmapUpdated() const;
};
Q_DECLARE_METATYPE(ExactCard)
#endif // EXACT_CARD_H