1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef PROTOBUFSCALARJSONSERIALIZERS_P_H
5#define PROTOBUFSCALARJSONSERIALIZERS_P_H
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include <QtProtobuf/qtprotobufexports.h>
18#include <QtProtobuf/qtprotobuftypes.h>
19
20#include <QtCore/private/qnumeric_p.h>
21#include <QtCore/qjsonarray.h>
22#include <QtCore/qjsonvalue.h>
23#include <QtCore/qtconfigmacros.h>
24
25#include <limits>
26
27QT_BEGIN_NAMESPACE
28
29namespace ProtobufScalarJsonSerializers {
30// Tests if V is JSON compatible integer
31// int32 | int64 | uint32 | sint32 | sint64 | fixed32 | sfixed32 | sfixed64
32template <typename T>
33constexpr inline bool IsJsonInt = false;
34template <>
35constexpr inline bool IsJsonInt<QtProtobuf::int32> = true;
36template <>
37constexpr inline bool IsJsonInt<QtProtobuf::int64> = true;
38template <>
39constexpr inline bool IsJsonInt<QtProtobuf::uint32> = true;
40template <>
41constexpr inline bool IsJsonInt<QtProtobuf::sint32> = true;
42template <>
43constexpr inline bool IsJsonInt<QtProtobuf::sint64> = true;
44template <>
45constexpr inline bool IsJsonInt<QtProtobuf::fixed32> = true;
46template <>
47constexpr inline bool IsJsonInt<QtProtobuf::sfixed32> = true;
48template <>
49constexpr inline bool IsJsonInt<QtProtobuf::sfixed64> = true;
50
51template <typename T>
52using if_json_int = std::enable_if_t<IsJsonInt<T>, bool>;
53
54// Tests if V is JSON incompatible 64-bit unsigned integer
55// uint64 | fixed64
56template <typename T>
57constexpr inline bool IsJsonInt64 = false;
58template <>
59constexpr inline bool IsJsonInt64<QtProtobuf::uint64> = true;
60template <>
61constexpr inline bool IsJsonInt64<QtProtobuf::fixed64> = true;
62
63template <typename T>
64using if_json_int64 = std::enable_if_t<IsJsonInt64<T>, bool>;
65
66// Tests if V is JSON compatible floating point number
67// float | double
68template <typename T>
69constexpr inline bool IsJsonFloatingPoint = false;
70template <>
71constexpr inline bool IsJsonFloatingPoint<float> = true;
72template <>
73constexpr inline bool IsJsonFloatingPoint<double> = true;
74
75template <typename T>
76using if_json_floating_point = std::enable_if_t<IsJsonFloatingPoint<T>, bool>;
77
78// Special value strings
79constexpr inline QLatin1StringView Infinity("Infinity");
80constexpr inline QLatin1StringView NegInfinity("-Infinity");
81constexpr inline QLatin1StringView NaN("NaN");
82
83constexpr inline QByteArrayView InfinityLower("infinity");
84constexpr inline QByteArrayView NegInfinityLower("-infinity");
85constexpr inline QByteArrayView NaNLower("nan");
86
87constexpr inline QLatin1StringView True("true");
88constexpr inline QLatin1StringView False("false");
89
90template <typename T, if_json_int<T> = true>
91QJsonValue serialize(T propertyValue)
92{
93 return QJsonValue(qint64(propertyValue));
94}
95
96template <typename T, if_json_int64<T> = true>
97QJsonValue serialize(T propertyValue)
98{
99 return QJsonValue(QString::number(propertyValue));
100}
101
102template <typename T, if_json_floating_point<T> = true>
103QJsonValue serialize(T propertyValue)
104{
105 if (propertyValue == -std::numeric_limits<T>::infinity())
106 return QJsonValue(NegInfinity);
107
108 if (propertyValue == std::numeric_limits<T>::infinity())
109 return QJsonValue(Infinity);
110
111 if (propertyValue != propertyValue)
112 return QJsonValue(NaN);
113
114 return QJsonValue(propertyValue);
115}
116
117inline QJsonValue serialize(bool propertyValue)
118{
119 return QJsonValue(propertyValue);
120}
121
122inline QJsonValue serialize(const QString &propertyValue)
123{
124 return QJsonValue(propertyValue);
125}
126
127inline QJsonValue serialize(const QByteArray &propertyValue)
128{
129 return QJsonValue(QString::fromUtf8(ba: propertyValue.toBase64()));
130}
131
132template <typename L>
133QJsonValue serializeList(const QVariant &propertyValue)
134{
135 QJsonArray arr;
136 L listValue = propertyValue.value<L>();
137 for (const auto &value : listValue)
138 arr.append(value: serialize(value));
139
140 return QJsonValue(arr);
141}
142
143template <typename T>
144QJsonValue serializeCommon(const QVariant &propertyValue)
145{
146 return serialize(propertyValue.value<T>());
147}
148
149Q_PROTOBUF_EXPORT bool validateJsonNumberString(const QString &input);
150
151template <typename T, if_json_int<T> = true>
152T deserialize(const QJsonValue &value, bool &ok)
153{
154 auto variantValue = value.toVariant();
155 qint64 raw = 0;
156 switch (variantValue.metaType().id()) {
157 case QMetaType::QString:
158 if (!validateJsonNumberString(input: variantValue.toString()))
159 ok = false;
160 else
161 raw = variantValue.toLongLong(ok: &ok);
162 break;
163 case QMetaType::LongLong:
164 raw = variantValue.toLongLong(ok: &ok);
165 break;
166 case QMetaType::Double: {
167 double d = value.toDouble();
168 ok = convertDoubleTo(v: d, value: &raw) && double(raw) == d;
169 } break;
170 default:
171 break;
172 }
173
174 // For types that "smaller" than qint64 we need to check if the value fits its limits range
175 if constexpr (sizeof(T) != sizeof(qint64)) {
176 constexpr auto Limits = []() {
177 if constexpr (std::is_same_v<T, QtProtobuf::sfixed32>
178 || std::is_same_v<T, QtProtobuf::int32>)
179 return std::numeric_limits<qint32>{};
180 else if constexpr (std::is_same_v<T, QtProtobuf::fixed32>)
181 return std::numeric_limits<quint32>{};
182 else
183 return std::numeric_limits<T>{};
184 }();
185 ok = ok && (raw >= Limits.min() && raw <= Limits.max());
186 }
187
188 return ok ? T(raw) : T{};
189}
190
191template <typename T, if_json_int64<T> = true>
192T deserialize(const QJsonValue &value, bool &ok)
193{
194 quint64 raw = 0;
195 auto variantValue = value.toVariant();
196 switch (variantValue.metaType().id()) {
197 case QMetaType::QString:
198 case QMetaType::LongLong:
199 // Here we attempt converting the value to ULongLong
200 raw = variantValue.toULongLong(ok: &ok);
201 break;
202 case QMetaType::Double: {
203 double d = value.toDouble();
204 ok = convertDoubleTo(v: d, value: &raw) && double(raw) == d;
205 } break;
206 default:
207 break;
208 }
209 return T(raw);
210}
211
212template <typename T, if_json_floating_point<T> = true>
213T deserialize(const QJsonValue &value, bool &ok)
214{
215 ok = true;
216 QByteArray data = value.toVariant().toByteArray();
217 if (data.compare(a: NegInfinityLower, cs: Qt::CaseInsensitive) == 0)
218 return -std::numeric_limits<T>::infinity();
219
220 if (data.compare(a: InfinityLower, cs: Qt::CaseInsensitive) == 0)
221 return std::numeric_limits<T>::infinity();
222
223 if (data.compare(a: NaNLower, cs: Qt::CaseInsensitive) == 0)
224 return T(NAN);
225
226 if constexpr (std::is_same_v<T, float>)
227 return data.toFloat(ok: &ok);
228 else
229 return data.toDouble(ok: &ok);
230}
231
232template <typename T, std::enable_if_t<std::is_same_v<T, bool>, bool> = true>
233bool deserialize(const QJsonValue &value, bool &ok)
234{
235 if (value.isBool()) {
236 ok = true;
237 return value.toBool();
238 } else if (value.isString()) {
239 if (value.toString() == True) {
240 ok = true;
241 return true;
242 } else if (value.toString() == False) {
243 ok = true;
244 return false;
245 }
246 }
247 return false;
248}
249
250template <typename T, std::enable_if_t<std::is_same_v<T, QString>, bool> = true>
251QString deserialize(const QJsonValue &value, bool &ok)
252{
253 ok = value.isString();
254 return value.toString();
255}
256
257template <typename T, std::enable_if_t<std::is_same_v<T, QByteArray>, bool> = true>
258QByteArray deserialize(const QJsonValue &value, bool &ok)
259{
260 QByteArray data = value.toVariant().toByteArray();
261 if (value.isString()) {
262 ok = true;
263 return QByteArray::fromBase64(base64: data);
264 }
265 return {};
266}
267
268template <typename L /*list*/, typename T /*element*/>
269QVariant deserializeList(const QJsonValue &value, bool &ok)
270{
271 if (!value.isArray()) {
272 ok = false;
273 return {};
274 }
275
276 L list;
277 QJsonArray array = value.toArray();
278 for (auto arrayValue : array) {
279 ok = false;
280 T value = deserialize<T>(arrayValue, ok);
281 if (!ok)
282 break;
283 list.append(value);
284 }
285 return QVariant::fromValue(list);
286}
287
288template <typename T>
289QVariant deserializeCommon(const QJsonValue &value, bool &ok)
290{
291 ok = false;
292 return QVariant::fromValue<T>(deserialize<T>(value, ok));
293}
294
295} // namespace ProtobufScalarJsonSerializers
296QT_END_NAMESPACE
297
298#endif // PROTOBUFSCALARJSONSERIALIZERS_P_H
299

source code of qtgrpc/src/protobuf/protobufscalarjsonserializers_p.h