mirror of https://github.com/qt/qtgrpc.git
Move the scalar protobuf type serializer to a seprate TU
Move the scalar type serializers to a separated translation units. This make code more readable. Adjust constraints to follow common project coding standards and make constrains compilation faster. Make complex functions out-of-line to avoid unwanted binary expansion. Pick-to: 6.8 6.9 Task-number: QTBUG-128812 Change-Id: Ib5311f392e32e5f31416448d2f306ae91155f873 Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
This commit is contained in:
parent
3df9d285da
commit
6a1012792c
|
@ -4,6 +4,8 @@
|
|||
qt_internal_add_module(Protobuf
|
||||
SOURCES
|
||||
protobuffieldpresencechecker_p.h
|
||||
protobufscalarserializers_p.h protobufscalarserializers_p.cpp
|
||||
protobufscalarjsonserializers_p.h
|
||||
qtprotobufglobal.h
|
||||
qabstractprotobufserializer.cpp qabstractprotobufserializer.h
|
||||
qprotobufdeserializerbase_p.h qprotobufdeserializerbase.cpp
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef PROTOBUFSCALARJSONSERIALIZERS_P_H
|
||||
#define PROTOBUFSCALARJSONSERIALIZERS_P_H
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtProtobuf/qtprotobuftypes.h>
|
||||
|
||||
#include <QtCore/private/qnumeric_p.h>
|
||||
#include <QtCore/qjsonarray.h>
|
||||
#include <QtCore/qjsonvalue.h>
|
||||
#include <QtCore/qtconfigmacros.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace ProtobufScalarJsonSerializers {
|
||||
// Tests if V is JSON compatible integer
|
||||
// int32 | int64 | uint32 | sint32 | sint64 | fixed32 | sfixed32 | sfixed64
|
||||
template <typename T>
|
||||
constexpr inline bool IsJsonInt = false;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt<QtProtobuf::int32> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt<QtProtobuf::int64> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt<QtProtobuf::uint32> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt<QtProtobuf::sint32> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt<QtProtobuf::sint64> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt<QtProtobuf::fixed32> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt<QtProtobuf::sfixed32> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt<QtProtobuf::sfixed64> = true;
|
||||
|
||||
template <typename T>
|
||||
using if_json_int = std::enable_if_t<IsJsonInt<T>, bool>;
|
||||
|
||||
// Tests if V is JSON incompatible 64-bit unsigned integer
|
||||
// uint64 | fixed64
|
||||
template <typename T>
|
||||
constexpr inline bool IsJsonInt64 = false;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt64<QtProtobuf::uint64> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonInt64<QtProtobuf::fixed64> = true;
|
||||
|
||||
template <typename T>
|
||||
using if_json_int64 = std::enable_if_t<IsJsonInt64<T>, bool>;
|
||||
|
||||
// Tests if V is JSON compatible floating point number
|
||||
// float | double
|
||||
template <typename T>
|
||||
constexpr inline bool IsJsonFloatingPoint = false;
|
||||
template <>
|
||||
constexpr inline bool IsJsonFloatingPoint<float> = true;
|
||||
template <>
|
||||
constexpr inline bool IsJsonFloatingPoint<double> = true;
|
||||
|
||||
template <typename T>
|
||||
using if_json_floating_point = std::enable_if_t<IsJsonFloatingPoint<T>, bool>;
|
||||
|
||||
template <typename T, if_json_int<T> = true>
|
||||
QJsonValue serialize(T propertyValue)
|
||||
{
|
||||
return QJsonValue(qint64(propertyValue));
|
||||
}
|
||||
|
||||
template <typename T, if_json_int64<T> = true>
|
||||
QJsonValue serialize(T propertyValue)
|
||||
{
|
||||
return QJsonValue(QString::number(propertyValue));
|
||||
}
|
||||
|
||||
template <typename T, if_json_floating_point<T> = true>
|
||||
QJsonValue serialize(T propertyValue)
|
||||
{
|
||||
if (propertyValue == -std::numeric_limits<T>::infinity())
|
||||
return QJsonValue(QLatin1String("-Infinity"));
|
||||
|
||||
if (propertyValue == std::numeric_limits<T>::infinity())
|
||||
return QJsonValue(QLatin1String("Infinity"));
|
||||
|
||||
if (propertyValue != propertyValue)
|
||||
return QJsonValue(QLatin1String("NaN"));
|
||||
|
||||
return QJsonValue(propertyValue);
|
||||
}
|
||||
|
||||
inline QJsonValue serialize(bool propertyValue)
|
||||
{
|
||||
return QJsonValue(propertyValue);
|
||||
}
|
||||
|
||||
inline QJsonValue serialize(const QString &propertyValue)
|
||||
{
|
||||
return QJsonValue(propertyValue);
|
||||
}
|
||||
|
||||
inline QJsonValue serialize(const QByteArray &propertyValue)
|
||||
{
|
||||
return QJsonValue(QString::fromUtf8(propertyValue.toBase64()));
|
||||
}
|
||||
|
||||
template <typename L>
|
||||
QJsonValue serializeList(const QVariant &propertyValue)
|
||||
{
|
||||
QJsonArray arr;
|
||||
L listValue = propertyValue.value<L>();
|
||||
for (const auto &value : listValue)
|
||||
arr.append(serialize(value));
|
||||
|
||||
return QJsonValue(arr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QJsonValue serializeCommon(const QVariant &propertyValue)
|
||||
{
|
||||
return serialize(propertyValue.value<T>());
|
||||
}
|
||||
|
||||
template <typename T, if_json_int<T> = true>
|
||||
T deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
auto variantValue = value.toVariant();
|
||||
qint64 raw = 0;
|
||||
switch (variantValue.metaType().id()) {
|
||||
case QMetaType::QString: // TODO: check if string has prepending/appending whitespaces.
|
||||
case QMetaType::LongLong:
|
||||
raw = variantValue.toLongLong(&ok);
|
||||
break;
|
||||
case QMetaType::Double: {
|
||||
double d = value.toDouble();
|
||||
ok = convertDoubleTo(d, &raw) && double(raw) == d;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// For types that "smaller" than qint64 we need to check if the value fits its limits range
|
||||
if constexpr (sizeof(T) != sizeof(qint64)) {
|
||||
if (ok) {
|
||||
if constexpr (std::is_same_v<T, QtProtobuf::sfixed32>
|
||||
|| std::is_same_v<T, QtProtobuf::int32>) {
|
||||
using limits = std::numeric_limits<qint32>;
|
||||
ok = raw >= limits::min() && raw <= limits::max();
|
||||
} else if constexpr (std::is_same_v<T, QtProtobuf::fixed32>) {
|
||||
using limits = std::numeric_limits<quint32>;
|
||||
ok = raw >= limits::min() && raw <= limits::max();
|
||||
} else {
|
||||
using limits = std::numeric_limits<T>;
|
||||
ok = raw >= limits::min() && raw <= limits::max();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return T(raw);
|
||||
}
|
||||
|
||||
template <typename T, if_json_int64<T> = true>
|
||||
T deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
quint64 raw = 0;
|
||||
auto variantValue = value.toVariant();
|
||||
switch (variantValue.metaType().id()) {
|
||||
case QMetaType::QString:
|
||||
case QMetaType::LongLong:
|
||||
// Here we attempt converting the value to ULongLong
|
||||
raw = variantValue.toULongLong(&ok);
|
||||
break;
|
||||
case QMetaType::Double: {
|
||||
double d = value.toDouble();
|
||||
ok = convertDoubleTo(d, &raw) && double(raw) == d;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return T(raw);
|
||||
}
|
||||
|
||||
template <typename T, if_json_floating_point<T> = true>
|
||||
T deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
ok = true;
|
||||
QByteArray data = value.toVariant().toByteArray();
|
||||
if (data.toLower() == QByteArray("-infinity"))
|
||||
return -std::numeric_limits<T>::infinity();
|
||||
|
||||
if (data.toLower() == QByteArray("infinity"))
|
||||
return std::numeric_limits<T>::infinity();
|
||||
|
||||
if (data.toLower() == QByteArray("nan"))
|
||||
return T(NAN);
|
||||
|
||||
if constexpr (std::is_same_v<T, float>)
|
||||
return data.toFloat(&ok);
|
||||
else
|
||||
return data.toDouble(&ok);
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, bool>, bool> = true>
|
||||
bool deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
if (value.isBool()) {
|
||||
ok = true;
|
||||
return value.toBool();
|
||||
} else if (value.isString()) {
|
||||
if (value.toString() == QLatin1String("true")) {
|
||||
ok = true;
|
||||
return true;
|
||||
} else if (value.toString() == QLatin1String("false")) {
|
||||
ok = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, QString>, bool> = true>
|
||||
QString deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
ok = value.isString();
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, QByteArray>, bool> = true>
|
||||
QByteArray deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
QByteArray data = value.toVariant().toByteArray();
|
||||
if (value.isString()) {
|
||||
ok = true;
|
||||
return QByteArray::fromBase64(data);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename L /*list*/, typename T /*element*/>
|
||||
QVariant deserializeList(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
if (!value.isArray()) {
|
||||
ok = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
L list;
|
||||
QJsonArray array = value.toArray();
|
||||
for (auto arrayValue : array) {
|
||||
ok = false;
|
||||
T value = deserialize<T>(arrayValue, ok);
|
||||
if (!ok)
|
||||
break;
|
||||
list.append(value);
|
||||
}
|
||||
return QVariant::fromValue(list);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QVariant deserializeCommon(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
ok = false;
|
||||
return QVariant::fromValue<T>(deserialize<T>(value, ok));
|
||||
}
|
||||
|
||||
} // namespace ProtobufScalarJsonSerializers
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // PROTOBUFSCALARJSONSERIALIZERS_P_H
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <QtProtobuf/private/protobufscalarserializers_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*
|
||||
Gets length of a byte-array and prepends to it its serialized length value
|
||||
using the appropriate serialization algorithm
|
||||
|
||||
Returns 'data' with its length prepended
|
||||
*/
|
||||
QByteArray ProtobufScalarSerializers::prependLengthDelimitedSize(const QByteArray &data)
|
||||
{
|
||||
return serializeVarintCommon<uint32_t>(data.size()) + data;
|
||||
}
|
||||
|
||||
std::optional<QByteArray>
|
||||
ProtobufScalarSerializers::deserializeLengthDelimited(QProtobufSelfcheckIterator &it)
|
||||
{
|
||||
if (it.bytesLeft() != 0) {
|
||||
if (auto opt = deserializeVarintCommon<QtProtobuf::uint64>(it); opt) {
|
||||
if (quint64 length = opt.value(); it.isValid() && quint64(it.bytesLeft()) >= length
|
||||
&& length <= quint64(QByteArray::maxSize())) {
|
||||
QByteArray result(it.data(), qsizetype(length));
|
||||
it += length;
|
||||
return { std::move(result) };
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -0,0 +1,397 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef PROTOBUFSCALARSERIALIZERS_P_H
|
||||
#define PROTOBUFSCALARSERIALIZERS_P_H
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtProtobuf/private/qprotobufselfcheckiterator_p.h>
|
||||
#include <QtProtobuf/qtprotobuftypes.h>
|
||||
|
||||
#include <QtCore/qtconfigmacros.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace ProtobufScalarSerializers {
|
||||
// The below type trait structures help to determine the required encoding method for protobuf
|
||||
// types.
|
||||
// See https://protobuf.dev/programming-guides/encoding for details.
|
||||
|
||||
// Tests if V is a Varint-compatible unsigned integer type.
|
||||
// uint32 | uint64
|
||||
template <typename V>
|
||||
using is_unsigned_int = std::conjunction<std::is_integral<V>, std::is_unsigned<V>>;
|
||||
|
||||
template <typename V>
|
||||
using if_unsigned_int = std::enable_if_t<is_unsigned_int<V>::value, bool>;
|
||||
|
||||
// Tests if V is a Varint-compatible signed integer type.
|
||||
// int32 | int64
|
||||
template <typename V>
|
||||
constexpr inline bool IsSignedInt = false;
|
||||
template <>
|
||||
constexpr inline bool IsSignedInt<QtProtobuf::int32> = true;
|
||||
template <>
|
||||
constexpr inline bool IsSignedInt<QtProtobuf::int64> = true;
|
||||
|
||||
template <typename V>
|
||||
using is_signed_int = std::integral_constant<bool, IsSignedInt<V>>;
|
||||
|
||||
template <typename V>
|
||||
using if_signed_int = std::enable_if_t<is_signed_int<V>::value, bool>;
|
||||
|
||||
// Tests if V is a Varint-compatible signed integer type, with ZigZag encoding support.
|
||||
// sint32 | sint64
|
||||
template <typename V>
|
||||
using is_zigzag_int = std::conjunction<std::is_integral<V>, std::is_signed<V>>;
|
||||
|
||||
template <typename V>
|
||||
using if_zigzag_int = std::enable_if_t<is_zigzag_int<V>::value, bool>;
|
||||
|
||||
// Tests if V is a Varint-compatible integer type.
|
||||
// int32 | int64 | uint32 | uint64 | sint32 | sint64
|
||||
template <typename V>
|
||||
using is_int = std::disjunction<std::is_integral<V>, is_signed_int<V>>;
|
||||
|
||||
// Tests if V is a 32-bit integer without the need for encoding.
|
||||
// sfixed32 | fixed32
|
||||
template <typename V>
|
||||
constexpr inline bool IsFixed32Int = false;
|
||||
template <>
|
||||
constexpr inline bool IsFixed32Int<QtProtobuf::fixed32> = true;
|
||||
template <>
|
||||
constexpr inline bool IsFixed32Int<QtProtobuf::sfixed32> = true;
|
||||
|
||||
template <typename V>
|
||||
using is_fixed32_int = std::integral_constant<bool, IsFixed32Int<V>>;
|
||||
|
||||
// Tests if V is a 64-bit integer without the need for encoding.
|
||||
// sfixed64 | fixed64
|
||||
template <typename V>
|
||||
constexpr inline bool IsFixed64Int = false;
|
||||
template <>
|
||||
constexpr inline bool IsFixed64Int<QtProtobuf::fixed64> = true;
|
||||
template <>
|
||||
constexpr inline bool IsFixed64Int<QtProtobuf::sfixed64> = true;
|
||||
|
||||
template <typename V>
|
||||
using is_fixed64_int = std::integral_constant<bool, IsFixed64Int<V>>;
|
||||
|
||||
// Tests if V is a 32-bit type without the need for encoding.
|
||||
// sfixed32 | fixed32 | float
|
||||
template <typename V>
|
||||
using is_i32 = std::disjunction<is_fixed32_int<V>, std::is_same<V, float>>;
|
||||
|
||||
// Tests if V is a 64-bit type without the need for encoding.
|
||||
// sfixed64 | fixed64 | double
|
||||
template <typename V>
|
||||
using is_i64 = std::disjunction<is_fixed64_int<V>, std::is_same<V, double>>;
|
||||
|
||||
// Tests if V is a type without the need for encoding.
|
||||
// sfixed32 | fixed32 | float | sfixed64 | fixed64 | double
|
||||
template <typename V>
|
||||
using is_i32_or_i64 = std::disjunction<is_i32<V>, is_i64<V>>;
|
||||
|
||||
template <typename V>
|
||||
using if_i32_or_i64 = std::enable_if_t<is_i32_or_i64<V>::value, bool>;
|
||||
|
||||
// Tests if V is the length-delimited non-message, non-list type.
|
||||
// QString | QByteArray
|
||||
template <typename V>
|
||||
constexpr inline bool IsLengthDelimited = false;
|
||||
template <>
|
||||
constexpr inline bool IsLengthDelimited<QByteArray> = true;
|
||||
template <>
|
||||
constexpr inline bool IsLengthDelimited<QString> = true;
|
||||
|
||||
template <typename V>
|
||||
using is_length_delimited = std::integral_constant<bool, IsLengthDelimited<V>>;
|
||||
|
||||
template <typename V>
|
||||
using if_length_delimited = std::enable_if_t<is_length_delimited<V>::value, bool>;
|
||||
|
||||
template <typename V>
|
||||
using if_not_length_delimited = std::enable_if_t<std::negation_v<is_length_delimited<V>>, bool>;
|
||||
|
||||
// Test if V can be stored in non-packed repeated field
|
||||
template <typename V>
|
||||
using is_non_packed_compatible = std::disjunction<is_int<V>, is_i32_or_i64<V>,
|
||||
is_length_delimited<V>>;
|
||||
|
||||
template <typename V>
|
||||
using if_non_packed_compatible = std::enable_if_t<is_non_packed_compatible<V>::value, bool>;
|
||||
|
||||
[[nodiscard]] QByteArray prependLengthDelimitedSize(const QByteArray &data);
|
||||
[[nodiscard]] std::optional<QByteArray> deserializeLengthDelimited(QProtobufSelfcheckIterator &it);
|
||||
|
||||
// ###########################################################################
|
||||
// Serializers
|
||||
// ###########################################################################
|
||||
|
||||
template <typename V, if_unsigned_int<V> = true>
|
||||
[[nodiscard]] QByteArray serializeVarintCommon(const V &value)
|
||||
{
|
||||
if (value == 0)
|
||||
return QByteArray(1, char(0));
|
||||
|
||||
quint64 varint = value;
|
||||
QByteArray result;
|
||||
|
||||
while (varint != 0) {
|
||||
// Put 7 bits to result buffer and mark as "not last" (0b10000000)
|
||||
result.append((varint & 0b01111111) | 0b10000000);
|
||||
// Divide values to chunks of 7 bits and move to next chunk
|
||||
varint >>= 7;
|
||||
}
|
||||
|
||||
result.data()[result.size() - 1] &= ~0b10000000;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------Integral and floating point types serializers---------------
|
||||
/*
|
||||
Serialization of unsigned integral types
|
||||
|
||||
Use Varint encoding[0]:
|
||||
"Varints are a method of serializing integers using one or more bytes. Smaller numbers
|
||||
[regardless its type] take a smaller number of bytes."
|
||||
|
||||
[0]: https://protobuf.dev/programming-guides/encoding
|
||||
|
||||
value: Value to serialize
|
||||
Returns a byte array with 'value' encoded
|
||||
*/
|
||||
template <typename V, if_unsigned_int<V> = true>
|
||||
[[nodiscard]] QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
return serializeVarintCommon(value);
|
||||
}
|
||||
|
||||
/*
|
||||
Serialization of fixed-length primitive types
|
||||
|
||||
Natural layout of bits is used: value is encoded in a byte array same way as it is located in
|
||||
memory
|
||||
|
||||
value: Value to serialize
|
||||
returns a byte array with 'value' encoded
|
||||
*/
|
||||
template <typename V, if_i32_or_i64<V> = true>
|
||||
[[nodiscard]] QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
// Reserve required number of bytes
|
||||
QByteArray result(sizeof(V), Qt::Uninitialized);
|
||||
qToUnaligned(qToLittleEndian(value), result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
Serialization of signed integral types
|
||||
Uses ZigZag encoding[0] first then apply serialization as for unsigned integral types
|
||||
|
||||
[0]: https://protobuf.dev/programming-guides/encoding
|
||||
|
||||
value: Value to serialize
|
||||
Returns a byte array with 'value' encoded
|
||||
*/
|
||||
template <typename V, if_zigzag_int<V> = true>
|
||||
[[nodiscard]] QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
using UV = std::make_unsigned_t<V>;
|
||||
UV uValue = 0;
|
||||
|
||||
// Use ZigZag convertion first and apply unsigned variant next
|
||||
V zigZagValue = (value << 1) ^ (value >> (sizeof(UV) * 8 - 1));
|
||||
uValue = static_cast<UV>(zigZagValue);
|
||||
return serializeBasic(uValue);
|
||||
}
|
||||
|
||||
template <typename V, if_signed_int<V> = true>
|
||||
[[nodiscard]] QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
// Non-ZigZag signed integers should always be (de)serialized as the
|
||||
// QtProtobuf::uint64
|
||||
return serializeBasic(static_cast<QtProtobuf::uint64>(value));
|
||||
}
|
||||
|
||||
//------------------QString and QByteArray types serializers-----------------
|
||||
template <typename V, if_length_delimited<V> = true>
|
||||
[[nodiscard]] QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
// Varint serialize field size and apply result as starting point
|
||||
if constexpr (std::is_same<V, QString>::value)
|
||||
return prependLengthDelimitedSize(value.toUtf8());
|
||||
else
|
||||
return prependLengthDelimitedSize(value);
|
||||
}
|
||||
|
||||
//--------------------------List types serializers---------------------------
|
||||
template <typename V, if_not_length_delimited<V> = true>
|
||||
[[nodiscard]] QByteArray serializeListType(const QList<V> &listValue)
|
||||
{
|
||||
if (listValue.isEmpty())
|
||||
return {};
|
||||
|
||||
QByteArray serializedList;
|
||||
for (auto &value : listValue)
|
||||
serializedList.append(serializeBasic<V>(value));
|
||||
|
||||
// If internal field type is not LengthDelimited, exact amount of fields to be specified
|
||||
serializedList = prependLengthDelimitedSize(serializedList);
|
||||
return serializedList;
|
||||
}
|
||||
|
||||
template <typename V, if_non_packed_compatible<V> = true>
|
||||
[[nodiscard]] QByteArray serializeNonPackedList(const QList<V> &listValue, const QByteArray &header)
|
||||
{
|
||||
QByteArray serializedList;
|
||||
for (auto &value : listValue) {
|
||||
serializedList.append(header);
|
||||
serializedList.append(serializeBasic<V>(value));
|
||||
}
|
||||
return serializedList;
|
||||
}
|
||||
|
||||
// ###########################################################################
|
||||
// Deserializers
|
||||
// ###########################################################################
|
||||
template <typename V, if_unsigned_int<V> = true>
|
||||
[[nodiscard]] std::optional<V> deserializeVarintCommon(QProtobufSelfcheckIterator &it)
|
||||
{
|
||||
quint64 value = 0;
|
||||
int k = 0;
|
||||
while (true) {
|
||||
if (it.bytesLeft() == 0)
|
||||
return std::nullopt;
|
||||
quint64 byte = quint64(static_cast<unsigned char>(*it));
|
||||
value += (byte & 0b01111111) << k;
|
||||
k += 7;
|
||||
if (((*it) & 0b10000000) == 0)
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
++it;
|
||||
return { V(value) };
|
||||
}
|
||||
|
||||
//-------------Integral and floating point types deserializers---------------
|
||||
template <typename V, if_i32_or_i64<V> = false>
|
||||
[[nodiscard]] bool deserializeBasic(QProtobufSelfcheckIterator &it, QVariant &variantValue)
|
||||
{
|
||||
qsizetype size = sizeof(V);
|
||||
if (it.bytesLeft() < size)
|
||||
return false;
|
||||
variantValue = QVariant::fromValue(qFromLittleEndian(qFromUnaligned<V>(it.data())));
|
||||
it += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V, if_unsigned_int<V> = false>
|
||||
[[nodiscard]] bool deserializeBasic(QProtobufSelfcheckIterator &it, QVariant &variantValue)
|
||||
{
|
||||
auto opt = deserializeVarintCommon<V>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
variantValue = QVariant::fromValue(opt.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V, if_zigzag_int<V> = false>
|
||||
[[nodiscard]] bool deserializeBasic(QProtobufSelfcheckIterator &it, QVariant &variantValue)
|
||||
{
|
||||
using UV = std::make_unsigned_t<V>;
|
||||
auto opt = deserializeVarintCommon<UV>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
UV unsignedValue = opt.value();
|
||||
V value = (unsignedValue >> 1) ^ (-1 * (unsignedValue & 1));
|
||||
variantValue = QVariant::fromValue<V>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V, if_signed_int<V> = false>
|
||||
[[nodiscard]] bool deserializeBasic(QProtobufSelfcheckIterator &it, QVariant &variantValue)
|
||||
{
|
||||
// Non-ZigZag signed integers should always be (de)serialized as the
|
||||
// QtProtobuf::uint64
|
||||
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
QtProtobuf::uint64 unsignedValue = opt.value();
|
||||
V value = static_cast<V>(unsignedValue);
|
||||
variantValue = QVariant::fromValue(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------QString and QByteArray types deserializers----------------
|
||||
template <typename V, if_length_delimited<V> = false>
|
||||
[[nodiscard]] bool deserializeBasic(QProtobufSelfcheckIterator &it, QVariant &variantValue)
|
||||
{
|
||||
std::optional<QByteArray> result = deserializeLengthDelimited(it);
|
||||
if (!result) {
|
||||
variantValue = QVariant();
|
||||
return false;
|
||||
}
|
||||
if constexpr (std::is_same<QString, V>::value)
|
||||
variantValue = QVariant::fromValue(QString::fromUtf8(*result));
|
||||
else
|
||||
variantValue = QVariant::fromValue(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------List types deserializers--------------------------
|
||||
template <typename V>
|
||||
[[nodiscard]] bool deserializeList(QProtobufSelfcheckIterator &it, QVariant &previousValue)
|
||||
{
|
||||
QList<V> out;
|
||||
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
quint64 count = opt.value();
|
||||
if (count > quint64(std::numeric_limits<qsizetype>::max()))
|
||||
return false;
|
||||
QProtobufSelfcheckIterator lastVarint = it + count;
|
||||
if (!lastVarint.isValid())
|
||||
return false;
|
||||
while (it != lastVarint) {
|
||||
QVariant variantValue;
|
||||
if (!deserializeBasic<V>(it, variantValue))
|
||||
return false;
|
||||
out.append(variantValue.value<V>());
|
||||
}
|
||||
previousValue.setValue(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] bool deserializeNonPackedList(QProtobufSelfcheckIterator &it, QVariant &previousValue)
|
||||
{
|
||||
Q_ASSERT_X(previousValue.metaType() == QMetaType::fromType<QList<V>>() && previousValue.data(),
|
||||
"QProtobufSerializerPrivate::deserializeNonPackedList",
|
||||
"Previous value metatype doesn't match the list metatype");
|
||||
QVariant variantValue;
|
||||
if (deserializeBasic<V>(it, variantValue)) {
|
||||
QList<V> *out = reinterpret_cast<QList<V> *>(previousValue.data());
|
||||
out->append(variantValue.value<V>());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ProtobufScalarSerializers
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // PROTOBUFSCALARSERIALIZERS_P_H
|
|
@ -4,6 +4,7 @@
|
|||
#include <QtProtobuf/qprotobufjsonserializer.h>
|
||||
|
||||
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
|
||||
#include <QtProtobuf/private/protobufscalarjsonserializers_p.h>
|
||||
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
|
||||
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
||||
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
||||
|
@ -11,15 +12,13 @@
|
|||
#include <QtProtobuf/private/qtprotobufserializerhelpers_p.h>
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qjsonarray.h>
|
||||
#include <QtCore/qjsondocument.h>
|
||||
#include <QtCore/qjsonobject.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/private/qnumeric_p.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
@ -41,6 +40,7 @@ QT_BEGIN_NAMESPACE
|
|||
|
||||
using namespace Qt::StringLiterals;
|
||||
using namespace QtProtobufPrivate;
|
||||
using namespace ProtobufScalarJsonSerializers;
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -120,38 +120,6 @@ private:
|
|||
class QProtobufJsonSerializerPrivate final
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerPrivate)
|
||||
|
||||
// Tests if V is JSON compatible integer
|
||||
// int32 | int64 | uint32 | sint32 | sint64 | fixed32 | sfixed32 | sfixed64
|
||||
template <typename V>
|
||||
struct IsJsonInt
|
||||
: std::integral_constant<
|
||||
bool,
|
||||
std::is_same_v<V, QtProtobuf::int32> || std::is_same_v<V, QtProtobuf::int64>
|
||||
|| std::is_same_v<V, QtProtobuf::uint32> || std::is_same_v<V, QtProtobuf::sint32>
|
||||
|| std::is_same_v<V, QtProtobuf::sint64> || std::is_same_v<V, QtProtobuf::fixed32>
|
||||
|| std::is_same_v<V, QtProtobuf::sfixed32>
|
||||
|| std::is_same_v<V, QtProtobuf::sfixed64>>
|
||||
{
|
||||
};
|
||||
|
||||
// Tests if V is JSON incompatible 64-bit unsigned integer
|
||||
// uint64 | fixed64
|
||||
template <typename V>
|
||||
struct IsJsonInt64
|
||||
: std::integral_constant<
|
||||
bool, std::is_same_v<V, QtProtobuf::uint64> || std::is_same_v<V, QtProtobuf::fixed64>>
|
||||
{
|
||||
};
|
||||
|
||||
// Tests if V is JSON compatible floating point number
|
||||
// float | double
|
||||
template <typename V>
|
||||
struct IsJsonFloatingPoint
|
||||
: std::integral_constant<bool, std::is_same_v<V, float> || std::is_same_v<V, double>>
|
||||
{
|
||||
};
|
||||
|
||||
public:
|
||||
using Serializer = std::function<QJsonValue(const QVariant &)>;
|
||||
using Deserializer = std::function<QVariant(const QJsonValue&, bool &ok)>;
|
||||
|
@ -167,16 +135,14 @@ public:
|
|||
template <typename T>
|
||||
static SerializationHandlers createCommonHandler()
|
||||
{
|
||||
return { QProtobufJsonSerializerPrivate::serializeCommon<T>,
|
||||
QProtobufJsonSerializerPrivate::deserializeCommon<T>,
|
||||
return { serializeCommon<T>, deserializeCommon<T>,
|
||||
ProtobufFieldPresenceChecker::isPresent<T> };
|
||||
}
|
||||
|
||||
template <typename L, typename T>
|
||||
static SerializationHandlers createCommonListHandler()
|
||||
{
|
||||
return { QProtobufJsonSerializerPrivate::serializeList<L>,
|
||||
QProtobufJsonSerializerPrivate::deserializeList<L, T>,
|
||||
return { serializeList<L>, deserializeList<L, T>,
|
||||
ProtobufFieldPresenceChecker::isPresent<L> };
|
||||
}
|
||||
|
||||
|
@ -190,59 +156,6 @@ public:
|
|||
|
||||
using SerializerRegistry = QHash<int/*metatypeid*/, SerializationHandlers>;
|
||||
|
||||
template <typename T>
|
||||
static QJsonValue serializeCommon(const QVariant &propertyValue)
|
||||
{
|
||||
return serialize(propertyValue.value<T>());
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<IsJsonInt<T>::value, bool> = true>
|
||||
static QJsonValue serialize(T propertyValue)
|
||||
{
|
||||
return QJsonValue(qint64(propertyValue));
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<IsJsonInt64<T>::value, bool> = true>
|
||||
static QJsonValue serialize(T propertyValue)
|
||||
{
|
||||
return QJsonValue(QString::number(propertyValue));
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<IsJsonFloatingPoint<T>::value, bool> = true>
|
||||
static QJsonValue serialize(T propertyValue)
|
||||
{
|
||||
if (propertyValue == -std::numeric_limits<T>::infinity())
|
||||
return QJsonValue("-Infinity"_L1);
|
||||
|
||||
if (propertyValue == std::numeric_limits<T>::infinity())
|
||||
return QJsonValue("Infinity"_L1);
|
||||
|
||||
if (propertyValue != propertyValue)
|
||||
return QJsonValue("NaN"_L1);
|
||||
|
||||
return QJsonValue(propertyValue);
|
||||
}
|
||||
|
||||
static QJsonValue serialize(bool propertyValue) { return QJsonValue(propertyValue); }
|
||||
|
||||
static QJsonValue serialize(const QString &propertyValue) { return QJsonValue(propertyValue); }
|
||||
|
||||
static QJsonValue serialize(const QByteArray &propertyValue)
|
||||
{
|
||||
return QJsonValue(QString::fromUtf8(propertyValue.toBase64()));
|
||||
}
|
||||
|
||||
template <typename L>
|
||||
static QJsonValue serializeList(const QVariant &propertyValue)
|
||||
{
|
||||
QJsonArray arr;
|
||||
L listValue = propertyValue.value<L>();
|
||||
for (const auto &value : listValue) {
|
||||
arr.append(serialize(value));
|
||||
}
|
||||
return QJsonValue(arr);
|
||||
}
|
||||
|
||||
QProtobufJsonSerializerPrivate() : deserializer(this)
|
||||
{
|
||||
[[maybe_unused]] static bool initialized = []() -> bool {
|
||||
|
@ -263,14 +176,10 @@ public:
|
|||
handlers[qMetaTypeId<bool>()] = createCommonHandler<bool>();
|
||||
handlers[QMetaType::QString] = createCommonHandler<QString>();
|
||||
handlers[QMetaType::QByteArray] = createCommonHandler<QByteArray>();
|
||||
handlers[QMetaType::Float] = { QProtobufJsonSerializerPrivate::serializeCommon<float>,
|
||||
QProtobufJsonSerializerPrivate::deserializeCommon<float>,
|
||||
handlers[QMetaType::Float] = { serializeCommon<float>, deserializeCommon<float>,
|
||||
QProtobufJsonSerializerPrivate::isPresent<float> };
|
||||
handlers[QMetaType::Double] = {
|
||||
QProtobufJsonSerializerPrivate::serializeCommon<double>,
|
||||
QProtobufJsonSerializerPrivate::deserializeCommon<double>,
|
||||
QProtobufJsonSerializerPrivate::isPresent<double>
|
||||
};
|
||||
handlers[QMetaType::Double] = { serializeCommon<double>, deserializeCommon<double>,
|
||||
isPresent<double> };
|
||||
|
||||
handlers[qMetaTypeId<QtProtobuf::boolList>()] = createCommonListHandler<
|
||||
QtProtobuf::boolList, bool>();
|
||||
|
@ -306,148 +215,6 @@ public:
|
|||
}
|
||||
~QProtobufJsonSerializerPrivate() = default;
|
||||
|
||||
template <typename T>
|
||||
static QVariant deserializeCommon(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
ok = false;
|
||||
return QVariant::fromValue<T>(deserialize<T>(value, ok));
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<IsJsonInt<T>::value, bool> = true>
|
||||
static T deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
auto variantValue = value.toVariant();
|
||||
qint64 raw = 0;
|
||||
switch (variantValue.metaType().id()) {
|
||||
case QMetaType::QString: // TODO: check if string has prepending/appending whitespaces.
|
||||
case QMetaType::LongLong:
|
||||
raw = variantValue.toLongLong(&ok);
|
||||
break;
|
||||
case QMetaType::Double: {
|
||||
double d = value.toDouble();
|
||||
ok = convertDoubleTo(d, &raw) && double(raw) == d;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// For types that "smaller" than qint64 we need to check if the value fits its limits range
|
||||
if constexpr (sizeof(T) != sizeof(qint64)) {
|
||||
if (ok) {
|
||||
if constexpr (std::is_same_v<T, QtProtobuf::sfixed32>
|
||||
|| std::is_same_v<T, QtProtobuf::int32>) {
|
||||
using limits = std::numeric_limits<qint32>;
|
||||
ok = raw >= limits::min() && raw <= limits::max();
|
||||
} else if constexpr (std::is_same_v<T, QtProtobuf::fixed32>) {
|
||||
using limits = std::numeric_limits<quint32>;
|
||||
ok = raw >= limits::min() && raw <= limits::max();
|
||||
} else {
|
||||
using limits = std::numeric_limits<T>;
|
||||
ok = raw >= limits::min() && raw <= limits::max();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return T(raw);
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<IsJsonInt64<T>::value, bool> = true>
|
||||
static T deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
quint64 raw = 0;
|
||||
auto variantValue = value.toVariant();
|
||||
switch (variantValue.metaType().id()) {
|
||||
case QMetaType::QString:
|
||||
case QMetaType::LongLong:
|
||||
// Here we attempt converting the value to ULongLong
|
||||
raw = variantValue.toULongLong(&ok);
|
||||
break;
|
||||
case QMetaType::Double: {
|
||||
double d = value.toDouble();
|
||||
ok = convertDoubleTo(d, &raw) && double(raw) == d;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return T(raw);
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<IsJsonFloatingPoint<T>::value, bool> = true>
|
||||
static T deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
ok = true;
|
||||
QByteArray data = value.toVariant().toByteArray();
|
||||
if (data.toLower() == "-infinity"_ba)
|
||||
return -std::numeric_limits<T>::infinity();
|
||||
|
||||
if (data.toLower() == "infinity"_ba)
|
||||
return std::numeric_limits<T>::infinity();
|
||||
|
||||
if (data.toLower() == "nan"_ba)
|
||||
return T(NAN);
|
||||
|
||||
if constexpr (std::is_same_v<T, float>)
|
||||
return data.toFloat(&ok);
|
||||
else
|
||||
return data.toDouble(&ok);
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, bool>, bool> = true>
|
||||
static bool deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
if (value.isBool()) {
|
||||
ok = true;
|
||||
return value.toBool();
|
||||
} else if (value.isString()) {
|
||||
if (value.toString() == "true"_L1) {
|
||||
ok = true;
|
||||
return true;
|
||||
} else if (value.toString() == "false"_L1) {
|
||||
ok = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, QString>, bool> = true>
|
||||
static QString deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
ok = value.isString();
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, QByteArray>, bool> = true>
|
||||
static QByteArray deserialize(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
QByteArray data = value.toVariant().toByteArray();
|
||||
if (value.isString()) {
|
||||
ok = true;
|
||||
return QByteArray::fromBase64(data);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename L /*list*/, typename T /*element*/>
|
||||
static QVariant deserializeList(const QJsonValue &value, bool &ok)
|
||||
{
|
||||
if (!value.isArray()) {
|
||||
ok = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
L list;
|
||||
QJsonArray array = value.toArray();
|
||||
for (auto arrayValue : array) {
|
||||
ok = false;
|
||||
T value = deserialize<T>(arrayValue, ok);
|
||||
if (!ok)
|
||||
break;
|
||||
list.append(value);
|
||||
}
|
||||
return QVariant::fromValue(list);
|
||||
}
|
||||
|
||||
void clearError();
|
||||
|
||||
QAbstractProtobufSerializer::Error lastError = QAbstractProtobufSerializer::Error::None;
|
||||
|
@ -477,8 +244,7 @@ bool QProtobufJsonSerializerImpl::serializeEnum(QVariant &value,
|
|||
return false;
|
||||
if (!ProtobufFieldPresenceChecker::isPresent<QStringList>(value))
|
||||
return true;
|
||||
m_result.insert(jsonName.toString(),
|
||||
QProtobufJsonSerializerPrivate::serializeList<QStringList>(value));
|
||||
m_result.insert(jsonName.toString(), serializeList<QStringList>(value));
|
||||
} else {
|
||||
if (!value.convert(QMetaType::fromType<QString>()))
|
||||
return false;
|
||||
|
@ -486,8 +252,7 @@ bool QProtobufJsonSerializerImpl::serializeEnum(QVariant &value,
|
|||
&& !isOneofOrOptionalField(fieldInfo.fieldFlags())) {
|
||||
return true;
|
||||
}
|
||||
m_result.insert(jsonName.toString(),
|
||||
QProtobufJsonSerializerPrivate::serializeCommon<QString>(value));
|
||||
m_result.insert(jsonName.toString(), serializeCommon<QString>(value));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -570,17 +335,14 @@ bool QProtobufJsonDeserializerImpl::deserializeEnum(QVariant &value,
|
|||
bool ok = false;
|
||||
auto &state = m_state.last();
|
||||
if (fieldInfo.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
|
||||
value = QProtobufJsonSerializerPrivate::deserializeList<QStringList,
|
||||
QString>(state.scalarValue, ok);
|
||||
value = deserializeList<QStringList, QString>(state.scalarValue, ok);
|
||||
} else {
|
||||
// It's allowed to pass single enum value as numeric value.
|
||||
// Make the backward value conversion and deserialize enum as QtProtobuf::int64.
|
||||
if (state.scalarValue.isString()) {
|
||||
value = QProtobufJsonSerializerPrivate::deserializeCommon<QString>(state.scalarValue,
|
||||
ok);
|
||||
value = deserializeCommon<QString>(state.scalarValue, ok);
|
||||
} else {
|
||||
value = QProtobufJsonSerializerPrivate::deserializeCommon<
|
||||
QtProtobuf::int64>(state.scalarValue, ok);
|
||||
value = deserializeCommon<QtProtobuf::int64>(state.scalarValue, ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <QtProtobuf/qprotobufserializer.h>
|
||||
|
||||
#include <QtProtobuf/private/protobufscalarserializers_p.h>
|
||||
#include <QtProtobuf/private/qprotobufmessage_p.h>
|
||||
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
||||
#include <QtProtobuf/private/qprotobufserializer_p.h>
|
||||
|
@ -37,6 +38,7 @@ QT_BEGIN_NAMESPACE
|
|||
|
||||
using namespace Qt::StringLiterals;
|
||||
using namespace QtProtobufPrivate;
|
||||
using namespace ProtobufScalarSerializers;
|
||||
|
||||
template<std::size_t N>
|
||||
using SerializerRegistryType =
|
||||
|
@ -44,25 +46,22 @@ using SerializerRegistryType =
|
|||
|
||||
namespace {
|
||||
|
||||
#define QT_CONSTRUCT_PROTOBUF_SERIALIZATION_HANDLER(Type, WireType) \
|
||||
{ QMetaType::fromType<Type>(), \
|
||||
QProtobufSerializerPrivate::serializeWrapper< \
|
||||
Type, QProtobufSerializerPrivate::serializeBasic<Type>>, \
|
||||
QProtobufSerializerPrivate::deserializeBasic<Type>, \
|
||||
ProtobufFieldPresenceChecker::isPresent<Type>, WireType }
|
||||
#define QT_CONSTRUCT_PROTOBUF_LIST_SERIALIZATION_HANDLER(ListType, Type) \
|
||||
{ QMetaType::fromType<ListType>(), \
|
||||
QProtobufSerializerPrivate::serializeWrapper< \
|
||||
ListType, QProtobufSerializerPrivate::serializeListType<Type>>, \
|
||||
QProtobufSerializerPrivate::deserializeList<Type>, \
|
||||
ProtobufFieldPresenceChecker::isPresent<ListType>, QtProtobuf::WireTypes::LengthDelimited }
|
||||
#define QT_CONSTRUCT_PROTOBUF_SERIALIZATION_HANDLER(Type, WireType) \
|
||||
{ QMetaType::fromType<Type>(), \
|
||||
QProtobufSerializerPrivate::serializeWrapper<Type, serializeBasic<Type>>, \
|
||||
deserializeBasic<Type>, ProtobufFieldPresenceChecker::isPresent<Type>, WireType }
|
||||
#define QT_CONSTRUCT_PROTOBUF_LIST_SERIALIZATION_HANDLER(ListType, Type) \
|
||||
{ QMetaType::fromType<ListType>(), \
|
||||
QProtobufSerializerPrivate::serializeWrapper<ListType, serializeListType<Type>>, \
|
||||
deserializeList<Type>, ProtobufFieldPresenceChecker::isPresent<ListType>, \
|
||||
QtProtobuf::WireTypes::LengthDelimited }
|
||||
|
||||
#define QT_CONSTRUCT_PROTOBUF_NON_PACKED_LIST_SERIALIZATION_HANDLER(ListType, Type, WireType) \
|
||||
{ QMetaType::fromType<ListType>(), \
|
||||
QProtobufSerializerPrivate::serializeNonPackedWrapper< \
|
||||
ListType, QProtobufSerializerPrivate::serializeNonPackedList<Type>>, \
|
||||
QProtobufSerializerPrivate::deserializeNonPackedList<Type>, \
|
||||
ProtobufFieldPresenceChecker::isPresent<ListType>, WireType }
|
||||
QProtobufSerializerPrivate::serializeNonPackedWrapper<ListType, \
|
||||
serializeNonPackedList<Type>>, \
|
||||
deserializeNonPackedList<Type>, ProtobufFieldPresenceChecker::isPresent<ListType>, \
|
||||
WireType }
|
||||
|
||||
constexpr SerializerRegistryType<30> IntegratedTypesSerializers = { {
|
||||
QT_CONSTRUCT_PROTOBUF_SERIALIZATION_HANDLER(float, QtProtobuf::WireTypes::Fixed32),
|
||||
|
@ -206,13 +205,11 @@ bool QProtobufSerializerImpl::serializeEnum(QVariant &value,
|
|||
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::NonPacked)) {
|
||||
const auto header = encodeHeader(fieldInfo.fieldNumber(),
|
||||
QtProtobuf::WireTypes::Varint);
|
||||
m_result.append(QProtobufSerializerPrivate::serializeNonPackedList<
|
||||
QtProtobuf::int64>(listValue, header));
|
||||
m_result.append(serializeNonPackedList<QtProtobuf::int64>(listValue, header));
|
||||
} else {
|
||||
m_result.append(encodeHeader(fieldInfo.fieldNumber(),
|
||||
QtProtobuf::WireTypes::LengthDelimited));
|
||||
m_result.append(QProtobufSerializerPrivate::serializeListType<
|
||||
QtProtobuf::int64>(listValue));
|
||||
m_result.append(serializeListType<QtProtobuf::int64>(listValue));
|
||||
}
|
||||
} else {
|
||||
if (!value.canConvert<QtProtobuf::int64>())
|
||||
|
@ -224,8 +221,7 @@ bool QProtobufSerializerImpl::serializeEnum(QVariant &value,
|
|||
}
|
||||
|
||||
m_result.append(encodeHeader(fieldInfo.fieldNumber(), QtProtobuf::WireTypes::Varint));
|
||||
m_result.append(QProtobufSerializerPrivate::serializeBasic<
|
||||
QtProtobuf::int64>(value.value<QtProtobuf::int64>()));
|
||||
m_result.append(serializeBasic<QtProtobuf::int64>(value.value<QtProtobuf::int64>()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -264,7 +260,7 @@ void QProtobufSerializerImpl::serializeMessageFieldEnd(const QProtobufMessage *m
|
|||
|
||||
QByteArray last = m_state.takeLast();
|
||||
last.append(encodeHeader(fieldInfo.fieldNumber(), QtProtobuf::WireTypes::LengthDelimited));
|
||||
last.append(QProtobufSerializerPrivate::serializeVarintCommon<uint32_t>(m_result.size()));
|
||||
last.append(serializeVarintCommon<uint32_t>(m_result.size()));
|
||||
last.append(m_result);
|
||||
m_result = last;
|
||||
}
|
||||
|
@ -284,7 +280,7 @@ QByteArray QProtobufSerializerImpl::encodeHeader(int fieldIndex, QtProtobuf::Wir
|
|||
// Returns a varint-encoded fieldIndex and wireType
|
||||
|
||||
uint32_t header = (fieldIndex << 3) | int(wireType);
|
||||
return QProtobufSerializerPrivate::serializeVarintCommon<uint32_t>(header);
|
||||
return serializeVarintCommon<uint32_t>(header);
|
||||
}
|
||||
|
||||
QProtobufDeserializerImpl::QProtobufDeserializerImpl(QProtobufSerializerPrivate *parent)
|
||||
|
@ -318,16 +314,15 @@ bool QProtobufDeserializerImpl::deserializeEnum(QVariant &value,
|
|||
value.convert(QMetaType::fromType<QList<QtProtobuf::int64>>());
|
||||
bool result = false;
|
||||
if (m_wireType == QtProtobuf::WireTypes::Varint) {
|
||||
result = QProtobufSerializerPrivate::deserializeNonPackedList<QtProtobuf::int64>(m_it,
|
||||
value);
|
||||
result = deserializeNonPackedList<QtProtobuf::int64>(m_it, value);
|
||||
} else if (m_wireType == QtProtobuf::WireTypes::LengthDelimited) {
|
||||
result = QProtobufSerializerPrivate::deserializeList<QtProtobuf::int64>(m_it, value);
|
||||
result = deserializeList<QtProtobuf::int64>(m_it, value);
|
||||
}
|
||||
value.convert(metaType);
|
||||
return result;
|
||||
}
|
||||
|
||||
return QProtobufSerializerPrivate::deserializeBasic<QtProtobuf::int64>(m_it, value);
|
||||
return deserializeBasic<QtProtobuf::int64>(m_it, value);
|
||||
}
|
||||
|
||||
int QProtobufDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
|
||||
|
@ -364,8 +359,7 @@ int QProtobufDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
|
|||
|
||||
if (ordering->fieldFlags(index).testAnyFlags({ QtProtobufPrivate::FieldFlag::Message,
|
||||
QtProtobufPrivate::FieldFlag::Map })) {
|
||||
auto
|
||||
opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
||||
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
||||
if (!opt) {
|
||||
setUnexpectedEndOfStreamError();
|
||||
return -1;
|
||||
|
@ -473,7 +467,7 @@ void QProtobufDeserializerImpl::skipVarint()
|
|||
void QProtobufDeserializerImpl::skipLengthDelimited()
|
||||
{
|
||||
//Get length of length-delimited field
|
||||
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
||||
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
||||
if (!opt) {
|
||||
m_it += m_it.bytesLeft() + 1;
|
||||
return;
|
||||
|
@ -493,7 +487,7 @@ bool QProtobufDeserializerImpl::decodeHeader(QProtobufSelfcheckIterator &it, int
|
|||
{
|
||||
if (it.bytesLeft() == 0)
|
||||
return false;
|
||||
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<uint32_t>(it);
|
||||
auto opt = deserializeVarintCommon<uint32_t>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
uint32_t header = opt.value();
|
||||
|
|
|
@ -34,9 +34,6 @@
|
|||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QProtobufSerializerImpl final : public QProtobufSerializerBase
|
||||
|
@ -105,93 +102,6 @@ private:
|
|||
|
||||
class QProtobufSerializerPrivate
|
||||
{
|
||||
// The below type trait structures help to determine the required encoding method for protobuf
|
||||
// types.
|
||||
// See https://protobuf.dev/programming-guides/encoding for details.
|
||||
|
||||
// Tests if V is a Varint-compatible unsigned integer type.
|
||||
// uint32 | uint64
|
||||
template<typename V>
|
||||
struct IsUnsignedInt
|
||||
: std::integral_constant<bool, std::is_integral<V>::value && std::is_unsigned<V>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is a Varint-compatible signed integer type.
|
||||
// int32 | int64
|
||||
template<typename V>
|
||||
struct IsSignedInt
|
||||
: std::integral_constant<bool,
|
||||
std::is_same<V, QtProtobuf::int32>::value
|
||||
|| std::is_same<V, QtProtobuf::int64>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is a Varint-compatible signed integer type, with ZigZag encoding support.
|
||||
// sint32 | sint64
|
||||
template<typename V>
|
||||
struct IsZigZagInt
|
||||
: std::integral_constant<bool, std::is_integral<V>::value && std::is_signed<V>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is a Varint-compatible integer type.
|
||||
// int32 | int64 | uint32 | uint64 | sint32 | sint64
|
||||
template<typename V>
|
||||
struct IsInt : std::integral_constant<bool, std::is_integral<V>::value || IsSignedInt<V>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is a 32-bit integer without the need for encoding.
|
||||
// sfixed32 | fixed32
|
||||
template<typename V>
|
||||
struct IsFixed32Int
|
||||
: std::integral_constant<bool,
|
||||
std::is_same<V, QtProtobuf::fixed32>::value
|
||||
|| std::is_same<V, QtProtobuf::sfixed32>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is a 64-bit integer without the need for encoding.
|
||||
// sfixed64 | fixed64
|
||||
template<typename V>
|
||||
struct IsFixed64Int
|
||||
: std::integral_constant<bool,
|
||||
std::is_same<V, QtProtobuf::fixed64>::value
|
||||
|| std::is_same<V, QtProtobuf::sfixed64>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is an integer without the need for encoding.
|
||||
// sfixed32 | fixed32 | sfixed64 | fixed64
|
||||
template<typename V>
|
||||
struct IsFixedInt
|
||||
: std::integral_constant<bool, IsFixed32Int<V>::value || IsFixed64Int<V>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is a 32-bit type without the need for encoding.
|
||||
// sfixed32 | fixed32 | float
|
||||
template<typename V>
|
||||
struct IsI32
|
||||
: std::integral_constant<bool, IsFixed32Int<V>::value || std::is_same<V, float>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is a 64-bit type without the need for encoding.
|
||||
// sfixed64 | fixed64 | double
|
||||
template<typename V>
|
||||
struct IsI64
|
||||
: std::integral_constant<bool, IsFixed64Int<V>::value || std::is_same<V, double>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is a type without the need for encoding.
|
||||
// sfixed32 | fixed32 | float | sfixed64 | fixed64 | double
|
||||
template<typename V>
|
||||
struct IsI32OrI64 : std::integral_constant<bool, IsI64<V>::value || IsI32<V>::value>
|
||||
{};
|
||||
|
||||
// Tests if V is the length-delimited non-message, non-list type.
|
||||
// QString | QByteArray
|
||||
template<typename V>
|
||||
struct IsLengthDelimited
|
||||
: std::integral_constant<
|
||||
bool, std::is_same<V, QString>::value || std::is_same<V, QByteArray>::value>
|
||||
{
|
||||
};
|
||||
|
||||
public:
|
||||
// Serializer is interface function for serialize method
|
||||
using Serializer = QByteArray (*)(const QVariant &, const QByteArray &);
|
||||
|
@ -212,326 +122,6 @@ public:
|
|||
|
||||
QProtobufSerializerPrivate();
|
||||
~QProtobufSerializerPrivate() = default;
|
||||
// ###########################################################################
|
||||
// Serializers
|
||||
// ###########################################################################
|
||||
|
||||
template<typename V, typename std::enable_if_t<IsUnsignedInt<V>::value, int> = 0>
|
||||
static QByteArray serializeVarintCommon(const V &value)
|
||||
{
|
||||
if (value == 0)
|
||||
return QByteArray(1, char(0));
|
||||
|
||||
qProtoDebug() << "value" << value;
|
||||
quint64 varint = value;
|
||||
QByteArray result;
|
||||
|
||||
while (varint != 0) {
|
||||
// Put 7 bits to result buffer and mark as "not last" (0b10000000)
|
||||
result.append((varint & 0b01111111) | 0b10000000);
|
||||
// Divide values to chunks of 7 bits and move to next chunk
|
||||
varint >>= 7;
|
||||
}
|
||||
|
||||
result.data()[result.size() - 1] &= ~0b10000000;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------Integral and floating point types serializers---------------
|
||||
/*
|
||||
Serialization of fixed-length primitive types
|
||||
|
||||
Natural layout of bits is used: value is encoded in a byte array same way as it is located in
|
||||
memory
|
||||
|
||||
value: Value to serialize
|
||||
returns a byte array with 'value' encoded
|
||||
*/
|
||||
template<typename V, typename std::enable_if_t<IsI32OrI64<V>::value, int> = 0>
|
||||
static QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
qProtoDebug() << "value" << value;
|
||||
|
||||
// Reserve required number of bytes
|
||||
QByteArray result(sizeof(V), Qt::Uninitialized);
|
||||
qToUnaligned(qToLittleEndian(value), result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
Serialization of signed integral types
|
||||
Uses ZigZag encoding[0] first then apply serialization as for unsigned integral types
|
||||
|
||||
[0]: https://protobuf.dev/programming-guides/encoding
|
||||
|
||||
value: Value to serialize
|
||||
Returns a byte array with 'value' encoded
|
||||
*/
|
||||
template<typename V, typename std::enable_if_t<IsZigZagInt<V>::value, int> = 0>
|
||||
static QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
qProtoDebug() << "value" << value;
|
||||
using UV = std::make_unsigned_t<V>;
|
||||
UV uValue = 0;
|
||||
|
||||
// Use ZigZag convertion first and apply unsigned variant next
|
||||
V zigZagValue = (value << 1) ^ (value >> (sizeof(UV) * 8 - 1));
|
||||
uValue = static_cast<UV>(zigZagValue);
|
||||
return serializeBasic(uValue);
|
||||
}
|
||||
|
||||
template<typename V, typename std::enable_if_t<IsSignedInt<V>::value, int> = 0>
|
||||
static QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
qProtoDebug() << "value" << value;
|
||||
// Non-ZigZag signed integers should always be (de)serialized as the
|
||||
// QtProtobuf::uint64
|
||||
return serializeBasic(static_cast<QtProtobuf::uint64>(value));
|
||||
}
|
||||
|
||||
/*!
|
||||
Serialization of unsigned integral types
|
||||
|
||||
Use Varint encoding[0]:
|
||||
"Varints are a method of serializing integers using one or more bytes. Smaller numbers
|
||||
[regardless its type] take a smaller number of bytes."
|
||||
|
||||
[0]: https://protobuf.dev/programming-guides/encoding
|
||||
|
||||
value: Value to serialize
|
||||
Returns a byte array with 'value' encoded
|
||||
*/
|
||||
template<typename V, typename std::enable_if_t<IsUnsignedInt<V>::value, int> = 0>
|
||||
static QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
qProtoDebug() << "value" << value;
|
||||
return serializeVarintCommon(value);
|
||||
}
|
||||
|
||||
//------------------QString and QByteArray types serializers-----------------
|
||||
template<typename V, typename std::enable_if_t<IsLengthDelimited<V>::value, int> = 0>
|
||||
static QByteArray serializeBasic(const V &value)
|
||||
{
|
||||
qProtoDebug("data.size: %" PRIdQSIZETYPE, value.size());
|
||||
// Varint serialize field size and apply result as starting point
|
||||
if constexpr (std::is_same<V, QString>::value)
|
||||
return prependLengthDelimitedSize(value.toUtf8());
|
||||
else
|
||||
return prependLengthDelimitedSize(value);
|
||||
}
|
||||
|
||||
//--------------------------List types serializers---------------------------
|
||||
template<typename V, typename std::enable_if_t<!IsLengthDelimited<V>::value, int> = 0>
|
||||
static QByteArray serializeListType(const QList<V> &listValue)
|
||||
{
|
||||
qProtoDebug("listValue.count %" PRIdQSIZETYPE, listValue.count());
|
||||
|
||||
if (listValue.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray serializedList;
|
||||
for (auto &value : listValue) {
|
||||
serializedList.append(serializeBasic<V>(value));
|
||||
}
|
||||
// If internal field type is not LengthDelimited, exact amount of fields to be specified
|
||||
serializedList = prependLengthDelimitedSize(serializedList);
|
||||
return serializedList;
|
||||
}
|
||||
|
||||
template<typename V,
|
||||
typename std::enable_if_t<
|
||||
IsInt<V>::value || IsI32OrI64<V>::value || IsLengthDelimited<V>::value, int> =
|
||||
0>
|
||||
static QByteArray serializeNonPackedList(const QList<V> &listValue, const QByteArray &header)
|
||||
{
|
||||
qProtoDebug("listValue.count %" PRIdQSIZETYPE, listValue.count());
|
||||
QByteArray serializedList;
|
||||
for (auto &value : listValue) {
|
||||
serializedList.append(header);
|
||||
serializedList.append(serializeBasic<V>(value));
|
||||
}
|
||||
return serializedList;
|
||||
}
|
||||
|
||||
// ###########################################################################
|
||||
// Deserializers
|
||||
// ###########################################################################
|
||||
template<typename V, typename std::enable_if_t<IsUnsignedInt<V>::value, int> = 0>
|
||||
[[nodiscard]] static std::optional<V>
|
||||
deserializeVarintCommon(QProtobufSelfcheckIterator &it)
|
||||
{
|
||||
qProtoDebug("currentByte: 0x%x", *it);
|
||||
|
||||
quint64 value = 0;
|
||||
int k = 0;
|
||||
while (true) {
|
||||
if (it.bytesLeft() == 0)
|
||||
return std::nullopt;
|
||||
quint64 byte = quint64(static_cast<unsigned char>(*it));
|
||||
value += (byte & 0b01111111) << k;
|
||||
k += 7;
|
||||
if (((*it) & 0b10000000) == 0)
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
++it;
|
||||
return { V(value) };
|
||||
}
|
||||
|
||||
//-------------Integral and floating point types deserializers---------------
|
||||
template<typename V, typename std::enable_if_t<IsI32OrI64<V>::value, int> = 0>
|
||||
[[nodiscard]] static bool deserializeBasic(QProtobufSelfcheckIterator &it,
|
||||
QVariant &variantValue)
|
||||
{
|
||||
qsizetype size = sizeof(V);
|
||||
if (it.bytesLeft() < size)
|
||||
return false;
|
||||
qProtoDebug("currentByte: 0x%x", *it);
|
||||
variantValue = QVariant::fromValue(qFromLittleEndian(qFromUnaligned<V>(it.data())));
|
||||
it += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V, typename std::enable_if_t<IsUnsignedInt<V>::value, int> = 0>
|
||||
[[nodiscard]] static bool deserializeBasic(QProtobufSelfcheckIterator &it,
|
||||
QVariant &variantValue)
|
||||
{
|
||||
qProtoDebug("currentByte: 0x%x", *it);
|
||||
|
||||
auto opt = deserializeVarintCommon<V>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
variantValue = QVariant::fromValue(opt.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V, typename std::enable_if_t<IsZigZagInt<V>::value, int> = 0>
|
||||
[[nodiscard]] static bool deserializeBasic(QProtobufSelfcheckIterator &it,
|
||||
QVariant &variantValue)
|
||||
{
|
||||
qProtoDebug("currentByte: 0x%x", *it);
|
||||
using UV = std::make_unsigned_t<V>;
|
||||
auto opt = deserializeVarintCommon<UV>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
UV unsignedValue = opt.value();
|
||||
V value = (unsignedValue >> 1) ^ (-1 * (unsignedValue & 1));
|
||||
variantValue = QVariant::fromValue<V>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V, typename std::enable_if_t<IsSignedInt<V>::value, int> = 0>
|
||||
[[nodiscard]] static bool deserializeBasic(QProtobufSelfcheckIterator &it,
|
||||
QVariant &variantValue)
|
||||
{
|
||||
qProtoDebug("currentByte: 0x%x", *it);
|
||||
// Non-ZigZag signed integers should always be (de)serialized as the
|
||||
// QtProtobuf::uint64
|
||||
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
QtProtobuf::uint64 unsignedValue = opt.value();
|
||||
V value = static_cast<V>(unsignedValue);
|
||||
variantValue = QVariant::fromValue(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------QString and QByteArray types deserializers----------------
|
||||
template<typename V, typename std::enable_if_t<IsLengthDelimited<V>::value, int> = 0>
|
||||
[[nodiscard]] static bool deserializeBasic(QProtobufSelfcheckIterator &it,
|
||||
QVariant &variantValue)
|
||||
{
|
||||
std::optional<QByteArray> result = deserializeLengthDelimited(it);
|
||||
if (!result) {
|
||||
variantValue = QVariant();
|
||||
return false;
|
||||
}
|
||||
if constexpr (std::is_same<QString, V>::value)
|
||||
variantValue = QVariant::fromValue(QString::fromUtf8(*result));
|
||||
else
|
||||
variantValue = QVariant::fromValue(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------List types deserializers--------------------------
|
||||
template<typename V>
|
||||
[[nodiscard]] static bool deserializeList(QProtobufSelfcheckIterator &it,
|
||||
QVariant &previousValue)
|
||||
{
|
||||
qProtoDebug("currentByte: 0x%x", *it);
|
||||
|
||||
QList<V> out;
|
||||
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(it);
|
||||
if (!opt)
|
||||
return false;
|
||||
quint64 count = opt.value();
|
||||
if (count > quint64(std::numeric_limits<qsizetype>::max()))
|
||||
return false;
|
||||
QProtobufSelfcheckIterator lastVarint = it + count;
|
||||
if (!lastVarint.isValid())
|
||||
return false;
|
||||
while (it != lastVarint) {
|
||||
QVariant variantValue;
|
||||
if (!deserializeBasic<V>(it, variantValue))
|
||||
return false;
|
||||
out.append(variantValue.value<V>());
|
||||
}
|
||||
previousValue.setValue(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
[[nodiscard]] static bool deserializeNonPackedList(QProtobufSelfcheckIterator &it,
|
||||
QVariant &previousValue)
|
||||
{
|
||||
qProtoDebug("currentByte: 0x%x", *it);
|
||||
Q_ASSERT_X(previousValue.metaType() == QMetaType::fromType<QList<V>>()
|
||||
&& previousValue.data(),
|
||||
"QProtobufSerializerPrivate::deserializeNonPackedList",
|
||||
"Previous value metatype doesn't match the list metatype");
|
||||
QVariant variantValue;
|
||||
if (deserializeBasic<V>(it, variantValue)) {
|
||||
QList<V> *out = reinterpret_cast<QList<V> *>(previousValue.data());
|
||||
out->append(variantValue.value<V>());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ###########################################################################
|
||||
// Common functions
|
||||
// ###########################################################################
|
||||
static std::optional<QByteArray> deserializeLengthDelimited(QProtobufSelfcheckIterator &it)
|
||||
{
|
||||
if (it.bytesLeft() == 0)
|
||||
return std::nullopt;
|
||||
qProtoDebug("currentByte: 0x%x", *it);
|
||||
|
||||
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(it);
|
||||
if (!opt)
|
||||
return std::nullopt;
|
||||
quint64 length = opt.value();
|
||||
if (!it.isValid() || quint64(it.bytesLeft()) < length
|
||||
|| length > quint64(QByteArray::maxSize())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
QByteArray result(it.data(), qsizetype(length));
|
||||
it += length;
|
||||
return { result };
|
||||
}
|
||||
|
||||
/*!
|
||||
Gets length of a byte-array and prepends to it its serialized length value
|
||||
using the appropriate serialization algorithm
|
||||
|
||||
Returns 'data' with its length prepended
|
||||
*/
|
||||
static QByteArray prependLengthDelimitedSize(const QByteArray &data)
|
||||
{
|
||||
return serializeVarintCommon<uint32_t>(data.size()) + data;
|
||||
}
|
||||
|
||||
template<typename T, QByteArray (*s)(const T &)>
|
||||
static QByteArray serializeWrapper(const QVariant &variantValue, const QByteArray &header)
|
||||
|
|
Loading…
Reference in New Issue