Browse Source

Implement complex lists serialization

- Add serialization of complex list types
- Implement tests
Alexey Edelev 6 years ago
parent
commit
2d253f3609
4 changed files with 66 additions and 22 deletions
  1. 3 2
      src/generator/templates.h
  2. 2 0
      src/lib/protobufobject.cpp
  3. 57 14
      src/lib/protobufobject.h
  4. 4 6
      tests/serializationtest.cpp

+ 3 - 2
src/generator/templates.h

@@ -52,10 +52,11 @@ static const char *ComplexTypeRegistrationMethodTemplate = "\nstatic void regist
 static const char *ComplexTypeRegistrationTemplate = "void $classname$::registerTypes()\n{\n"
                                                      "    static bool registationDone = false;\n"
                                                      "    if (!registationDone) {\n\n"
-                                                     "        qRegisterMetaType<$classname$>(\"$classname$\");\n"
-                                                     "        qRegisterMetaType<$classname$List>(\"$classname$List\");\n"
+                                                     "        int metaTypeId = qRegisterMetaType<$classname$>(\"$classname$\");\n"
+                                                     "        int listMetaTypeId = qRegisterMetaType<$classname$List>(\"$classname$List\");\n"
                                                      "        qRegisterMetaType<$namespaces$::$classname$>(\"$namespaces$::$classname$\");\n"
                                                      "        qRegisterMetaType<$namespaces$::$classname$List>(\"$namespaces$::$classname$List\");\n"
+                                                     "        registerSerializers(metaTypeId, listMetaTypeId);\n"
                                                      "    }\n}\n";
 static const char *ComplexListTypeUsingTemplate = "using $classname$List = QList<$classname$>;\n";
 

+ 2 - 0
src/lib/protobufobject.cpp

@@ -26,3 +26,5 @@
 #include "protobufobject.h"
 
 using namespace qtprotobuf;
+
+ProtobufObjectPrivate::ListSerializerRegistry ProtobufObjectPrivate::listSerializers;

+ 57 - 14
src/lib/protobufobject.h

@@ -34,6 +34,7 @@
 #include <memory>
 #include <type_traits>
 #include <typeinfo>
+#include <functional>
 
 #include <qtprotobuftypes.h>
 #include <qtprotobuflogging.h>
