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 }