Переглянути джерело

Hide basic types serializers

- Move implementation of basic types to separate class
- Prepare ProtobufObjectPrivate for replacment by serializers registry
Alexey Edelev 5 роки тому
батько
коміт
dec765c130

+ 1 - 4
src/generator/templates.cpp

@@ -176,10 +176,7 @@ const char *Templates::RegisterQmlListPropertyMetaTypeTemplate = "qRegisterMetaT
 
 const char *Templates::QEnumTemplate = "Q_ENUM($type$)\n";
 
-const char *Templates::MapSerializationRegisterTemplate = "qtprotobuf::ProtobufObjectPrivate::wrapSerializer<$classname$::$type$>(\n"
-                                                          "qtprotobuf::ProtobufObjectPrivate::serializeMap<$key_type$, $value_type$>,\n"
-                                                          "qtprotobuf::ProtobufObjectPrivate::deserializeMap<$key_type$, $value_type$>\n"
-                                                          ", qtprotobuf::LengthDelimited);\n";
+const char *Templates::MapSerializationRegisterTemplate = "qtprotobuf::ProtobufObjectPrivate::registerMap<$key_type$, $value_type$>();";
 
 const char *Templates::ClassDefinitionTemplate = "\nclass $classname$ : public $parent_class$\n"
                                                  "{\n";

+ 35 - 41
src/protobuf/qprotobufobject_p.cpp

@@ -25,50 +25,44 @@
 
 #include "qprotobufobject_p.h"
 
