Browse Source

Remove linkage between ProtobufObjectPrivate and proto-serializer specific functions

- Define interface functions that should be reimplemented by serialization provider
- ProtobufObjectPrivate now is ready to be serialization registry
Alexey Edelev 5 years ago
parent
commit
b26cc26dd3

+ 1 - 1
src/protobuf/CMakeLists.txt

@@ -70,4 +70,4 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/ProtobufLookup.cmake" "${CMAKE_BINARY_
 install(FILES "${CMAKE_BINARY_DIR}/ProtobufLookup.cmake" DESTINATION "${TARGET_CMAKE_DIR}")
 
 # TODO: keep quick plugin for future development, but it's useless for now
-add_subdirectory("quick")
+#add_subdirectory("quick")

+ 91 - 59
src/protobuf/qprotobufobject_p.cpp

@@ -65,97 +65,129 @@ void ProtobufObjectPrivate::registerSerializers()
     QProtobufSerializerPrivate::wrapSerializer<QByteArrayList>(QProtobufSerializerPrivate::serializeListType<QByteArray>, QProtobufSerializerPrivate::deserializeList<QByteArray>, LengthDelimited);
 }
 
-QByteArray ProtobufObjectPrivate::serializeProperty(const QVariant &propertyValue, int fieldIndex, const QMetaProperty &metaProperty)
+QByteArray ProtobufObjectPrivate::serializeProperty(const QVariant &propertyValue, int fieldIndex, bool isEnum)
 {
     qProtoDebug() << __func__ << "propertyValue" << propertyValue << "fieldIndex" << fieldIndex
-                  << "typeName" << metaProperty.typeName() << static_cast<QMetaType::Type>(propertyValue.type());
+                  << static_cast<QMetaType::Type>(propertyValue.type());
 
     QByteArray result;
     WireTypes type = UnknownWireType;
 
-    if (metaProperty.isEnumType()) {
-        type = Varint;
-        result.append(QProtobufSerializerPrivate::serializeBasic(int64(propertyValue.value<int32_t>()), fieldIndex));
-    } else {
-        result.append(serializeUserType(propertyValue, fieldIndex, type));
+    int userType = propertyValue.userType();
+    if (isEnum) {
+        userType = qMetaTypeId<int64>();
     }
+    auto serializer = serializers.at(userType);//Throws exception if not found
+    type = serializer.type;
+    result.append(serializer.serializer(propertyValue, fieldIndex));
 
     if (fieldIndex != NotUsedFieldIndex
             && type != UnknownWireType) {
-        result.prepend(encodeHeader(fieldIndex, type));
+        result.prepend(QProtobufSerializerPrivate::encodeHeader(fieldIndex, type));
     }
     return result;
 }
 
-QByteArray ProtobufObjectPrivate::serializeUserType(const QVariant &propertyValue, int &fieldIndex, WireTypes &type)
+QByteArray ProtobufObjectPrivate::serializeObjectCommon(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
 {
-    qProtoDebug() << __func__ << "propertyValue" << propertyValue << "fieldIndex" << fieldIndex;
+    QByteArray result;
+    for (const auto &field : propertyOrdering) {
+        int propertyIndex = field.second;
+        int fieldIndex = field.first;
+        Q_ASSERT_X(fieldIndex < 536870912 && fieldIndex > 0, "", "fieldIndex is out of range");
+        QMetaProperty metaProperty = metaObject.property(propertyIndex);
+        const char *propertyName = metaProperty.name();
+        const QVariant &propertyValue = object->property(propertyName);
+        result.append(ProtobufObjectPrivate::serializeProperty(propertyValue, fieldIndex, metaProperty.isEnumType()));
+    }
 
-    int userType = propertyValue.userType();
-    auto serializer = serializers.at(userType);//Throws exception if not found
-    type = serializer.type;
-    return serializer.serializer(propertyValue, fieldIndex);
+    return result;
 }
 
-void ProtobufObjectPrivate::deserializeProperty(QObject *object, const QMetaProperty &metaProperty, SelfcheckIterator &it, WireTypes wireType)
+void ProtobufObjectPrivate::deserializeObjectCommon(QObject *object, const QByteArray &data, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
 {
-    qProtoDebug() << __func__ << " wireType: " << wireType << " metaProperty: " << metaProperty.typeName()
-                  << "currentByte:" << QString::number((*it), 16);
-
-    QVariant newPropertyValue;
-    if (metaProperty.isEnumType()) {
-        newPropertyValue = QVariant::fromValue(int32_t(QProtobufSerializerPrivate::deserializeBasic<int64>(it).value<int64>()._t));
-    } else {
-        newPropertyValue = metaProperty.read(object);
-        deserializeUserType(metaProperty, it, newPropertyValue);
+    for (SelfcheckIterator it(data); it != data.end();) {
+        deserializeProperty(object, it, propertyOrdering, metaObject);
     }
-    metaProperty.write(object, newPropertyValue);
 }
 
-void ProtobufObjectPrivate::deserializeUserType(const QMetaProperty &metaProperty, SelfcheckIterator &it, QVariant &out)
+QByteArray ProtobufObjectPrivate::serializeObject(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
 {
-    qProtoDebug() << __func__ << "userType" << metaProperty.userType() << "typeName" << metaProperty.typeName()
-                  << "currentByte:" << QString::number((*it), 16);
-    int userType = metaProperty.userType();
-    auto deserializer = serializers.at(userType).deserializer;//Throws exception if not found
-    deserializer(it, out);
+    return QProtobufSerializerPrivate::prependLengthDelimitedSize(serializeObjectCommon(object, propertyOrdering, metaObject));
 }
 
-void ProtobufObjectPrivate::skipVarint(SelfcheckIterator &it)
+void ProtobufObjectPrivate::deserializeObject(QObject *object, SelfcheckIterator &it, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
 {
-    while ((*it) & 0x80) {
-        ++it;
-    }
-    ++it;
+    QByteArray array = QProtobufSerializerPrivate::deserializeLengthDelimited(it);
+    deserializeObjectCommon(object, array, propertyOrdering, metaObject);
 }
 
-void ProtobufObjectPrivate::skipLengthDelimited(SelfcheckIterator &it)
+QByteArray ProtobufObjectPrivate::serializeListObject(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject, int fieldIndex)
 {
-    //Get length of lenght-delimited field
-    unsigned int length = QProtobufSerializerPrivate::deserializeBasic<uint32>(it).toUInt();
-    it += length;
+    QByteArray result = QProtobufSerializerPrivate::encodeHeader(fieldIndex, LengthDelimited);
+    result.append(serializeObject(object, propertyOrdering, metaObject));
+    return result;
 }
 
-int ProtobufObjectPrivate::skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type)
+QByteArray ProtobufObjectPrivate::serializeMapPair(const QVariant &key, const QVariant &value, int fieldIndex)
 {
-    const auto initialIt = QByteArray::const_iterator(it);
-    switch (type) {
-    case WireTypes::Varint:
-        skipVarint(it);
-        break;
-    case WireTypes::Fixed32:
-        it += sizeof(decltype(fixed32::_t));
-        break;
-    case WireTypes::Fixed64:
-        it += sizeof(decltype(fixed64::_t));
-        break;
-    case WireTypes::LengthDelimited:
-        skipLengthDelimited(it);
-        break;
-    case WireTypes::UnknownWireType:
-    default:
-        throw std::invalid_argument("Cannot skip due to undefined length of the redundant field.");
+    QByteArray result = QProtobufSerializerPrivate::encodeHeader(fieldIndex, LengthDelimited);
+    result.append(QProtobufSerializerPrivate::prependLengthDelimitedSize(serializeProperty(key, 1, false) + serializeProperty(value, 2, false)));
+    return result;
+}
+
+void ProtobufObjectPrivate::deserializeMapPair(QVariant &key, QVariant &value, SelfcheckIterator &it)
+{
+    int mapIndex = 0;
+    WireTypes type = WireTypes::UnknownWireType;
+    unsigned int count = QProtobufSerializerPrivate::deserializeVarintCommon<uint32>(it);
+    qProtoDebug() << __func__ << "count:" << count;
+    SelfcheckIterator last = it + count;
+    while (it != last) {
+        QProtobufSerializerPrivate::decodeHeader(it, mapIndex, type);
+        if(mapIndex == 1) {
+            deserializeMapField(key, it);
+        } else {
+            deserializeMapField(value, it);
+        }
+    }
+}
+
+
+void ProtobufObjectPrivate::deserializeProperty(QObject *object, SelfcheckIterator &it, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
+{
+    //Each iteration we expect iterator is setup to beginning of next chunk
+    int fieldNumber = NotUsedFieldIndex;
+    WireTypes wireType = UnknownWireType;
+    if (!QProtobufSerializerPrivate::decodeHeader(it, fieldNumber, wireType)) {
+        qProtoCritical() << "Message received doesn't contains valid header byte. "
+                            "Trying next, but seems stream is broken" << QString::number((*it), 16);
+        throw std::invalid_argument("Message received doesn't contains valid header byte. "
+                              "Seems stream is broken");
+    }
+
+    auto propertyNumberIt = propertyOrdering.find(fieldNumber);
+    if (propertyNumberIt == std::end(propertyOrdering)) {
+        auto bytesCount = QProtobufSerializerPrivate::skipSerializedFieldBytes(it, wireType);
+        qProtoWarning() << "Message received contains unexpected/optional field. WireType:" << wireType
+                        << ", field number: " << fieldNumber << "Skipped:" << (bytesCount + 1) << "bytes";
+        return;
     }
 
-    return std::distance(initialIt, QByteArray::const_iterator(it));
+    int propertyIndex = propertyNumberIt->second;
+    QMetaProperty metaProperty = metaObject.property(propertyIndex);
+
+    qProtoDebug() << __func__ << " wireType: " << wireType << " metaProperty: " << metaProperty.typeName()
+                  << "currentByte:" << QString::number((*it), 16);
+
+    QVariant newPropertyValue;
+    if (metaProperty.isEnumType()) {
+        newPropertyValue = QVariant::fromValue(int32_t(QProtobufSerializerPrivate::deserializeBasic<int64>(it).value<int64>()._t));
+    } else {
+        newPropertyValue = metaProperty.read(object);
+        int userType = metaProperty.userType();
+        auto deserializer = serializers.at(userType).deserializer;//Throws exception if not found
+        deserializer(it, newPropertyValue);
+    }
+    metaProperty.write(object, newPropertyValue);
 }

+ 35 - 258
src/protobuf/qprotobufobject_p.h

@@ -46,50 +46,6 @@
 
 namespace qtprotobuf {
 
-template <typename V,
-          typename std::enable_if_t<std::is_integral<V>::value
-                                    && std::is_unsigned<V>::value, int> = 0>
-static V deserializeVarintCommon(SelfcheckIterator &it) {
-    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-    V value = 0;
-    int k = 0;
-    while (true) {
-        uint64_t byte = static_cast<uint64_t>(*it);
-        value += (byte & 0b01111111) << k;
-        k += 7;
-        if (((*it) & 0b10000000) == 0) {
-            break;
-        }
-        ++it;
-    }
-    ++it;
-    return value;
-}
-
-template <typename V,
-          typename std::enable_if_t<std::is_integral<V>::value
-                                    && std::is_unsigned<V>::value, int> = 0>
-static QByteArray serializeVarintCommon(const V &value) {
-    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;
-    }
-
-    if (result.isEmpty()) {
-        result.append('\0');
-    }
-
-    result.data()[result.size() - 1] &= ~0b10000000;
-    return result;
-}
-
 class QTPROTOBUFSHARED_EXPORT ProtobufObjectPrivate
 {
     ProtobufObjectPrivate() = delete;
@@ -110,45 +66,22 @@ public:
 
     static void registerSerializers();
 
-    static QByteArray encodeHeader(int fieldIndex, WireTypes wireType);
-    static bool decodeHeader(SelfcheckIterator &it, int &fieldIndex, WireTypes &wireType);
+    static QByteArray serializeProperty(const QVariant &propertyValue, int fieldIndex, bool isEnum);
+    static void deserializeProperty(QObject *object, SelfcheckIterator &it, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);
+
+    static QByteArray serializeObject(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);
+    static void deserializeObject(QObject *object, SelfcheckIterator &it, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);
 
-    static QByteArray serializeProperty(const QVariant &propertyValue, int fieldIndex, const QMetaProperty &metaProperty);
-    static QByteArray serializeUserType(const QVariant &propertyValue, int &fieldIndex, WireTypes &type);
+    static QByteArray serializeListObject(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject, int fieldIndex);
 
-    static void deserializeProperty(QObject *object, const QMetaProperty &metaProperty, SelfcheckIterator &it, WireTypes wireType);
-    static void deserializeUserType(const QMetaProperty &metaProperty, SelfcheckIterator &it, QVariant &out);
+    static QByteArray serializeMapPair(const QVariant &key, const QVariant &value, int fieldIndex);
+    static void deserializeMapPair(QVariant &key, QVariant &value, SelfcheckIterator &it);
 
+    static QByteArray serializeObjectCommon(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);//TODO make protected
+    static void deserializeObjectCommon(QObject *object, const QByteArray &array, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);//TODO make protected
     //###########################################################################
     //                           Serialization helpers
     //###########################################################################
-
-    /**
-     * @brief Gets length of a byte-array and prepends to it its serialized length value
-     *      using the appropriate serialization algorithm
-     *
-     *
-     * @param[in, out] serializedList Byte-array to be prepended
-     */
-    static void prependLengthDelimitedSize(QByteArray &serializedList)
-    {
-        serializedList.prepend(serializeVarintCommon<uint32_t>(serializedList.size()));
-    }
-
-    //----------------Serialize length delimited bytes and strings---------------
-    static QByteArray serializeLengthDelimited(const QString &data) {
-        qProtoDebug() << __func__ << "data.size" << data.size() << "data" << data;
-        return serializeLengthDelimited(data.toUtf8());
-    }
-
-    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
-        prependLengthDelimitedSize(result);
-        return result;
-    }
-
     //------------------------Serialize lists of any type------------------------
     template<typename V,
              typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
@@ -166,8 +99,7 @@ public:
                 qProtoWarning() << "Null pointer in list";
                 continue;
             }
-            serializedList.append(encodeHeader(outFieldIndex, LengthDelimited));
-            serializedList.append(serializeLengthDelimited(serializeObject<V>(value.data())));
+            serializedList.append(serializeListObject(value.data(), V::propertyOrdering, V::staticMetaObject, outFieldIndex));
         }
 
         outFieldIndex = NotUsedFieldIndex;
@@ -182,15 +114,9 @@ public:
         QMap<K,V> mapValue = value.value<QMap<K,V>>();
         using ItType = typename QMap<K,V>::const_iterator;
         QByteArray mapResult;
-        auto kSerializer = serializers.at(qMetaTypeId<K>());//Throws exception if not found
-        auto vSerializer = serializers.at(qMetaTypeId<V>());//Throws exception if not found
 
         for ( ItType it = mapValue.constBegin(); it != mapValue.constEnd(); it++) {
-            QByteArray result;
-            result = mapSerializeHelper<K, 1>(it.key(), kSerializer) + mapSerializeHelper<V, 2>(it.value(), vSerializer);
-            prependLengthDelimitedSize(result);
-            result.prepend(encodeHeader(outFieldIndex, LengthDelimited));
-            mapResult.append(result);
+            mapResult.append(serializeMapPair(QVariant::fromValue<K>(it.key()), QVariant::fromValue<V>(it.value()), outFieldIndex));
         }
         outFieldIndex = NotUsedFieldIndex;
         return mapResult;
@@ -202,61 +128,21 @@ public:
         QMap<K, QSharedPointer<V>> mapValue = value.value<QMap<K, QSharedPointer<V>>>();
         using ItType = typename QMap<K, QSharedPointer<V>>::const_iterator;
         QByteArray mapResult;
-        auto kSerializer = serializers.at(qMetaTypeId<K>());//Throws exception if not found
-        auto vSerializer = serializers.at(qMetaTypeId<V *>());//Throws exception if not found
 
         for ( ItType it = mapValue.constBegin(); it != mapValue.constEnd(); it++) {
-            QByteArray result;
             if (it.value().isNull()) {
                 qProtoWarning() << __func__ << "Trying to serialize map value that contains nullptr";
                 continue;
             }
-            result = mapSerializeHelper<K, 1>(it.key(), kSerializer) + mapSerializeHelper<V, 2>(it.value().data(), vSerializer);
-            prependLengthDelimitedSize(result);
-            result.prepend(encodeHeader(outFieldIndex, LengthDelimited));
-            mapResult.append(result);
+            mapResult.append(serializeMapPair(QVariant::fromValue<K>(it.key()), QVariant::fromValue<V *>(it.value().data()), outFieldIndex));
         }
         outFieldIndex = NotUsedFieldIndex;
         return mapResult;
     }
 
-    template <typename V, int num,
-              typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
-    static QByteArray mapSerializeHelper(const V &value, const SerializationHandlers &handlers) {
-        int mapIndex = num;
-        QByteArray result = handlers.serializer(QVariant::fromValue<V>(value), mapIndex);
-        if (mapIndex != NotUsedFieldIndex
-                && handlers.type != UnknownWireType) {
-            result.prepend(encodeHeader(mapIndex, handlers.type));
-        }
-        return result;
-    }
-
-    template <typename V, int num,
-              typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static QByteArray mapSerializeHelper(V *value, const SerializationHandlers &handlers) {
-        int mapIndex = num;
-        QByteArray result = handlers.serializer(QVariant::fromValue<V *>(value), mapIndex);
-        if (mapIndex != NotUsedFieldIndex
-                && handlers.type != UnknownWireType) {
-            result.prepend(encodeHeader(mapIndex, handlers.type));
-        }
-        return result;
-    }
-
     //###########################################################################
     //                           Deserialization helpers
     //###########################################################################
-    //---------------Deserialize length delimited bytes and strings--------------
-    static QByteArray deserializeLengthDelimited(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        unsigned int length = deserializeVarintCommon<uint32>(it);
-        QByteArray result((QByteArray::const_iterator&)it, length);
-        it += length;
-        return result;
-    }
-
     //-----------------------Deserialize lists of any type-----------------------
     template <typename V,
               typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
@@ -275,56 +161,28 @@ public:
               typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
     static void deserializeMap(SelfcheckIterator &it, QVariant &previous) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-        QMap<K,V> out = previous.value<QMap<K,V>>();
-
-        int mapIndex = 0;
-        WireTypes type = WireTypes::UnknownWireType;
-
-        K key;
-        V value;
-
-        unsigned int count = deserializeVarintCommon<uint32>(it);
-        qProtoDebug() << __func__ << "count:" << count;
-        SelfcheckIterator last = it + count;
-        while (it != last) {
-            decodeHeader(it, mapIndex, type);
-            if(mapIndex == 1) {
-                key = deserializeMapHelper<K>(it);
-            } else {
-                value = deserializeMapHelper<V>(it);
-            }
-        }
 
-        out[key] = value;
-        previous = QVariant::fromValue<QMap<K,V>>(out);
+        QMap<K, V> out = previous.value<QMap<K, V>>();
+        QVariant key = QVariant::fromValue<K>(K());
+        QVariant value = QVariant::fromValue<V>(V());
+
+        deserializeMapPair(key, value, it);
+        out[key.value<K>()] = value.value<V>();
+        previous = QVariant::fromValue<QMap<K, V>>(out);
     }
 
     template <typename K, typename V,
               typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
     static void deserializeMap(SelfcheckIterator &it, QVariant &previous) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-        auto out = previous.value<QMap<K, QSharedPointer<V>>>();
 
-        int mapIndex = 0;
-        WireTypes type = WireTypes::UnknownWireType;
-
-        K key;
-        V *value;
-
-        unsigned int count = deserializeVarintCommon<uint32>(it);
-        qProtoDebug() << __func__ << "count:" << count;
-        SelfcheckIterator last = it + count;
-        while (it != last) {
-            decodeHeader(it, mapIndex, type);
-            if(mapIndex == 1) {
-                key = deserializeMapHelper<K>(it);
-            } else {
-                value = deserializeMapHelper<V>(it);
-            }
-        }
+        auto out = previous.value<QMap<K, QSharedPointer<V>>>();
+        QVariant key = QVariant::fromValue<K>(K());
+        QVariant value = QVariant::fromValue<V *>(nullptr);
 
-        out[key] = QSharedPointer<V>(value);
-        previous = QVariant::fromValue<QMap<K,QSharedPointer<V>>>(out);
+        deserializeMapPair(key, value, it);
+        out[key.value<K>()] = QSharedPointer<V>(value.value<V *>());
+        previous = QVariant::fromValue<QMap<K, QSharedPointer<V>>>(out);
     }
 
     template <typename T,
@@ -345,6 +203,11 @@ public:
         return value.value<T>();
     }
 
+    static void deserializeMapField(QVariant &value, SelfcheckIterator &it) {
+        auto serializer = serializers.at(value.userType());//Throws exception if not found
+        serializer.deserializer(it, value);
+    }
+
     //-----------------------Functions to work with objects------------------------
     template<typename T>
     static void registerSerializers() {
@@ -371,14 +234,14 @@ public:
     template <typename T,
               typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
     static QByteArray serializeComplexType(const QVariant &value, int &/*outFieldIndex*/) {
-        return ProtobufObjectPrivate::serializeLengthDelimited(serializeObject<T>(value.value<T *>()));
+        return ProtobufObjectPrivate::serializeObject(value.value<T *>(), T::propertyOrdering, T::staticMetaObject);
     }
 
     template <typename T,
               typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
     static void deserializeComplexType(SelfcheckIterator &it, QVariant &to) {
         T *value = new T;
-        deserializeObject<T>(value, ProtobufObjectPrivate::deserializeLengthDelimited(it));
+        deserializeObject(value, it, T::propertyOrdering, T::staticMetaObject);
         to = QVariant::fromValue<T *>(value);
     }
 
@@ -405,28 +268,9 @@ public:
     template<typename T>
     static QByteArray serializeObject(const QObject *object) {
         qProtoDebug() << T::staticMetaObject.className() << "serialize";
-
-        QByteArray result;
-        for (const auto field : T::propertyOrdering) {
-            int propertyIndex = field.second;
-            int fieldIndex = field.first;
-            ASSERT_FIELD_NUMBER(fieldIndex);
-            QMetaProperty metaProperty = T::staticMetaObject.property(propertyIndex);
-            const char *propertyName = metaProperty.name();
-            const QVariant &propertyValue = object->property(propertyName);
-            result.append(ProtobufObjectPrivate::serializeProperty(propertyValue, fieldIndex, metaProperty));
-        }
-
-        return result;
+        return serializeObjectCommon(object, T::propertyOrdering, T::staticMetaObject);
     }
 
-    // this set of 3 methods is used to skip bytes corresponding to an unexpected property
-    // in a serialized message met while the message being deserialized
-    static void skipVarint(SelfcheckIterator &it);
-    static void skipLengthDelimited(SelfcheckIterator &it);
-    static int skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type);
-
-
     /**
     * @brief Deserialization of a byte-array into a registered qtproto message object
     *
@@ -439,31 +283,7 @@ public:
     template<typename T>
     static void deserializeObject(QObject *object, const QByteArray &array) {
         qProtoDebug() << T::staticMetaObject.className() << "deserialize";
-
-        for (SelfcheckIterator it(array); it != array.end();) {
-            //Each iteration we expect iterator is setup to beginning of next chunk
-            int fieldNumber = NotUsedFieldIndex;
-            WireTypes wireType = UnknownWireType;
-            if (!ProtobufObjectPrivate::decodeHeader(it, fieldNumber, wireType)) {
-                qProtoCritical() << "Message received doesn't contains valid header byte. "
-                                    "Trying next, but seems stream is broken" << QString::number((*it), 16);
-                throw std::invalid_argument("Message received doesn't contains valid header byte. "
-                                      "Seems stream is broken");
-            }
-
-            auto propertyNumberIt = T::propertyOrdering.find(fieldNumber);
-            if (propertyNumberIt == std::end(T::propertyOrdering)) {
-                auto bytesCount = skipSerializedFieldBytes(it, wireType);
-                qProtoWarning() << "Message received contains unexpected/optional field. WireType:" << wireType
-                                << ", field number: " << fieldNumber << "Skipped:" << (bytesCount + 1) << "bytes";
-                continue;
-            }
-
-            int propertyIndex = propertyNumberIt->second;
-            QMetaProperty metaProperty = T::staticMetaObject.property(propertyIndex);
-
-            ProtobufObjectPrivate::deserializeProperty(object, metaProperty, it, wireType);
-        }
+        deserializeObjectCommon(object, array, T::propertyOrdering, T::staticMetaObject);
     }
 
     //###########################################################################
@@ -499,47 +319,4 @@ public:
     }
 };
 
-//###########################################################################
-//                             Common functions
-//###########################################################################
-
-/*! @brief Encode a property field index and its type into output bytes
- *
- * @details
- * Header byte
- *  Meaning    |  Field index  |  Type
- *  ---------- | ------------- | --------
- *  bit number | 7  6  5  4  3 | 2  1  0
- * @param fieldIndex The index of a property in parent object
- * @param wireType Serialization type used for the property with index @p fieldIndex
- *
- * @return Varint encoded fieldIndex and wireType
- */
-inline QByteArray ProtobufObjectPrivate::encodeHeader(int fieldIndex, WireTypes wireType)
-{
-    uint32_t header = (fieldIndex << 3) | wireType;
-    return serializeVarintCommon<uint32_t>(header);
-}
-
-/*! @brief Decode a property field index and its serialization type from input bytes
- *
- * @param[in] Iterator that points to header with encoded field index and serialization type
- * @param[out] fieldIndex Decoded index of a property in parent object
- * @param[out] wireType Decoded serialization type used for the property with index @p fieldIndex
- *
- * @return true if both decoded wireType and fieldIndex have "allowed" values and false, otherwise
- */
-inline bool ProtobufObjectPrivate::decodeHeader(SelfcheckIterator &it, int &fieldIndex, WireTypes &wireType)
-{
-    uint32_t header = deserializeVarintCommon<uint32_t>(it);
-    wireType = static_cast<WireTypes>(header & 0b00000111);
-    fieldIndex = header >> 3;
-
-    constexpr int maxFieldIndex = (1 << 29) - 1;
-    return fieldIndex <= maxFieldIndex && fieldIndex > 0 && (wireType == Varint
-                                                  || wireType == Fixed64
-                                                  || wireType == Fixed32
-                                                  || wireType == LengthDelimited);
-}
-
 }

