Browse Source

Implement ZigZag varint serialization

- Complete varint serializer
Alexey Edelev 6 years ago
parent
commit
834ac4d1ac
2 changed files with 111 additions and 21 deletions
  1. 19 6
      src/lib/protobufobject.h
  2. 92 15
      tests/serializationtest.cpp

+ 19 - 6
src/lib/protobufobject.h

@@ -72,19 +72,32 @@ public:
         //TODO
     }
 
-    template <typename V, typename = typename std::enable_if<std::is_integral<V>::value>::type>
+    template <typename V,
+              typename std::enable_if_t<std::is_signed<V>::value, int> = 0>
+    QByteArray serializeVarint(V value, int fieldIndex) {
+        using UV = typename std::make_unsigned<V>::type;
+        //Use ZigZag convertion first and apply unsigned variant next
+        value = (value << 1) ^ (value >> (sizeof(UV) * 8 - 1));
+        UV uValue = *(UV *)&value;
+        return serializeVarint(uValue, fieldIndex);
+    }
+
+    template <typename V,
+              typename std::enable_if_t<std::is_unsigned<V>::value, int> = 0>
     QByteArray serializeVarint(V value, int fieldIndex) {
         /*  Header byte
-         *  bits    | 7 6 5 4   3 | 2 1 0
-         *  ----------------------------
-         *  meaning | Field index | Type
+         *  bits    | 7  6  5  4  3 |  2  1  0
+         *  -----------------------------------
+         *  meaning |  Field index  |   Type
          */
-        char typeByte = (fieldIndex << 3) | Varint;
+        Q_ASSERT_X(fieldIndex < 128 && fieldIndex > 0, T::staticMetaObject.className(), "fieldIndex is out of range");
+
+        unsigned char typeByte = (fieldIndex << 3) | Varint;
         QByteArray result;
         //Reserve maximum required amount of bytes
         result.reserve(sizeof(V) + 1);
         //Put type byte at beginning
-        result.append(typeByte);
+        result.append(*(char *)&typeByte);
 
         while(value > 0) {
             //Put first 7 bits to result buffer and mark as not last

+ 92 - 15
tests/serializationtest.cpp

@@ -38,54 +38,131 @@ TEST_F(SerializationTest, IntMessageSrializeTest)
     SimpleIntMessage test;
     test.setTestFieldInt(15);
     QByteArray result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
     ASSERT_EQ(result.size(), 2);
     ASSERT_EQ(result.at(0), 0x08);
-    ASSERT_EQ(result.at(1), 0x0F);
+    ASSERT_EQ(result.at(1), '\x1e');
 
     test.setTestFieldInt(300);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
     ASSERT_EQ(result.size(), 3);
     ASSERT_EQ(result.at(0), 0x08);
-    ASSERT_EQ(result.at(1), '\xAC');
-    ASSERT_EQ(result.at(2), 0x02);
+    ASSERT_EQ(result.at(1), '\xd8');
+    ASSERT_EQ(result.at(2), '\x04');
 
     test.setTestFieldInt(65545);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
     ASSERT_EQ(result.size(), 4);
     ASSERT_EQ(result.at(0), 0x08);
-    ASSERT_EQ(result.at(1), '\x89');
+    ASSERT_EQ(result.at(1), '\x92');
     ASSERT_EQ(result.at(2), '\x80');
-    ASSERT_EQ(result.at(3), '\x04');
+    ASSERT_EQ(result.at(3), '\x08');
 
     test.setTestFieldInt(0);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
+    ASSERT_EQ(result.size(), 2);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\x00');
 
     test.setTestFieldInt(INT8_MAX + 1);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
+    ASSERT_EQ(result.size(), 3);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\x80');
+    ASSERT_EQ(result.at(2), '\x02');
 
     test.setTestFieldInt(INT16_MAX + 1);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
+    ASSERT_EQ(result.size(), 4);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\x80');
+    ASSERT_EQ(result.at(2), '\x80');
+    ASSERT_EQ(result.at(3), '\x04');
 
     test.setTestFieldInt(INT8_MAX);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
+    ASSERT_EQ(result.size(), 3);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\xfe');
+    ASSERT_EQ(result.at(2), '\x01');
 
     test.setTestFieldInt(INT16_MAX);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
+    ASSERT_EQ(result.size(), 4);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\xfe');
+    ASSERT_EQ(result.at(2), '\xff');
+    ASSERT_EQ(result.at(3), '\x03');
 
     test.setTestFieldInt(INT32_MAX);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
+    ASSERT_EQ(result.size(), 6);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\xfe');
+    ASSERT_EQ(result.at(2), '\xff');
+    ASSERT_EQ(result.at(3), '\xff');
+    ASSERT_EQ(result.at(4), '\xff');
+    ASSERT_EQ(result.at(5), '\x0f');
+
+    test.setTestFieldInt(-1);
+    result = test.serialize();
+    ASSERT_EQ(result.size(), 2);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\x01');
+
+    test.setTestFieldInt(-462);
+    result = test.serialize();
+    ASSERT_EQ(result.size(), 3);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\x9b');
+    ASSERT_EQ(result.at(2), '\x07');
+
+    test.setTestFieldInt(-63585);
+    result = test.serialize();
+    ASSERT_EQ(result.size(), 4);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\xc1');
+    ASSERT_EQ(result.at(2), '\xe1');
+    ASSERT_EQ(result.at(3), '\x07');
+
+
+    test.setTestFieldInt(INT8_MIN);
+    result = test.serialize();
+    ASSERT_EQ(result.size(), 3);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\xff');
+    ASSERT_EQ(result.at(2), '\x01');
+
+    test.setTestFieldInt(INT16_MIN);
+    result = test.serialize();
+    ASSERT_EQ(result.size(), 4);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\xff');
+    ASSERT_EQ(result.at(2), '\xff');
+    ASSERT_EQ(result.at(3), '\x03');
 
     test.setTestFieldInt(INT32_MIN);
     result = test.serialize();
-    qDebug() << "Out result:" << result.toHex();
+    ASSERT_EQ(result.size(), 6);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\xff');
+    ASSERT_EQ(result.at(2), '\xff');
+    ASSERT_EQ(result.at(3), '\xff');
+    ASSERT_EQ(result.at(4), '\xff');
+    ASSERT_EQ(result.at(5), '\x0f');
+
+    test.setTestFieldInt(INT8_MIN - 1);
+    result = test.serialize();
+    ASSERT_EQ(result.size(), 3);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\x81');
+    ASSERT_EQ(result.at(2), '\x02');
+
+    test.setTestFieldInt(INT16_MIN - 1);
+    result = test.serialize();
+    ASSERT_EQ(result.size(), 4);
+    ASSERT_EQ(result.at(0), 0x08);
+    ASSERT_EQ(result.at(1), '\x81');
+    ASSERT_EQ(result.at(2), '\x80');
+    ASSERT_EQ(result.at(3), '\x04');
 }