QEasingCurve: fix (de)serialization in QDataStream

The serialization code did stream out a function pointer as an integer,
and then tried to set it back -- effectively, it has *never* worked
since the beginning of public history, unless
serialization/deserialization were done within the same process.

While we cannot support streaming custom easing functions, we can
recover the non-custom functions from the type, which was also streamed
out; setType will take care of that, and we'll just ignore the
subsequent field in the stream.

If one tries to stream out a QEasingCurve with a custom curve, what do
we do? I've decided to just print a warning and stream _something_ out,
so I can keep some degree of behavioral compatibility and aggressively
cherrypick this patch.
AFAIK, there's no support for such a scenario in QDataStream: all
out-stream operators have a wide contract, and there's no Status flag
that meaningfully represents this case (and I doubt anyone checks QDS'
status while writing into it).

Change-Id: Ifa80cf3a9003cab074ddf112022c09b364497007
Fixes: QTBUG-132575
Pick-to: 6.9 6.8 6.5 6.2 5.15
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Samuel Gaist <samuel.gaist@idiap.ch>
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Giuseppe D'Angelo 2025-01-07 14:18:48 +01:00
parent afa34d5f37
commit 78a46bf16b

View File

@ -276,7 +276,7 @@
QT_BEGIN_NAMESPACE
static bool isConfigFunction(QEasingCurve::Type type)
static constexpr bool isConfigFunction(QEasingCurve::Type type)
{
return (type >= QEasingCurve::InElastic
&& type <= QEasingCurve::OutInBounce) ||
@ -1493,13 +1493,30 @@ QDebug operator<<(QDebug debug, const QEasingCurve &item)
Writes the given \a easing curve to the given \a stream and returns a
reference to the stream.
\warning Writing easing curves of QEasingCurve::Custom type
(that is, curves with a custom easing function) is not supported.
\sa {Serializing Qt Data Types}
*/
QDataStream &operator<<(QDataStream &stream, const QEasingCurve &easing)
{
if (easing.d_ptr->type == QEasingCurve::Custom) {
qWarning("QEasingCurve: Cannot serialize an easing curve with a custom easing function");
// Backwards compatibility: stream _something_ out.
// Deliberately choose a curve that uses a config and not a
// easing function. If this curve is deserialized from old
// code, it will ignore the function pointer (cf.
// QTBUG-132575).
static_assert(isConfigFunction(QEasingCurve::InElastic));
stream << QEasingCurve(QEasingCurve::InElastic);
return stream;
}
stream << quint8(easing.d_ptr->type);
stream << quint64(quintptr(easing.d_ptr->func));
// Unused; for backwards compatibility
stream << quint64(0);
bool hasConfig = easing.d_ptr->config;
stream << hasConfig;
@ -1525,11 +1542,16 @@ QDataStream &operator>>(QDataStream &stream, QEasingCurve &easing)
quint8 int_type;
stream >> int_type;
type = static_cast<QEasingCurve::Type>(int_type);
if (type == QEasingCurve::Custom) {
qWarning("QEasingCurve: Cannot deserialize an easing curve with a custom easing function");
stream.setStatus(QDataStream::ReadCorruptData);
type = QEasingCurve::Linear;
}
easing.setType(type);
quint64 ptr_func;
// Unused; for backwards compatibility
[[maybe_unused]] quint64 ptr_func;
stream >> ptr_func;
easing.d_ptr->func = QEasingCurve::EasingFunction(quintptr(ptr_func));
bool hasConfig;
stream >> hasConfig;