cborserializer.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #include "cborserializer.h"
  2. #include "cborserializer_p.h"
  3. #include <cmath>
  4. #include <QtCore/QCborStreamReader>
  5. #include <QtCore/QCborStreamWriter>
  6. #include <QtCore/QtEndian>
  7. using namespace QtJsonSerializer;
  8. Q_LOGGING_CATEGORY(QtJsonSerializer::logCbor, "qt.jsonserializer.serializer.cbor")
  9. CborSerializer::CborSerializer(QObject *parent) :
  10. SerializerBase{*new CborSerializerPrivate{}, parent}
  11. {
  12. Q_D(CborSerializer);
  13. d->typeTags = {
  14. {QMetaType::QColor, static_cast<QCborTag>(CborSerializer::Color)},
  15. {QMetaType::QFont, static_cast<QCborTag>(CborSerializer::Font)}
  16. };
  17. }
  18. bool CborSerializer::handleSpecialNumbers() const
  19. {
  20. Q_D(const CborSerializer);
  21. return d->handleSpecialNumbers;
  22. }
  23. void CborSerializer::setTypeTag(int metaTypeId, QCborTag tag)
  24. {
  25. Q_D(CborSerializer);
  26. Q_ASSERT_X(metaTypeId != QMetaType::UnknownType, Q_FUNC_INFO, "You cannot assign a tag to QMetaType::UnknownType");
  27. QWriteLocker lock{&d->typeTagsLock};
  28. if (tag == TypeConverter::NoTag) {
  29. d->typeTags.remove(metaTypeId);
  30. qCDebug(logCbor) << "Added Type-Tag for" << QMetaTypeName(metaTypeId)
  31. << "as" << tag;
  32. } else {
  33. d->typeTags.insert(metaTypeId, tag);
  34. qCDebug(logCbor) << "Removed Type-Tag for metaTypeId" << QMetaTypeName(metaTypeId);
  35. }
  36. }
  37. QCborTag CborSerializer::typeTag(int metaTypeId) const
  38. {
  39. Q_D(const CborSerializer);
  40. QReadLocker lock{&d->typeTagsLock};
  41. const auto tag = d->typeTags.value(metaTypeId, TypeConverter::NoTag);
  42. if (tag != TypeConverter::NoTag) {
  43. qCDebug(logCbor) << "Found Type-Tag for metaTypeId" << QMetaTypeName(metaTypeId)
  44. << "as" << tag;
  45. } else
  46. qCDebug(logCbor) << "No Type-Tag found for metaTypeId" << QMetaTypeName(metaTypeId);
  47. return tag;
  48. }
  49. QCborValue CborSerializer::serialize(const QVariant &data) const
  50. {
  51. return serializeVariant(data.userType(), data);
  52. }
  53. void CborSerializer::serializeTo(QIODevice *device, const QVariant &data, QCborValue::EncodingOptions options) const
  54. {
  55. if (!device->isOpen() || !device->isWritable())
  56. throw SerializationException{"QIODevice must be open and writable!"};
  57. QCborStreamWriter writer{device};
  58. serializeVariant(data.userType(), data).toCbor(writer, options);
  59. }
  60. QByteArray CborSerializer::serializeTo(const QVariant &data, QCborValue::EncodingOptions options) const
  61. {
  62. return serializeVariant(data.userType(), data).toCbor(options);
  63. }
  64. QVariant CborSerializer::deserialize(const QCborValue &cbor, int metaTypeId, QObject *parent) const
  65. {
  66. return deserializeVariant(metaTypeId, cbor, parent);
  67. }
  68. QVariant CborSerializer::deserializeFrom(QIODevice *device, int metaTypeId, QObject *parent) const
  69. {
  70. if (!device->isOpen() || !device->isReadable())
  71. throw DeserializationException{"QIODevice must be open and readable!"};
  72. QCborStreamReader reader{device};
  73. const auto cbor = QCborValue::fromCbor(reader);
  74. if (const auto error = reader.lastError(); error.c != QCborError::NoError)
  75. throw DeserializationException("Failed to read file as CBOR with error: " + error.toString().toUtf8());
  76. return deserializeVariant(metaTypeId, cbor, parent);
  77. }
  78. QVariant CborSerializer::deserializeFrom(const QByteArray &data, int metaTypeId, QObject *parent) const
  79. {
  80. QCborParserError error;
  81. const auto cbor = QCborValue::fromCbor(data, &error);
  82. if (error.error.c != QCborError::NoError)
  83. throw DeserializationException("Failed to read file as CBOR with error: " + error.error.toString().toUtf8());
  84. return deserializeVariant(metaTypeId, cbor, parent);
  85. }
  86. std::variant<QCborValue, QJsonValue> CborSerializer::serializeGeneric(const QVariant &value) const
  87. {
  88. return serialize(value);
  89. }
  90. QVariant CborSerializer::deserializeGeneric(const std::variant<QCborValue, QJsonValue> &value, int metaTypeId, QObject *parent) const
  91. {
  92. return deserialize(std::get<QCborValue>(value), metaTypeId, parent);
  93. }
  94. void CborSerializer::setHandleSpecialNumbers(bool handleSpecialNumbers)
  95. {
  96. Q_D(CborSerializer);
  97. if(d->handleSpecialNumbers == handleSpecialNumbers)
  98. return;
  99. d->handleSpecialNumbers = handleSpecialNumbers;
  100. emit handleSpecialNumbersChanged(d->handleSpecialNumbers, {});
  101. }
  102. bool CborSerializer::jsonMode() const
  103. {
  104. return false;
  105. }
  106. QList<int> CborSerializer::typesForTag(QCborTag tag) const
  107. {
  108. Q_D(const CborSerializer);
  109. QReadLocker lock{&d->typeTagsLock};
  110. const auto keys = d->typeTags.keys(tag);
  111. qCDebug(logCbor) << "Found metaTypeIds for tag" << tag
  112. << "as" << keys;
  113. return keys;
  114. }
  115. // ------------- private implementation -------------
  116. namespace {
  117. template <typename TInt>
  118. bool testOverflow(const QByteArray &data) {
  119. return (data.size() == static_cast<int>(sizeof(TInt))) &&
  120. (data[0] & 0x80) != 0;
  121. }
  122. template <typename TInt, bool Invert = false>
  123. TInt extract(QByteArray data) {
  124. static_assert (std::is_integral_v<TInt>, "TInt must be an integer type");
  125. if (data.size() < static_cast<int>(sizeof(TInt)))
  126. data.prepend(QByteArray(static_cast<int>(sizeof(TInt)) - data.size(), 0));
  127. if constexpr (Invert)
  128. return ~qFromBigEndian<TInt>(data.data());
  129. else
  130. return qFromBigEndian<TInt>(data.data());
  131. }
  132. }
  133. QVariant CborSerializerPrivate::deserializeCborValue(int propertyType, const QCborValue &value) const
  134. {
  135. if (handleSpecialNumbers) {
  136. switch (value.tag()) {
  137. case static_cast<QCborTag>(QCborKnownTags::PositiveBignum):
  138. return deserializePositiveBignum(value.taggedValue().toByteArray());
  139. case static_cast<QCborTag>(QCborKnownTags::NegativeBignum):
  140. return deserializeNegativeBignum(value.taggedValue().toByteArray());
  141. case static_cast<QCborTag>(QCborKnownTags::Decimal):
  142. return deserializeDecimal(value.taggedValue().toArray());
  143. case static_cast<QCborTag>(QCborKnownTags::Bigfloat):
  144. return deserializeBigfloat(value.taggedValue().toArray());
  145. case static_cast<QCborTag>(ExtendedTags::RationaleNumber):
  146. return deserializeRationaleNumber(value.taggedValue().toArray());
  147. default:
  148. break;
  149. }
  150. }
  151. return SerializerBasePrivate::deserializeCborValue(propertyType, value);
  152. }
  153. QVariant CborSerializerPrivate::deserializePositiveBignum(const QByteArray &data) const
  154. {
  155. const auto dSize = static_cast<size_t>(data.size());
  156. if (dSize > static_cast<int>(sizeof(quint64))) {
  157. throw DeserializationException{"Unable to handle PositiveBignum tagged integers, bigger then " +
  158. QByteArray::number(static_cast<int>(sizeof(quint64))) +
  159. " bytes"};
  160. }
  161. if (dSize <= sizeof (quint8))
  162. return extract<quint8>(data);
  163. else if (dSize <= sizeof (quint16))
  164. return extract<quint16>(data);
  165. else if (dSize <= sizeof (quint32))
  166. return extract<quint32>(data);
  167. else
  168. return QVariant::fromValue(extract<quint64>(data));
  169. }
  170. QVariant CborSerializerPrivate::deserializeNegativeBignum(const QByteArray &data) const
  171. {
  172. const auto dSize = static_cast<size_t>(data.size());
  173. if (dSize > static_cast<int>(sizeof(qint64)) || testOverflow<qint64>(data)) {
  174. throw DeserializationException{"Unable to handle NegativeBignum tagged integers, bigger then " +
  175. QByteArray::number(static_cast<int>(sizeof(qint64))) +
  176. " bytes (- the first bit)"};
  177. }
  178. if (dSize <= sizeof (qint8) && !testOverflow<qint8>(data))
  179. return QVariant::fromValue<qint8>(extract<qint8, true>(data));
  180. else if (dSize <= sizeof (qint16) && !testOverflow<qint16>(data))
  181. return extract<qint16, true>(data);
  182. else if (dSize <= sizeof (qint32) && !testOverflow<qint32>(data))
  183. return extract<qint32, true>(data);
  184. else
  185. return QVariant::fromValue(extract<qint64, true>(data));
  186. }
  187. qreal CborSerializerPrivate::deserializeDecimal(const QCborArray &data) const
  188. {
  189. if (data.size() != 2)
  190. throw DeserializationException{"Decimal tagged types must be an array with exactly two elements"};
  191. return std::pow(10, data[0].toInteger()) * data[1].toInteger();
  192. }
  193. qreal CborSerializerPrivate::deserializeBigfloat(const QCborArray &data) const
  194. {
  195. if (data.size() != 2)
  196. throw DeserializationException{"Bigfloat tagged types must be an array with exactly two elements"};
  197. return std::ldexp(data[1].toInteger(), data[0].toInteger());
  198. }
  199. qreal CborSerializerPrivate::deserializeRationaleNumber(const QCborArray &data) const
  200. {
  201. if (data.size() != 2)
  202. throw DeserializationException{"RationaleNumber tagged types must be an array with exactly two elements"};
  203. return static_cast<long double>(data[0].toInteger()) /
  204. static_cast<long double>(data[1].toInteger());
  205. }