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
|
qt_internal_add_module(Protobuf
|
||||||
SOURCES
|
SOURCES
|
||||||
protobuffieldpresencechecker_p.h
|
protobuffieldpresencechecker_p.h
|
||||||
|
protobufscalarserializers_p.h protobufscalarserializers_p.cpp
|
||||||
|
protobufscalarjsonserializers_p.h
|
||||||
qtprotobufglobal.h
|
qtprotobufglobal.h
|
||||||
qabstractprotobufserializer.cpp qabstractprotobufserializer.h
|
qabstractprotobufserializer.cpp qabstractprotobufserializer.h
|
||||||
qprotobufdeserializerbase_p.h qprotobufdeserializerbase.cpp
|
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/qprotobufjsonserializer.h>
|
||||||
|
|
||||||
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
|
#include <QtProtobuf/private/protobuffieldpresencechecker_p.h>
|
||||||
|
#include <QtProtobuf/private/protobufscalarjsonserializers_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
|
#include <QtProtobuf/private/qprotobufdeserializerbase_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
#include <QtProtobuf/private/qprotobufserializerbase_p.h>
|
||||||
|
@ -11,15 +12,13 @@
|
||||||
#include <QtProtobuf/private/qtprotobufserializerhelpers_p.h>
|
#include <QtProtobuf/private/qtprotobufserializerhelpers_p.h>
|
||||||
|
|
||||||
#include <QtCore/qcoreapplication.h>
|
#include <QtCore/qcoreapplication.h>
|
||||||
|
#include <QtCore/qhash.h>
|
||||||
#include <QtCore/qjsonarray.h>
|
#include <QtCore/qjsonarray.h>
|
||||||
#include <QtCore/qjsondocument.h>
|
#include <QtCore/qjsondocument.h>
|
||||||
#include <QtCore/qjsonobject.h>
|
#include <QtCore/qjsonobject.h>
|
||||||
#include <QtCore/qvariant.h>
|
#include <QtCore/qvariant.h>
|
||||||
#include <QtCore/qhash.h>
|
|
||||||
#include <QtCore/private/qnumeric_p.h>
|
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <limits>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
@ -41,6 +40,7 @@ QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
using namespace QtProtobufPrivate;
|
using namespace QtProtobufPrivate;
|
||||||
|
using namespace ProtobufScalarJsonSerializers;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -120,38 +120,6 @@ private:
|
||||||
class QProtobufJsonSerializerPrivate final
|
class QProtobufJsonSerializerPrivate final
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY_MOVE(QProtobufJsonSerializerPrivate)
|
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:
|
public:
|
||||||
using Serializer = std::function<QJsonValue(const QVariant &)>;
|
using Serializer = std::function<QJsonValue(const QVariant &)>;
|
||||||
using Deserializer = std::function<QVariant(const QJsonValue&, bool &ok)>;
|
using Deserializer = std::function<QVariant(const QJsonValue&, bool &ok)>;
|
||||||
|
@ -167,16 +135,14 @@ public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static SerializationHandlers createCommonHandler()
|
static SerializationHandlers createCommonHandler()
|
||||||
{
|
{
|
||||||
return { QProtobufJsonSerializerPrivate::serializeCommon<T>,
|
return { serializeCommon<T>, deserializeCommon<T>,
|
||||||
QProtobufJsonSerializerPrivate::deserializeCommon<T>,
|
|
||||||
ProtobufFieldPresenceChecker::isPresent<T> };
|
ProtobufFieldPresenceChecker::isPresent<T> };
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename L, typename T>
|
template <typename L, typename T>
|
||||||
static SerializationHandlers createCommonListHandler()
|
static SerializationHandlers createCommonListHandler()
|
||||||
{
|
{
|
||||||
return { QProtobufJsonSerializerPrivate::serializeList<L>,
|
return { serializeList<L>, deserializeList<L, T>,
|
||||||
QProtobufJsonSerializerPrivate::deserializeList<L, T>,
|
|
||||||
ProtobufFieldPresenceChecker::isPresent<L> };
|
ProtobufFieldPresenceChecker::isPresent<L> };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,59 +156,6 @@ public:
|
||||||
|
|
||||||
using SerializerRegistry = QHash<int/*metatypeid*/, SerializationHandlers>;
|
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)
|
QProtobufJsonSerializerPrivate() : deserializer(this)
|
||||||
{
|
{
|
||||||
[[maybe_unused]] static bool initialized = []() -> bool {
|
[[maybe_unused]] static bool initialized = []() -> bool {
|
||||||
|
@ -263,14 +176,10 @@ public:
|
||||||
handlers[qMetaTypeId<bool>()] = createCommonHandler<bool>();
|
handlers[qMetaTypeId<bool>()] = createCommonHandler<bool>();
|
||||||
handlers[QMetaType::QString] = createCommonHandler<QString>();
|
handlers[QMetaType::QString] = createCommonHandler<QString>();
|
||||||
handlers[QMetaType::QByteArray] = createCommonHandler<QByteArray>();
|
handlers[QMetaType::QByteArray] = createCommonHandler<QByteArray>();
|
||||||
handlers[QMetaType::Float] = { QProtobufJsonSerializerPrivate::serializeCommon<float>,
|
handlers[QMetaType::Float] = { serializeCommon<float>, deserializeCommon<float>,
|
||||||
QProtobufJsonSerializerPrivate::deserializeCommon<float>,
|
|
||||||
QProtobufJsonSerializerPrivate::isPresent<float> };
|
QProtobufJsonSerializerPrivate::isPresent<float> };
|
||||||
handlers[QMetaType::Double] = {
|
handlers[QMetaType::Double] = { serializeCommon<double>, deserializeCommon<double>,
|
||||||
QProtobufJsonSerializerPrivate::serializeCommon<double>,
|
isPresent<double> };
|
||||||
QProtobufJsonSerializerPrivate::deserializeCommon<double>,
|
|
||||||
QProtobufJsonSerializerPrivate::isPresent<double>
|
|
||||||
};
|
|
||||||
|
|
||||||
handlers[qMetaTypeId<QtProtobuf::boolList>()] = createCommonListHandler<
|
handlers[qMetaTypeId<QtProtobuf::boolList>()] = createCommonListHandler<
|
||||||
QtProtobuf::boolList, bool>();
|
QtProtobuf::boolList, bool>();
|
||||||
|
@ -306,148 +215,6 @@ public:
|
||||||
}
|
}
|
||||||
~QProtobufJsonSerializerPrivate() = default;
|
~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();
|
void clearError();
|
||||||
|
|
||||||
QAbstractProtobufSerializer::Error lastError = QAbstractProtobufSerializer::Error::None;
|
QAbstractProtobufSerializer::Error lastError = QAbstractProtobufSerializer::Error::None;
|
||||||
|
@ -477,8 +244,7 @@ bool QProtobufJsonSerializerImpl::serializeEnum(QVariant &value,
|
||||||
return false;
|
return false;
|
||||||
if (!ProtobufFieldPresenceChecker::isPresent<QStringList>(value))
|
if (!ProtobufFieldPresenceChecker::isPresent<QStringList>(value))
|
||||||
return true;
|
return true;
|
||||||
m_result.insert(jsonName.toString(),
|
m_result.insert(jsonName.toString(), serializeList<QStringList>(value));
|
||||||
QProtobufJsonSerializerPrivate::serializeList<QStringList>(value));
|
|
||||||
} else {
|
} else {
|
||||||
if (!value.convert(QMetaType::fromType<QString>()))
|
if (!value.convert(QMetaType::fromType<QString>()))
|
||||||
return false;
|
return false;
|
||||||
|
@ -486,8 +252,7 @@ bool QProtobufJsonSerializerImpl::serializeEnum(QVariant &value,
|
||||||
&& !isOneofOrOptionalField(fieldInfo.fieldFlags())) {
|
&& !isOneofOrOptionalField(fieldInfo.fieldFlags())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
m_result.insert(jsonName.toString(),
|
m_result.insert(jsonName.toString(), serializeCommon<QString>(value));
|
||||||
QProtobufJsonSerializerPrivate::serializeCommon<QString>(value));
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -570,17 +335,14 @@ bool QProtobufJsonDeserializerImpl::deserializeEnum(QVariant &value,
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
auto &state = m_state.last();
|
auto &state = m_state.last();
|
||||||
if (fieldInfo.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
|
if (fieldInfo.fieldFlags().testFlag(QtProtobufPrivate::FieldFlag::Repeated)) {
|
||||||
value = QProtobufJsonSerializerPrivate::deserializeList<QStringList,
|
value = deserializeList<QStringList, QString>(state.scalarValue, ok);
|
||||||
QString>(state.scalarValue, ok);
|
|
||||||
} else {
|
} else {
|
||||||
// It's allowed to pass single enum value as numeric value.
|
// It's allowed to pass single enum value as numeric value.
|
||||||
// Make the backward value conversion and deserialize enum as QtProtobuf::int64.
|
// Make the backward value conversion and deserialize enum as QtProtobuf::int64.
|
||||||
if (state.scalarValue.isString()) {
|
if (state.scalarValue.isString()) {
|
||||||
value = QProtobufJsonSerializerPrivate::deserializeCommon<QString>(state.scalarValue,
|
value = deserializeCommon<QString>(state.scalarValue, ok);
|
||||||
ok);
|
|
||||||
} else {
|
} else {
|
||||||
value = QProtobufJsonSerializerPrivate::deserializeCommon<
|
value = deserializeCommon<QtProtobuf::int64>(state.scalarValue, ok);
|
||||||
QtProtobuf::int64>(state.scalarValue, ok);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <QtProtobuf/qprotobufserializer.h>
|
#include <QtProtobuf/qprotobufserializer.h>
|
||||||
|
|
||||||
|
#include <QtProtobuf/private/protobufscalarserializers_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufmessage_p.h>
|
#include <QtProtobuf/private/qprotobufmessage_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
#include <QtProtobuf/private/qprotobufregistration_p.h>
|
||||||
#include <QtProtobuf/private/qprotobufserializer_p.h>
|
#include <QtProtobuf/private/qprotobufserializer_p.h>
|
||||||
|
@ -37,6 +38,7 @@ QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
using namespace QtProtobufPrivate;
|
using namespace QtProtobufPrivate;
|
||||||
|
using namespace ProtobufScalarSerializers;
|
||||||
|
|
||||||
template<std::size_t N>
|
template<std::size_t N>
|
||||||
using SerializerRegistryType =
|
using SerializerRegistryType =
|
||||||
|
@ -44,25 +46,22 @@ using SerializerRegistryType =
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#define QT_CONSTRUCT_PROTOBUF_SERIALIZATION_HANDLER(Type, WireType) \
|
#define QT_CONSTRUCT_PROTOBUF_SERIALIZATION_HANDLER(Type, WireType) \
|
||||||
{ QMetaType::fromType<Type>(), \
|
{ QMetaType::fromType<Type>(), \
|
||||||
QProtobufSerializerPrivate::serializeWrapper< \
|
QProtobufSerializerPrivate::serializeWrapper<Type, serializeBasic<Type>>, \
|
||||||
Type, QProtobufSerializerPrivate::serializeBasic<Type>>, \
|
deserializeBasic<Type>, ProtobufFieldPresenceChecker::isPresent<Type>, WireType }
|
||||||
QProtobufSerializerPrivate::deserializeBasic<Type>, \
|
#define QT_CONSTRUCT_PROTOBUF_LIST_SERIALIZATION_HANDLER(ListType, Type) \
|
||||||
ProtobufFieldPresenceChecker::isPresent<Type>, WireType }
|
{ QMetaType::fromType<ListType>(), \
|
||||||
#define QT_CONSTRUCT_PROTOBUF_LIST_SERIALIZATION_HANDLER(ListType, Type) \
|
QProtobufSerializerPrivate::serializeWrapper<ListType, serializeListType<Type>>, \
|
||||||
{ QMetaType::fromType<ListType>(), \
|
deserializeList<Type>, ProtobufFieldPresenceChecker::isPresent<ListType>, \
|
||||||
QProtobufSerializerPrivate::serializeWrapper< \
|
QtProtobuf::WireTypes::LengthDelimited }
|
||||||
ListType, QProtobufSerializerPrivate::serializeListType<Type>>, \
|
|
||||||
QProtobufSerializerPrivate::deserializeList<Type>, \
|
|
||||||
ProtobufFieldPresenceChecker::isPresent<ListType>, QtProtobuf::WireTypes::LengthDelimited }
|
|
||||||
|
|
||||||
#define QT_CONSTRUCT_PROTOBUF_NON_PACKED_LIST_SERIALIZATION_HANDLER(ListType, Type, WireType) \
|
#define QT_CONSTRUCT_PROTOBUF_NON_PACKED_LIST_SERIALIZATION_HANDLER(ListType, Type, WireType) \
|
||||||
{ QMetaType::fromType<ListType>(), \
|
{ QMetaType::fromType<ListType>(), \
|
||||||
QProtobufSerializerPrivate::serializeNonPackedWrapper< \
|
QProtobufSerializerPrivate::serializeNonPackedWrapper<ListType, \
|
||||||
ListType, QProtobufSerializerPrivate::serializeNonPackedList<Type>>, \
|
serializeNonPackedList<Type>>, \
|
||||||
QProtobufSerializerPrivate::deserializeNonPackedList<Type>, \
|
deserializeNonPackedList<Type>, ProtobufFieldPresenceChecker::isPresent<ListType>, \
|
||||||
ProtobufFieldPresenceChecker::isPresent<ListType>, WireType }
|
WireType }
|
||||||
|
|
||||||
constexpr SerializerRegistryType<30> IntegratedTypesSerializers = { {
|
constexpr SerializerRegistryType<30> IntegratedTypesSerializers = { {
|
||||||
QT_CONSTRUCT_PROTOBUF_SERIALIZATION_HANDLER(float, QtProtobuf::WireTypes::Fixed32),
|
QT_CONSTRUCT_PROTOBUF_SERIALIZATION_HANDLER(float, QtProtobuf::WireTypes::Fixed32),
|
||||||
|
@ -206,13 +205,11 @@ bool QProtobufSerializerImpl::serializeEnum(QVariant &value,
|
||||||
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::NonPacked)) {
|
if (fieldFlags.testFlag(QtProtobufPrivate::FieldFlag::NonPacked)) {
|
||||||
const auto header = encodeHeader(fieldInfo.fieldNumber(),
|
const auto header = encodeHeader(fieldInfo.fieldNumber(),
|
||||||
QtProtobuf::WireTypes::Varint);
|
QtProtobuf::WireTypes::Varint);
|
||||||
m_result.append(QProtobufSerializerPrivate::serializeNonPackedList<
|
m_result.append(serializeNonPackedList<QtProtobuf::int64>(listValue, header));
|
||||||
QtProtobuf::int64>(listValue, header));
|
|
||||||
} else {
|
} else {
|
||||||
m_result.append(encodeHeader(fieldInfo.fieldNumber(),
|
m_result.append(encodeHeader(fieldInfo.fieldNumber(),
|
||||||
QtProtobuf::WireTypes::LengthDelimited));
|
QtProtobuf::WireTypes::LengthDelimited));
|
||||||
m_result.append(QProtobufSerializerPrivate::serializeListType<
|
m_result.append(serializeListType<QtProtobuf::int64>(listValue));
|
||||||
QtProtobuf::int64>(listValue));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!value.canConvert<QtProtobuf::int64>())
|
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(encodeHeader(fieldInfo.fieldNumber(), QtProtobuf::WireTypes::Varint));
|
||||||
m_result.append(QProtobufSerializerPrivate::serializeBasic<
|
m_result.append(serializeBasic<QtProtobuf::int64>(value.value<QtProtobuf::int64>()));
|
||||||
QtProtobuf::int64>(value.value<QtProtobuf::int64>()));
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -264,7 +260,7 @@ void QProtobufSerializerImpl::serializeMessageFieldEnd(const QProtobufMessage *m
|
||||||
|
|
||||||
QByteArray last = m_state.takeLast();
|
QByteArray last = m_state.takeLast();
|
||||||
last.append(encodeHeader(fieldInfo.fieldNumber(), QtProtobuf::WireTypes::LengthDelimited));
|
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);
|
last.append(m_result);
|
||||||
m_result = last;
|
m_result = last;
|
||||||
}
|
}
|
||||||
|
@ -284,7 +280,7 @@ QByteArray QProtobufSerializerImpl::encodeHeader(int fieldIndex, QtProtobuf::Wir
|
||||||
// Returns a varint-encoded fieldIndex and wireType
|
// Returns a varint-encoded fieldIndex and wireType
|
||||||
|
|
||||||
uint32_t header = (fieldIndex << 3) | int(wireType);
|
uint32_t header = (fieldIndex << 3) | int(wireType);
|
||||||
return QProtobufSerializerPrivate::serializeVarintCommon<uint32_t>(header);
|
return serializeVarintCommon<uint32_t>(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
QProtobufDeserializerImpl::QProtobufDeserializerImpl(QProtobufSerializerPrivate *parent)
|
QProtobufDeserializerImpl::QProtobufDeserializerImpl(QProtobufSerializerPrivate *parent)
|
||||||
|
@ -318,16 +314,15 @@ bool QProtobufDeserializerImpl::deserializeEnum(QVariant &value,
|
||||||
value.convert(QMetaType::fromType<QList<QtProtobuf::int64>>());
|
value.convert(QMetaType::fromType<QList<QtProtobuf::int64>>());
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (m_wireType == QtProtobuf::WireTypes::Varint) {
|
if (m_wireType == QtProtobuf::WireTypes::Varint) {
|
||||||
result = QProtobufSerializerPrivate::deserializeNonPackedList<QtProtobuf::int64>(m_it,
|
result = deserializeNonPackedList<QtProtobuf::int64>(m_it, value);
|
||||||
value);
|
|
||||||
} else if (m_wireType == QtProtobuf::WireTypes::LengthDelimited) {
|
} 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);
|
value.convert(metaType);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QProtobufSerializerPrivate::deserializeBasic<QtProtobuf::int64>(m_it, value);
|
return deserializeBasic<QtProtobuf::int64>(m_it, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int QProtobufDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
|
int QProtobufDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
|
||||||
|
@ -364,8 +359,7 @@ int QProtobufDeserializerImpl::nextFieldIndex(QProtobufMessage *message)
|
||||||
|
|
||||||
if (ordering->fieldFlags(index).testAnyFlags({ QtProtobufPrivate::FieldFlag::Message,
|
if (ordering->fieldFlags(index).testAnyFlags({ QtProtobufPrivate::FieldFlag::Message,
|
||||||
QtProtobufPrivate::FieldFlag::Map })) {
|
QtProtobufPrivate::FieldFlag::Map })) {
|
||||||
auto
|
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
||||||
opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
|
||||||
if (!opt) {
|
if (!opt) {
|
||||||
setUnexpectedEndOfStreamError();
|
setUnexpectedEndOfStreamError();
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -473,7 +467,7 @@ void QProtobufDeserializerImpl::skipVarint()
|
||||||
void QProtobufDeserializerImpl::skipLengthDelimited()
|
void QProtobufDeserializerImpl::skipLengthDelimited()
|
||||||
{
|
{
|
||||||
//Get length of length-delimited field
|
//Get length of length-delimited field
|
||||||
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
auto opt = deserializeVarintCommon<QtProtobuf::uint64>(m_it);
|
||||||
if (!opt) {
|
if (!opt) {
|
||||||
m_it += m_it.bytesLeft() + 1;
|
m_it += m_it.bytesLeft() + 1;
|
||||||
return;
|
return;
|
||||||
|
@ -493,7 +487,7 @@ bool QProtobufDeserializerImpl::decodeHeader(QProtobufSelfcheckIterator &it, int
|
||||||
{
|
{
|
||||||
if (it.bytesLeft() == 0)
|
if (it.bytesLeft() == 0)
|
||||||
return false;
|
return false;
|
||||||
auto opt = QProtobufSerializerPrivate::deserializeVarintCommon<uint32_t>(it);
|
auto opt = deserializeVarintCommon<uint32_t>(it);
|
||||||
if (!opt)
|
if (!opt)
|
||||||
return false;
|
return false;
|
||||||
uint32_t header = opt.value();
|
uint32_t header = opt.value();
|
||||||
|
|
|
@ -34,9 +34,6 @@
|
||||||
#include <QtCore/qhash.h>
|
#include <QtCore/qhash.h>
|
||||||
#include <QtCore/qvariant.h>
|
#include <QtCore/qvariant.h>
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class QProtobufSerializerImpl final : public QProtobufSerializerBase
|
class QProtobufSerializerImpl final : public QProtobufSerializerBase
|
||||||
|
@ -105,93 +102,6 @@ private:
|
||||||
|
|
||||||
class QProtobufSerializerPrivate
|
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:
|
public:
|
||||||
// Serializer is interface function for serialize method
|
// Serializer is interface function for serialize method
|
||||||
using Serializer = QByteArray (*)(const QVariant &, const QByteArray &);
|
using Serializer = QByteArray (*)(const QVariant &, const QByteArray &);
|
||||||
|
@ -212,326 +122,6 @@ public:
|
||||||
|
|
||||||
QProtobufSerializerPrivate();
|
QProtobufSerializerPrivate();
|
||||||
~QProtobufSerializerPrivate() = default;
|
~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 &)>
|
template<typename T, QByteArray (*s)(const T &)>
|
||||||
static QByteArray serializeWrapper(const QVariant &variantValue, const QByteArray &header)
|
static QByteArray serializeWrapper(const QVariant &variantValue, const QByteArray &header)
|
||||||
|
|
Loading…
Reference in New Issue