@@ -53,7 +54,11 @@ enum WireTypes {
 constexpr int NotUsedFieldIndex = -1;
 
 class ProtobufObjectPrivate : public QObject {
-protected:
+public:
+    using ListSerializer = std::function<QByteArray(const ProtobufObjectPrivate *, const QVariant &, int &)>;
+    using ListSerializerRegistry = std::unordered_map<int/*metatypeid*/, ListSerializer>;
+    static ListSerializerRegistry listSerializers;
+
     explicit ProtobufObjectPrivate(QObject *parent = nullptr) : QObject(parent) {}
 
     /*  Header byte
@@ -75,7 +80,7 @@ protected:
                                                       || wireType == LengthDelimited);
     }
 
-    QByteArray serializeValue(const QVariant& propertyValue, int fieldIndex, bool isFixed = false) {
+    QByteArray serializeValue(const QVariant& propertyValue, int fieldIndex, bool isFixed = false) const {
         qProtoDebug() << __func__ << "propertyValue" << propertyValue << "fieldIndex" << fieldIndex << "isFixed" << isFixed;
         QByteArray result;
         WireTypes type = UnknownWireType;
@@ -143,7 +148,7 @@ protected:
         return result;
     }
 
-    QByteArray serializeLengthDelimited(const QByteArray &data) {
+    QByteArray serializeLengthDelimited(const QByteArray &data) const {
         qProtoDebug() << __func__ << "data.size" << data.size() << "data" << data.toHex();
         //Varint serialize field size and apply result as starting point
         QByteArray result = serializeVarint(static_cast<unsigned int>(data.size()));
@@ -151,9 +156,15 @@ protected:
         return result;
     }
 
-    QByteArray serializeUserType(const QVariant &propertyValue, int &fieldIndex) {
+    QByteArray serializeUserType(const QVariant &propertyValue, int &fieldIndex) const {
         qProtoDebug() << __func__ << "propertyValue" << propertyValue << "fieldIndex" << fieldIndex;
         int userType = propertyValue.userType();
+
+        auto it = listSerializers.find(userType);
+        if (it != std::end(listSerializers)) {
+            return (it->second)(this, propertyValue, fieldIndex);
+        }
+
         if (userType == qMetaTypeId<IntList>()) {
             return serializeListType(propertyValue.value<IntList>(), fieldIndex);
         } else if(userType == qMetaTypeId<FloatList>()) {
@@ -174,7 +185,7 @@ protected:
     template<typename V,
              typename std::enable_if_t<std::is_integral<V>::value
                                        || std::is_floating_point<V>::value, int> = 0>
-    QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) {
+    QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) const {
         qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
         if (listValue.count() <= 0) {
             outFieldIndex = NotUsedFieldIndex;
@@ -193,7 +204,7 @@ protected:
     template<typename V,
              typename std::enable_if_t<std::is_same<V, QString>::value
                                        || std::is_same<V, QByteArray>::value, int> = 0>
-    QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) {
+    QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) const {
         qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
         if (listValue.count() <= 0) {
             outFieldIndex = NotUsedFieldIndex;
@@ -209,8 +220,26 @@ protected:
         return serializedList;
     }
 
+    template<typename V,
+             typename std::enable_if_t<std::is_base_of<ProtobufObjectPrivate, V>::value, int> = 0>
+    QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) const {
+        qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
+        if (listValue.count() <= 0) {
+            outFieldIndex = NotUsedFieldIndex;
+            return QByteArray();
+        }
+
+        QByteArray serializedList;
+        for(auto& value : listValue) {
+            serializedList.append(value.serialize());
+        }
+
+        serializedList.prepend(serializeVarint(static_cast<unsigned int>(serializedList.size())));
+        return serializedList;
+    }
+
     //TODO: This specialization is deprecated and won't be used in future
-    QByteArray serializeListType(const QVariantList &listValue, int &outFieldIndex)
+    QByteArray serializeListType(const QVariantList &listValue, int &outFieldIndex) const
     {
         qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
         if (listValue.count() <= 0) {
@@ -239,7 +268,7 @@ protected:
               typename std::enable_if_t<std::is_floating_point<V>::value
                                         || std::is_same<V, unsigned int>::value
                                         || std::is_same<V, qulonglong>::value, int> = 0>
-    QByteArray serializeFixed(V value) {
+    QByteArray serializeFixed(V value) const {
         qProtoDebug() << __func__ << "value" << value;
         //Reserve required amount of bytes
         QByteArray result(sizeof(V), '\0');
@@ -249,7 +278,7 @@ protected:
 
     template <typename V,
               typename std::enable_if_t<std::is_signed<V>::value, int> = 0>
-    QByteArray serializeVarint(V value) {
+    QByteArray serializeVarint(V value) const {
         qProtoDebug() << __func__ << "value" << value;
         using UV = typename std::make_unsigned<V>::type;
         //Use ZigZag convertion first and apply unsigned variant next
@@ -260,7 +289,7 @@ protected:
 
     template <typename V,
               typename std::enable_if_t<std::is_unsigned<V>::value, int> = 0>
-    QByteArray serializeVarint(V value) {
+    QByteArray serializeVarint(V value) const {
         qProtoDebug() << __func__ << "value" << value;
         QByteArray result;
         //Reserve maximum required amount of bytes
@@ -447,7 +476,7 @@ protected:
     }
 
 public:
-    virtual QByteArray serializePrivate() = 0;
+    virtual QByteArray serializePrivate() const = 0;
     virtual void deserializePrivate(const QByteArray &data) = 0;
 };
 
@@ -455,7 +484,11 @@ template <typename T>
 class ProtobufObject : public ProtobufObjectPrivate
 {
 protected:
-    QByteArray serializePrivate() override {
+    static void registerSerializers(int metaTypeId, int listMetaTypeId) {
+        listSerializers[listMetaTypeId] = ListSerializer(serializeComplexListType);
+    }
+
+    QByteArray serializePrivate() const override {
         qProtoDebug() << T::staticMetaObject.className() << "serializePrivate";
         return serialize();
     }
@@ -468,10 +501,10 @@ protected:
 public:
     explicit ProtobufObject(QObject *parent = nullptr) : ProtobufObjectPrivate(parent) {}
 
-    QByteArray serialize() {
+    QByteArray serialize() const {
         qProtoDebug() << T::staticMetaObject.className() << "serialize";
         QByteArray result;
-        T *instance = dynamic_cast<T *>(this);
+        const T *instance = dynamic_cast<const T *>(this);
         for (auto field : T::propertyOrdering) {
             int propertyIndex = field.second;
             int fieldIndex = field.first;
@@ -486,6 +519,16 @@ public:
 
         return result;
     }
+//TODO: migrate to this function for complex types serialization
+//    static QByteArray serializeSelf(const QVariant &variantValue) {
+//        T value = variantValue.value<T>();
+//        return value->serialize();
+//    }
+
+    static QByteArray serializeComplexListType(const ProtobufObjectPrivate* serializer, const QVariant &listValue, int &outFieldIndex) {
+        QList<T> list = listValue.value<QList<T>>();
+        return serializer->serializeListType(list, outFieldIndex);
+    }
 
     void deserialize(const QByteArray &array) {
         qProtoDebug() << T::staticMetaObject.className() << "deserialize";

+ 4 - 6
tests/serializationtest.cpp

@@ -531,22 +531,20 @@ TEST_F(SerializationTest, RepeatedFloatMessageTest)
 
 TEST_F(SerializationTest, RepeatedComplexMessageTest)
 {
-    return;//disabled
-#if 0
     SimpleStringMessage stringMsg;
     stringMsg.setTestFieldString("qwerty");
     ComplexMessage msg;
     msg.setTestFieldInt(25);
     msg.setTestComplexField(stringMsg);
     RepeatedComplexMessage test;
-    test.setTestRepeatedComplex({QVariant::fromValue<ComplexMessage>(msg)});
+    test.setTestRepeatedComplex({msg});
     qDebug() << test.testRepeatedComplex().count();
     QByteArray result = test.serialize();
     qDebug() << "result " << result.toHex();
-    ASSERT_TRUE(result == QByteArray::fromHex("0a14cdcccc3e9a99993f0000003f3333b33f9a99193f"));
+    ASSERT_TRUE(result == QByteArray::fromHex("0a0c083212083206717765727479")
+                || result == QByteArray::fromHex("0a0c120832067177657274790832"));
 
-    test.setTestRepeatedComplex(QVariantList());
+    test.setTestRepeatedComplex({});
     result = test.serialize();
     ASSERT_TRUE(result.isEmpty());
-#endif
 }