[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [dferry] /: Arguments::Reader/Writer: add isInsideEmptyArray() and currentSignature().
From:       Andreas Hartmetz <ahartmetz () gmail ! com>
Date:       2016-10-17 23:32:34
Message-ID: E1bwHOU-0000V1-CR () code ! kde ! org
[Download RAW message or body]

Git commit e5ebd40e0ebf7123d6106057b332ed29d38c5509 by Andreas Hartmetz.
Committed on 17/10/2016 at 23:22.
Pushed by ahartmetz into branch 'master'.

Arguments::Reader/Writer: add isInsideEmptyArray() and currentSignature().

They will (probably) be needed in QtDBus and they are somewhat useful
in any case.

M  +21   -22   applications/analyzer/argumentsmodel.cpp
M  +83   -26   serialization/arguments.cpp
M  +4    -0    serialization/arguments.h
M  +8    -15   tests/serialization/tst_arguments.cpp

http://commits.kde.org/dferry/e5ebd40e0ebf7123d6106057b332ed29d38c5509

diff --git a/applications/analyzer/argumentsmodel.cpp b/applications/analyzer/argumentsmodel.cpp
index 0dc97b6..4e8dbb4 100644
--- a/applications/analyzer/argumentsmodel.cpp
+++ b/applications/analyzer/argumentsmodel.cpp
@@ -88,7 +88,8 @@ QAbstractItemModel* createArgumentsModel(Message *message)
     }
 
     bool isDone = false;
-    int emptyNesting = 0;
+    // Cache it, don't call Reader::isInsideEmptyArray() on every data element.
+    bool inEmptyArray = false;
 
     while (!isDone) {
         switch(reader.state()) {
@@ -112,63 +113,61 @@ QAbstractItemModel* createArgumentsModel(Message *message)
             parent = ascend(parent, model);
             break;
         case Arguments::BeginArray: {
-            const bool hasData = reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty);
-            parent = descend(parent, hasData ? "Array" : "Array (no elements, showing just types)");
-            emptyNesting += hasData ? 0 : 1;
+            inEmptyArray = !reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty);
+            parent = descend(parent, inEmptyArray ? "Array (no elements, showing just types)" : "Array");
             break; }
         case Arguments::EndArray:
             reader.endArray();
+            inEmptyArray = reader.isInsideEmptyArray();
             parent = ascend(parent, model);
-            emptyNesting = qMax(emptyNesting - 1, 0);
             break;
         case Arguments::BeginDict: {
-            const bool hasData = reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty);
-            parent = descend(parent, hasData ? "Dict" : "Dict (no elements, showing just types)");
-            emptyNesting += hasData ? 0 : 1;
+            inEmptyArray = !reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty);
+            parent = descend(parent, inEmptyArray ? "Dict (no elements, showing just types)" : "Dict");
             break; }
         case Arguments::EndDict:
             reader.endDict();
+            inEmptyArray = reader.isInsideEmptyArray();
             parent = ascend(parent, model);
-            emptyNesting = qMax(emptyNesting - 1, 0);
             break;
         case Arguments::Byte:
-            addKeyValue(parent, "byte", emptyNesting, reader.readByte());
+            addKeyValue(parent, "byte", inEmptyArray, reader.readByte());
             break;
         case Arguments::Boolean:
-            addKeyValue(parent, "boolean", emptyNesting, reader.readBoolean());
+            addKeyValue(parent, "boolean", inEmptyArray, reader.readBoolean());
             break;
         case Arguments::Int16:
-            addKeyValue(parent, "int16", emptyNesting, reader.readInt16());
+            addKeyValue(parent, "int16", inEmptyArray, reader.readInt16());
             break;
         case Arguments::Uint16:
-            addKeyValue(parent, "uint16", emptyNesting, reader.readUint16());
+            addKeyValue(parent, "uint16", inEmptyArray, reader.readUint16());
             break;
         case Arguments::Int32:
-            addKeyValue(parent, "int32", emptyNesting, reader.readInt32());
+            addKeyValue(parent, "int32", inEmptyArray, reader.readInt32());
             break;
         case Arguments::Uint32:
-            addKeyValue(parent, "uint32", emptyNesting, reader.readUint32());
+            addKeyValue(parent, "uint32", inEmptyArray, reader.readUint32());
             break;
         case Arguments::Int64:
-            addKeyValue(parent, "int64", emptyNesting, reader.readInt64());
+            addKeyValue(parent, "int64", inEmptyArray, reader.readInt64());
             break;
         case Arguments::Uint64:
-            addKeyValue(parent, "uint64", emptyNesting, reader.readUint64());
+            addKeyValue(parent, "uint64", inEmptyArray, reader.readUint64());
             break;
         case Arguments::Double:
-            addKeyValue(parent, "double", emptyNesting, reader.readDouble());
+            addKeyValue(parent, "double", inEmptyArray, reader.readDouble());
             break;
         case Arguments::String:
-            addKeyValue(parent, "string", emptyNesting, reader.readString().ptr);
+            addKeyValue(parent, "string", inEmptyArray, reader.readString().ptr);
             break;
         case Arguments::ObjectPath:
-            addKeyValue(parent, "object path", emptyNesting, reader.readObjectPath().ptr);
+            addKeyValue(parent, "object path", inEmptyArray, reader.readObjectPath().ptr);
             break;
         case Arguments::Signature:
-            addKeyValue(parent, "type signature", emptyNesting, reader.readSignature().ptr);
+            addKeyValue(parent, "type signature", inEmptyArray, reader.readSignature().ptr);
             break;
         case Arguments::UnixFd:
-            addKeyValue(parent, "file descriptor", emptyNesting, QVariant());
+            addKeyValue(parent, "file descriptor", inEmptyArray, QVariant());
             break;
         case Arguments::InvalidData:
         case Arguments::NeedMoreData:
diff --git a/serialization/arguments.cpp b/serialization/arguments.cpp
index 0e7c5a5..6c03f83 100644
--- a/serialization/arguments.cpp
+++ b/serialization/arguments.cpp
@@ -507,7 +507,10 @@ std::string Arguments::prettyPrint() const
     std::string nestingPrefix;
 
     bool isDone = false;
-    int emptyNesting = 0;
+
+    // Cache it, don't call Reader::isInsideEmptyArray() on every data element. This isn't really
+    // a big deal for performance here, but in other situations it is, so set a good example :)
+    bool inEmptyArray = false;
 
     while (!isDone) {
         // HACK use nestingPrefix to determine when we're switching from key to value - this can be done
@@ -548,27 +551,26 @@ std::string Arguments::prettyPrint() const
                 const std::pair<Arguments::IoState, chunk> bytes = reader.readPrimitiveArray();
                 assert(bytes.first == Arguments::Byte);
                 assert(bytes.second.length > 0);
+                inEmptyArray = reader.isInsideEmptyArray(); // Maybe not necessary, but safe
                 ret << nestingPrefix << "array of bytes [ " << uint(bytes.second.ptr[0]);
                 for (uint32 i = 1; i < bytes.second.length; i++) {
                     ret << ", " << uint(bytes.second.ptr[i]);
                 }
                 ret << " ]\n";
             } else {
-                const bool hasData = reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty);
-                emptyNesting += hasData ? 0 : 1;
+                inEmptyArray = !reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty);
                 ret << nestingPrefix << "begin array\n";
                 nestingPrefix += "[ ";
             }
             break;
         case Arguments::EndArray:
             reader.endArray();
-            emptyNesting = std::max(emptyNesting - 1, 0);
+            inEmptyArray = reader.isInsideEmptyArray();
             nestingPrefix.resize(nestingPrefix.size() - 2);
             ret << nestingPrefix << "end array\n";
             break;
         case Arguments::BeginDict: {
-            const bool hasData = reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty);
-            emptyNesting += hasData ? 0 : 1;
+            inEmptyArray = !reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty);
             ret << nestingPrefix << "begin dict\n";
             nestingPrefix += "{K ";
             break; }
@@ -583,14 +585,14 @@ std::string Arguments::prettyPrint() const
 #endif
         case Arguments::EndDict:
             reader.endDict();