+#include "qprotobufserializer_p.h"
+
 using namespace qtprotobuf;
 
 ProtobufObjectPrivate::SerializerRegistry ProtobufObjectPrivate::serializers = {};
 
 void ProtobufObjectPrivate::registerSerializers()
 {
-    wrapSerializer<float>(ProtobufObjectPrivate::serializeBasic<float>, ProtobufObjectPrivate::deserializeBasic<float>, Fixed32);
-    wrapSerializer<double>(ProtobufObjectPrivate::serializeBasic<double>, ProtobufObjectPrivate::deserializeBasic<double>, Fixed64);
-    wrapSerializer<int32>(ProtobufObjectPrivate::serializeBasic<int32>, ProtobufObjectPrivate::deserializeBasic<int32>, Varint);
-    wrapSerializer<int64>(ProtobufObjectPrivate::serializeBasic<int64>, ProtobufObjectPrivate::deserializeBasic<int64>, Varint);
-    wrapSerializer<uint32>(ProtobufObjectPrivate::serializeBasic<uint32>, ProtobufObjectPrivate::deserializeBasic<uint32>, Varint);
-    wrapSerializer<uint64>(ProtobufObjectPrivate::serializeBasic<uint64>, ProtobufObjectPrivate::deserializeBasic<uint64>, Varint);
-    wrapSerializer<sint32>(ProtobufObjectPrivate::serializeBasic<sint32>, ProtobufObjectPrivate::deserializeBasic<sint32>, Varint);
-    wrapSerializer<sint64>(ProtobufObjectPrivate::serializeBasic<sint64>, ProtobufObjectPrivate::deserializeBasic<sint64>, Varint);
-    wrapSerializer<fixed32>(ProtobufObjectPrivate::serializeBasic<fixed32>, ProtobufObjectPrivate::deserializeBasic<fixed32>, Fixed32);
-    wrapSerializer<fixed64>(ProtobufObjectPrivate::serializeBasic<fixed64>, ProtobufObjectPrivate::deserializeBasic<fixed64>, Fixed64);
-    wrapSerializer<sfixed32>(ProtobufObjectPrivate::serializeBasic<sfixed32>, ProtobufObjectPrivate::deserializeBasic<sfixed32>, Fixed32);
-    wrapSerializer<sfixed64>(ProtobufObjectPrivate::serializeBasic<sfixed64>, ProtobufObjectPrivate::deserializeBasic<sfixed64>, Fixed64);
-    wrapSerializer<bool>(ProtobufObjectPrivate::serializeBasic<uint32>, ProtobufObjectPrivate::deserializeBasic<uint32>, Varint);
-
-    wrapSerializer<QString>([](const QString &data, int &/*fieldIndex*/) {
-        return serializeLengthDelimited(data);
-    }, [](SelfcheckIterator &it){
-        return QVariant::fromValue(QString::fromUtf8(deserializeLengthDelimited(it)));
-    }, LengthDelimited);
-
-    wrapSerializer<QByteArray>([](const QByteArray &data, int &/*fieldIndex*/) {
-        return serializeLengthDelimited(data);
-    }, ProtobufObjectPrivate::deserializeLengthDelimited, LengthDelimited);
-
-    wrapSerializer<FloatList>(ProtobufObjectPrivate::serializeListType<float>, ProtobufObjectPrivate::deserializeList<float>, LengthDelimited);
-    wrapSerializer<DoubleList>(ProtobufObjectPrivate::serializeListType<double>, ProtobufObjectPrivate::deserializeList<double>, LengthDelimited);
-    wrapSerializer<fixed32List>(ProtobufObjectPrivate::serializeListType<fixed32>, ProtobufObjectPrivate::deserializeList<fixed32>, LengthDelimited);
-    wrapSerializer<fixed64List>(ProtobufObjectPrivate::serializeListType<fixed64>, ProtobufObjectPrivate::deserializeList<fixed64>, LengthDelimited);
-    wrapSerializer<sfixed32List>(ProtobufObjectPrivate::serializeListType<sfixed32>, ProtobufObjectPrivate::deserializeList<sfixed32>, LengthDelimited);
-    wrapSerializer<sfixed64List>(ProtobufObjectPrivate::serializeListType<sfixed64>, ProtobufObjectPrivate::deserializeList<sfixed64>, LengthDelimited);
-    wrapSerializer<int32List>(ProtobufObjectPrivate::serializeListType<int32>, ProtobufObjectPrivate::deserializeList<int32>, LengthDelimited);
-    wrapSerializer<int64List>(ProtobufObjectPrivate::serializeListType<int64>, ProtobufObjectPrivate::deserializeList<int64>, LengthDelimited);
-    wrapSerializer<sint32List>(ProtobufObjectPrivate::serializeListType<sint32>, ProtobufObjectPrivate::deserializeList<sint32>, LengthDelimited);
-    wrapSerializer<sint64List>(ProtobufObjectPrivate::serializeListType<sint64>, ProtobufObjectPrivate::deserializeList<sint64>, LengthDelimited);
-    wrapSerializer<uint32List>(ProtobufObjectPrivate::serializeListType<uint32>, ProtobufObjectPrivate::deserializeList<uint32>, LengthDelimited);
-    wrapSerializer<uint64List>(ProtobufObjectPrivate::serializeListType<uint64>, ProtobufObjectPrivate::deserializeList<uint64>, LengthDelimited);
-    wrapSerializer<QStringList>(ProtobufObjectPrivate::serializeListType<QString>, ProtobufObjectPrivate::deserializeList<QString>, LengthDelimited);
-    wrapSerializer<QByteArrayList>(ProtobufObjectPrivate::serializeListType<QByteArray>, ProtobufObjectPrivate::deserializeList<QByteArray>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<float>(QProtobufSerializerPrivate::serializeBasic<float>, QProtobufSerializerPrivate::deserializeBasic<float>, Fixed32);
+    QProtobufSerializerPrivate::wrapSerializer<double>(QProtobufSerializerPrivate::serializeBasic<double>, QProtobufSerializerPrivate::deserializeBasic<double>, Fixed64);
+    QProtobufSerializerPrivate::wrapSerializer<int32>(QProtobufSerializerPrivate::serializeBasic<int32>, QProtobufSerializerPrivate::deserializeBasic<int32>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<int64>(QProtobufSerializerPrivate::serializeBasic<int64>, QProtobufSerializerPrivate::deserializeBasic<int64>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<uint32>(QProtobufSerializerPrivate::serializeBasic<uint32>, QProtobufSerializerPrivate::deserializeBasic<uint32>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<uint64>(QProtobufSerializerPrivate::serializeBasic<uint64>, QProtobufSerializerPrivate::deserializeBasic<uint64>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<sint32>(QProtobufSerializerPrivate::serializeBasic<sint32>, QProtobufSerializerPrivate::deserializeBasic<sint32>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<sint64>(QProtobufSerializerPrivate::serializeBasic<sint64>, QProtobufSerializerPrivate::deserializeBasic<sint64>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<fixed32>(QProtobufSerializerPrivate::serializeBasic<fixed32>, QProtobufSerializerPrivate::deserializeBasic<fixed32>, Fixed32);
+    QProtobufSerializerPrivate::wrapSerializer<fixed64>(QProtobufSerializerPrivate::serializeBasic<fixed64>, QProtobufSerializerPrivate::deserializeBasic<fixed64>, Fixed64);
+    QProtobufSerializerPrivate::wrapSerializer<sfixed32>(QProtobufSerializerPrivate::serializeBasic<sfixed32>, QProtobufSerializerPrivate::deserializeBasic<sfixed32>, Fixed32);
+    QProtobufSerializerPrivate::wrapSerializer<sfixed64>(QProtobufSerializerPrivate::serializeBasic<sfixed64>, QProtobufSerializerPrivate::deserializeBasic<sfixed64>, Fixed64);
+    QProtobufSerializerPrivate::wrapSerializer<bool>(QProtobufSerializerPrivate::serializeBasic<uint32>, QProtobufSerializerPrivate::deserializeBasic<uint32>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<QString>(QProtobufSerializerPrivate::serializeBasic<QString>, QProtobufSerializerPrivate::deserializeBasic<QString>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<QByteArray>(QProtobufSerializerPrivate::serializeBasic<QByteArray>, QProtobufSerializerPrivate::deserializeBasic<QByteArray>, LengthDelimited);
+
+    QProtobufSerializerPrivate::wrapSerializer<FloatList>(QProtobufSerializerPrivate::serializeListType<float>, QProtobufSerializerPrivate::deserializeList<float>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<DoubleList>(QProtobufSerializerPrivate::serializeListType<double>, QProtobufSerializerPrivate::deserializeList<double>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<fixed32List>(QProtobufSerializerPrivate::serializeListType<fixed32>, QProtobufSerializerPrivate::deserializeList<fixed32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<fixed64List>(QProtobufSerializerPrivate::serializeListType<fixed64>, QProtobufSerializerPrivate::deserializeList<fixed64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<sfixed32List>(QProtobufSerializerPrivate::serializeListType<sfixed32>, QProtobufSerializerPrivate::deserializeList<sfixed32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<sfixed64List>(QProtobufSerializerPrivate::serializeListType<sfixed64>, QProtobufSerializerPrivate::deserializeList<sfixed64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<int32List>(QProtobufSerializerPrivate::serializeListType<int32>, QProtobufSerializerPrivate::deserializeList<int32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<int64List>(QProtobufSerializerPrivate::serializeListType<int64>, QProtobufSerializerPrivate::deserializeList<int64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<sint32List>(QProtobufSerializerPrivate::serializeListType<sint32>, QProtobufSerializerPrivate::deserializeList<sint32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<sint64List>(QProtobufSerializerPrivate::serializeListType<sint64>, QProtobufSerializerPrivate::deserializeList<sint64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<uint32List>(QProtobufSerializerPrivate::serializeListType<uint32>, QProtobufSerializerPrivate::deserializeList<uint32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<uint64List>(QProtobufSerializerPrivate::serializeListType<uint64>, QProtobufSerializerPrivate::deserializeList<uint64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<QStringList>(QProtobufSerializerPrivate::serializeListType<QString>, QProtobufSerializerPrivate::deserializeList<QString>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<QByteArrayList>(QProtobufSerializerPrivate::serializeListType<QByteArray>, QProtobufSerializerPrivate::deserializeList<QByteArray>, LengthDelimited);
 }
 
 QByteArray ProtobufObjectPrivate::serializeProperty(const QVariant &propertyValue, int fieldIndex, const QMetaProperty &metaProperty)
@@ -81,7 +75,7 @@ QByteArray ProtobufObjectPrivate::serializeProperty(const QVariant &propertyValu
 
     if (metaProperty.isEnumType()) {
         type = Varint;
-        result.append(serializeBasic(int64(propertyValue.value<int32_t>()), fieldIndex));
+        result.append(QProtobufSerializerPrivate::serializeBasic(int64(propertyValue.value<int32_t>()), fieldIndex));
     } else {
         result.append(serializeUserType(propertyValue, fieldIndex, type));
     }
@@ -110,7 +104,7 @@ void ProtobufObjectPrivate::deserializeProperty(QObject *object, const QMetaProp
 
     QVariant newPropertyValue;
     if (metaProperty.isEnumType()) {
-        newPropertyValue = QVariant::fromValue(int32_t(deserializeBasic<int64>(it).value<int64>()._t));
+        newPropertyValue = QVariant::fromValue(int32_t(QProtobufSerializerPrivate::deserializeBasic<int64>(it).value<int64>()._t));
     } else {
         newPropertyValue = metaProperty.read(object);
         deserializeUserType(metaProperty, it, newPropertyValue);
@@ -138,7 +132,7 @@ void ProtobufObjectPrivate::skipVarint(SelfcheckIterator &it)
 void ProtobufObjectPrivate::skipLengthDelimited(SelfcheckIterator &it)
 {
     //Get length of lenght-delimited field
-    unsigned int length = deserializeBasic<uint32>(it).toUInt();
+    unsigned int length = QProtobufSerializerPrivate::deserializeBasic<uint32>(it).toUInt();
     it += length;
 }
 

+ 67 - 330
src/protobuf/qprotobufobject_p.h

@@ -46,6 +46,50 @@
 
 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;
@@ -66,34 +110,6 @@ public:
 
     static void registerSerializers();
 
-    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)
-    {
-        serializers[qMetaTypeId<T>()] = {
-            [s](const QVariant &value, int &fieldIndex) {
-                return s(value.value<T>(), fieldIndex);
-            },
-            [d](SelfcheckIterator &it, QVariant & value){
-                value = d(it);
-            },
-            type
-        };
-    }
-
-    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<void(SelfcheckIterator &it, QVariant & value)> &d, WireTypes type)
-    {
-        serializers[qMetaTypeId<T>()] = {
-            [s](const QVariant &value, int &fieldIndex) {
-                return s(value.value<T>(), fieldIndex);
-            },
-            d,
-            type
-        };
-    }
-
     static QByteArray encodeHeader(int fieldIndex, WireTypes wireType);
     static bool decodeHeader(SelfcheckIterator &it, int &fieldIndex, WireTypes &wireType);
 
@@ -116,135 +132,7 @@ public:
      */
     static void prependLengthDelimitedSize(QByteArray &serializedList)
     {
-        int empty = NotUsedFieldIndex;
-        auto result = serializeBasic(static_cast<uint32_t>(serializedList.size()), empty);
-        //Zero case.
-        if (result.isEmpty()) {
-            result.append('\0');
-        }
-        serializedList.prepend(result);
-    }
-
-    //----------------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 <typename V,
-              typename std::enable_if_t<std::is_floating_point<V>::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<V *>(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 <typename V,
-              typename std::enable_if_t<std::is_same<V, fixed32>::value
-                                        || std::is_same<V, fixed64>::value
-                                        || std::is_same<V, sfixed32>::value
-                                        || std::is_same<V, sfixed64>::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<V *>(result.data()) = value;
-        return result;
-    }
-
-    /**
-     *@brief Serialization of signed integral types
-     *
-     * Use <a href="https://developers.google.com/protocol-buffers/docs/encoding">ZigZag encoding</a> first,
-     * then apply serialization as for unsigned integral types
-     * @see serializeBasic\<typename V, typename std::enable_if_t\<std::is_integral\<V\>::value && std::is_unsigned\<V\>::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 <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        && std::is_signed<V>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "value" << value;
-        using UV = typename std::make_unsigned<V>::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<UV>(zigZagValue);
-        return serializeBasic(uValue, outFieldIndex);
-    }
-
-    template <typename V,
-              typename std::enable_if_t<std::is_same<V, int32>::value
-                                        || std::is_same<V, int64>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "value" << value;
-        using UV = typename std::make_unsigned<V>::type;
-        return serializeBasic(static_cast<UV>(value), outFieldIndex);
-    }
-
-    /**
-    *@brief Serialization of unsigned integral types
-    *
-    * Use <a href="https://developers.google.com/protocol-buffers/docs/encoding">Varint encoding</a>:
-    * "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 <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        && std::is_unsigned<V>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "value" << value;
-        V varint = value;
-        QByteArray result;
-        //Reserve maximum required number of bytes
-        result.reserve(sizeof(V));
-        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;
-        }
-
-        /* NOTE: Zero case aligned to reference cpp implementation. Where 0 ignored.
-         * if (result.isEmpty()) {
-         *     result.append('\0');
-         * }
-         */
-
-        // 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;
+        serializedList.prepend(serializeVarintCommon<uint32_t>(serializedList.size()));
     }
 
     //----------------Serialize length delimited bytes and strings---------------
@@ -262,49 +150,6 @@ public:
     }
 
     //------------------------Serialize lists of any type------------------------
-    template<typename V,
-             typename std::enable_if_t<!(std::is_same<V, QString>::value
-                                       || std::is_same<V, QByteArray>::value
-                                       || std::is_base_of<QObject, V>::value), int> = 0>
-    static QByteArray serializeListType(const QList<V> &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<V>(value, empty));
-        }
-        //If internal field type is not LengthDelimited, exact amount of fields to be specified
-        prependLengthDelimitedSize(serializedList);
-        return serializedList;
-    }
-
-    template<typename V,
-             typename std::enable_if_t<std::is_same<V, QString>::value
-                                       || std::is_same<V, QByteArray>::value, int> = 0>
-    static QByteArray serializeListType(const QList<V> &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(encodeHeader(outFieldIndex, LengthDelimited));
-            serializedList.append(serializeLengthDelimited(value));
-        }
-
-        outFieldIndex = NotUsedFieldIndex;
-        return serializedList;
-    }
-
     template<typename V,
              typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
     static QByteArray serializeListType(const QList<QSharedPointer<V>> &listValue, int &outFieldIndex) {
@@ -333,7 +178,8 @@ public:
     //-------------------------Serialize maps of any type------------------------
     template<typename K, typename V,
              typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
-    static QByteArray serializeMap(const QMap<K,V> &mapValue, int &outFieldIndex) {
+    static QByteArray serializeMap(const QVariant &value, int &outFieldIndex) {
+        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
@@ -352,7 +198,8 @@ public:
 
     template<typename K, typename V,
              typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static QByteArray serializeMap(const QMap<K, QSharedPointer<V>> &mapValue, int &outFieldIndex) {
+    static QByteArray serializeMap(const QVariant &value, int &outFieldIndex) {
+        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
@@ -400,124 +247,17 @@ public:
     //###########################################################################
     //                           Deserialization helpers
     //###########################################################################
-    template <typename V,
-              typename std::enable_if_t<std::is_floating_point<V>::value
-                                        || std::is_same<V, sfixed32>::value
-                                        || std::is_same<V, sfixed64>::value
-                                        || std::is_same<V, fixed32>::value
-                                        || std::is_same<V, fixed64>::value, int> = 0>
-    static uint32 getRepeatedFieldCount(SelfcheckIterator &it) {
-        return deserializeBasic<uint32>(it).value<uint32>() / sizeof(V);
-    }
-
-    template <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        || std::is_same<V, sint32>::value
-                                        || std::is_same<V, sint64>::value, int> = 0>
-    static uint32 getRepeatedFieldCount(SelfcheckIterator &it) {
-        return deserializeBasic<uint32>(it).value<uint32>();
-    }
-
-    //---------------Deserialize basic integral and floating point---------------
-    template <typename V,
-              typename std::enable_if_t<std::is_floating_point<V>::value
-                                        || std::is_same<V, fixed32>::value
-                                        || std::is_same<V, fixed64>::value
-                                        || std::is_same<V, sfixed32>::value
-                                        || std::is_same<V, sfixed64>::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 <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        && std::is_unsigned<V>::value, int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        return QVariant::fromValue(deserializeVarintCommon<V>(it));
-    }
-
-    template <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        && std::is_signed<V>::value,int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-        using  UV = typename std::make_unsigned<V>::type;
-        UV unsignedValue = deserializeVarintCommon<UV>(it);
-        V value = (unsignedValue >> 1) ^ (-1 * (unsignedValue & 1));
-        return QVariant::fromValue<V>(value);
-    }
-
-    template <typename V,
-              typename std::enable_if_t<std::is_same<int32, V>::value
-                                        || std::is_same<int64, V>::value, int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-        using  UV = typename std::make_unsigned<V>::type;
-        UV unsignedValue = deserializeVarintCommon<UV>(it);
-        V value = static_cast<V>(unsignedValue);
-        return QVariant::fromValue(value);
-    }
-
-    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;
-    }
-
     //---------------Deserialize length delimited bytes and strings--------------
     static QByteArray deserializeLengthDelimited(SelfcheckIterator &it) {
         qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
-        unsigned int length = deserializeBasic<uint32>(it).toUInt();
+        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_same<V, QByteArray>::value, int> = 0>
-    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        QByteArrayList list = previous.value<QByteArrayList>();
-        list.append(deserializeLengthDelimited(it));
-        previous.setValue(list);
-    }
-
-    template <typename V,
-              typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
-    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        QStringList list = previous.value<QStringList>();
-        QByteArray value = deserializeLengthDelimited(it);
-        qCritical() << "!!!deserializeLengthDelimited(it)" << QString::fromUtf8(value);
-        list.append(QString::fromUtf8(value));
-        previous.setValue(list);
-    }
-
     template <typename V,
               typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
     static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
@@ -530,23 +270,6 @@ public:
         previous.setValue(list);
     }
 
-    template <typename V,
-              typename std::enable_if_t<!(std::is_same<V, QString>::value
-                                        || std::is_same<V, QByteArray>::value
-                                        || std::is_base_of<QObject, V>::value), int> = 0>
-    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        QList<V> out;
-        unsigned int count = deserializeBasic<uint32>(it).toUInt();
-        SelfcheckIterator lastVarint = it + count;
-        while (it != lastVarint) {
-            QVariant variant = deserializeBasic<V>(it);
-            out.append(variant.value<V>());
-        }
-        previous.setValue(out);
-    }
-
     //-----------------------Deserialize maps of any type------------------------
     template <typename K, typename V,
               typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
@@ -560,7 +283,7 @@ public:
         K key;
         V value;
 
-        unsigned int count = deserializeBasic<uint32>(it).toUInt();
+        unsigned int count = deserializeVarintCommon<uint32>(it);
         qProtoDebug() << __func__ << "count:" << count;
         SelfcheckIterator last = it + count;
         while (it != last) {
@@ -588,7 +311,7 @@ public:
         K key;
         V *value;
 
-        unsigned int count = deserializeBasic<uint32>(it).toUInt();
+        unsigned int count = deserializeVarintCommon<uint32>(it);
         qProtoDebug() << __func__ << "count:" << count;
         SelfcheckIterator last = it + count;
         while (it != last) {
@@ -631,6 +354,20 @@ public:
                 ProtobufObjectPrivate::Deserializer(deserializeComplexListType<T>), LengthDelimited};
     }
 
+    template<typename K, typename V,
+             typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
+    static void registerMap() {
+        ProtobufObjectPrivate::serializers[qMetaTypeId<QMap<K, V>>()] = {qtprotobuf::ProtobufObjectPrivate::serializeMap<K, V>,
+        qtprotobuf::ProtobufObjectPrivate::deserializeMap<K, V>, LengthDelimited};
+    }
+
+    template<typename K, typename V,
+             typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
+    static void registerMap() {
+        ProtobufObjectPrivate::serializers[qMetaTypeId<QMap<K, QSharedPointer<V>>>()] = {qtprotobuf::ProtobufObjectPrivate::serializeMap<K, V>,
+        qtprotobuf::ProtobufObjectPrivate::deserializeMap<K, V>, LengthDelimited};
+    }
+
     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*/) {
@@ -781,7 +518,7 @@ public:
 inline QByteArray ProtobufObjectPrivate::encodeHeader(int fieldIndex, WireTypes wireType)
 {
     uint32_t header = (fieldIndex << 3) | wireType;
-    return serializeBasic(header, fieldIndex);
+    return serializeVarintCommon<uint32_t>(header);
 }
 
 /*! @brief Decode a property field index and its serialization type from input bytes

+ 372 - 0
src/protobuf/qprotobufserializer_p.h

@@ -0,0 +1,372 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>, Viktor Kopp <vifactor@gmail.com>
+ *
+ * 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 <QString>
+#include <QByteArray>
+
+#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 <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)
+    {
+        ProtobufObjectPrivate::serializers[qMetaTypeId<T>()] = {
+            [s](const QVariant &value, int &fieldIndex) {
+                return s(value.value<T>(), fieldIndex);
+            },
+            [d](SelfcheckIterator &it, QVariant & value){
+                value = d(it);
+            },
+            type
+        };
+    }
+
+    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<void(SelfcheckIterator &it, QVariant & value)> &d, WireTypes type)
+    {
+        ProtobufObjectPrivate::serializers[qMetaTypeId<T>()] = {
+            [s](const QVariant &value, int &fieldIndex) {
+                return s(value.value<T>(), 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 <typename V,
+              typename std::enable_if_t<std::is_floating_point<V>::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<V *>(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 <typename V,
+              typename std::enable_if_t<std::is_same<V, fixed32>::value
+                                        || std::is_same<V, fixed64>::value
+                                        || std::is_same<V, sfixed32>::value
+                                        || std::is_same<V, sfixed64>::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<V *>(result.data()) = value;
+        return result;
+    }
+
+    /**
+     *@brief Serialization of signed integral types
+     *
+     * Use <a href="https://developers.google.com/protocol-buffers/docs/encoding">ZigZag encoding</a> first,
+     * then apply serialization as for unsigned integral types
+     * @see serializeBasic\<typename V, typename std::enable_if_t\<std::is_integral\<V\>::value && std::is_unsigned\<V\>::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 <typename V,
+              typename std::enable_if_t<std::is_integral<V>::value
+                                        && std::is_signed<V>::value, int> = 0>
+    static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
+        qProtoDebug() << __func__ << "value" << value;
+        using UV = typename std::make_unsigned<V>::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<UV>(zigZagValue);
+        return serializeBasic(uValue, outFieldIndex);
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_same<V, int32>::value
+                                        || std::is_same<V, int64>::value, int> = 0>
+    static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
+        qProtoDebug() << __func__ << "value" << value;
+        using UV = typename std::make_unsigned<V>::type;
+        return serializeBasic(static_cast<UV>(value), outFieldIndex);
+    }
+
+    /**
+    *@brief Serialization of unsigned integral types
+    *
+    * Use <a href="https://developers.google.com/protocol-buffers/docs/encoding">Varint encoding</a>:
+    * "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 <typename V,
+              typename std::enable_if_t<std::is_integral<V>::value
+                                        && std::is_unsigned<V>::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 <typename V,
+              typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
+    static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
+        return serializeLengthDelimited(value.toUtf8());
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
+    static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
+        return serializeLengthDelimited(value);
+    }
+
+    //---------------Deserialize basic integral and floating point---------------
+    template <typename V,
+              typename std::enable_if_t<std::is_floating_point<V>::value
+                                        || std::is_same<V, fixed32>::value
+                                        || std::is_same<V, fixed64>::value
+                                        || std::is_same<V, sfixed32>::value
+                                        || std::is_same<V, sfixed64>::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 <typename V,
+              typename std::enable_if_t<std::is_integral<V>::value
+                                        && std::is_unsigned<V>::value, int> = 0>
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+        return QVariant::fromValue(deserializeVarintCommon<V>(it));
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_integral<V>::value
+                                        && std::is_signed<V>::value,int> = 0>
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+        using  UV = typename std::make_unsigned<V>::type;
+        UV unsignedValue = deserializeVarintCommon<UV>(it);
+        V value = (unsignedValue >> 1) ^ (-1 * (unsignedValue & 1));
+        return QVariant::fromValue<V>(value);
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_same<int32, V>::value
+                                        || std::is_same<int64, V>::value, int> = 0>
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+        using  UV = typename std::make_unsigned<V>::type;
+        UV unsignedValue = deserializeVarintCommon<UV>(it);
+        V value = static_cast<V>(unsignedValue);
+        return QVariant::fromValue(value);
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_same<QByteArray, V>::value, int> = 0>
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
+        return QVariant::fromValue(deserializeLengthDelimited(it));
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_same<QString, V>::value, int> = 0>
+    static QVariant deserializeBasic(SelfcheckIterator &it) {
+        return QVariant::fromValue(QString::fromUtf8(deserializeLengthDelimited(it)));
+    }
+
+    template<typename V,
+             typename std::enable_if_t<!(std::is_same<V, QString>::value
+                                       || std::is_same<V, QByteArray>::value
+                                       || std::is_base_of<QObject, V>::value), int> = 0>
+    static QByteArray serializeListType(const QList<V> &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<V>(value, empty));
+        }
+        //If internal field type is not LengthDelimited, exact amount of fields to be specified
+        ProtobufObjectPrivate::prependLengthDelimitedSize(serializedList);
+        return serializedList;
+    }
+
+    template<typename V,
+             typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
+    static QByteArray serializeListType(const QList<V> &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<typename V,
+             typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
+    static QByteArray serializeListType(const QList<V> &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 <typename V,
+              typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
+    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+        QByteArrayList list = previous.value<QByteArrayList>();
+        list.append(deserializeLengthDelimited(it));
+        previous.setValue(list);
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
+    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+        QStringList list = previous.value<QStringList>();
+        QByteArray value = deserializeLengthDelimited(it);
+        list.append(QString::fromUtf8(value));
+        previous.setValue(list);
+    }
+
+    template <typename V,
+              typename std::enable_if_t<!(std::is_same<V, QString>::value
+                                        || std::is_same<V, QByteArray>::value
+                                        || std::is_base_of<QObject, V>::value), int> = 0>
+    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+        QList<V> out;
+        unsigned int count = deserializeVarintCommon<uint32>(it);
+        SelfcheckIterator lastVarint = it + count;
+        while (it != lastVarint) {
+            QVariant variant = deserializeBasic<V>(it);
+            out.append(variant.value<V>());
+        }
+        previous.setValue(out);
+    }
+
+    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;
+    }
+
+    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;
+    }
+};
+
+}