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

List:       kde-commits
Subject:    [dferry] /: Add beginDictEntry() / endDictEntry() and corresponding states.
From:       Andreas Hartmetz <ahartmetz () gmail ! com>
Date:       2016-12-04 18:52:11
Message-ID: E1cDbtT-0001fw-6O () code ! kde ! org
[Download RAW message or body]

Git commit 244ba3c620a304e2ade00553744bead9bb444470 by Andreas Hartmetz.
Committed on 03/12/2016 at 21:29.
Pushed by ahartmetz into branch 'master'.

Add beginDictEntry() / endDictEntry() and corresponding states.

...as a compile time option. With only a little testing because
it is mainly for use in QtDBus which already has tests.
That said, the tests pass with and without WITH_DICT_ENTRY
defined.

M  +152  -7    serialization/arguments.cpp
M  +18   -1    serialization/arguments.h
M  +96   -2    tests/serialization/tst_arguments.cpp
M  +5    -0    util/error.h

https://commits.kde.org/dferry/244ba3c620a304e2ade00553744bead9bb444470

diff --git a/serialization/arguments.cpp b/serialization/arguments.cpp
index 9da1408..5ad3148 100644
--- a/serialization/arguments.cpp
+++ b/serialization/arguments.cpp
@@ -53,10 +53,10 @@ static byte alignmentLog2(uint32 alignment)
 
 static cstring printableState(Arguments::IoState state)
 {
-    if (state < Arguments::NotStarted || state > Arguments::UnixFd) {
+    if (state < Arguments::NotStarted || state >= Arguments::LastState) {
         return cstring();
     }
-    static const char *strings[Arguments::UnixFd + 1] = {
+    static const char *strings[Arguments::LastState] = {
         "NotStarted",
         "Finished",
         "NeedMoreData",
@@ -84,6 +84,11 @@ static cstring printableState(Arguments::IoState state)
         "ObjectPath",
         "Signature",
         "UnixFd"
+#ifdef WITH_DICT_ENTRY
+        ,
+        "BeginDictEntry",
+        "EndDictEntry"
+#endif
     };
     return cstring(strings[state]);
 }
@@ -143,6 +148,16 @@ void Arguments::copyOneElement(Arguments::Reader *reader, \
Arguments::Writer *wri  reader->endDict();
         writer->endDict();
         break;
+#ifdef WITH_DICT_ENTRY
+    case Arguments::BeginDictEntry:
+        reader->beginDictEntry();
+        writer->beginDictEntry();
+        break;
+    case Arguments::EndDictEntry:
+        reader->endDictEntry();
+        writer->endDictEntry();
+        break;
+#endif
     case Arguments::Byte:
         writer->writeByte(reader->readByte());
         break;
@@ -356,9 +371,21 @@ public:
         MaxFullSignatureLength = MaxSignatureLength + 1
     };
 
+#ifdef WITH_DICT_ENTRY
+    enum DictEntryState : uint32
+    {
+        RequireBeginDictEntry = 0,
+        InDictEntry,
+        RequireEndDictEntry,
+        AfterEndDictEntry
+    };
+#endif
     struct ArrayInfo
     {
         uint32 containedTypeBegin; // to rewind when reading the next element
+#ifdef WITH_DICT_ENTRY
+        DictEntryState dictEntryState;
+#endif
     };
 
     struct VariantInfo
@@ -1312,8 +1339,19 @@ void Arguments::Reader::advanceState()
                 if (likely(!d->m_nilArrayNesting) && d->m_dataPosition < \
                arrayInfo.dataEnd) {
                     d->m_dataPosition = align(d->m_dataPosition, 8); // align to \
                dict entry
                     d->m_signaturePosition = arrayInfo.containedTypeBegin;
+#ifdef WITH_DICT_ENTRY
+                    d->m_signaturePosition--;
+                    m_state = EndDictEntry;
+                    m_u.Uint32 = 0; // meaning: more dict entries follow (state \
after next is BeginDictEntry) +                    return;
+#endif
                     break;
                 }
+#ifdef WITH_DICT_ENTRY
+                m_state = EndDictEntry;
+                m_u.Uint32 = 1; // meaning: array end reached (state after next is \
EndDict) +                return;
+#endif
                 m_state = EndDict;
                 return;
             }
@@ -1661,9 +1699,18 @@ bool Arguments::Reader::beginDict(EmptyArrayOption option)
 
     if (unlikely(d->m_nilArrayNesting && option == SkipIfEmpty)) {
         skipArrayOrDictSignature(true);
+#ifdef WITH_DICT_ENTRY
+        const bool ret = !d->m_nilArrayNesting;
+        advanceState();
+        endDictEntry();
+        return ret;
+    }
+    m_state = BeginDictEntry;
+#else
     }
 
     advanceState();
+#endif
     return !d->m_nilArrayNesting;
 }
 
@@ -1693,6 +1740,24 @@ void Arguments::Reader::endDict()
     advanceState();
 }
 
+#ifdef WITH_DICT_ENTRY
+void Arguments::Reader::beginDictEntry()
+{
+    VALID_IF(m_state == BeginDictEntry, Error::ReadWrongType);
+    advanceState();
+}
+
+void Arguments::Reader::endDictEntry()
+{
+    VALID_IF(m_state == EndDictEntry, Error::ReadWrongType);
+    if (m_u.Uint32 == 0) {
+        m_state = BeginDictEntry;
+    } else {
+        m_state = EndDict;
+    }
+}
+#endif
+
 void Arguments::Reader::beginStruct()
 {
     VALID_IF(m_state == BeginStruct, Error::ReadWrongType);
@@ -1816,6 +1881,14 @@ void Arguments::Reader::skipCurrentElement()
         case Arguments::BeginDict:
             skipDict();
             break;
+#ifdef WITH_DICT_ENTRY
+        case Arguments::BeginDictEntry:
+            beginDictEntry();
+            break;
+        case Arguments::EndDictEntry:
+            endDictEntry();
+            break;
+#endif
         case Arguments::EndDict:
             assert(stateOnEntry == EndDict); // only way this can happen - we \
                gracefully "skip" EndDict
                                              // and DON'T decrease nestingLevel b/c \
it would go negative. @@ -2160,9 +2233,8 @@ void \
                Arguments::Writer::advanceState(cstring signatureFragment, IoState \
                newState
         VALID_IF(d->m_signaturePosition + signatureFragment.length <= \
MaxSignatureLength,  Error::SignatureTooLong);
     }
-
     if (!d->m_aggregateStack.empty()) {
-        const Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
+        Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
         switch (aggregateInfo.aggregateType) {
         case BeginVariant:
             // arrays and variants may contain just one single complete type; note \
that this will @@ -2183,8 +2255,43 @@ void Arguments::Writer::advanceState(cstring \
signatureFragment, IoState newState  break;
         case BeginDict:
             if (d->m_signaturePosition == aggregateInfo.arr.containedTypeBegin) {
+#ifdef WITH_DICT_ENTRY
+                if (aggregateInfo.arr.dictEntryState == \
Private::RequireBeginDictEntry) { +                    // This is only reached \
immediately after beginDict() so it's kinda wasteful, oh well. +                    \
VALID_IF(newState == BeginDictEntry, Error::MissingBeginDictEntry); +                 \
aggregateInfo.arr.dictEntryState = Private::InDictEntry; +                    m_state \
= DictKey; +                    return; // BeginDictEntry writes no data
+                }
+#endif
                 VALID_IF(isPrimitiveType || isStringType, \
Error::InvalidKeyTypeInDict);  }
+#ifdef WITH_DICT_ENTRY
+            // TODO test this part of the state machine
+            if (d->m_signaturePosition >= aggregateInfo.arr.containedTypeBegin + 2) \
{ +                if (aggregateInfo.arr.dictEntryState == \
Private::RequireEndDictEntry) { +                    VALID_IF(newState == \
EndDictEntry, Error::MissingEndDictEntry); +                    \
aggregateInfo.arr.dictEntryState = Private::AfterEndDictEntry; +                    \
m_state = BeginDictEntry; +                    return; // EndDictEntry writes no data
+                } else {
+                    // v should've been caught earlier
+                    assert(aggregateInfo.arr.dictEntryState == \
Private::AfterEndDictEntry); +                    VALID_IF(newState == BeginDictEntry \
|| newState == EndDict, Error::MissingBeginDictEntry); +                    // "fall \
through", the rest (another iteration or finish) is handled below +                }
+            } else if (d->m_signaturePosition >= \
aggregateInfo.arr.containedTypeBegin + 1) { +                \
assert(aggregateInfo.arr.dictEntryState == Private::InDictEntry); +                \
aggregateInfo.arr.dictEntryState = Private::RequireEndDictEntry; +                // \
Setting EndDictEntry after writing a primitive type works fine, but setting it after \
+                // ending another aggregate would be somewhat involved and need to \
happen somewhere +                // else, so just don't do that. We still produce an \
error when endDictEntry() is not +                // used correctly.
+                // m_state = EndDictEntry;
+
+                // continue and write the dict entry's value
+            }
+#endif
             // first type has been checked already, second must be present (checked \
in EndDict  // state handler). no third type allowed.
             if (d->m_signaturePosition >= aggregateInfo.arr.containedTypeBegin + 2
@@ -2196,7 +2303,13 @@ void Arguments::Writer::advanceState(cstring \
                signatureFragment, IoState newState
                 d->m_signaturePosition = aggregateInfo.arr.containedTypeBegin;
                 isWritingSignature = false;
                 m_state = DictKey;
+#ifdef WITH_DICT_ENTRY
+                assert(newState == BeginDictEntry);
+                aggregateInfo.arr.dictEntryState = Private::InDictEntry;
+                return; // BeginDictEntry writes no data
+#endif
             }
+
             break;
         default:
             break;
@@ -2325,18 +2438,24 @@ void Arguments::Writer::advanceState(cstring \
                signatureFragment, IoState newState
             // not re-opened before each element: there is no observable difference \
for clients  VALID_IF(d->m_nesting.beginParen(), Error::ExcessiveNesting);
         }
+
         aggregateInfo.aggregateType = newState;
         aggregateInfo.arr.containedTypeBegin = d->m_signaturePosition;
-        d->m_aggregateStack.reserve(8);
-        d->m_aggregateStack.push_back(aggregateInfo);
 
         d->m_elements.push_back(Private::ElementInfo(4, \
Private::ElementInfo::ArrayLengthField));  if (newState == BeginDict) {
             d->m_elements.push_back(Private::ElementInfo(structAlignment, 0)); // \
align to dict entry +#ifdef WITH_DICT_ENTRY
+            m_state = BeginDictEntry;
+            aggregateInfo.arr.dictEntryState = Private::RequireBeginDictEntry;
+#else
             m_state = DictKey;
+#endif
         }
-        break; }
 
+        d->m_aggregateStack.reserve(8);
+        d->m_aggregateStack.push_back(aggregateInfo);
+        break; }
     case EndDict:
     case EndArray: {
         const bool isDict = newState == EndDict;
@@ -2367,6 +2486,11 @@ void Arguments::Writer::advanceState(cstring \
signatureFragment, IoState newState  //     due to alignment changes from a different \
                start address
         d->m_elements.push_back(Private::ElementInfo(1, \
Private::ElementInfo::ArrayLengthEndMark));  break; }
+#ifdef WITH_DICT_ENTRY
+    case BeginDictEntry:
+    case EndDictEntry:
+        break;
+#endif
     default:
         VALID_IF(false, Error::InvalidType);
         break;
@@ -2442,6 +2566,27 @@ void Arguments::Writer::endDict()
     advanceState(cstring("}", strlen("}")), EndDict);
 }
 
+#ifdef WITH_DICT_ENTRY
+void Arguments::Writer::beginDictEntry()
+{
+    VALID_IF(m_state == BeginDictEntry, Error::MisplacedBeginDictEntry);
+    advanceState(cstring(), BeginDictEntry);
+}
+
+void Arguments::Writer::endDictEntry()
+{
+    if (!d->m_aggregateStack.empty()) {
+        Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
+        if (aggregateInfo.aggregateType == BeginDict
+            && aggregateInfo.arr.dictEntryState == Private::RequireEndDictEntry) {
+            advanceState(cstring(), EndDictEntry);
+            return;
+        }
+    }
+    VALID_IF(false, Error::MisplacedEndDictEntry);
+}
+#endif
+
 void Arguments::Writer::beginStruct()
 {
     advanceState(cstring("(", strlen("(")), BeginStruct);
diff --git a/serialization/arguments.h b/serialization/arguments.h
index 85d3f09..faa8c1e 100644
--- a/serialization/arguments.h
+++ b/serialization/arguments.h
@@ -34,6 +34,8 @@
 class Error;
 class Message;
 
+//#define WITH_DICT_ENTRY
+
 class DFERRY_EXPORT Arguments
 {
 public:
@@ -90,7 +92,12 @@ public:
         String,
         ObjectPath,
         Signature,
-        UnixFd
+        UnixFd,
+#ifdef WITH_DICT_ENTRY
+        BeginDictEntry,
+        EndDictEntry,
+#endif
+        LastState
     };
 
     Arguments(); // constructs an empty argument list
@@ -258,6 +265,11 @@ public:
         // instead of the type of primitive.
         Arguments::IoState peekPrimitiveArray(EmptyArrayOption option = SkipIfEmpty) \
const;  
+#ifdef WITH_DICT_ENTRY
+        void beginDictEntry();
+        void endDictEntry();
+#endif
+
     private:
         class Private;
         friend class Private;
@@ -342,6 +354,11 @@ public:
 
         void writePrimitiveArray(IoState type, chunk data);
 
+#ifdef WITH_DICT_ENTRY
+        void beginDictEntry();
+        void endDictEntry();
+#endif
+
         class Private;
         friend class Private;
 
diff --git a/tests/serialization/tst_arguments.cpp \
b/tests/serialization/tst_arguments.cpp index 9771a2b..340ab09 100644
--- a/tests/serialization/tst_arguments.cpp
+++ b/tests/serialization/tst_arguments.cpp
@@ -64,6 +64,22 @@ static bool stringsEqual(cstring s1, cstring s2)
     return chunksEqual(chunk(s1.ptr, s1.length), chunk(s2.ptr, s2.length));
 }
 
+static void maybeBeginDictEntry(Arguments::Writer *writer)
+{
+    (void) writer;
+#ifdef WITH_DICT_ENTRY
+    writer->beginDictEntry();
+#endif
+}
+
+static void maybeEndDictEntry(Arguments::Writer *writer)
+{
+    (void) writer;
+#ifdef WITH_DICT_ENTRY
+    writer->endDictEntry();
+#endif
+}
+
 // This class does:
 // 1) iterates over the full Arguments with m_reader
 // 2) skips whole aggregates at and below nesting level m_skipAggregatesFromLevel \
with m_skippingReader @@ -98,7 +114,24 @@ public:
             }
         }
     }
+#ifdef WITH_DICT_ENTRY
+    void beginDictEntry()
+    {
+        m_reader->beginDictEntry();
+        if (m_nestingLevel < m_skipAggregatesFromLevel && m_nilArrayNesting < \
m_skipNilArraysFromLevel) { +            m_skippingReader->beginDictEntry();
+        }
+    }
+
+    void endDictEntry()
+    {
+        m_reader->endDictEntry();
+        if (m_nestingLevel < m_skipAggregatesFromLevel && m_nilArrayNesting < \
m_skipNilArraysFromLevel) { +            m_skippingReader->endDictEntry();
+        }
 
+    }
+#endif
     template<typename F, typename G>
     void beginAggregate(F beginFunc, G skipFunc)
     {
@@ -225,6 +258,14 @@ static void testReadWithSkip(const Arguments &arg, bool \
debugPrint)  case Arguments::BeginDict:
                     checker.beginArrayAggregate(&Arguments::Reader::beginDict, \
&Arguments::Reader::skipDict);  break;
+#ifdef WITH_DICT_ENTRY
+                case Arguments::BeginDictEntry:
+                    checker.beginDictEntry();
+                    break;
+                case Arguments::EndDictEntry:
+                    checker.endDictEntry();
+                    break;
+#endif
                 case Arguments::EndDict:
                     checker.endAggregate(&Arguments::Reader::endDict, true);
                     break;
@@ -292,6 +333,10 @@ static void defaultReadToWrite(Arguments::Reader *reader, \
Arguments::Writer *wri  case Arguments::BeginVariant:
     case Arguments::EndVariant:
     case Arguments::EndArray:
+#ifdef WITH_DICT_ENTRY
+    case Arguments::BeginDictEntry:
+    case Arguments::EndDictEntry:
+#endif
     case Arguments::EndDict:
     case Arguments::Byte:
     case Arguments::Boolean:
@@ -696,6 +741,7 @@ static void test_nesting()
         Arguments::Writer writer;
         for (int i = 0; i < 32; i++) {
             writer.beginDict();
+            maybeBeginDictEntry(&writer);
             writer.writeInt32(i); // key, next nested dict is value
         }
         TEST(writer.state() != Arguments::InvalidData);
@@ -706,6 +752,7 @@ static void test_nesting()
         Arguments::Writer writer;
         for (int i = 0; i < 32; i++) {
             writer.beginDict();
+            maybeBeginDictEntry(&writer);
             writer.writeInt32(i); // key, next nested dict is value
         }
         TEST(writer.state() != Arguments::InvalidData);
@@ -904,17 +951,22 @@ static void test_writerMisuse()
     {
         Arguments::Writer writer;
         writer.beginDict();
+        maybeBeginDictEntry(&writer);
         writer.writeByte(1);
         writer.writeByte(2);
+        maybeEndDictEntry(&writer);
         writer.endDict();
         TEST(writer.state() != Arguments::InvalidData);
     }
     {
         Arguments::Writer writer;
         writer.beginDict();
+        maybeBeginDictEntry(&writer);
         writer.writeByte(1);
         writer.writeByte(2);
+        maybeEndDictEntry(&writer);
         // second key-value pair
+        maybeBeginDictEntry(&writer);
         TEST(writer.state() != Arguments::InvalidData);
         writer.writeUint16(3); // wrong, incompatible with first element
         TEST(writer.state() == Arguments::InvalidData);
@@ -922,9 +974,12 @@ static void test_writerMisuse()
     {
         Arguments::Writer writer;
         writer.beginDict();
+        maybeBeginDictEntry(&writer);
         writer.writeByte(1);
         writer.writeByte(2);
+        maybeEndDictEntry(&writer);
         // second key-value pair
+        maybeBeginDictEntry(&writer);
         writer.writeByte(3);
         TEST(writer.state() != Arguments::InvalidData);
         writer.writeUint16(4); // wrong, incompatible with first element
@@ -934,6 +989,7 @@ static void test_writerMisuse()
     {
         Arguments::Writer writer;
         writer.beginDict();
+        maybeBeginDictEntry(&writer);
         writer.beginVariant(); // wrong, key type must be basic
         TEST(writer.state() == Arguments::InvalidData);
     }
@@ -1022,16 +1078,21 @@ static void test_complicated()
         writer.writeByte(115);
         writer.beginVariant();
             writer.beginDict();
+                maybeBeginDictEntry(&writer);
                 writer.writeByte(23);
                 writer.beginVariant();
                     writer.writeString(cstring("twenty-three"));
                 writer.endVariant();
+                maybeEndDictEntry(&writer);
                 // key-value pair 2
+                maybeBeginDictEntry(&writer);
                 writer.writeByte(83);
                 writer.beginVariant();
                 writer.writeObjectPath(cstring("/foo/bar/object"));
                 writer.endVariant();
+                maybeEndDictEntry(&writer);
                 // key-value pair 3
+                maybeBeginDictEntry(&writer);
                 writer.writeByte(234);
                 writer.beginVariant();
                     writer.beginArray();
@@ -1040,11 +1101,14 @@ static void test_complicated()
                         writer.writeUint16(234);
                     writer.endArray();
                 writer.endVariant();
+                maybeEndDictEntry(&writer);
                 // key-value pair 4
+                maybeBeginDictEntry(&writer);
                 writer.writeByte(25);
                 writer.beginVariant();
                     addSomeVariantStuff(&writer);
                 writer.endVariant();
+                maybeEndDictEntry(&writer);
             writer.endDict();
         writer.endVariant();
         writer.writeString("Hello D-Bus!");
@@ -1171,8 +1235,10 @@ static void test_isWritingSignatureBug()
         writer.beginArray();
             writer.beginStruct();
                 writer.beginDict();
+                    maybeBeginDictEntry(&writer);
                     writer.writeByte(1);
                     writer.writeByte(2);
+                    maybeEndDictEntry(&writer);
                 writer.endDict();
                 // Must add more stuff after the inner dict to ensure that the \
                signature position of the
                 // dict's value is well inside the existing signature in the second \
dict entry. @@ -1182,11 +1248,14 @@ static void test_isWritingSignatureBug()
             writer.endStruct();
             writer.beginStruct();
                 writer.beginDict();
+                    maybeBeginDictEntry(&writer);
                     writer.writeByte(1);
                     writer.writeByte(2);
+                    maybeEndDictEntry(&writer);
                     // In the second pass, we are definitely NOT writing a new part \
                of the dict signature,
                     // which used to go (that was the bug!!) through a different \
code path in  // Arguments::Writer::advanceState().
+                    maybeBeginDictEntry(&writer);
                     writer.writeByte(1);
                     TEST(writer.state() != Arguments::InvalidData);
                     writer.writeUint16(2);
@@ -1564,7 +1633,8 @@ static void test_emptyArrayAndDict()
         // Test RestartEmptyArrayToWriteTypes and writing an empty array inside the \
>1st iteration of another array  Arguments::Writer writer;
         writer.beginArray((i & 2) ? Arguments::Writer::WriteTypesOfEmptyArray : \
                Arguments::Writer::NonEmptyArray);
-            writer.beginArray(Arguments::Writer::NonEmptyArray); // don't care, the \
logic error is only in the second iteration +            // v don't care, the logic \
error is only in the second iteration +            \
writer.beginArray(Arguments::Writer::NonEmptyArray);  \
writer.writeString(cstring("a"));  writer.endArray();
             if (i & 1) {
@@ -1610,8 +1680,10 @@ static void test_emptyArrayAndDict()
     {
         Arguments::Writer writer;
         writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray);
+        maybeBeginDictEntry(&writer);
         writer.writeByte(0);
         writer.writeString(cstring("a"));
+        maybeEndDictEntry(&writer);
         writer.endDict();
         TEST(writer.state() != Arguments::InvalidData);
         Arguments arg = writer.finish();
@@ -1621,9 +1693,11 @@ static void test_emptyArrayAndDict()
     {
         Arguments::Writer writer;
         writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray);
+        maybeBeginDictEntry(&writer);
         writer.writeString(cstring("a"));
         writer.beginVariant();
         writer.endVariant();
+        maybeEndDictEntry(&writer);
         writer.endDict();
         TEST(writer.state() != Arguments::InvalidData);
         Arguments arg = writer.finish();
@@ -1633,12 +1707,16 @@ static void test_emptyArrayAndDict()
     {
         Arguments::Writer writer;
         writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray);
+        maybeBeginDictEntry(&writer);
         writer.writeString(cstring("a"));
         writer.beginVariant();
         writer.endVariant();
+        maybeEndDictEntry(&writer);
+        maybeBeginDictEntry(&writer);
         writer.writeString(cstring("a"));
         writer.beginVariant();
         writer.endVariant();
+        maybeEndDictEntry(&writer);
         writer.endDict();
         TEST(writer.state() != Arguments::InvalidData);
         Arguments arg = writer.finish();
@@ -1648,6 +1726,7 @@ static void test_emptyArrayAndDict()
     {
         Arguments::Writer writer;
         writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray);
+        maybeBeginDictEntry(&writer);
         writer.writeString(cstring("a"));
         writer.beginVariant();
         TEST(writer.state() != Arguments::InvalidData);
@@ -1655,6 +1734,7 @@ static void test_emptyArrayAndDict()
         // variants in nil arrays may contain data but it will be discarded, i.e. \
there will only be an  // empty variant in the output
         writer.endVariant();
+        maybeEndDictEntry(&writer);
         writer.endDict();
         Arguments arg = writer.finish();
         TEST(writer.state() == Arguments::Finished);
@@ -1664,21 +1744,31 @@ static void test_emptyArrayAndDict()
         // Test RestartEmptyArrayToWriteTypes and writing an empty dict inside the \
>1st iteration of another dict  Arguments::Writer writer;
         writer.beginDict((i & 2) ? Arguments::Writer::WriteTypesOfEmptyArray : \
Arguments::Writer::NonEmptyArray); +            maybeBeginDictEntry(&writer);
             writer.writeString(cstring("a"));
-            writer.beginDict(Arguments::Writer::NonEmptyArray); // don't care, the \
logic error is only in the second iteration +            // v don't care, the logic \
error is only in the second iteration +            \
writer.beginDict(Arguments::Writer::NonEmptyArray); +                \
maybeBeginDictEntry(&writer);  writer.writeString(cstring("a"));
                 writer.writeInt32(1234);
+                maybeEndDictEntry(&writer);
             writer.endDict();
+            maybeEndDictEntry(&writer);
+            maybeBeginDictEntry(&writer);
             writer.writeString(cstring("a"));
             if (i & 1) {
                 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray);
+                    maybeBeginDictEntry(&writer);
             } else {
                 writer.beginDict(Arguments::Writer::NonEmptyArray);
                 writer.beginDict(Arguments::Writer::RestartEmptyArrayToWriteTypes);
+                    maybeBeginDictEntry(&writer);
             }
                     writer.writeString(cstring("a"));
                     writer.writeInt32(1234);
+                    maybeEndDictEntry(&writer);
             writer.endDict();
+            maybeEndDictEntry(&writer);
         writer.endDict();
         TEST(writer.state() != Arguments::InvalidData);
         Arguments arg = writer.finish();
@@ -1690,6 +1780,7 @@ static void test_emptyArrayAndDict()
             Arguments::Writer writer;
             for (int j = 0; j <= i; j++) {
                 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray);
+                    maybeBeginDictEntry(&writer);
                 if (j == 32) {
                     TEST(writer.state() == Arguments::InvalidData);
                 }
@@ -1701,6 +1792,7 @@ static void test_emptyArrayAndDict()
             }
             writer.writeUint16(52345);
             for (int j = 0; j <= i; j++) {
+                maybeEndDictEntry(&writer);
                 writer.endDict();
             }
             TEST(writer.state() != Arguments::InvalidData);
@@ -1734,6 +1826,8 @@ int main(int, char *[])
     test_signatureLengths();
     test_emptyArrayAndDict();
 
+    // TODO (maybe): specific tests for begin/endDictEntry() for both Reader and \
Writer. +
     // TODO many more misuse tests for Writer and maybe some for Reader
     std::cout << "Passed!\n";
 }
diff --git a/util/error.h b/util/error.h
index cba439f..eeed6ae 100644
--- a/util/error.h
+++ b/util/error.h
@@ -75,6 +75,11 @@ public:
         InvalidKeyTypeInDict,
         GreaterTwoTypesInDict,
         ArrayOrDictTooLong,
+
+        MissingBeginDictEntry = 1019,
+        MisplacedBeginDictEntry,
+        MissingEndDictEntry,
+        MisplacedEndDictEntry,
         // we have a lot of error codes at our disposal, so reserve some for easy \
classification  // by range
         MaxArgumentsError = 1023,


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

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