-            emptyNesting = std::max(emptyNesting - 1, 0);
+            inEmptyArray = reader.isInsideEmptyArray();
             nestingPrefix.resize(nestingPrefix.size() - strlen("{V "));
             ret << nestingPrefix << "end dict\n";
             break;
         case Arguments::Boolean: {
             bool b = reader.readBoolean();
             ret << nestingPrefix << "bool: ";
-            if (emptyNesting) {
+            if (inEmptyArray) {
                 ret << "<nil>";
             } else {
                 ret << (b ? "true" : "false");
@@ -598,37 +600,37 @@ std::string Arguments::prettyPrint() const
             ret << '\n';
             break; }
         case Arguments::Byte:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, int(reader.readByte()), "byte");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, int(reader.readByte()), "byte");
             break;
         case Arguments::Int16:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readInt16(), "int16");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readInt16(), "int16");
             break;
         case Arguments::Uint16:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readUint16(), "uint16");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readUint16(), "uint16");
             break;
         case Arguments::Int32:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readInt32(), "int32");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readInt32(), "int32");
             break;
         case Arguments::Uint32:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readUint32(), "uint32");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readUint32(), "uint32");
             break;
         case Arguments::Int64:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readInt64(), "int64");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readInt64(), "int64");
             break;
         case Arguments::Uint64:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readUint64(), "uint64");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readUint64(), "uint64");
             break;
         case Arguments::Double:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readDouble(), "double");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readDouble(), "double");
             break;
         case Arguments::String:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readString(), "string");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readString(), "string");
             break;
         case Arguments::ObjectPath:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readObjectPath(), "object path");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readObjectPath(), "object path");
             break;
         case Arguments::Signature:
-            printMaybeNil(&ret, nestingPrefix, emptyNesting, reader.readSignature(), "type signature");
+            printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readSignature(), "type signature");
             break;
         case Arguments::UnixFd:
             // TODO
@@ -940,6 +942,36 @@ cstring Arguments::Reader::stateString() const
     return printableState(m_state);
 }
 
