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 "malloccache.h"
0029 
0030 #include <cstring>
0031 
0032 #ifdef HAVE_BOOST
0033 #include <boost/container/small_vector.hpp>
0034 #endif
0035 
0036 static constexpr byte alignLog[9] = { 0, 0, 1, 0, 2, 0, 0, 0, 3 };
0037 inline constexpr byte alignmentLog2(uint32 alignment)
0038 {
0039     // The following is not constexpr in C++14, and it hasn't triggered in ages
0040     // assert(alignment <= 8 && (alignment < 2 || alignLog[alignment] != 0));
0041     return alignLog[alignment];
0042 }
0043 
0044 class Arguments::Writer::Private
0045 {
0046 public:
0047     Private()
0048        : m_signaturePosition(0),
0049          m_data(reinterpret_cast<byte *>(malloc(InitialDataCapacity))),
0050          m_dataCapacity(InitialDataCapacity),
0051          m_dataPosition(SignatureReservedSpace),
0052          m_nilArrayNesting(0)
0053     {
0054         m_signature.ptr = reinterpret_cast<char *>(m_data + 1); // reserve a byte for length prefix
0055         m_signature.length = 0;
0056     }
0057 
0058     Private(const Private &other);
0059     void operator=(const Private &other);
0060 
0061     void reserveData(uint32 size, IoState *state)
0062     {
0063         size += 16; // enough extra for anything but variable-length data such as strings and arrays
0064         if (likely(size <= m_dataCapacity)) {
0065             return;
0066         }
0067         uint32 newCapacity = m_dataCapacity;
0068         do {
0069             newCapacity *= 2;
0070         } while (size > newCapacity);
0071 
0072         m_signature.ptr -= reinterpret_cast<size_t>(m_data);
0073         m_data = reinterpret_cast<byte *>(realloc(m_data, newCapacity));
0074         m_signature.ptr += reinterpret_cast<size_t>(m_data);
0075         m_dataCapacity = newCapacity;
0076 
0077         // Here, we trade off getting an ArgumentsTooLong error as early as possible for fewer
0078         // conditional branches in the hot path. Because only the final message length has a well-defined
0079         // limit, and Arguments doesn't, a precise check has limited usefulness anwyay. This is just
0080         // a sanity check and out of bounds access / overflow protection.
0081 
0082         // In most cases, callers do not need to check for errors: Very large single arguments are
0083         // already rejected, and we actually allocate the too large buffer to prevent out-of-bounds
0084         // access. Any following writing API calls will then cleanly abort due to m_state == InvalidData.
0085         // ### Callers DO need to check m_state before possibly overwriting it, hiding the error!
0086         if (newCapacity > Arguments::MaxMessageLength * 3) {
0087             *state = InvalidData;
0088             m_error.setCode(Error::ArgumentsTooLong);
0089         }
0090     }
0091 
0092     bool insideVariant()
0093     {
0094         return !m_queuedData.empty();
0095     }
0096 
0097     // We don't know how long a variant signature is when starting the variant, but we have to
0098     // insert the signature into the datastream before the data. For that reason, we need a
0099     // postprocessing pass to fix things up once the outermost variant is closed.
0100     // QueuedDataInfo stores enough information about data inside variants to be able to do
0101     // the patching up while respecting alignment and other requirements.
0102     struct QueuedDataInfo
0103     {
0104         constexpr QueuedDataInfo(byte alignment, byte size_)
0105             : alignmentExponent(alignmentLog2(alignment)),
0106               size(size_)
0107         {}
0108         byte alignment() const { return 1 << alignmentExponent; }
0109 
0110         byte alignmentExponent : 2; // powers of 2, so 1, 2, 4, 8
0111         byte size : 6; // that's up to 63
0112         enum SizeCode {
0113             LargestSize = 60,
0114             ArrayLengthField,
0115             ArrayLengthEndMark,
0116             VariantSignature
0117         };
0118     };
0119 
0120     // The parameter is not a QueuedDataInfo because the compiler doesn't seem to optimize away
0121     // QueuedDataInfo construction when insideVariant() is false, despite inlining.
0122     void maybeQueueData(byte alignment, byte size)
0123     {
0124         if (insideVariant()) {
0125             m_queuedData.push_back(QueuedDataInfo(alignment, size));
0126         }
0127     }
0128 
0129     // Caution: does not ensure that enough space is available!
0130     void appendBulkData(chunk data)
0131     {
0132         // Align only the first of the back-to-back data chunks - otherwise, when storing values which
0133         // are 8 byte aligned, the second half of an element straddling a chunk boundary
0134         // (QueuedDataInfo::LargestSize == 60) would start at an 8-byte aligned position (so 64)
0135         // instead of 60 where we want it in order to just write a contiguous block of data.
0136         memcpy(m_data + m_dataPosition, data.ptr, data.length);
0137         m_dataPosition += data.length;
0138         if (insideVariant()) {
0139             for (uint32 l = data.length; l; ) {
0140                 uint32 chunkSize = std::min(l, uint32(QueuedDataInfo::LargestSize));
0141                 m_queuedData.push_back(QueuedDataInfo(1, chunkSize));
0142                 l -= chunkSize;
0143             }
0144         }
0145     }
0146 
0147     void alignData(uint32 alignment)
0148     {
0149         if (insideVariant()) {
0150             m_queuedData.push_back(QueuedDataInfo(alignment, 0));
0151         }
0152         zeroPad(m_data, alignment, &m_dataPosition);
0153     }
0154 
0155     uint32 m_dataElementsCountBeforeNilArray;
0156     uint32 m_dataPositionBeforeVariant;
0157 
0158     Nesting m_nesting;
0159     cstring m_signature;
0160     uint32 m_signaturePosition;
0161 
0162     byte *m_data;
0163     uint32 m_dataCapacity;
0164     uint32 m_dataPosition;
0165 
0166     int m_nilArrayNesting;
0167     std::vector<int> m_fileDescriptors;
0168     Error m_error;
0169 
0170     enum {
0171         InitialDataCapacity = 512,
0172         // max signature length (255) + length prefix(1) + null terminator(1), rounded up to multiple of 8
0173         // because that doesn't change alignment
0174         SignatureReservedSpace = 264
0175     };
0176 
0177 #ifdef WITH_DICT_ENTRY
0178     enum DictEntryState : byte
0179     {
0180         RequireBeginDictEntry = 0,
0181         InDictEntry,
0182         RequireEndDictEntry,
0183         AfterEndDictEntry
0184     };
0185 #endif
0186     struct ArrayInfo
0187     {
0188         uint32 containedTypeBegin; // to rewind when reading the next element
0189 #ifdef WITH_DICT_ENTRY
0190         DictEntryState dictEntryState;
0191         uint32 lengthFieldPosition : 24;
0192 #else
0193         uint32 lengthFieldPosition;
0194 #endif
0195     };
0196 
0197     struct VariantInfo
0198     {
0199         // a variant switches the currently parsed signature, so we
0200         // need to store the old signature and parse position.
0201         uint32 prevSignatureOffset; // relative to m_data
0202         uint32 prevSignaturePosition;
0203     };
0204 
0205     struct StructInfo
0206     {
0207         uint32 containedTypeBegin;
0208     };
0209 
0210     struct AggregateInfo
0211     {
0212         IoState aggregateType; // can be BeginArray, BeginDict, BeginStruct, BeginVariant
0213         union {
0214             ArrayInfo arr;
0215             VariantInfo var;
0216             StructInfo sct;
0217         };
0218     };
0219 
0220     // this keeps track of which aggregates we are currently in
0221 #ifdef HAVE_BOOST
0222     boost::container::small_vector<AggregateInfo, 8> m_aggregateStack;
0223 #else
0224     std::vector<AggregateInfo> m_aggregateStack;
0225 #endif
0226     std::vector<QueuedDataInfo> m_queuedData;
0227 };
0228 
0229 thread_local static MallocCache<sizeof(Arguments::Writer::Private), 4> allocCache;
0230 
0231 Arguments::Writer::Private::Private(const Private &other)
0232 {
0233     *this = other;
0234 }
0235 
0236 void Arguments::Writer::Private::operator=(const Private &other)
0237 {
0238     if (&other == this) {
0239         assert(false); // if this happens, the (internal) caller did something wrong
0240         return;
0241     }
0242 
0243     m_dataElementsCountBeforeNilArray = other.m_dataElementsCountBeforeNilArray;
0244     m_dataPositionBeforeVariant = other.m_dataPositionBeforeVariant;
0245 
0246     m_nesting = other.m_nesting;
0247     m_signature.ptr = other.m_signature.ptr; // ### still needs adjustment, done after allocating m_data
0248     m_signature.length = other.m_signature.length;
0249     m_signaturePosition = other.m_signaturePosition;
0250 
0251     m_dataCapacity = other.m_dataCapacity;
0252     m_dataPosition = other.m_dataPosition;
0253     // handle *m_data and the data it's pointing to
0254     m_data = reinterpret_cast<byte *>(malloc(m_dataCapacity));
0255     memcpy(m_data, other.m_data, m_dataPosition);
0256     m_signature.ptr += m_data - other.m_data;
0257 
0258     m_nilArrayNesting = other.m_nilArrayNesting;
0259     m_fileDescriptors = other.m_fileDescriptors;
0260     m_error = other.m_error;
0261 
0262     m_aggregateStack = other.m_aggregateStack;
0263     m_queuedData = other.m_queuedData;
0264 }
0265 
0266 Arguments::Writer::Writer()
0267    : d(new(allocCache.allocate()) Private),
0268      m_state(AnyData)
0269 {
0270 }
0271 
0272 Arguments::Writer::Writer(Writer &&other)
0273    : d(other.d),
0274      m_state(other.m_state),
0275      m_u(other.m_u)
0276 {
0277     other.d = nullptr;
0278 }
0279 
0280 void Arguments::Writer::operator=(Writer &&other)
0281 {
0282     if (&other == this) {
0283         return;
0284     }
0285     d = other.d;
0286     m_state = other.m_state;
0287     m_u = other.m_u;
0288 
0289     other.d = nullptr;
0290 }
0291 
0292 Arguments::Writer::Writer(const Writer &other)
0293    : d(nullptr),
0294      m_state(other.m_state),
0295      m_u(other.m_u)
0296 {
0297     if (other.d) {
0298         d = new(allocCache.allocate()) Private(*other.d);
0299     }
0300 
0301 }
0302 
0303 void Arguments::Writer::operator=(const Writer &other)
0304 {
0305     if (&other == this) {
0306         return;
0307     }
0308     m_state = other.m_state;
0309     m_u = other.m_u;
0310     if (d && other.d) {
0311         *d = *other.d;
0312     } else {
0313         Writer temp(other);
0314         std::swap(d, temp.d);
0315     }
0316 }
0317 
0318 Arguments::Writer::~Writer()
0319 {
0320     if (d) {
0321         free(d->m_data);
0322         d->m_data = nullptr;
0323         d->~Private();
0324         allocCache.free(d);
0325         d = nullptr;
0326     }
0327 }
0328 
0329 bool Arguments::Writer::isValid() const
0330 {
0331     return !d->m_error.isError();
0332 }
0333 
0334 Error Arguments::Writer::error() const
0335 {
0336     return d->m_error;
0337 }
0338 
0339 cstring Arguments::Writer::stateString() const
0340 {
0341     return printableState(m_state);
0342 }
0343 
0344 bool Arguments::Writer::isInsideEmptyArray() const
0345 {
0346     return d->m_nilArrayNesting > 0;
0347 }
0348 
0349 cstring Arguments::Writer::currentSignature() const
0350 {
0351     // A signature must be null-terminated to be valid.
0352     // We're only overwriting uninitialized memory, no need to undo that later.
0353     d->m_signature.ptr[d->m_signature.length] = '\0';
0354     return d->m_signature;
0355 }
0356 
0357 uint32 Arguments::Writer::currentSignaturePosition() const
0358 {
0359     return d->m_signaturePosition;
0360 }
0361 
0362 void Arguments::Writer::doWritePrimitiveType(IoState type, uint32 alignAndSize)
0363 {
0364     zeroPad(d->m_data, alignAndSize, &d->m_dataPosition);
0365 
0366     switch(type) {
0367     case Boolean: {
0368         uint32 num = m_u.Boolean ? 1 : 0;
0369         basic::writeUint32(d->m_data + d->m_dataPosition, num);
0370         break; }
0371     case Byte:
0372         d->m_data[d->m_dataPosition] = m_u.Byte;
0373         break;
0374     case Int16:
0375         basic::writeInt16(d->m_data + d->m_dataPosition, m_u.Int16);
0376         break;
0377     case Uint16:
0378         basic::writeUint16(d->m_data + d->m_dataPosition, m_u.Uint16);
0379         break;
0380     case Int32:
0381         basic::writeInt32(d->m_data + d->m_dataPosition, m_u.Int32);
0382         break;
0383     case Uint32:
0384         basic::writeUint32(d->m_data + d->m_dataPosition, m_u.Uint32);
0385         break;
0386     case Int64:
0387         basic::writeInt64(d->m_data + d->m_dataPosition, m_u.Int64);
0388         break;
0389     case Uint64:
0390         basic::writeUint64(d->m_data + d->m_dataPosition, m_u.Uint64);
0391         break;
0392     case Double:
0393         basic::writeDouble(d->m_data + d->m_dataPosition, m_u.Double);
0394         break;
0395     case UnixFd: {
0396         const uint32 index = d->m_fileDescriptors.size();
0397         if (!d->m_nilArrayNesting) {
0398             d->m_fileDescriptors.push_back(m_u.Int32);
0399         }
0400         basic::writeUint32(d->m_data + d->m_dataPosition, index);
0401         break; }
0402     default:
0403         assert(false);
0404         VALID_IF(false, Error::InvalidType);
0405     }
0406 
0407     d->m_dataPosition += alignAndSize;
0408     d->maybeQueueData(alignAndSize, alignAndSize);
0409 }
0410 
0411 void Arguments::Writer::doWriteString(IoState type, uint32 lengthPrefixSize)
0412 {
0413     if (type == String) {
0414         VALID_IF(Arguments::isStringValid(cstring(m_u.String.ptr, m_u.String.length)),
0415                  Error::InvalidString);
0416     } else if (type == ObjectPath) {
0417         VALID_IF(Arguments::isObjectPathValid(cstring(m_u.String.ptr, m_u.String.length)),
0418                  Error::InvalidObjectPath);
0419     } else if (type == Signature) {
0420         VALID_IF(Arguments::isSignatureValid(cstring(m_u.String.ptr, m_u.String.length)),
0421                  Error::InvalidSignature);
0422     }
0423 
0424     d->reserveData(d->m_dataPosition + m_u.String.length, &m_state);
0425 
0426     zeroPad(d->m_data, lengthPrefixSize, &d->m_dataPosition);
0427 
0428     if (lengthPrefixSize == 1) {
0429         d->m_data[d->m_dataPosition] = m_u.String.length;
0430     } else {
0431         basic::writeUint32(d->m_data + d->m_dataPosition, m_u.String.length);
0432     }
0433     d->m_dataPosition += lengthPrefixSize;
0434     d->maybeQueueData(lengthPrefixSize, lengthPrefixSize);
0435 
0436     d->appendBulkData(chunk(m_u.String.ptr, m_u.String.length + 1));
0437 }
0438 
0439 void Arguments::Writer::advanceState(cstring signatureFragment, IoState newState)
0440 {
0441     // what needs to happen here:
0442     // - if we are in an existing portion of the signature (like writing the >1st iteration of an array)
0443     //   check if the type to be written is the same as the one that's already in the signature
0444     //   - otherwise we still need to check if the data we're adding conforms with the spec, e.g.
0445     //     no empty structs, dict entries must have primitive key type and exactly one value type
0446     // - check well-formedness of data: strings, maximum serialized array length and message length
0447     //   (variant signature length only being known after finishing a variant introduces uncertainty
0448     //    of final data stream size - due to alignment padding, a variant signature longer by one can
0449     //    cause an up to seven bytes longer message. in other cases it won't change message length at all.)
0450     // - increase size of data buffer when it gets too small
0451     // - store information about variants and arrays, in order to:
0452     //   - know what the final binary message size will be
0453     //   - in finish(), create the final data stream with inline variant signatures and array lengths
0454 
0455     // can't do the following because a dict is one aggregate in our counting, but two according to
0456     // the spec: an array (one) containing dict entries (two)
0457     // assert(d->m_nesting.total() == d->m_aggregateStack.size());
0458     assert((d->m_nesting.total() == 0) == d->m_aggregateStack.empty());
0459 
0460     uint32 alignment = 1;
0461     bool isPrimitiveType = false;
0462     bool isStringType = false;
0463 
0464     if (signatureFragment.length) {
0465         const TypeInfo ty = typeInfo(signatureFragment.ptr[0]);
0466         alignment = ty.alignment;
0467         isPrimitiveType = ty.isPrimitive;
0468         isStringType = ty.isString;
0469     }
0470 
0471     d->reserveData(d->m_dataPosition, &m_state);
0472     if (unlikely(m_state == InvalidData)) { // this is not only for reserveData, but also pre-existing errors
0473         return;
0474     }
0475 
0476     m_state = AnyData;
0477 
0478     bool isWritingSignature = d->m_signaturePosition == d->m_signature.length;
0479     if (isWritingSignature) {
0480         // signature additions must conform to syntax
0481         VALID_IF(d->m_signaturePosition + signatureFragment.length <= MaxSignatureLength,
0482                  Error::SignatureTooLong);
0483     }
0484     if (!d->m_aggregateStack.empty()) {
0485         Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
0486         switch (aggregateInfo.aggregateType) {
0487         case BeginVariant:
0488             // arrays and variants may contain just one single complete type; note that this will
0489             // trigger only when not inside an aggregate inside the variant or (see below) array
0490             if (d->m_signaturePosition >= 1) {
0491                 VALID_IF(newState == EndVariant, Error::NotSingleCompleteTypeInVariant);
0492             }
0493             break;
0494         case BeginArray:
0495             if (d->m_signaturePosition >= aggregateInfo.arr.containedTypeBegin + 1
0496                 && newState != EndArray) {
0497                 // we are not at start of contained type's signature, the array is at top of stack
0498                 // -> we are at the end of the single complete type inside the array, start the next
0499                 // entry. TODO: check compatibility (essentially what's in the else branch below)
0500                 d->m_signaturePosition = aggregateInfo.arr.containedTypeBegin;
0501                 isWritingSignature = false;
0502             }
0503             break;
0504         case BeginDict:
0505             if (d->m_signaturePosition == aggregateInfo.arr.containedTypeBegin) {
0506 #ifdef WITH_DICT_ENTRY
0507                 if (aggregateInfo.arr.dictEntryState == Private::RequireBeginDictEntry) {
0508                     // This is only reached immediately after beginDict() so it's kinda wasteful, oh well.
0509                     VALID_IF(newState == BeginDictEntry, Error::MissingBeginDictEntry);
0510                     aggregateInfo.arr.dictEntryState = Private::InDictEntry;
0511                     m_state = DictKey;
0512                     return; // BeginDictEntry writes no data
0513                 }
0514 #endif
0515                 VALID_IF(isPrimitiveType || isStringType, Error::InvalidKeyTypeInDict);
0516             }
0517 #ifdef WITH_DICT_ENTRY
0518             // TODO test this part of the state machine
0519             if (d->m_signaturePosition >= aggregateInfo.arr.containedTypeBegin + 2) {
0520                 if (aggregateInfo.arr.dictEntryState == Private::RequireEndDictEntry) {
0521                     VALID_IF(newState == EndDictEntry, Error::MissingEndDictEntry);
0522                     aggregateInfo.arr.dictEntryState = Private::AfterEndDictEntry;
0523                     m_state = BeginDictEntry;
0524                     return; // EndDictEntry writes no data
0525                 } else {
0526                     // v should've been caught earlier
0527                     assert(aggregateInfo.arr.dictEntryState == Private::AfterEndDictEntry);
0528                     VALID_IF(newState == BeginDictEntry || newState == EndDict, Error::MissingBeginDictEntry);
0529                     // "fall through", the rest (another iteration or finish) is handled below
0530                 }
0531             } else if (d->m_signaturePosition >= aggregateInfo.arr.containedTypeBegin + 1) {
0532                 assert(aggregateInfo.arr.dictEntryState == Private::InDictEntry);
0533                 aggregateInfo.arr.dictEntryState = Private::RequireEndDictEntry;
0534                 // Setting EndDictEntry after writing a primitive type works fine, but setting it after
0535                 // ending another aggregate would be somewhat involved and need to happen somewhere
0536                 // else, so just don't do that. We still produce an error when endDictEntry() is not
0537                 // used correctly.
0538                 // m_state = EndDictEntry;
0539 
0540                 // continue and write the dict entry's value
0541             }
0542 #endif
0543             // first type has been checked already, second must be present (checked in EndDict
0544             // state handler). no third type allowed.
0545             if (d->m_signaturePosition >= aggregateInfo.arr.containedTypeBegin + 2
0546                 && newState != EndDict) {
0547                 // align to dict entry
0548                 d->alignData(StructAlignment);
0549                 d->m_signaturePosition = aggregateInfo.arr.containedTypeBegin;
0550                 isWritingSignature = false;
0551                 m_state = DictKey;
0552 #ifdef WITH_DICT_ENTRY
0553                 assert(newState == BeginDictEntry);
0554                 aggregateInfo.arr.dictEntryState = Private::InDictEntry;
0555                 return; // BeginDictEntry writes no data
0556 #endif
0557             }
0558 
0559             break;
0560         default:
0561             break;
0562         }
0563     }
0564 
0565     if (isWritingSignature) {
0566         // extend the signature
0567         for (uint32 i = 0; i < signatureFragment.length; i++) {
0568             d->m_signature.ptr[d->m_signaturePosition++] = signatureFragment.ptr[i];
0569         }
0570         d->m_signature.length += signatureFragment.length;
0571     } else {
0572         // Do not try to prevent several iterations through a nil array. Two reasons:
0573         // - We may be writing a nil array in the >1st iteration of a non-nil outer array.
0574         //   This would need to be distinguished from just iterating through a nil array
0575         //   several times. Which is well possible. We don't bother with that because...
0576         // - As a QtDBus unittest illustrates, somebody may choose to serialize a fixed length
0577         //   series of data elements as an array (instead of struct), so that a trivial
0578         //   serialization of such data just to fill in type information in an outer empty array
0579         //   would end up iterating through the inner, implicitly empty array several times.
0580         // All in all it is just not much of a benefit to be strict, so don't.
0581         //VALID_IF(likely(!d->m_nilArrayNesting), Error::ExtraIterationInEmptyArray);
0582 
0583         // signature must match first iteration (of an array/dict)
0584         VALID_IF(d->m_signaturePosition + signatureFragment.length <= d->m_signature.length,
0585                  Error::TypeMismatchInSubsequentArrayIteration);
0586         // TODO need to apply special checks for state changes with no explicit signature char?
0587         // (end of array, end of variant)
0588         for (uint32 i = 0; i < signatureFragment.length; i++) {
0589             VALID_IF(d->m_signature.ptr[d->m_signaturePosition++] == signatureFragment.ptr[i],
0590                      Error::TypeMismatchInSubsequentArrayIteration);
0591         }
0592     }
0593 
0594     if (isPrimitiveType) {
0595         doWritePrimitiveType(newState, alignment);
0596         return;
0597     }
0598     if (isStringType) {
0599         // In case of nil array, skip writing to make sure that the input string (which is explicitly
0600         // allowed to be garbage) is not validated and no wild pointer is dereferenced.
0601         if (likely(!d->m_nilArrayNesting)) {
0602             doWriteString(newState, alignment);
0603         } else {
0604             // The alignment of the first element in a nil array determines where array data starts,
0605             // which is needed to serialize the length correctly. Write the minimum to achieve that.
0606             // (The check to see if we're really at the first element is omitted - for performance
0607             // it's worth trying to add that check)
0608             d->alignData(alignment);
0609         }
0610         return;
0611     }
0612 
0613     Private::AggregateInfo aggregateInfo;
0614 
0615     switch (newState) {
0616     case BeginStruct:
0617         VALID_IF(d->m_nesting.beginParen(), Error::ExcessiveNesting);
0618         aggregateInfo.aggregateType = BeginStruct;
0619         aggregateInfo.sct.containedTypeBegin = d->m_signaturePosition;
0620         d->m_aggregateStack.push_back(aggregateInfo);
0621         d->alignData(alignment);
0622         break;
0623     case EndStruct:
0624         VALID_IF(!d->m_aggregateStack.empty(), Error::CannotEndStructHere);
0625         aggregateInfo = d->m_aggregateStack.back();
0626         VALID_IF(aggregateInfo.aggregateType == BeginStruct &&
0627                  d->m_signaturePosition > aggregateInfo.sct.containedTypeBegin + 1,
0628                  Error::EmptyStruct); // empty structs are not allowed
0629         d->m_nesting.endParen();
0630         d->m_aggregateStack.pop_back();
0631         break;
0632 
0633     case BeginVariant: {
0634         VALID_IF(d->m_nesting.beginVariant(), Error::ExcessiveNesting);
0635         aggregateInfo.aggregateType = BeginVariant;
0636 
0637         Private::VariantInfo &variantInfo = aggregateInfo.var;
0638         variantInfo.prevSignatureOffset = uint32(reinterpret_cast<byte *>(d->m_signature.ptr) - d->m_data);
0639         d->m_signature.ptr[-1] = byte(d->m_signature.length);
0640         variantInfo.prevSignaturePosition = d->m_signaturePosition;
0641 
0642         if (!d->insideVariant()) {
0643             d->m_dataPositionBeforeVariant = d->m_dataPosition;
0644         }
0645 
0646         d->m_aggregateStack.push_back(aggregateInfo);
0647 
0648         d->m_queuedData.reserve(16);
0649         d->m_queuedData.push_back(Private::QueuedDataInfo(1, Private::QueuedDataInfo::VariantSignature));
0650 
0651         const uint32 newDataPosition = d->m_dataPosition + Private::SignatureReservedSpace;
0652         d->reserveData(newDataPosition, &m_state);
0653         // allocate new signature in the data buffer, reserve one byte for length prefix
0654         d->m_signature.ptr = reinterpret_cast<char *>(d->m_data) + d->m_dataPosition + 1;
0655         d->m_signature.length = 0;
0656         d->m_signaturePosition = 0;
0657         d->m_dataPosition = newDataPosition;
0658         break; }
0659     case EndVariant: {
0660         VALID_IF(!d->m_aggregateStack.empty(), Error::CannotEndVariantHere);
0661         aggregateInfo = d->m_aggregateStack.back();
0662         VALID_IF(aggregateInfo.aggregateType == BeginVariant, Error::CannotEndVariantHere);
0663         d->m_nesting.endVariant();
0664         if (likely(!d->m_nilArrayNesting)) {
0665             // Empty variants are not allowed. As an exception, in nil arrays they are
0666             // allowed for writing a type signature like "av" in the shortest possible way.
0667             // No use adding stuff when it's not required or even possible.
0668             VALID_IF(d->m_signaturePosition > 0, Error::EmptyVariant);
0669             assert(d->m_signaturePosition <= MaxSignatureLength); // should have been caught earlier
0670         }
0671         d->m_signature.ptr[-1] = byte(d->m_signaturePosition);
0672 
0673         Private::VariantInfo &variantInfo = aggregateInfo.var;
0674         d->m_signature.ptr = reinterpret_cast<char *>(d->m_data) + variantInfo.prevSignatureOffset;
0675         d->m_signature.length = d->m_signature.ptr[-1];
0676         d->m_signaturePosition = variantInfo.prevSignaturePosition;
0677         d->m_aggregateStack.pop_back();
0678 
0679         // if not in any variant anymore, flush queued data and resume unqueued operation
0680         if (d->m_signature.ptr == reinterpret_cast<char *>(d->m_data) + 1) {
0681             flushQueuedData();
0682         }
0683 
0684         break; }
0685 
0686     case BeginDict:
0687     case BeginArray: {
0688         VALID_IF(d->m_nesting.beginArray(), Error::ExcessiveNesting);
0689         if (newState == BeginDict) {
0690             // not re-opened before each element: there is no observable difference for clients
0691             VALID_IF(d->m_nesting.beginParen(), Error::ExcessiveNesting);
0692         }
0693 
0694         aggregateInfo.aggregateType = newState;
0695         aggregateInfo.arr.containedTypeBegin = d->m_signaturePosition;
0696 
0697         zeroPad(d->m_data, sizeof(uint32), &d->m_dataPosition);
0698         basic::writeUint32(d->m_data + d->m_dataPosition, 0);
0699         aggregateInfo.arr.lengthFieldPosition = d->m_dataPosition;
0700         d->m_dataPosition += sizeof(uint32);
0701         d->maybeQueueData(sizeof(uint32), Private::QueuedDataInfo::ArrayLengthField);
0702 
0703         if (newState == BeginDict) {
0704             d->alignData(StructAlignment);
0705 #ifdef WITH_DICT_ENTRY
0706             m_state = BeginDictEntry;
0707             aggregateInfo.arr.dictEntryState = Private::RequireBeginDictEntry;
0708 #else
0709             m_state = DictKey;
0710 #endif
0711         }
0712 
0713         d->m_aggregateStack.push_back(aggregateInfo);
0714         break; }
0715     case EndDict:
0716     case EndArray: {
0717         const bool isDict = newState == EndDict;
0718 
0719         VALID_IF(!d->m_aggregateStack.empty(), Error::CannotEndArrayHere);
0720         aggregateInfo = d->m_aggregateStack.back();
0721         VALID_IF(aggregateInfo.aggregateType == (isDict ? BeginDict : BeginArray),
0722                  Error::CannotEndArrayOrDictHere);
0723         VALID_IF(d->m_signaturePosition >= aggregateInfo.arr.containedTypeBegin + (isDict ? 3 : 1),
0724                  Error::TooFewTypesInArrayOrDict);
0725         if (isDict) {
0726             d->m_nesting.endParen();
0727         }
0728         d->m_nesting.endArray();
0729 
0730         // array data starts (and in empty arrays ends) at the first array element position *after alignment*
0731         const uint32 contentAlign = isDict ? uint32(StructAlignment)
0732                         : typeInfo(d->m_signature.ptr[aggregateInfo.arr.containedTypeBegin]).alignment;
0733         const uint32 arrayDataStart = align(aggregateInfo.arr.lengthFieldPosition + sizeof(uint32),
0734                                             contentAlign);
0735 
0736         if (unlikely(d->m_nilArrayNesting)) {
0737             if (--d->m_nilArrayNesting == 0) {
0738                 d->m_dataPosition = arrayDataStart;
0739                 if (d->insideVariant()) {
0740                     assert(d->m_queuedData.begin() + d->m_dataElementsCountBeforeNilArray <=
0741                            d->m_queuedData.end());
0742                     d->m_queuedData.erase(d->m_queuedData.begin() + d->m_dataElementsCountBeforeNilArray,
0743                                           d->m_queuedData.end());
0744                     assert((d->m_queuedData.end() - 2)->size == Private::QueuedDataInfo::ArrayLengthField);
0745                     // align, but don't have actual data for the first element
0746                     d->m_queuedData.back().size = 0;
0747                 }
0748             }
0749         }
0750 
0751         // (arrange to) patch in the array length now that it is known
0752         if (d->insideVariant()) {
0753             d->m_queuedData.push_back(Private::QueuedDataInfo(1, Private::QueuedDataInfo::ArrayLengthEndMark));
0754         } else {
0755             const uint32 arrayLength = d->m_dataPosition - arrayDataStart;
0756             VALID_IF(arrayLength <= Arguments::MaxArrayLength, Error::ArrayOrDictTooLong);
0757             basic::writeUint32(d->m_data + aggregateInfo.arr.lengthFieldPosition, arrayLength);
0758         }
0759         d->m_aggregateStack.pop_back();
0760         break; }
0761 #ifdef WITH_DICT_ENTRY
0762     case BeginDictEntry:
0763     case EndDictEntry:
0764         break;
0765 #endif
0766     default:
0767         VALID_IF(false, Error::InvalidType);
0768         break;
0769     }
0770 }
0771 
0772 void Arguments::Writer::beginArrayOrDict(IoState beginWhat, ArrayOption option)
0773 {
0774     assert(beginWhat == BeginArray || beginWhat == BeginDict);
0775     if (unlikely(option == RestartEmptyArrayToWriteTypes)) {
0776         if (!d->m_aggregateStack.empty()) {
0777             Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
0778             if (aggregateInfo.aggregateType == beginWhat) {
0779                 // No writes to the array or dict may have occurred yet
0780 
0781                 if (d->m_signaturePosition == aggregateInfo.arr.containedTypeBegin) {
0782                     // Fix up state as if beginArray/Dict() had been called with WriteTypesOfEmptyArray
0783                     // in the first place. After that small fixup we're done and return.
0784                     // The code is a slightly modified version of code below under: if (isEmpty) {
0785                     if (!d->m_nilArrayNesting) {
0786                         d->m_nilArrayNesting = 1;
0787                         d->m_dataElementsCountBeforeNilArray = d->m_queuedData.size() + 2; // +2 as below
0788                         // Now correct for the elements already added in advanceState() with BeginArray / BeginDict
0789                         d->m_dataElementsCountBeforeNilArray -= (beginWhat == BeginDict) ? 2 : 1;
0790                     } else {
0791                         // The array may be implicitly nil (so our poor API client doesn't notice) because
0792                         // an array below in the aggregate stack is nil, so just allow this as a no-op.
0793                     }
0794                     return;
0795                 }
0796             }
0797         }
0798         VALID_IF(false, Error::InvalidStateToRestartEmptyArray);
0799     }
0800 
0801     const bool isEmpty = (option != NonEmptyArray) || d->m_nilArrayNesting;
0802     if (isEmpty) {
0803         if (!d->m_nilArrayNesting++) {
0804             // For simplictiy and performance in the fast path, we keep storing the data chunks and any
0805             // variant signatures written inside an empty array. When we close the array, though, we
0806             // throw away all that data and signatures and keep only changes in the signature containing
0807             // the topmost empty array.
0808             // +2 -> keep ArrayLengthField, and first data element for alignment purposes
0809             d->m_dataElementsCountBeforeNilArray = d->m_queuedData.size() + 2;
0810         }
0811     }
0812     if (beginWhat == BeginArray) {
0813         advanceState(cstring("a", strlen("a")), beginWhat);
0814     } else {
0815         advanceState(cstring("a{", strlen("a{")), beginWhat);
0816     }
0817 }
0818 
0819 void Arguments::Writer::beginArray(ArrayOption option)
0820 {
0821     beginArrayOrDict(BeginArray, option);
0822 }
0823 
0824 void Arguments::Writer::endArray()
0825 {
0826     advanceState(cstring(), EndArray);
0827 }
0828 
0829 void Arguments::Writer::beginDict(ArrayOption option)
0830 {
0831     beginArrayOrDict(BeginDict, option);
0832 }
0833 
0834 void Arguments::Writer::endDict()
0835 {
0836     advanceState(cstring("}", strlen("}")), EndDict);
0837 }
0838 
0839 #ifdef WITH_DICT_ENTRY
0840 void Arguments::Writer::beginDictEntry()
0841 {
0842     VALID_IF(m_state == BeginDictEntry, Error::MisplacedBeginDictEntry);
0843     advanceState(cstring(), BeginDictEntry);
0844 }
0845 
0846 void Arguments::Writer::endDictEntry()
0847 {
0848     if (!d->m_aggregateStack.empty()) {
0849         Private::AggregateInfo &aggregateInfo = d->m_aggregateStack.back();
0850         if (aggregateInfo.aggregateType == BeginDict
0851             && aggregateInfo.arr.dictEntryState == Private::RequireEndDictEntry) {
0852             advanceState(cstring(), EndDictEntry);
0853             return;
0854         }
0855     }
0856     VALID_IF(false, Error::MisplacedEndDictEntry);
0857 }
0858 #endif
0859 
0860 void Arguments::Writer::beginStruct()
0861 {
0862     advanceState(cstring("(", strlen("(")), BeginStruct);
0863 }
0864 
0865 void Arguments::Writer::endStruct()
0866 {
0867     advanceState(cstring(")", strlen(")")), EndStruct);
0868 }
0869 
0870 void Arguments::Writer::beginVariant()
0871 {
0872     advanceState(cstring("v", strlen("v")), BeginVariant);
0873 }
0874 
0875 void Arguments::Writer::endVariant()
0876 {
0877     advanceState(cstring(), EndVariant);
0878 }
0879 
0880 void Arguments::Writer::writeVariantForMessageHeader(char sig)
0881 {
0882     // Note: the signature we're working with there is a(yv)
0883     // If we know that and can trust the client, this can be very easy and fast...
0884     d->m_signature.ptr[3] = 'v';
0885     d->m_signature.length = 4;
0886     d->m_signaturePosition = 4;
0887 
0888     d->reserveData(d->m_dataPosition, &m_state);
0889     d->m_data[d->m_dataPosition++] = 1;
0890     d->m_data[d->m_dataPosition++] = sig;
0891     d->m_data[d->m_dataPosition++] = 0;
0892 }
0893 
0894 void Arguments::Writer::fixupAfterWriteVariantForMessageHeader()
0895 {
0896     // We just wrote something to the main signature when we shouldn't have.
0897     d->m_signature.length = 4;
0898     d->m_signaturePosition = 4;
0899 }
0900 
0901 static char letterForPrimitiveIoState(Arguments::IoState ios)
0902 {
0903     if (ios < Arguments::Boolean || ios > Arguments::Double) {
0904         return  'c'; // a known invalid letter that won't trip up typeInfo()
0905     }
0906     static const char letters[] = {
0907         'b', // Boolean
0908         'y', // Byte
0909         'n', // Int16
0910         'q', // Uint16
0911         'i', // Int32
0912         'u', // Uint32
0913         'x', // Int64
0914         't', // Uint64
0915         'd'  // Double
0916     };
0917     return letters[size_t(ios) - size_t(Arguments::Boolean)]; // TODO do we need the casts?
0918 }
0919 
0920 void Arguments::Writer::writePrimitiveArray(IoState type, chunk data)
0921 {
0922     const char letterCode = letterForPrimitiveIoState(type);
0923     if (letterCode == 'c') {
0924         m_state = InvalidData;
0925         d->m_error.setCode(Error::NotPrimitiveType);
0926         return;
0927     }
0928     if (data.length > Arguments::MaxArrayLength) {
0929         m_state = InvalidData;
0930         d->m_error.setCode(Error::ArrayOrDictTooLong);
0931         return;
0932     }
0933 
0934     const TypeInfo elementType = typeInfo(letterCode);
0935     if (!isAligned(data.length, elementType.alignment)) {
0936         m_state = InvalidData;
0937         d->m_error.setCode(Error::CannotEndArrayOrDictHere);
0938         return;
0939     }
0940 
0941     beginArray(data.length ? NonEmptyArray : WriteTypesOfEmptyArray);
0942 
0943     // dummy write to write the signature...
0944     m_u.Uint64 = 0;
0945     advanceState(cstring(&letterCode, /*length*/ 1), elementType.state());
0946 
0947     if (!data.length) {
0948         // oh! a nil array (which is valid)
0949         endArray();
0950         return;
0951     }
0952 
0953     // undo the dummy write (except for the preceding alignment bytes, if any)
0954     d->m_dataPosition -= elementType.alignment;
0955     if (d->insideVariant()) {
0956         d->m_queuedData.pop_back();
0957         d->m_queuedData.push_back(Private::QueuedDataInfo(elementType.alignment, 0));
0958     }
0959 
0960     // append the payload
0961     d->reserveData(d->m_dataPosition + data.length, &m_state);
0962     d->appendBulkData(data);
0963 
0964     endArray();
0965 }
0966 
0967 Arguments Arguments::Writer::finish()
0968 {
0969     // what needs to happen here:
0970     // - check if the message can be closed - basically the aggregate stack must be empty
0971     // - close the signature by adding the terminating null
0972 
0973     Arguments args;
0974 
0975     if (m_state == InvalidData) {
0976         args.d->m_error = d->m_error;
0977         return args; // heavily relying on NRVO in all returns here!
0978     }
0979     if (d->m_nesting.total() != 0) {
0980         m_state = InvalidData;
0981         d->m_error.setCode(Error::CannotEndArgumentsHere);
0982         args.d->m_error = d->m_error;
0983         return args;
0984     }
0985     assert(!d->m_nilArrayNesting);
0986     assert(!d->insideVariant());
0987 
0988     assert(d->m_signaturePosition <= MaxSignatureLength); // this should have been caught before
0989     assert(d->m_signature.ptr == reinterpret_cast<char *>(d->m_data) + 1);
0990 
0991     // Note that we still keep the full SignatureReservedSpace for the main signature, which means
0992     // less copying around to shrink the gap between signature and data, but also wastes an enormous
0993     // amount of space (relative to the possible minimum) in some cases. It should not be a big space
0994     // problem because normally not many D-Bus Message / Arguments instances exist at the same time.
0995 
0996     d->m_signature.length = d->m_signaturePosition;
0997     d->m_signature.ptr[d->m_signature.length] = '\0';
0998 
0999     // OK, so this length check is more of a sanity check. The actual limit limits the size of the
1000     // full message. Here we take the size of the "payload" and don't add the size of the signature -
1001     // why bother doing it accurately when the real check with full information comes later anyway?
1002     bool success = true;
1003     const uint32 dataSize = d->m_dataPosition - Private::SignatureReservedSpace;
1004     if (success && dataSize > Arguments::MaxMessageLength) {
1005         success = false;
1006         d->m_error.setCode(Error::ArgumentsTooLong);
1007     }
1008 
1009     if (!dataSize || !success) {
1010         args.d->m_memOwnership = nullptr;
1011         args.d->m_signature = cstring();
1012         args.d->m_data = chunk();
1013     } else {
1014         args.d->m_memOwnership = d->m_data;
1015         args.d->m_signature = cstring(d->m_data + 1 /* w/o length prefix */, d->m_signature.length);
1016         args.d->m_data = chunk(d->m_data + Private::SignatureReservedSpace, dataSize);
1017         d->m_data = nullptr; // now owned by Arguments and later freed there
1018     }
1019 
1020     if (success) {
1021         args.d->m_fileDescriptors = std::move(d->m_fileDescriptors);
1022         m_state = Finished;
1023     } else {
1024         m_state = InvalidData;
1025         args.d->m_error = d->m_error;
1026     }
1027     return args;
1028 }
1029 
1030 struct ArrayLengthField
1031 {
1032     uint32 lengthFieldPosition;
1033     uint32 dataStartPosition;
1034 };
1035 
1036 void Arguments::Writer::flushQueuedData()
1037 {
1038     const uint32 count = d->m_queuedData.size();
1039     assert(count); // just don't call this method otherwise!
1040 
1041     // Note: if one of signature or data is nonempty, the other must also be nonempty.
1042     // Even "empty" things like empty arrays or null strings have a size field, in that case
1043     // (for all(?) types) of value zero.
1044 
1045     // Copy the signature and main data (thus the whole contents) into one allocated block,
1046     // which is good to have for performance and simplicity reasons.
1047 
1048     // The maximum alignment blowup for naturally aligned types is just less than a factor of 2.
1049     // Structs and dict entries are always 8 byte aligned so they add a maximum blowup of 7 bytes
1050     // each (when they contain a byte).
1051     // Those estimates are very conservative (but easy!), so some space optimization is possible.
1052 
1053     uint32 inPos = d->m_dataPositionBeforeVariant;
1054     uint32 outPos = d->m_dataPositionBeforeVariant;
1055     byte *const buffer = d->m_data;
1056 
1057     std::vector<ArrayLengthField> lengthFieldStack;
1058 
1059     for (uint32 i = 0; i < count; i++) {
1060         const Private::QueuedDataInfo ei = d->m_queuedData[i];
1061         switch (ei.size) {
1062         case 0: {
1063                 inPos = align(inPos, ei.alignment());
1064                 zeroPad(buffer, ei.alignment(), &outPos);
1065             }
1066             break;
1067         default: {
1068                 assert(ei.size && ei.size <= Private::QueuedDataInfo::LargestSize);
1069                 inPos = align(inPos, ei.alignment());
1070                 zeroPad(buffer, ei.alignment(), &outPos);
1071                 // copy data chunk
1072                 memmove(buffer + outPos, buffer + inPos, ei.size);
1073                 inPos += ei.size;
1074                 outPos += ei.size;
1075             }
1076             break;
1077         case Private::QueuedDataInfo::ArrayLengthField: {
1078                 //   start of an array
1079                 // alignment padding before length field
1080                 inPos = align(inPos, ei.alignment());
1081                 zeroPad(buffer, ei.alignment(), &outPos);
1082                 // reserve length field
1083                 ArrayLengthField al;
1084                 al.lengthFieldPosition = outPos;
1085                 inPos += sizeof(uint32);
1086                 outPos += sizeof(uint32);
1087                 // alignment padding before first array element
1088                 assert(i + 1 < d->m_queuedData.size());
1089                 const uint32 contentsAlignment = d->m_queuedData[i + 1].alignment();
1090                 inPos = align(inPos, contentsAlignment);
1091                 zeroPad(buffer, contentsAlignment, &outPos);
1092                 // array data starts at the first array element position after alignment
1093                 al.dataStartPosition = outPos;
1094                 lengthFieldStack.push_back(al);
1095             }
1096             break;
1097         case Private::QueuedDataInfo::ArrayLengthEndMark: {
1098                 //   end of an array
1099                 // just put the now known array length in front of the array
1100                 const ArrayLengthField al = lengthFieldStack.back();
1101                 const uint32 arrayLength = outPos - al.dataStartPosition;
1102                 if (arrayLength > Arguments::MaxArrayLength) {
1103                     m_state = InvalidData;
1104                     d->m_error.setCode(Error::ArrayOrDictTooLong);
1105                     i = count + 1; // break out of the loop
1106                     break;
1107                 }
1108                 basic::writeUint32(buffer + al.lengthFieldPosition, arrayLength);
1109                 lengthFieldStack.pop_back();
1110             }
1111             break;
1112         case Private::QueuedDataInfo::VariantSignature: {
1113                 // move the signature and add its null terminator
1114                 const uint32 length = buffer[inPos] + 1; // + length prefix
1115                 memmove(buffer + outPos, buffer + inPos, length);
1116                 buffer[outPos + length] = '\0';
1117                 outPos += length + 1; // + null terminator
1118                 inPos += Private::Private::SignatureReservedSpace;
1119             }
1120             break;
1121         }
1122     }
1123     assert(m_state == InvalidData || lengthFieldStack.empty());
1124 
1125     d->m_dataPosition = outPos;
1126     d->m_queuedData.clear();
1127 }
1128 
1129 std::vector<Arguments::IoState> Arguments::Writer::aggregateStack() const
1130 {
1131     std::vector<IoState> ret;
1132     ret.reserve(d->m_aggregateStack.size());
1133     for (Private::AggregateInfo &aggregate : d->m_aggregateStack) {
1134         ret.push_back(aggregate.aggregateType);
1135     }
1136     return ret;
1137 }
1138 
1139 uint32 Arguments::Writer::aggregateDepth() const
1140 {
1141     return d->m_aggregateStack.size();
1142 }
1143 
1144 Arguments::IoState Arguments::Writer::currentAggregate() const
1145 {
1146     if (d->m_aggregateStack.empty()) {
1147         return NotStarted;
1148     }
1149     return d->m_aggregateStack.back().aggregateType;
1150 }
1151 
1152 chunk Arguments::Writer::peekSerializedData() const
1153 {
1154     chunk ret;
1155     if (isValid() && m_state != InvalidData && d->m_nesting.total() == 0) {
1156         ret.ptr = d->m_data + Private::SignatureReservedSpace;
1157         ret.length = d->m_dataPosition - Private::SignatureReservedSpace;
1158     }
1159     return ret;
1160 }
1161 
1162 const std::vector<int> &Arguments::Writer::fileDescriptors() const
1163 {
1164     return d->m_fileDescriptors;
1165 }
1166 
1167 void Arguments::Writer::writeBoolean(bool b)
1168 {
1169     m_u.Boolean = b;
1170     advanceState(cstring("b", strlen("b")), Boolean);
1171 }
1172 
1173 void Arguments::Writer::writeByte(byte b)
1174 {
1175     m_u.Byte = b;
1176     advanceState(cstring("y", strlen("y")), Byte);
1177 }
1178 
1179 void Arguments::Writer::writeInt16(int16 i)
1180 {
1181     m_u.Int16 = i;
1182     advanceState(cstring("n", strlen("n")), Int16);
1183 }
1184 
1185 void Arguments::Writer::writeUint16(uint16 i)
1186 {
1187     m_u.Uint16 = i;
1188     advanceState(cstring("q", strlen("q")), Uint16);
1189 }
1190 
1191 void Arguments::Writer::writeInt32(int32 i)
1192 {
1193     m_u.Int32 = i;
1194     advanceState(cstring("i", strlen("i")), Int32);
1195 }
1196 
1197 void Arguments::Writer::writeUint32(uint32 i)
1198 {
1199     m_u.Uint32 = i;
1200     advanceState(cstring("u", strlen("u")), Uint32);
1201 }
1202 
1203 void Arguments::Writer::writeInt64(int64 i)
1204 {
1205     m_u.Int64 = i;
1206     advanceState(cstring("x", strlen("x")), Int64);
1207 }
1208 
1209 void Arguments::Writer::writeUint64(uint64 i)
1210 {
1211     m_u.Uint64 = i;
1212     advanceState(cstring("t", strlen("t")), Uint64);
1213 }
1214 
1215 void Arguments::Writer::writeDouble(double d)
1216 {
1217     m_u.Double = d;
1218     advanceState(cstring("d", strlen("d")), Double);
1219 }
1220 
1221 void Arguments::Writer::writeString(cstring string)
1222 {
1223     m_u.String.ptr = string.ptr;
1224     m_u.String.length = string.length;
1225     advanceState(cstring("s", strlen("s")), String);
1226 }
1227 
1228 void Arguments::Writer::writeObjectPath(cstring objectPath)
1229 {
1230     m_u.String.ptr = objectPath.ptr;
1231     m_u.String.length = objectPath.length;
1232     advanceState(cstring("o", strlen("o")), ObjectPath);
1233 }
1234 
1235 void Arguments::Writer::writeSignature(cstring signature)
1236 {
1237     m_u.String.ptr = signature.ptr;
1238     m_u.String.length = signature.length;
1239     advanceState(cstring("g", strlen("g")), Signature);
1240 }
1241 
1242 void Arguments::Writer::writeUnixFd(int32 fd)
1243 {
1244     m_u.Int32 = fd;
1245     advanceState(cstring("h", strlen("h")), UnixFd);
1246 }