Browse Source

Add basic support of map serializer

- Add map serializer and helper funciton
- Add and update tests
Alexey Edelev 6 years ago
parent
commit
50fe9348c5

+ 11 - 4
src/generator/protobufsourcegenerator.cpp

@@ -45,27 +45,34 @@ void ProtobufSourceGenerator::printRegisterBody()
     mPrinter.Print({{"classname", mClassName}, {"namespaces", mNamespacesColonDelimited}},
                    Templates::ComplexTypeRegistrationTemplate);
 
+    Indent();
+    Indent();
     for (int i = 0; i < mMessage->field_count(); i++) {
         const FieldDescriptor* field = mMessage->field(i);
         if (field->type() == FieldDescriptor::TYPE_ENUM
                 && isLocalMessageEnum(mMessage, field)) {
-            mPrinter.Print({{"classname", mClassName + "::" + field->enum_type()->name() + "List"},
+            mPrinter.Print({{"type", mClassName + "::" + field->enum_type()->name() + "List"},
                             {"namespaces", mNamespacesColonDelimited}},
                            Templates::RegisterMetaTypeTemplateNoNamespace);
-            mPrinter.Print({{"classname", mClassName+ "::" + field->enum_type()->name() + "List"},
+            mPrinter.Print({{"type", mClassName+ "::" + field->enum_type()->name() + "List"},
                             {"namespaces", mNamespacesColonDelimited}},
                            Templates::RegisterMetaTypeTemplate);
         } else if (field->is_map()) {
-            mPrinter.Print({{"classname", field->message_type()->name()},
+            mPrinter.Print({{"type", field->message_type()->name()},
                             {"namespaces", mClassName}},
                            Templates::RegisterMetaTypeTemplate);
-            mPrinter.Print({{"classname", field->message_type()->name()},
+            mPrinter.Print({{"type", field->message_type()->name()},
                             {"namespaces", mNamespacesColonDelimited + "::" + mClassName}},
                            Templates::RegisterMetaTypeTemplate);
+            mPrinter.Print({{"classname", mClassName},
+                            {"type", field->message_type()->name()}},
+                             Templates::MapSerializationRegisterTemplate);
         }
     }
 
+    Outdent();
     mPrinter.Print(Templates::SimpleBlockEnclosureTemplate);
+    Outdent();
     mPrinter.Print(Templates::SimpleBlockEnclosureTemplate);
 }
 

+ 6 - 3
src/generator/templates.cpp

@@ -128,12 +128,15 @@ const char *Templates::ConstructorContentTemplate = "\n{\n    registerTypes();\n
 
 const char *Templates::DeclareMetaTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$)\n";
 const char *Templates::DeclareComplexListTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$List)\n";
-const char *Templates::RegisterMetaTypeDefaultTemplate = "        qRegisterMetaType<$namespaces$::$classname$>();\n";
-const char *Templates::RegisterMetaTypeTemplateNoNamespace = "        qRegisterMetaType<$namespaces$::$classname$>(\"$classname$\");\n";
-const char *Templates::RegisterMetaTypeTemplate = "        qRegisterMetaType<$namespaces$::$classname$>(\"$namespaces$::$classname$\");\n";
+const char *Templates::RegisterMetaTypeDefaultTemplate = "qRegisterMetaType<$namespaces$::$type$>();\n";
+const char *Templates::RegisterMetaTypeTemplateNoNamespace = "qRegisterMetaType<$namespaces$::$type$>(\"$type$\");\n";
+const char *Templates::RegisterMetaTypeTemplate = "qRegisterMetaType<$namespaces$::$type$>(\"$namespaces$::$type$\");\n";
 
 const char *Templates::QEnumTemplate = "Q_ENUM($type$)\n";
 
+const char *Templates::MapSerializationRegisterTemplate = "qtprotobuf::ProtobufObjectPrivate::wrapSerializer<$classname$::$type$>(qtprotobuf::ProtobufObjectPrivate::serializeMap<$classname$::$type$::key_type, $classname$::$type$::mapped_type>,\n"
+                                                                             "std::function<QVariant(QByteArray::const_iterator &)>(), qtprotobuf::LengthDelimited);\n";
+
 const char *Templates::ClassDefinitionTemplate = "\nclass $classname$ : public $parent_class$\n"
                                                  "{\n";
 const char *Templates::ClientMethodDeclarationSyncTemplate = "Q_INVOKABLE bool $method_name$(const $param_type$ &$param_name$, $return_type$ &$return_name$);\n";

+ 1 - 0
src/generator/templates.h

@@ -93,6 +93,7 @@ public:
     static const char *RegisterMetaTypeTemplate;
     static const char *RegisterMetaTypeTemplateNoNamespace;
     static const char *QEnumTemplate;
+    static const char *MapSerializationRegisterTemplate;
 
     //Service templates
     static const char *ConstructorDefinitionSyncTemplate;

+ 32 - 1
src/protobuf/protobufobject_p.h

@@ -111,7 +111,7 @@ public:
                                         || std::is_same<V, fint64>::value
                                         || std::is_same<V, sfint32>::value
                                         || std::is_same<V, sfint64>::value, int> = 0>
-    static QByteArray serializeBasic(V value, int &) {
+    static QByteArray serializeBasic(V value, int &/*outFieldIndex*/) {
         qProtoDebug() << __func__ << "value" << value;
 
         //Reserve required amount of bytes
@@ -254,6 +254,37 @@ public:
         return serializedList;
     }
 
+    //-------------------------Serialize maps of any type------------------------
+
+    template<typename K, typename V>
+    static QByteArray serializeMap(const QMap<K,V> &mapValue, int &outFieldIndex) {
+        using ItType = typename QMap<K,V>::const_iterator;
+        QByteArray mapResult;
+        auto kSerializer = serializers[qMetaTypeId<K>()];
+        auto vSerializer = serializers[qMetaTypeId<V>()];
+
+        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(encodeHeaderByte(outFieldIndex, LengthDelimited));
+            mapResult.append(result);
+        }
+        outFieldIndex = NotUsedFieldIndex;
+        return mapResult;
+    }
+
+    template <typename V, int num>
+    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(encodeHeaderByte(mapIndex, handlers.type));
+        }
+        return result;
+    }
+
     //###########################################################################
     //                           Deserialization helpers
     //###########################################################################

+ 11 - 0
tests/serializationtest.cpp

@@ -56,6 +56,7 @@
 #include "repeatedcomplexmessage.h"
 #include "simpleboolmessage.h"
 #include "simpleenummessage.h"
+#include "simplesint32mapmessage.h"
 #include "qtprotobuf.h"
 
 using namespace qtprotobufnamespace::tests;
@@ -1635,6 +1636,16 @@ TEST_F(SerializationTest, SimpleEnumMessageSerializeTest)
     ASSERT_TRUE(result == QByteArray::fromHex("0802"));
 }
 
+TEST_F(SerializationTest, SimpleSInt32MapSerializeTest)
+{
+    SimpleSInt32MapMessage test;
+    test.setMapField({{10, {"ten"}}, {42, {"fourty two"}}, {15, {"fifteen"}}});
+    QByteArray result = test.serialize();
+
+    ASSERT_STREQ(result.toHex().toStdString().c_str(),
+                "0a0c0d0a0000001205320374656e0a100d0f000000120932076669667465656e0a130d2a000000120c320a666f757274792074776f");
+}
+
 //TEST_F(SerializationTest, DISABLE_BenchmarkTest)
 //{
 //    SimpleIntMessage msg;