+ 153 - 5
src/protobuf/qprotobufserializer_p.h

@@ -38,6 +38,50 @@ class QProtobufSerializerPrivate {
     QProtobufSerializerPrivate(ProtobufObjectPrivate &&) = delete;
     QProtobufSerializerPrivate &operator =(QProtobufSerializerPrivate &&) = delete;
 public:
+    template <typename V,
+              typename std::enable_if_t<std::is_integral<V>::value
+                                        && std::is_unsigned<V>::value, int> = 0>
+    static QByteArray serializeVarintCommon(const V &value) {
+        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;
+        }
+
+        if (result.isEmpty()) {
+            result.append('\0');
+        }
+
+        result.data()[result.size() - 1] &= ~0b10000000;
+        return result;
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_integral<V>::value
+                                        && std::is_unsigned<V>::value, int> = 0>
+    static V deserializeVarintCommon(SelfcheckIterator &it) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+        V value = 0;
+        int k = 0;
+        while (true) {
+            uint64_t byte = static_cast<uint64_t>(*it);
+            value += (byte & 0b01111111) << k;
+            k += 7;
+            if (((*it) & 0b10000000) == 0) {
+                break;
+            }
+            ++it;
+        }
+        ++it;
+        return value;
+    }
+
     template <typename T,
               typename std::enable_if_t<!std::is_base_of<QObject, T>::value, int> = 0>
     static void wrapSerializer(const std::function<QByteArray(const T &, int &)> &s, const std::function<QVariant(SelfcheckIterator &)> &d, WireTypes type)
