Cockatrice/libcockatrice_card/libcockatrice/card/printing/printing_info.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

152 lines
4.3 KiB
C++

#ifndef COCKATRICE_PRINTING_INFO_H
#define COCKATRICE_PRINTING_INFO_H
#include "../set/card_set.h"
#include "libcockatrice/card/lazy_properties_hash.h"
#include <QHash>
#include <QList>
#include <QMap>
#include <QSharedPointer>
#include <QString>
#include <QStringList>
class PrintingInfo;
using SetToPrintingsMap = QMap<QString, QList<PrintingInfo>>;
/**
* @class PrintingInfo
* @ingroup CardPrintings
*
* @brief Represents metadata for a specific variation of a card within a set.
*
* A card can have multiple variations across sets. PrintingInfo associates
* a card with one such variation, and provides per-printing attributes
* such as identifiers or additional properties.
*
* Equality is defined as both the set and the property values being equal.
*/
class PrintingInfo
{
public:
/**
* @brief Constructs a PrintingInfo associated with a specific set.
*
* @param _set The set this printing belongs to (defaults to null).
* @param _properties The printing properties (defaults to empty)
*/
explicit PrintingInfo(const CardSetPtr &_set = nullptr, const LazyPropertiesHash &_properties = {});
/**
* @brief Destroys the PrintingInfo.
*
* Defaulted since no special cleanup is required.
*/
~PrintingInfo() = default;
/**
* @brief Equality operator.
*
* Two PrintingInfo objects are equal if they refer to the same set
* and contain the exact same property key/value pairs.
*
* @param other Another PrintingInfo to compare against.
* @return True if both set and properties are equal, otherwise false.
*/
bool operator==(const PrintingInfo &other) const
{
return this->set == other.set && this->getPropertiesHash() == other.getPropertiesHash();
}
/**
* @brief check if the info is empty, as if default constructed.
*
* @return True if both set and properties are empty, otherwise false.
*/
bool isEmpty() const
{
return set == nullptr && getPropertiesHash().isEmpty();
}
private:
CardSetPtr set; ///< The set this variation belongs to.
LazyPropertiesHash properties; ///< Key-value store for variation-specific attributes.
public:
/**
* @brief Returns the set this printing belongs to.
*
* @return Pointer to the associated CardSet.
*/
[[nodiscard]] CardSetPtr getSet() const
{
return set;
}
/**
* @brief Returns the list of property names defined for this printing.
*
* @return List of keys stored in the properties map.
*/
[[nodiscard]] QStringList getProperties() const
{
return getPropertiesHash().keys();
}
[[nodiscard]] const QHash<QString, QString> &getPropertiesHash() const
{
return properties.getProperties();
}
/**
* @brief Retrieves the value of a specific property.
*
* @param propertyName The key name of the property to query.
* @return The property value as a string, or an empty string if not set.
*/
[[nodiscard]] QString getProperty(const QString &propertyName) const
{
const auto &propertiesHash = getPropertiesHash();
const auto it = propertiesHash.constFind(propertyName);
return it != propertiesHash.constEnd() ? it.value() : QString();
}
/**
* @brief Sets or updates the value of a specific property.
*
* If the property already exists, its value is replaced.
*
* @param _name The name of the property.
* @param _value The string value to assign.
*/
void setProperty(const QString &_name, const QString &_value);
/**
* @brief Returns the providerID for this printing.
*
* @return A string representing the providerID.
*/
[[nodiscard]] QString getUuid() const;
/**
* @brief Returns the flavorName for this printing.
*
* @return The flavorName, or empty if it isn't present.
*/
[[nodiscard]] QString getFlavorName() const;
/**
* @brief Returns the artist name credited for this printing's artwork.
*
* Requires a card database generated with artist data.
*
* @return The artist name, or empty if it isn't present.
*/
[[nodiscard]] QString getArtist() const
{
return getProperty("artist");
}
};
#endif // COCKATRICE_PRINTING_INFO_H