[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