@@ -269,7 +313,7 @@ public:
             serializedList.append(serializeBasic<V>(value, empty));
         }
         //If internal field type is not LengthDelimited, exact amount of fields to be specified
-        ProtobufObjectPrivate::prependLengthDelimitedSize(serializedList);
+        serializedList = prependLengthDelimitedSize(serializedList);
         return serializedList;
     }
 
@@ -285,7 +329,7 @@ public:
 
         QByteArray serializedList;
         for (auto &value : listValue) {
-            serializedList.append(ProtobufObjectPrivate::encodeHeader(outFieldIndex, LengthDelimited));
+            serializedList.append(QProtobufSerializerPrivate::encodeHeader(outFieldIndex, LengthDelimited));
             serializedList.append(serializeLengthDelimited(value.toUtf8()));
         }
 
@@ -305,7 +349,7 @@ public:
 
         QByteArray serializedList;
         for (auto &value : listValue) {
-            serializedList.append(ProtobufObjectPrivate::encodeHeader(outFieldIndex, LengthDelimited));
+            serializedList.append(QProtobufSerializerPrivate::encodeHeader(outFieldIndex, LengthDelimited));
             serializedList.append(serializeLengthDelimited(value));
         }
 
@@ -355,7 +399,7 @@ public:
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
         unsigned int length = deserializeVarintCommon<uint32>(it);
