/* * MIT License * * Copyright (c) 2019 Alexey Edelev , Viktor Kopp * * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and * to permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in all copies * or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include "qtprotobuftypes.h" #include "qprotobufobject_p.h" namespace qtprotobuf { class QProtobufSerializerPrivate { QProtobufSerializerPrivate() = delete; ~QProtobufSerializerPrivate() = delete; Q_DISABLE_COPY(QProtobufSerializerPrivate) QProtobufSerializerPrivate(ProtobufObjectPrivate &&) = delete; QProtobufSerializerPrivate &operator =(QProtobufSerializerPrivate &&) = delete; public: template ::value, int> = 0> static void wrapSerializer(const std::function &s, const std::function &d, WireTypes type) { ProtobufObjectPrivate::serializers[qMetaTypeId()] = { [s](const QVariant &value, int &fieldIndex) { return s(value.value(), fieldIndex); }, [d](SelfcheckIterator &it, QVariant & value){ value = d(it); }, type }; } template ::value, int> = 0> static void wrapSerializer(const std::function &s, const std::function &d, WireTypes type) { ProtobufObjectPrivate::serializers[qMetaTypeId()] = { [s](const QVariant &value, int &fieldIndex) { return s(value.value(), fieldIndex); }, d, type }; } //----------------Serialize basic integral and floating point---------------- /** * @brief 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 * * @param[in] value Value to serialize * @param[out] outFieldIndex Index of the value in parent structure (ignored) * @return Byte array with value encoded */ template ::value, int> = 0> static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) { qProtoDebug() << __func__ << "value" << value; //Reserve required number of bytes QByteArray result(sizeof(V), '\0'); *reinterpret_cast(result.data()) = value; return result; } /** * @brief Serialization of fixed length integral types * * @details Natural layout of bits is employed * * @param[in] value Value to serialize * @param[out] outFieldIndex Index of the value in parent structure (ignored) * @return Byte array with value encoded */ template ::value || std::is_same::value || std::is_same::value || std::is_same::value, int> = 0> static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) { qProtoDebug() << __func__ << "value" << value; //Reserve required number of bytes QByteArray result(sizeof(V), '\0'); *reinterpret_cast(result.data()) = value; return result; } /** *@brief Serialization of signed integral types * * Use ZigZag encoding first, * then apply serialization as for unsigned integral types * @see serializeBasic\::value && std::is_unsigned\::value, int\> = 0\>(V, int) * * @param[in] value Value to serialize * @param[out] outFieldIndex Index of the value in parent structure * @return Byte array with value encoded */ template ::value && std::is_signed::value, int> = 0> static QByteArray serializeBasic(const V &value, int &outFieldIndex) { qProtoDebug() << __func__ << "value" << value; using UV = typename std::make_unsigned::type; UV uValue = 0; //Use ZigZag convertion first and apply unsigned variant next V zigZagValue = (value << 1) ^ (value >> (sizeof(UV) * 8 - 1)); uValue = static_cast(zigZagValue); return serializeBasic(uValue, outFieldIndex); } template ::value || std::is_same::value, int> = 0> static QByteArray serializeBasic(const V &value, int &outFieldIndex) { qProtoDebug() << __func__ << "value" << value; using UV = typename std::make_unsigned::type; return serializeBasic(static_cast(value), outFieldIndex); } /** *@brief Serialization of unsigned integral types * * Use Varint encoding: * "Varints are a method of serializing integers using one or more bytes. Smaller numbers * [regardless its type] take a smaller number of bytes." * * @param[in] value Value to serialize * @param[out] outFieldIndex Index of the value in parent structure * @return Byte array with value encoded */ template ::value && std::is_unsigned::value, int> = 0> static QByteArray serializeBasic(const V &value, int &outFieldIndex) { qProtoDebug() << __func__ << "value" << value; V 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; } // Invalidate field index in case if serialized result is empty // NOTE: the field will not be sent if its index is equal to NotUsedFieldIndex if (result.size() == 0) { outFieldIndex = NotUsedFieldIndex; } else { //Mark last chunk as last by clearing last bit result.data()[result.size() - 1] &= ~0b10000000; } return result; } template ::value, int> = 0> static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) { return serializeLengthDelimited(value.toUtf8()); } template ::value, int> = 0> static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) { return serializeLengthDelimited(value); } //---------------Deserialize basic integral and floating point--------------- template ::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value, int> = 0> static QVariant deserializeBasic(SelfcheckIterator &it) { qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16); QVariant newPropertyValue(QVariant::fromValue(*(V *)((QByteArray::const_iterator&)it))); it += sizeof(V); return newPropertyValue; } template ::value && std::is_unsigned::value, int> = 0> static QVariant deserializeBasic(SelfcheckIterator &it) { qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16); return QVariant::fromValue(deserializeVarintCommon(it)); } template ::value && std::is_signed::value,int> = 0> static QVariant deserializeBasic(SelfcheckIterator &it) { qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16); using UV = typename std::make_unsigned::type; UV unsignedValue = deserializeVarintCommon(it); V value = (unsignedValue >> 1) ^ (-1 * (unsignedValue & 1)); return QVariant::fromValue(value); } template ::value || std::is_same::value, int> = 0> static QVariant deserializeBasic(SelfcheckIterator &it) { qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16); using UV = typename std::make_unsigned::type; UV unsignedValue = deserializeVarintCommon(it); V value = static_cast(unsignedValue); return QVariant::fromValue(value); } template ::value, int> = 0> static QVariant deserializeBasic(SelfcheckIterator &it) { return QVariant::fromValue(deserializeLengthDelimited(it)); } template ::value, int> = 0> static QVariant deserializeBasic(SelfcheckIterator &it) { return QVariant::fromValue(QString::fromUtf8(deserializeLengthDelimited(it))); } template::value || std::is_same::value || std::is_base_of::value), int> = 0> static QByteArray serializeListType(const QList &listValue, int &outFieldIndex) { qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex; if (listValue.count() <= 0) { outFieldIndex = NotUsedFieldIndex; return QByteArray(); } int empty = NotUsedFieldIndex; QByteArray serializedList; for (auto &value : listValue) { serializedList.append(serializeBasic(value, empty)); } //If internal field type is not LengthDelimited, exact amount of fields to be specified ProtobufObjectPrivate::prependLengthDelimitedSize(serializedList); return serializedList; } template::value, int> = 0> static QByteArray serializeListType(const QList &listValue, int &outFieldIndex) { qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex; if (listValue.count() <= 0) { outFieldIndex = NotUsedFieldIndex; return QByteArray(); } QByteArray serializedList; for (auto &value : listValue) { serializedList.append(ProtobufObjectPrivate::encodeHeader(outFieldIndex, LengthDelimited)); serializedList.append(serializeLengthDelimited(value.toUtf8())); } outFieldIndex = NotUsedFieldIndex; return serializedList; } template::value, int> = 0> static QByteArray serializeListType(const QList &listValue, int &outFieldIndex) { qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex; if (listValue.count() <= 0) { outFieldIndex = NotUsedFieldIndex; return QByteArray(); } QByteArray serializedList; for (auto &value : listValue) { serializedList.append(ProtobufObjectPrivate::encodeHeader(outFieldIndex, LengthDelimited)); serializedList.append(serializeLengthDelimited(value)); } outFieldIndex = NotUsedFieldIndex; return serializedList; } template ::value, int> = 0> static void deserializeList(SelfcheckIterator &it, QVariant &previous) { qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16); QByteArrayList list = previous.value(); list.append(deserializeLengthDelimited(it)); previous.setValue(list); } template ::value, int> = 0> static void deserializeList(SelfcheckIterator &it, QVariant &previous) { qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16); QStringList list = previous.value(); QByteArray value = deserializeLengthDelimited(it); list.append(QString::fromUtf8(value)); previous.setValue(list); } template ::value || std::is_same::value || std::is_base_of::value), int> = 0> static void deserializeList(SelfcheckIterator &it, QVariant &previous) { qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16); QList out; unsigned int count = deserializeVarintCommon(it); SelfcheckIterator lastVarint = it + count; while (it != lastVarint) { QVariant variant = deserializeBasic(it); out.append(variant.value()); } previous.setValue(out); } static QByteArray deserializeLengthDelimited(SelfcheckIterator &it) { qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16); unsigned int length = deserializeVarintCommon(it); QByteArray result((QByteArray::const_iterator&)it, length); it += length; return result; } static QByteArray serializeLengthDelimited(const QByteArray &data) { qProtoDebug() << __func__ << "data.size" << data.size() << "data" << data.toHex(); QByteArray result(data); //Varint serialize field size and apply result as starting point ProtobufObjectPrivate::prependLengthDelimitedSize(result); return result; } }; }