+bool Arguments::Reader::isInsideEmptyArray() const
+{
+    if (d->m_nilArrayNesting > 1) {
+        return true;
+    } else if (d->m_nilArrayNesting == 1) {
+        // Special case 1: in state BeginArray/Dict, when we've just entered the nil array according
+        // to our internal state, the API user cannot know that yet -> return false;
+        // Since this is easily done here, don't complicate the rest of the code with a hacky solution.
+        if (m_state == BeginArray || m_state == BeginDict) {
+            return false;
+        }
+        // Special case 2: in state EndArray/Dict, when we've just left the nil array according
+        // to our internal state, advanceState() and endArray/Dict() make sure that
+        // d->m_nilArrayNesting is only decremented after endArray/Dict() has been called.
+        // Nothing to do for us here, but this is the point to explain it.
+        return true;
+    } else {
+        assert(d->m_nilArrayNesting == 0);
+        return false;
+    }
+}
+
+cstring Arguments::Reader::currentSignature() const
+{
+    // ### not sure if determining length like that is the best way, should probably be as similar to
+    //     libdbus-1 as possible (unless we can do much better).
+    return cstring(d->m_signature.ptr,
+                   std::max(uint32(0), std::min(d->m_signature.length, d->m_signaturePosition)));
+}
+
 void Arguments::Reader::replaceData(chunk data)
 {
     VALID_IF(data.length >= d->m_dataPosition, Error::ReplacementDataIsShorter);
@@ -1167,20 +1199,24 @@ void Arguments::Reader::advanceState()
             const bool isDict = aggregateInfo.aggregateType == BeginDict;
             if (d->m_signaturePosition > aggregateInfo.arr.containedTypeBegin + (isDict ? 1 : 0)) {
                 const Private::ArrayInfo &arrayInfo = aggregateInfo.arr;
-                bool moreElements = false;
+                bool moreIterations = false;
                 if (unlikely(d->m_nilArrayNesting)) {
                     // we're done iterating over it once
                     // TODO unit-test this
-                    d->m_nilArrayNesting--;
+                    // HACK: decreasing m_nilArrayNesting here would be much cleaner! But from the
+                    // user's POV, the nil array has not been left before the call to endArray().
+                    // We will set state EndArray / EndDict, so we know that the user "must" call
+                    // endArray()/endDict(), so adjust d->m_nilArrayNesting in those methods.
+                    //d->m_nilArrayNesting--;
                 } else if (d->m_dataPosition < arrayInfo.dataEnd) {
                     if (isDict) {
                         d->m_dataPosition = align(d->m_dataPosition, 8); // align to dict entry
                     }
                     // rewind to start of contained type and read the type info there
                     d->m_signaturePosition = arrayInfo.containedTypeBegin;
-                    moreElements = true;
+                    moreIterations = true;
                 }
-                if (!moreElements) {
+                if (!moreIterations) {
                     // TODO check that final data position is where it should be according to the
                     // serialized array length
                     m_state = isDict ? EndDict : EndArray;
@@ -1326,7 +1362,8 @@ void Arguments::Reader::advanceState()
         VALID_IF(d->m_nesting.beginArray(), Error::MalformedMessageData);
         if (firstElementTy.state() == BeginDict) {
             d->m_signaturePosition++;
-            // TODO check whether the first type is a primitive or string type!
+            // TODO check whether the first type is a primitive or string type! // ### isn't that already
+            // checked for the main signature and / or variants, though?
             // only closed at end of dict - there is no observable difference for clients
             VALID_IF(d->m_nesting.beginParen(), Error::MalformedMessageData);
         }
@@ -1462,7 +1499,11 @@ void Arguments::Reader::skipArray()
 
 void Arguments::Reader::endArray()
 {
-    advanceStateFrom(EndArray);
+    VALID_IF(m_state == EndArray, Error::ReadWrongType);
+    if (unlikely(d->m_nilArrayNesting)) {
+        d->m_nilArrayNesting--;
+    }
+    advanceState();
 }
 
 static bool isAligned(uint32 value, uint32 alignment)
@@ -1562,7 +1603,11 @@ void Arguments::Reader::skipDict()
 
 void Arguments::Reader::endDict()
 {
-    advanceStateFrom(EndDict);
+    VALID_IF(m_state == EndDict, Error::ReadWrongType);
+    if (unlikely(d->m_nilArrayNesting)) {
+        d->m_nilArrayNesting--;
+    }
+    advanceState();
 }
 
 void Arguments::Reader::beginStruct()
@@ -1853,6 +1898,18 @@ cstring Arguments::Writer::stateString() const
     return printableState(m_state);
 }
 
+bool Arguments::Writer::isInsideEmptyArray() const
+{
+    return d->m_nilArrayNesting > 0;
+}
+
+cstring Arguments::Writer::currentSignature() const
+{
+    // ### see comment in Reader::currentSignature
+    return cstring(d->m_signature.ptr,
+                   std::max(uint32(0), std::min(d->m_signature.length, d->m_signaturePosition)));
+}
+
 void Arguments::Writer::doWritePrimitiveType(uint32 alignAndSize)
 {
     d->m_dataPosition = align(d->m_dataPosition, alignAndSize);
diff --git a/serialization/arguments.h b/serialization/arguments.h
index d557c8a..cf7719d 100644
--- a/serialization/arguments.h
+++ b/serialization/arguments.h
@@ -169,6 +169,8 @@ public:
 
         IoState state() const { return m_state; }
         cstring stateString() const;
+        bool isInsideEmptyArray() const;
+        cstring currentSignature() const; // current signature, either main signature or current variant
         // HACK call this in NeedMoreData state when more data has been added; this replaces m_data
         // WARNING: calling replaceData() invalidates copies (if any) of this Reader
         void replaceData(chunk data);
@@ -288,6 +290,8 @@ public:
 
         IoState state() const { return m_state; }
         cstring stateString() const;
+        bool isInsideEmptyArray() const;
+        cstring currentSignature() const; // current signature, either main signature or current variant
 
         enum ArrayOption
         {
diff --git a/tests/serialization/tst_arguments.cpp b/tests/serialization/tst_arguments.cpp
index 3546d04..39dce0b 100644
--- a/tests/serialization/tst_arguments.cpp
+++ b/tests/serialization/tst_arguments.cpp
@@ -70,7 +70,7 @@ static bool stringsEqual(cstring s1, cstring s2)
 // 3) skips nil arrays at and below nil array nesting level m_skipNilArraysFromLevel with m_skippingReader
 // It even skips aggregates inside nil arrays as 2) + 3) imply.
 // It checks:
-// a) where nothing is skipped that the aggregate structure and data read is the same
+// a) where nothing is skipped, that the aggregate structure and data read are the same
 class SkipChecker
 {
 public:
@@ -284,7 +284,7 @@ static void testReadWithSkip(const Arguments &arg, bool debugPrint)
 
 // When using this to iterate over the reader, it will make an exact copy using the Writer.
 // You need to do something only in states where something special should happen.
-static void defaultReadToWrite(Arguments::Reader *reader, Arguments::Writer *writer, uint32 *emptyNesting)
+static void defaultReadToWrite(Arguments::Reader *reader, Arguments::Writer *writer)
 {
     switch(reader->state()) {
     case Arguments::BeginStruct:
@@ -307,23 +307,19 @@ static void defaultReadToWrite(Arguments::Reader *reader, Arguments::Writer *wri
         const bool hasData = reader->beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty);
         writer->beginArray(hasData ? Arguments::Writer::NonEmptyArray
                                     : Arguments::Writer::WriteTypesOfEmptyArray);
-        *emptyNesting += hasData ? 0 : 1;
         break; }
     case Arguments::EndArray:
         reader->endArray();
         writer->endArray();
-        *emptyNesting = std::max(*emptyNesting - 1, 0u);
         break;
     case Arguments::BeginDict: {
         const bool hasData = reader->beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty);
         writer->beginDict(hasData ? Arguments::Writer::NonEmptyArray
                                     : Arguments::Writer::WriteTypesOfEmptyArray);
-        *emptyNesting += hasData ? 0 : 1;
         break; }
     case Arguments::EndDict:
         reader->endDict();
         writer->endDict();
-        *emptyNesting = std::max(*emptyNesting - 1, 0u);
         break;
     case Arguments::Byte:
         writer->writeByte(reader->readByte());
@@ -354,7 +350,7 @@ static void defaultReadToWrite(Arguments::Reader *reader, Arguments::Writer *wri
         break;
     case Arguments::String: {
         cstring s = reader->readString();
-        if (*emptyNesting) {
+        if (reader->isInsideEmptyArray()) {
             s = cstring("");
         } else {
             TEST(Arguments::isStringValid(s));
@@ -363,7 +359,7 @@ static void defaultReadToWrite(Arguments::Reader *reader, Arguments::Writer *wri
         break; }
     case Arguments::ObjectPath: {
         cstring objectPath = reader->readObjectPath();
-        if (*emptyNesting) {
+        if (reader->isInsideEmptyArray()) {
             objectPath = cstring("/");
         } else {
             TEST(Arguments::isObjectPathValid(objectPath));
@@ -372,7 +368,7 @@ static void defaultReadToWrite(Arguments::Reader *reader, Arguments::Writer *wri
         break; }
     case Arguments::Signature: {
         cstring signature = reader->readSignature();
-        if (*emptyNesting) {
+        if (reader->isInsideEmptyArray()) {
             signature = cstring("");
         } else {
             TEST(Arguments::isSignatureValid(signature));
@@ -428,7 +424,6 @@ static void doRoundtripWithShortReads(const Arguments &original, uint32 dataIncr
     chunk data = original.data();
     chunk shortData;
     bool isDone = false;
-    uint32 emptyNesting = 0;
 
     while (!isDone) {
         TEST(writer.state() != Arguments::InvalidData);
@@ -460,7 +455,7 @@ static void doRoundtripWithShortReads(const Arguments &original, uint32 dataIncr
             reader.replaceData(shortData);
             break; }
         default:
-            defaultReadToWrite(&reader, &writer, &emptyNesting);
+            defaultReadToWrite(&reader, &writer);
             break;
         }
     }
@@ -478,7 +473,6 @@ static void doRoundtripWithReaderCopy(const Arguments &original, uint32 dataIncr
     Arguments::Writer writer;
 
     bool isDone = false;
-    uint32 emptyNesting = 0;
     uint32 i = 0;
 
     while (!isDone) {
@@ -496,7 +490,7 @@ static void doRoundtripWithReaderCopy(const Arguments &original, uint32 dataIncr
             isDone = true;
             break;
         default:
-            defaultReadToWrite(reader, &writer, &emptyNesting);
+            defaultReadToWrite(reader, &writer);
             break;
         }
     }
@@ -512,7 +506,6 @@ static void doRoundtripWithWriterCopy(const Arguments &original, uint32 dataIncr
     Arguments::Writer *writer = new Arguments::Writer;
 
     bool isDone = false;
-    uint32 emptyNesting = 0;
     uint32 i = 0;
 
     while (!isDone) {
@@ -530,7 +523,7 @@ static void doRoundtripWithWriterCopy(const Arguments &original, uint32 dataIncr
             isDone = true;
             break;
         default:
-            defaultReadToWrite(&reader, writer, &emptyNesting);
+            defaultReadToWrite(&reader, writer);
             break;
         }
     }
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic