File indexing completed on 2024-04-28 16:59:45

0001 /*
0002    Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LGPL.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 
0019    Alternatively, this file is available under the Mozilla Public License
0020    Version 1.1.  You may obtain a copy of the License at
0021    http://www.mozilla.org/MPL/
0022 */
0023 
0024 #include "arguments.h"
0025 #include "arguments_p.h"
0026 
0027 #include "basictypeio.h"
0028 #include "error.h"
0029 #include "malloccache.h"
0030 #include "message.h"
0031 #include "platform.h"
0032 
0033 #include <cstddef>
0034 
0035 #ifdef HAVE_BOOST
0036 #include <boost/container/small_vector.hpp>
0037 #endif
0038 
0039 
0040 class Arguments::Reader::Private
0041 {
0042 public:
0043     Private()
0044        : m_args(nullptr),
0045          m_signaturePosition(uint32(-1)),
0046          m_dataPosition(0),
0047          m_nilArrayNesting(0)
0048     {}
0049 
0050     const Arguments *m_args;
0051     cstring m_signature;
0052     uint32 m_signaturePosition;
0053     chunk m_data;
0054     uint32 m_dataPosition;
0055     uint32 m_nilArrayNesting; // this keeps track of how many nil arrays we are in
0056     Error m_error;
0057     Nesting m_nesting;
0058 
0059     struct ArrayInfo
0060     {
0061         uint32 dataEnd; // one past the last data byte of the array
0062         uint32 containedTypeBegin; // to rewind when reading the next element
0063     };
0064 
0065     struct VariantInfo
0066     {
0067         podCstring prevSignature;     // a variant switches the currently parsed signature, so we
0068         uint32 prevSignaturePosition; // need to store the old signature and parse position.
0069     };
0070 
0071     // for structs, we don't need to know more than that we are in a struct
0072 
0073     struct AggregateInfo
0074     {
0075         IoState aggregateType; // can be BeginArray, BeginDict, BeginStruct, BeginVariant
0076         union {
0077             ArrayInfo arr;
0078             VariantInfo var;
0079         };
0080     };
0081 
0082     // this keeps track of which aggregates we are currently in
0083 #ifdef HAVE_BOOST
0084     boost::container::small_vector<AggregateInfo, 8> m_aggregateStack;
0085 #else
0086     std::vector<AggregateInfo> m_aggregateStack;
0087 #endif
0088 };
0089 
0090 thread_local static MallocCache<sizeof(Arguments::Reader::Private), 4> allocCache;
0091 
0092 Arguments::Reader::Reader(const Arguments &al)
0093    : d(new(allocCache.allocate()) Private),
0094      m_state(NotStarted)
0095 {
0096     d->m_args = &al;
0097     beginRead();
0098 }
0099 
0100 Arguments::Reader::Reader(const Message &msg)
0101    : d(new(allocCache.allocate()) Private),
0102      m_state(NotStarted)
0103 {
0104     d->m_args = &msg.arguments();
0105     beginRead();
0106 }
0107 
0108 Arguments::Reader::Reader(Reader &&other)
0109    : d(other.d),
0110      m_state(other.m_state),
0111      m_u(other.m_u)
0112 {
0113     other.d = nullptr;
0114 }
0115 
0116 void Arguments::Reader::operator=(Reader &&other)
0117 {
0118     if (&other == this) {
0119         return;
0120     }
0121     if (d) {
0122         d->~Private();
0123         allocCache.free(d);
0124     }
0125     d = other.d;
0126     m_state = other.m_state;
0127     m_u = other.m_u;
0128 
0129     other.d = nullptr;
0130 }
0131 
0132 Arguments::Reader::Reader(const Reader &other)
0133    : d(nullptr),
0134      m_state(other.m_state),
0135      m_u(other.m_u)
0136 {
0137     if (other.d) {
0138         d = new(allocCache.allocate()) Private(*other.d);
0139     }
0140 }
0141 
0142 void Arguments::Reader::operator=(const Reader &other)
0143 {
0144     if (&other == this) {
0145         return;
0146     }
0147     m_state = other.m_state;
0148     m_u = other.m_u;
0149     if (d && other.d) {
0150         *d = *other.d;
0151     } else {
0152         Reader temp(other);
0153         std::swap(d, temp.d);
0154     }
0155 }
0156 
0157 Arguments::Reader::~Reader()
0158 {
0159     if (d) {
0160         d->~Private();
0161         allocCache.free(d);
0162         d = nullptr;
0163     }
0164 }
0165 
0166 void Arguments::Reader::beginRead()
0167 {
0168     VALID_IF(d->m_args, Error::NotAttachedToArguments);
0169     d->m_signature = d->m_args->d->m_signature;
0170     d->m_data = d->m_args->d->m_data;
0171     // as a slightly hacky optimizaton, we allow empty Argumentss to allocate no space for d->m_buffer.
0172     if (d->m_signature.length) {
0173         VALID_IF(Arguments::isSignatureValid(d->m_signature), Error::InvalidSignature);
0174     }
0175     advanceState();
0176 }
0177 
0178 bool Arguments::Reader::isValid() const
0179 {
0180     return d->m_args;
0181 }
0182 
0183 Error Arguments::Reader::error() const
0184 {
0185     return d->m_error;
0186 }
0187 
0188 cstring Arguments::Reader::stateString() const
0189 {
0190     return printableState(m_state);
0191 }
0192 
0193 bool Arguments::Reader::isInsideEmptyArray() const
0194 {
0195     return d->m_nilArrayNesting > 0;
0196 }
0197 
0198 cstring Arguments::Reader::currentSignature() const
0199 {
0200     return d->m_signature;
0201 }
0202 
0203 uint32 Arguments::Reader::currentSignaturePosition() const
0204 {
0205     return d->m_signaturePosition;
0206 }
0207 
0208 cstring Arguments::Reader::currentSingleCompleteTypeSignature() const
0209 {
0210     const uint32 startingLength = d->m_signature.length - d->m_signaturePosition;
0211     cstring sigCopy = { d->m_signature.ptr + d->m_signaturePosition, startingLength };
0212     Nesting nest;
0213     if (!parseSingleCompleteType(&sigCopy, &nest)) {
0214         // the signature should have been validated before, but e.g. in Finished state this may happen
0215         return cstring();
0216     }
0217     sigCopy.ptr = d->m_signature.ptr + d->m_signaturePosition;
0218     sigCopy.length = startingLength - sigCopy.length;
0219     return sigCopy;
0220 }
0221 
0222 void Arguments::Reader::replaceData(chunk data)
0223 {
0224     VALID_IF(data.length >= d->m_dataPosition, Error::ReplacementDataIsShorter);
0225 
0226     ptrdiff_t offset = data.ptr - d->m_data.ptr;
0227 
0228     // fix up variant signature addresses occurring on the aggregate stack pointing into m_data;
0229     // don't touch the original (= call parameter, not variant) signature, which does not point into m_data.
0230     bool isMainSignature = true;
0231     for (Private::AggregateInfo &aggregate : d->m_aggregateStack) {
0232         if (aggregate.aggregateType == BeginVariant) {
0233             if (isMainSignature) {
0234                 isMainSignature = false;
0235             } else {
0236                 aggregate.var.prevSignature.ptr += offset;
0237             }
0238         }
0239     }
0240     if (!isMainSignature) {
0241         d->m_signature.ptr += offset;
0242     }
0243 
0244     d->m_data = data;
0245     if (m_state == NeedMoreData) {
0246         advanceState();
0247     }
0248 }
0249 
0250 void Arguments::Reader::doReadPrimitiveType()
0251 {
0252     switch(m_state) {
0253     case Boolean: {
0254         uint32 num = basic::readUint32(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0255         m_u.Boolean = num == 1;
0256         VALID_IF(num <= 1, Error::MalformedMessageData);
0257         break; }
0258     case Byte:
0259         m_u.Byte = d->m_data.ptr[d->m_dataPosition];
0260         break;
0261     case Int16:
0262         m_u.Int16 = basic::readInt16(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0263         break;
0264     case Uint16:
0265         m_u.Uint16 = basic::readUint16(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0266         break;
0267     case Int32:
0268         m_u.Int32 = basic::readInt32(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0269         break;
0270     case Uint32:
0271         m_u.Uint32 = basic::readUint32(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0272         break;
0273     case Int64:
0274         m_u.Int64 = basic::readInt64(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0275         break;
0276     case Uint64:
0277         m_u.Uint64 = basic::readUint64(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0278         break;
0279     case Double:
0280         m_u.Double = basic::readDouble(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0281         break;
0282     case UnixFd: {
0283         uint32 index = basic::readUint32(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0284         if (!d->m_nilArrayNesting) {
0285             VALID_IF(index < d->m_args->d->m_fileDescriptors.size(), Error::MalformedMessageData);
0286             m_u.Int32 = d->m_args->d->m_fileDescriptors[index];
0287         } else {
0288             m_u.Int32 = int32(InvalidFileDescriptor);
0289         }
0290         break; }
0291     default:
0292         assert(false);
0293         VALID_IF(false, Error::MalformedMessageData);
0294     }
0295 }
0296 
0297 void Arguments::Reader::doReadString(uint32 lengthPrefixSize)
0298 {
0299     uint32 stringLength = 1;
0300     if (lengthPrefixSize == 1) {
0301         stringLength += d->m_data.ptr[d->m_dataPosition];
0302     } else {
0303         stringLength += basic::readUint32(d->m_data.ptr + d->m_dataPosition,
0304                                           d->m_args->d->m_isByteSwapped);
0305         VALID_IF(stringLength + 1 < Arguments::MaxArrayLength, Error::MalformedMessageData);
0306     }
0307     d->m_dataPosition += lengthPrefixSize;
0308     if (unlikely(d->m_dataPosition + stringLength > d->m_data.length)) {
0309         m_state = NeedMoreData;
0310         return;
0311     }
0312     m_u.String.ptr = reinterpret_cast<char *>(d->m_data.ptr) + d->m_dataPosition;
0313     m_u.String.length = stringLength - 1; // terminating null is not counted
0314     d->m_dataPosition += stringLength;
0315     bool isValidString = false;
0316     if (m_state == String) {
0317         isValidString = Arguments::isStringValid(cstring(m_u.String.ptr, m_u.String.length));
0318     } else if (m_state == ObjectPath) {
0319         isValidString = Arguments::isObjectPathValid(cstring(m_u.String.ptr, m_u.String.length));
0320     } else if (m_state == Signature) {
0321         isValidString = Arguments::isSignatureValid(cstring(m_u.String.ptr, m_u.String.length));
0322     }
0323     VALID_IF(isValidString, Error::MalformedMessageData);
0324 }
0325 
0326 void Arguments::Reader::advanceState()
0327 {
0328     // if we don't have enough data, the strategy is to keep everything unchanged
0329     // except for the state which will be NeedMoreData
0330     // we don't have to deal with invalid signatures here because they are checked beforehand EXCEPT
0331     // for aggregate nesting which cannot be checked using only one signature, due to variants.
0332     // variant signatures are only parsed while reading the data. individual variant signatures
0333     // ARE checked beforehand whenever we find one in this method.
0334 
0335     if (unlikely(m_state == InvalidData)) { // nonrecoverable...
0336         return;
0337     }
0338     // can't do the following because a dict is one aggregate in our counting, but two according to
0339     // the spec: an array (one) containing dict entries (two)
0340     // assert(d->m_nesting.total() == d->m_aggregateStack.size());
0341     assert((d->m_nesting.total() == 0) == d->m_aggregateStack.empty());
0342 
0343     const uint32 savedSignaturePosition = d->m_signaturePosition;
0344     const uint32 savedDataPosition = d->m_dataPosition;
0345 
0346     d->m_signaturePosition++;
0347     assert(d->m_signaturePosition <= d->m_signature.length);
0348 
0349     // check if we are about to close any aggregate or even the whole argument list
0350     if (d->m_aggregateStack.empty()) {
0351         // TODO check if there is still data left, if so it's probably an error
0352         if (d->m_signaturePosition >= d->m_signature.length) {
0353             m_state = Finished;
0354             return;
0355         }
0356     } else {
0357         const Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
0358         switch (aggregateInfo.aggregateType) {
0359         case BeginStruct:
0360             break; // handled later by TypeInfo knowing ')' -> EndStruct
0361         case BeginVariant:
0362             if (d->m_signaturePosition >= d->m_signature.length) {
0363                 m_state = EndVariant;
0364                 return;
0365             }
0366             break;
0367         case BeginArray:
0368             if (d->m_signaturePosition > aggregateInfo.arr.containedTypeBegin) {
0369                 // End of current iteration; either there are more or the array ends
0370                 const Private::ArrayInfo &arrayInfo = aggregateInfo.arr;
0371                 if (likely(!d->m_nilArrayNesting) && d->m_dataPosition < arrayInfo.dataEnd) {
0372                     // rewind to start of contained type and read the type info there
0373                     d->m_signaturePosition = arrayInfo.containedTypeBegin;
0374                     break; // proceed immediately to reading the next element in the array
0375                 }
0376                 // TODO check that final data position is where it should be according to the
0377                 // serialized array length (same in BeginDict!)
0378                 VALID_IF(d->m_dataPosition == arrayInfo.dataEnd, Error::MalformedMessageData);
0379                 m_state = EndArray;
0380                 return;
0381             }
0382             break;
0383         case BeginDict:
0384             if (d->m_signaturePosition > aggregateInfo.arr.containedTypeBegin + 1) {
0385                 // Almost like BeginArray, only differences are commented
0386                 const Private::ArrayInfo &arrayInfo = aggregateInfo.arr;
0387                 if (likely(!d->m_nilArrayNesting) && d->m_dataPosition < arrayInfo.dataEnd) {
0388                     d->m_dataPosition = align(d->m_dataPosition, 8); // align to dict entry
0389                     d->m_signaturePosition = arrayInfo.containedTypeBegin;
0390 #ifdef WITH_DICT_ENTRY
0391                     d->m_signaturePosition--;
0392                     m_state = EndDictEntry;
0393                     m_u.Uint32 = 0; // meaning: more dict entries follow (state after next is BeginDictEntry)
0394                     return;
0395 #endif
0396                     break;
0397                 }
0398 #ifdef WITH_DICT_ENTRY
0399                 m_state = EndDictEntry;
0400                 m_u.Uint32 = 1; // meaning: array end reached (state after next is EndDict)
0401                 return;
0402 #endif
0403                 m_state = EndDict;
0404                 return;
0405             }
0406             break;
0407         default:
0408             break;
0409         }
0410     }
0411 
0412     // for aggregate types, ty.alignment is just the alignment.
0413     // for primitive types, it's also the actual size.
0414     const TypeInfo ty = typeInfo(d->m_signature.ptr[d->m_signaturePosition]);
0415     m_state = ty.state();
0416 
0417     VALID_IF(m_state != InvalidData, Error::MalformedMessageData);
0418 
0419     // check if we have enough data for the next type, and read it
0420     // if we're in a nil array, we are iterating only over the types without reading any data
0421 
0422     if (likely(!d->m_nilArrayNesting)) {
0423         uint32 padStart = d->m_dataPosition;
0424         d->m_dataPosition = align(d->m_dataPosition, ty.alignment);
0425         if (unlikely(d->m_dataPosition > d->m_data.length)) {
0426             goto out_needMoreData;
0427         }
0428         VALID_IF(isPaddingZero(d->m_data, padStart, d->m_dataPosition), Error::MalformedMessageData);
0429 
0430         if (ty.isPrimitive || ty.isString) {
0431             if (unlikely(d->m_dataPosition + ty.alignment > d->m_data.length)) {
0432                 goto out_needMoreData;
0433             }
0434 
0435             if (ty.isPrimitive) {
0436                 doReadPrimitiveType();
0437                 d->m_dataPosition += ty.alignment;
0438             } else {
0439                 doReadString(ty.alignment);
0440                 if (unlikely(m_state == NeedMoreData)) {
0441                     goto out_needMoreData;
0442                 }
0443             }
0444             return;
0445         }
0446     } else {
0447         if (ty.isPrimitive || ty.isString) {
0448             return; // nothing to do! (readFoo() will return "random" data, so don't use that data!)
0449         }
0450     }
0451 
0452     // now the interesting part: aggregates
0453 
0454     switch (m_state) {
0455     case BeginStruct:
0456         VALID_IF(d->m_nesting.beginParen(), Error::MalformedMessageData);
0457         break;
0458     case EndStruct:
0459         if (!d->m_aggregateStack.size() || d->m_aggregateStack.back().aggregateType != BeginStruct) {
0460             assert(false); // should never happen due to the pre-validated signature
0461         }
0462         break;
0463 
0464     case BeginVariant: {
0465         cstring signature;
0466         if (unlikely(d->m_nilArrayNesting)) {
0467             static const char *emptyString = "";
0468             signature = cstring(emptyString, 0);
0469         } else {
0470             if (unlikely(d->m_dataPosition >= d->m_data.length)) {
0471                 goto out_needMoreData;
0472             }
0473             signature.length = d->m_data.ptr[d->m_dataPosition++];
0474             signature.ptr = reinterpret_cast<char *>(d->m_data.ptr) + d->m_dataPosition;
0475             d->m_dataPosition += signature.length + 1;
0476             if (unlikely(d->m_dataPosition > d->m_data.length)) {
0477                 goto out_needMoreData;
0478             }
0479             VALID_IF(Arguments::isSignatureValid(signature, Arguments::VariantSignature),
0480                      Error::MalformedMessageData);
0481         }
0482         // do not clobber nesting before potentially going to out_needMoreData!
0483         VALID_IF(d->m_nesting.beginVariant(), Error::MalformedMessageData);
0484 
0485         // use m_u as temporary storage - its contents are undefined anyway in state BeginVariant
0486         m_u.String.ptr = signature.ptr;
0487         m_u.String.length = signature.length;
0488         break; }
0489 
0490     case BeginArray: {
0491         // NB: Do not make non-idempotent changes to member variables before potentially going to
0492         //     out_needMoreData! We'll make the same change again after getting more data.
0493         uint32 arrayLength = 0;
0494         if (likely(!d->m_nilArrayNesting)) {
0495             if (unlikely(d->m_dataPosition + sizeof(uint32) > d->m_data.length)) {
0496                 goto out_needMoreData;
0497             }
0498             arrayLength = basic::readUint32(d->m_data.ptr + d->m_dataPosition, d->m_args->d->m_isByteSwapped);
0499             VALID_IF(arrayLength <= Arguments::MaxArrayLength, Error::MalformedMessageData);
0500             d->m_dataPosition += sizeof(uint32);
0501         }
0502 
0503         if (d->m_signature.ptr[d->m_signaturePosition + 1] == '{') {
0504             m_state = BeginDict;
0505         }
0506 
0507         uint32 dataEnd = d->m_dataPosition;
0508         // In case (and we don't check this) the internal type has greater alignment requirements than the
0509         // array index type (which aligns to 4 bytes), align to the nonexistent first element.
0510         // d->m_nilArrayNesting is only increased when the API client calls beginArray(), so
0511         // d->m_nilArrayNesting is the old state. As a side effect of that, it is possible to implement the
0512         // requirement that, in nested containers inside empty arrays, only the outermost array's first type
0513         // is used for alignment purposes.
0514         // TODO: unit-test this
0515         if (likely(!d->m_nilArrayNesting)) {
0516             const uint32 padStart = d->m_dataPosition;
0517             const uint32 alignment = m_state == BeginDict ? uint32(StructAlignment) :
0518                                      typeInfo(d->m_signature.ptr[d->m_signaturePosition + 1]).alignment;
0519             d->m_dataPosition = align(d->m_dataPosition, alignment);
0520             VALID_IF(isPaddingZero(d->m_data, padStart, d->m_dataPosition), Error::MalformedMessageData);
0521             dataEnd = d->m_dataPosition + arrayLength;
0522             if (unlikely(dataEnd > d->m_data.length)) {
0523                 goto out_needMoreData;
0524             }
0525         }
0526 
0527         VALID_IF(d->m_nesting.beginArray(), Error::MalformedMessageData);
0528         if (m_state == BeginDict) {
0529             // TODO check whether the first type is a primitive or string type! // ### isn't that already
0530             // checked for the main signature and / or variants, though?
0531             // only closed at end of dict - there is no observable difference for clients
0532             VALID_IF(d->m_nesting.beginParen(), Error::MalformedMessageData);
0533         }
0534         // temporarily store the future ArrayInfo::dataEnd in m_u.Uint32. used by {begin,skip}{Array,Dict}()
0535         m_u.Uint32 = dataEnd;
0536         break; }
0537 
0538     default:
0539         assert(false);
0540         break;
0541     }
0542 
0543     return;
0544 
0545 out_needMoreData:
0546     // we only start an array when the data for it has fully arrived (possible due to the length
0547     // prefix), so if we still run out of data in an array the input is invalid.
0548     VALID_IF(!d->m_nesting.array, Error::MalformedMessageData);
0549     m_state = NeedMoreData;
0550     d->m_signaturePosition = savedSignaturePosition;
0551     d->m_dataPosition = savedDataPosition;
0552 }
0553 
0554 void Arguments::Reader::skipArrayOrDictSignature(bool isDict)
0555 {
0556     // Note that we cannot just pass a dummy Nesting instance to parseSingleCompleteType, it must
0557     // actually check the nesting because an array may contain other nested aggregates. So we must
0558     // compensate for the already raised nesting levels from BeginArray handling in advanceState().
0559     d->m_nesting.endArray();
0560     if (isDict) {
0561         d->m_nesting.endParen();
0562         // the Reader ad-hoc parsing code moved at ahead by one to skip the '{', but parseSingleCompleteType()
0563         // needs to see the full dict signature, so fix it up
0564         d->m_signaturePosition--;
0565     }
0566 
0567     // parse the full (i.e. starting with the 'a') array (or dict) signature in order to skip it -
0568     // barring bugs, must have been too deep nesting inside variants if parsing fails
0569     cstring remainingSig(d->m_signature.ptr + d->m_signaturePosition,
0570                          d->m_signature.length - d->m_signaturePosition);
0571     VALID_IF(parseSingleCompleteType(&remainingSig, &d->m_nesting), Error::MalformedMessageData);
0572     d->m_signaturePosition = d->m_signature.length - remainingSig.length;
0573 
0574     // Compensate for pre-increment in advanceState()
0575     d->m_signaturePosition--;
0576 
0577     d->m_nesting.beginArray();
0578     if (isDict) {
0579         d->m_nesting.beginParen();
0580         // Compensate for code in advanceState() that kind of ignores the '}' at the end of a dict.
0581         // Unlike advanceState(), parseSingleCompleteType() does properly parse that one.
0582         d->m_signaturePosition--;
0583     }
0584 }
0585 
0586 bool Arguments::Reader::beginArray(EmptyArrayOption option)
0587 {
0588     if (unlikely(m_state != BeginArray)) {
0589         m_state = InvalidData;
0590         d->m_error.setCode(Error::ReadWrongType);
0591         return false;
0592     }
0593 
0594     Private::AggregateInfo aggregateInfo;
0595     aggregateInfo.aggregateType = BeginArray;
0596     Private::ArrayInfo &arrayInfo = aggregateInfo.arr; // also used for dict
0597     arrayInfo.dataEnd = m_u.Uint32;
0598     arrayInfo.containedTypeBegin = d->m_signaturePosition + 1;
0599     d->m_aggregateStack.push_back(aggregateInfo);
0600 
0601     const uint32 arrayLength = m_u.Uint32 - d->m_dataPosition;
0602     if (!arrayLength) {
0603         d->m_nilArrayNesting++;
0604     }
0605 
0606     if (unlikely(d->m_nilArrayNesting && option == SkipIfEmpty)) {
0607         skipArrayOrDictSignature(false);
0608     }
0609 
0610     advanceState();
0611     return !d->m_nilArrayNesting;
0612 }
0613 
0614 void Arguments::Reader::skipArrayOrDict(bool isDict)
0615 {
0616     // fast-forward the signature and data positions
0617     skipArrayOrDictSignature(isDict);
0618     d->m_dataPosition = m_u.Uint32;
0619 
0620     // m_state = isDict ? EndDict : EndArray; // nobody looks at it
0621     if (isDict) {
0622         d->m_nesting.endParen();
0623         d->m_signaturePosition++; // skip '}'
0624     }
0625     d->m_nesting.endArray();
0626 
0627     // proceed to next element
0628     advanceState();
0629 }
0630 
0631 void Arguments::Reader::skipArray()
0632 {
0633     if (unlikely(m_state != BeginArray)) {
0634         // TODO test this
0635         m_state = InvalidData;
0636         d->m_error.setCode(Error::ReadWrongType);
0637     } else {
0638         skipArrayOrDict(false);
0639     }
0640 }
0641 
0642 void Arguments::Reader::endArray()
0643 {
0644     VALID_IF(m_state == EndArray, Error::ReadWrongType);
0645     d->m_signaturePosition--; // fix up for the pre-increment of d->m_signaturePosition in advanceState()
0646     d->m_nesting.endArray();
0647     d->m_aggregateStack.pop_back();
0648     if (unlikely(d->m_nilArrayNesting)) {
0649         d->m_nilArrayNesting--;
0650     }
0651     advanceState();
0652 }
0653 
0654 std::pair<Arguments::IoState, chunk> Arguments::Reader::readPrimitiveArray()
0655 {
0656     auto ret = std::make_pair(InvalidData, chunk());
0657 
0658     if (m_state != BeginArray) {
0659         return ret;
0660     }
0661 
0662     // the point of "primitive array" accessors is that the data can be just memcpy()ed, so we
0663     // reject anything that needs validation, including booleans
0664 
0665     const TypeInfo elementType = typeInfo(d->m_signature.ptr[d->m_signaturePosition + 1]);
0666     if (!elementType.isPrimitive || elementType.state() == Boolean || elementType.state() == UnixFd) {
0667         return ret;
0668     }
0669     if (d->m_args->d->m_isByteSwapped && elementType.state() != Byte) {
0670         return ret;
0671     }
0672 
0673     const uint32 size = m_u.Uint32 - d->m_dataPosition;
0674     // does the end of data line up with the end of the last data element?
0675     if (!isAligned(size, elementType.alignment)) {
0676         return ret;
0677     }
0678     if (size) {
0679         ret.second.ptr = d->m_data.ptr + d->m_dataPosition;
0680         ret.second.length = size;
0681     }
0682     // No need to change  d->m_nilArrayNesting - it can't be observed while "in" the current array
0683 
0684     ret.first = elementType.state();
0685     d->m_signaturePosition += 1;
0686     d->m_dataPosition = m_u.Uint32;
0687     m_state = EndArray;
0688     d->m_nesting.endArray();
0689 
0690     // ... leave the array, there is nothing more to do in it
0691     advanceState();
0692 
0693     return ret;
0694 }
0695 
0696 Arguments::IoState Arguments::Reader::peekPrimitiveArray(EmptyArrayOption option) const
0697 {
0698     // almost duplicated from readPrimitiveArray(), so keep it in sync
0699     if (m_state != BeginArray) {
0700         return InvalidData;
0701     }
0702     const uint32 arrayLength = m_u.Uint32 - d->m_dataPosition;
0703     if (option == SkipIfEmpty && !arrayLength) {
0704         return BeginArray;
0705     }
0706     const TypeInfo elementType = typeInfo(d->m_signature.ptr[d->m_signaturePosition + 1]);
0707     if (!elementType.isPrimitive || elementType.state() == Boolean || elementType.state() == UnixFd) {
0708         return BeginArray;
0709     }
0710     if (d->m_args->d->m_isByteSwapped && elementType.state() != Byte) {
0711         return BeginArray;
0712     }
0713     return elementType.state();
0714 }
0715 
0716 bool Arguments::Reader::beginDict(EmptyArrayOption option)
0717 {
0718     if (unlikely(m_state != BeginDict)) {
0719         m_state = InvalidData;
0720         d->m_error.setCode(Error::ReadWrongType);
0721         return false;
0722     }
0723 
0724     d->m_signaturePosition++; // skip '{`
0725 
0726     Private::AggregateInfo aggregateInfo;
0727     aggregateInfo.aggregateType = BeginDict;
0728     Private::ArrayInfo &arrayInfo = aggregateInfo.arr; // also used for dict
0729     arrayInfo.dataEnd = m_u.Uint32;
0730     arrayInfo.containedTypeBegin = d->m_signaturePosition + 1;
0731     d->m_aggregateStack.push_back(aggregateInfo);
0732 
0733     const uint32 arrayLength = m_u.Uint32 - d->m_dataPosition;
0734     if (!arrayLength) {
0735         d->m_nilArrayNesting++;
0736     }
0737 
0738     if (unlikely(d->m_nilArrayNesting && option == SkipIfEmpty)) {
0739         skipArrayOrDictSignature(true);
0740 #ifdef WITH_DICT_ENTRY
0741         const bool ret = !d->m_nilArrayNesting;
0742         advanceState();
0743         endDictEntry();
0744         return ret;
0745     }
0746     m_state = BeginDictEntry;
0747 #else
0748     }
0749 
0750     advanceState();
0751 #endif
0752     return !d->m_nilArrayNesting;
0753 }
0754 
0755 void Arguments::Reader::skipDict()
0756 {
0757     if (unlikely(m_state != BeginDict)) {
0758         // TODO test this
0759         m_state = InvalidData;
0760         d->m_error.setCode(Error::ReadWrongType);
0761     } else {
0762         d->m_signaturePosition++; // skip '{' like beginDict() does - skipArrayOrDict() expects it
0763         skipArrayOrDict(true);
0764     }
0765 }
0766 
0767 bool Arguments::Reader::isDictKey() const
0768 {
0769     if (!d->m_aggregateStack.empty()) {
0770         const Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
0771         return aggregateInfo.aggregateType == BeginDict &&
0772                d->m_signaturePosition == aggregateInfo.arr.containedTypeBegin;
0773     }
0774     return false;
0775 }
0776 
0777 void Arguments::Reader::endDict()
0778 {
0779     VALID_IF(m_state == EndDict, Error::ReadWrongType);
0780     d->m_nesting.endParen();
0781     //d->m_signaturePosition++; // skip '}'
0782     //d->m_signaturePosition--; // fix up for the pre-increment of d->m_signaturePosition in advanceState()
0783     d->m_nesting.endArray();
0784     d->m_aggregateStack.pop_back();
0785     if (unlikely(d->m_nilArrayNesting)) {
0786         d->m_nilArrayNesting--;
0787     }
0788     advanceState();
0789 }
0790 
0791 #ifdef WITH_DICT_ENTRY
0792 void Arguments::Reader::beginDictEntry()
0793 {
0794     VALID_IF(m_state == BeginDictEntry, Error::ReadWrongType);
0795     advanceState();
0796 }
0797 
0798 void Arguments::Reader::endDictEntry()
0799 {
0800     VALID_IF(m_state == EndDictEntry, Error::ReadWrongType);
0801     if (m_u.Uint32 == 0) {
0802         m_state = BeginDictEntry;
0803     } else {
0804         m_state = EndDict;
0805     }
0806 }
0807 #endif
0808 
0809 void Arguments::Reader::beginStruct()
0810 {
0811     VALID_IF(m_state == BeginStruct, Error::ReadWrongType);
0812     Private::AggregateInfo aggregateInfo;
0813     aggregateInfo.aggregateType = BeginStruct;
0814     d->m_aggregateStack.push_back(aggregateInfo);
0815     advanceState();
0816 }
0817 
0818 void Arguments::Reader::skipStruct()
0819 {
0820     if (unlikely(m_state != BeginStruct)) {
0821         m_state = InvalidData;
0822         d->m_error.setCode(Error::ReadWrongType);
0823     } else {
0824         skipCurrentElement();
0825     }
0826 }
0827 
0828 void Arguments::Reader::endStruct()
0829 {
0830     VALID_IF(m_state == EndStruct, Error::ReadWrongType);
0831     d->m_nesting.endParen();
0832     d->m_aggregateStack.pop_back();
0833     advanceState();
0834 }
0835 
0836 void Arguments::Reader::beginVariant()
0837 {
0838     VALID_IF(m_state == BeginVariant, Error::ReadWrongType);
0839 
0840     Private::AggregateInfo aggregateInfo;
0841     aggregateInfo.aggregateType = BeginVariant;
0842     Private::VariantInfo &variantInfo = aggregateInfo.var;
0843     variantInfo.prevSignature.ptr = d->m_signature.ptr;
0844     variantInfo.prevSignature.length = d->m_signature.length;
0845     variantInfo.prevSignaturePosition = d->m_signaturePosition;
0846     d->m_aggregateStack.push_back(aggregateInfo);
0847     d->m_signature.ptr = m_u.String.ptr;
0848     d->m_signature.length = m_u.String.length;
0849     d->m_signaturePosition = uint32(-1); // we increment d->m_signaturePosition before reading a char
0850 
0851     advanceState();
0852 }
0853 
0854 void Arguments::Reader::skipVariant()
0855 {
0856     if (unlikely(m_state != BeginVariant)) {
0857         m_state = InvalidData;
0858         d->m_error.setCode(Error::ReadWrongType);
0859     } else {
0860         skipCurrentElement();
0861     }
0862 }
0863 
0864 void Arguments::Reader::endVariant()
0865 {
0866     VALID_IF(m_state == EndVariant, Error::ReadWrongType);
0867     d->m_nesting.endVariant();
0868 
0869     const Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
0870     const Private::VariantInfo &variantInfo = aggregateInfo.var;
0871     d->m_signature.ptr = variantInfo.prevSignature.ptr;
0872     d->m_signature.length = variantInfo.prevSignature.length;
0873     d->m_signaturePosition = variantInfo.prevSignaturePosition;
0874     d->m_aggregateStack.pop_back();
0875 
0876     advanceState();
0877 }
0878 
0879 void Arguments::Reader::skipCurrentElement()
0880 {
0881     // ### We could implement a skipping fast path for more aggregates, but it would be a lot of work, so
0882     //     until it's proven to be a problem, just reuse what we have.
0883 
0884 #ifndef NDEBUG
0885     Arguments::IoState stateOnEntry = m_state;
0886 #endif
0887     int nestingLevel = 0;
0888     bool isDone = false;
0889 
0890     while (!isDone) {
0891         switch(state()) {
0892         case Arguments::Finished:
0893             // Okay, that's a bit weird. I guess the graceful way to handle it is do nothing in release
0894             // mode, and explode in debug mode in order to warn the API client.
0895             // (We could use a warning message facility here, make one?)
0896             assert(false);
0897             isDone = true;
0898             break;
0899         case Arguments::BeginStruct:
0900             beginStruct();
0901             nestingLevel++;
0902             break;
0903         case Arguments::EndStruct:
0904             endStruct();
0905             nestingLevel--;
0906             if (!nestingLevel) {
0907                 assert(stateOnEntry == BeginStruct);
0908             }
0909             break;
0910         case Arguments::BeginVariant:
0911             beginVariant();
0912             nestingLevel++;
0913             break;
0914         case Arguments::EndVariant:
0915             endVariant();
0916             nestingLevel--;
0917             if (!nestingLevel) {
0918                 assert(stateOnEntry == BeginVariant);
0919             }
0920             break;
0921         case Arguments::BeginArray:
0922             skipArray();
0923             break;
0924         case Arguments::EndArray:
0925             assert(stateOnEntry == EndArray); // only way this can happen - we gracefully skip EndArray
0926                                               // and DON'T decrease nestingLevel b/c it would go negative.
0927             endArray();
0928             break;
0929         case Arguments::BeginDict:
0930             skipDict();
0931             break;
0932 #ifdef WITH_DICT_ENTRY
0933         case Arguments::BeginDictEntry:
0934             beginDictEntry();
0935             break;
0936         case Arguments::EndDictEntry:
0937             endDictEntry();
0938             break;
0939 #endif
0940         case Arguments::EndDict:
0941             assert(stateOnEntry == EndDict); // only way this can happen - we gracefully "skip" EndDict
0942                                              // and DON'T decrease nestingLevel b/c it would go negative.
0943             endDict();
0944             break;
0945         case Arguments::Boolean:
0946             readBoolean();
0947             break;
0948         case Arguments::Byte:
0949             readByte();
0950             break;
0951         case Arguments::Int16:
0952             readInt16();
0953             break;
0954         case Arguments::Uint16:
0955             readUint16();
0956             break;
0957         case Arguments::Int32:
0958             readInt32();
0959             break;
0960         case Arguments::Uint32:
0961             readUint32();
0962             break;
0963         case Arguments::Int64:
0964             readInt64();
0965             break;
0966         case Arguments::Uint64:
0967             readUint64();
0968             break;
0969         case Arguments::Double:
0970             readDouble();
0971             break;
0972         case Arguments::String:
0973             readString();
0974             break;
0975         case Arguments::ObjectPath:
0976             readObjectPath();
0977             break;
0978         case Arguments::Signature:
0979             readSignature();
0980             break;
0981         case Arguments::UnixFd:
0982             readUnixFd();
0983             break;
0984         case Arguments::NeedMoreData:
0985             // TODO handle this properly: rewind the state to before the aggregate - or get fancy and support
0986             // resuming, but that is going to get really ugly
0987             // fall through
0988         default:
0989             m_state = InvalidData;
0990             d->m_error.setCode(Error::StateNotSkippable);
0991             // fall through
0992         case Arguments::InvalidData:
0993             isDone = true;
0994             break;
0995         }
0996         if (!nestingLevel) {
0997             isDone = true;
0998         }
0999     }
1000 }
1001 
1002 std::vector<Arguments::IoState> Arguments::Reader::aggregateStack() const
1003 {
1004     std::vector<IoState> ret;
1005     ret.reserve(d->m_aggregateStack.size());
1006     for (Private::AggregateInfo &aggregate : d->m_aggregateStack) {
1007         ret.push_back(aggregate.aggregateType);
1008     }
1009     return ret;
1010 }
1011 
1012 uint32 Arguments::Reader::aggregateDepth() const
1013 {
1014     return d->m_aggregateStack.size();
1015 }
1016 
1017 Arguments::IoState Arguments::Reader::currentAggregate() const
1018 {
1019     if (d->m_aggregateStack.empty()) {
1020         return NotStarted;
1021     }
1022     return d->m_aggregateStack.back().aggregateType;
1023 }