-        QByteArray result((QByteArray::const_iterator&)it, length);
+        QByteArray result((QByteArray::const_iterator&)it, length); //TODO it's possible to void buffeer copying by setupimg new "end of QByteArray";
         it += length;
         return result;
     }
@@ -364,9 +408,113 @@ public:
         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);
+        result = prependLengthDelimitedSize(result);
         return result;
     }
+
+    static bool decodeHeader(SelfcheckIterator &it, int &fieldIndex, WireTypes &wireType);
+    static QByteArray encodeHeader(int fieldIndex, WireTypes wireType);
+
+    /**
+     * @brief Gets length of a byte-array and prepends to it its serialized length value
+     *      using the appropriate serialization algorithm
+     *
+     *
+     * @param[in, out] serializedList Byte-array to be prepended
+     */
+    static QByteArray prependLengthDelimitedSize(const QByteArray &data)
+    {
+        return serializeVarintCommon<uint32_t>(data.size()) + data;
+    }
+
+    // this set of 3 methods is used to skip bytes corresponding to an unexpected property
+    // in a serialized message met while the message being deserialized
+    static void skipVarint(SelfcheckIterator &it);
+    static void skipLengthDelimited(SelfcheckIterator &it);
+    static int skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type);
 };
 
+//###########################################################################
+//                             Common functions
+//###########################################################################
+
+/*! @brief Encode a property field index and its type into output bytes
+ *
+ * @details
+ * Header byte
+ *  Meaning    |  Field index  |  Type
+ *  ---------- | ------------- | --------
+ *  bit number | 7  6  5  4  3 | 2  1  0
+ * @param fieldIndex The index of a property in parent object
+ * @param wireType Serialization type used for the property with index @p fieldIndex
+ *
+ * @return Varint encoded fieldIndex and wireType
+ */
+inline QByteArray QProtobufSerializerPrivate::encodeHeader(int fieldIndex, WireTypes wireType)
+{
+    uint32_t header = (fieldIndex << 3) | wireType;
+    return serializeVarintCommon<uint32_t>(header);
+}
+
+/*! @brief Decode a property field index and its serialization type from input bytes
+ *
+ * @param[in] Iterator that points to header with encoded field index and serialization type
+ * @param[out] fieldIndex Decoded index of a property in parent object
+ * @param[out] wireType Decoded serialization type used for the property with index @p fieldIndex
+ *
+ * @return true if both decoded wireType and fieldIndex have "allowed" values and false, otherwise
+ */
+inline bool QProtobufSerializerPrivate::decodeHeader(SelfcheckIterator &it, int &fieldIndex, WireTypes &wireType)
+{
+    uint32_t header = deserializeVarintCommon<uint32_t>(it);
+    wireType = static_cast<WireTypes>(header & 0b00000111);
+    fieldIndex = header >> 3;
+
+    constexpr int maxFieldIndex = (1 << 29) - 1;
+    return fieldIndex <= maxFieldIndex && fieldIndex > 0 && (wireType == Varint
+                                                  || wireType == Fixed64
+                                                  || wireType == Fixed32
+                                                  || wireType == LengthDelimited);
+}
+
+void QProtobufSerializerPrivate::skipVarint(SelfcheckIterator &it)
+{
+    while ((*it) & 0x80) {
+        ++it;
+    }
+    ++it;
+}
+
+void QProtobufSerializerPrivate::skipLengthDelimited(SelfcheckIterator &it)
+{
+    //Get length of lenght-delimited field
+    unsigned int length = QProtobufSerializerPrivate::deserializeBasic<uint32>(it).toUInt();
+    it += length;
 }
+
+int QProtobufSerializerPrivate::skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type)
+{
+    const auto initialIt = QByteArray::const_iterator(it);
+    switch (type) {
+    case WireTypes::Varint:
+        skipVarint(it);
+        break;
+    case WireTypes::Fixed32:
+        it += sizeof(decltype(fixed32::_t));
+        break;
+    case WireTypes::Fixed64:
+        it += sizeof(decltype(fixed64::_t));
+        break;
+    case WireTypes::LengthDelimited:
+        skipLengthDelimited(it);
+        break;
+    case WireTypes::UnknownWireType:
+    default:
+        throw std::invalid_argument("Cannot skip due to undefined length of the redundant field.");
+    }
+
+    return std::distance(initialIt, QByteArray::const_iterator(it));
+}
+
+}
+

+ 1 - 1
tests/test_protobuf/serializationtest.cpp

@@ -1531,7 +1531,7 @@ TEST_F(SerializationTest, RepeatedComplexMessageTest)
     RepeatedComplexMessage test;
     test.setTestRepeatedComplex({msg, msg, msg});
     QByteArray result = test.serialize();
-    //qDebug() << "result " << result.toHex();
+    qDebug() << "result " << result.toHex();
 
     ASSERT_TRUE(result == QByteArray::fromHex("0a0c0819120832067177657274790a0c0819120832067177657274790a0c081912083206717765727479")
                 || result == QByteArray::fromHex("0a0c1208320671776572747908190a0c1208320671776572747908190a0c120832067177657274790819"));