Warning, file /sdk/dferry/serialization/arguments.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LGPL. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 0019 Alternatively, this file is available under the Mozilla Public License 0020 Version 1.1. You may obtain a copy of the License at 0021 http://www.mozilla.org/MPL/ 0022 */ 0023 0024 #include "arguments.h" 0025 #include "arguments_p.h" 0026 0027 #include "basictypeio.h" 0028 #include "error.h" 0029 #include "malloccache.h" 0030 #include "message.h" 0031 #include "platform.h" 0032 #include "stringtools.h" 0033 0034 #include <algorithm> 0035 #include <cassert> 0036 #include <cstddef> 0037 #include <cstdlib> 0038 #include <cstring> 0039 #include <sstream> 0040 0041 const TypeInfo &typeInfo(char letterCode) 0042 { 0043 assert(letterCode >= '('); 0044 static const TypeInfo low[2] = { 0045 { Arguments::BeginStruct, 8, false, false }, // ( 0046 { Arguments::EndStruct, 1, false, false } // ) 0047 }; 0048 if (letterCode <= ')') { 0049 return low[letterCode - '(']; 0050 } 0051 assert(letterCode >= 'a' && letterCode <= '}'); 0052 // entries for invalid letters are designed to be as inert as possible in the code using the data, 0053 // which may make it possible to catch errors at a common point with less special case code. 0054 static const TypeInfo high['}' - 'a' + 1] = { 0055 { Arguments::BeginArray, 4, false, false }, // a 0056 { Arguments::Boolean, 4, true, false }, // b 0057 { Arguments::InvalidData, 1, true, false }, // c 0058 { Arguments::Double, 8, true, false }, // d 0059 { Arguments::InvalidData, 1, true, false }, // e 0060 { Arguments::InvalidData, 1, true, false }, // f 0061 { Arguments::Signature, 1, false, true }, // g 0062 { Arguments::UnixFd, 4, true, false }, // h 0063 { Arguments::Int32, 4, true, false }, // i 0064 { Arguments::InvalidData, 1, true, false }, // j 0065 { Arguments::InvalidData, 1, true, false }, // k 0066 { Arguments::InvalidData, 1, true, false }, // l 0067 { Arguments::InvalidData, 1, true, false }, // m 0068 { Arguments::Int16, 2, true, false }, // n 0069 { Arguments::ObjectPath, 4, false, true }, // o 0070 { Arguments::InvalidData, 1, true, false }, // p 0071 { Arguments::Uint16, 2, true, false }, // q 0072 { Arguments::InvalidData, 1, true, false }, // r 0073 { Arguments::String, 4, false, true }, // s 0074 { Arguments::Uint64, 8, true, false }, // t 0075 { Arguments::Uint32, 4, true, false }, // u 0076 { Arguments::BeginVariant, 1, false, false }, // v 0077 { Arguments::InvalidData, 1, true, false }, // w 0078 { Arguments::Int64, 8, true, false }, // x 0079 { Arguments::Byte, 1, true, false }, // y 0080 { Arguments::InvalidData, 1, true, false }, // z 0081 { Arguments::BeginDict, 8, false, false }, // { 0082 { Arguments::InvalidData, 1, true, false }, // | 0083 { Arguments::EndDict, 1, false, false } // } 0084 }; 0085 return high[letterCode - 'a']; 0086 } 0087 0088 cstring printableState(Arguments::IoState state) 0089 { 0090 if (state < Arguments::NotStarted || state >= Arguments::LastState) { 0091 return cstring(); 0092 } 0093 static const char *strings[Arguments::LastState] = { 0094 "NotStarted", 0095 "Finished", 0096 "NeedMoreData", 0097 "InvalidData", 0098 "AnyData", 0099 "DictKey", 0100 "BeginArray", 0101 "EndArray", 0102 "BeginDict", 0103 "EndDict", 0104 "BeginStruct", 0105 "EndStruct", 0106 "BeginVariant", 0107 "EndVariant", 0108 "Boolean", 0109 "Byte", 0110 "Int16", 0111 "Uint16", 0112 "Int32", 0113 "Uint32", 0114 "Int64", 0115 "Uint64", 0116 "Double", 0117 "String", 0118 "ObjectPath", 0119 "Signature", 0120 "UnixFd" 0121 #ifdef WITH_DICT_ENTRY 0122 , 0123 "BeginDictEntry", 0124 "EndDictEntry" 0125 #endif 0126 }; 0127 return cstring(strings[state]); 0128 } 0129 0130 // When using this to iterate over the reader, it will make an exact copy using the Writer. 0131 // You need to do something only in states where something special should happen. 0132 // To check errors, "simply" (sorry!) check the reader->state() and writer()->state(). 0133 // Note that you don't have to check the state before each element, it is fine to call 0134 // read / write functions in error state, including with garbage data from the possibly 0135 // invalid reader, and the reader / writer state will remain frozen in the state in which 0136 // the first error occurred 0137 // TODO: that text above belongs into a "Reader and Writer state / errors" explanation of the docs 0138 0139 // static 0140 void Arguments::copyOneElement(Arguments::Reader *reader, Arguments::Writer *writer) 0141 { 0142 switch(reader->state()) { 0143 case Arguments::BeginStruct: 0144 reader->beginStruct(); 0145 writer->beginStruct(); 0146 break; 0147 case Arguments::EndStruct: 0148 reader->endStruct(); 0149 writer->endStruct(); 0150 break; 0151 case Arguments::BeginVariant: 0152 reader->beginVariant(); 0153 writer->beginVariant(); 0154 break; 0155 case Arguments::EndVariant: 0156 reader->endVariant(); 0157 writer->endVariant(); 0158 break; 0159 case Arguments::BeginArray: { 0160 // Application note: to avoid handling arrays as primitive (where applicable), just don't 0161 // call this function in BeginArray state and do as in the else case. 0162 const Arguments::IoState primitiveType = reader->peekPrimitiveArray(); 0163 if (primitiveType != BeginArray) { // InvalidData can't happen because the state *is* BeginArray 0164 const std::pair<Arguments::IoState, chunk> arrayData = reader->readPrimitiveArray(); 0165 writer->writePrimitiveArray(arrayData.first, arrayData.second); 0166 } else { 0167 const bool hasData = reader->beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); 0168 writer->beginArray(hasData ? Arguments::Writer::NonEmptyArray 0169 : Arguments::Writer::WriteTypesOfEmptyArray); 0170 } 0171 break; } 0172 case Arguments::EndArray: 0173 reader->endArray(); 0174 writer->endArray(); 0175 break; 0176 case Arguments::BeginDict: { 0177 const bool hasData = reader->beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty); 0178 writer->beginDict(hasData ? Arguments::Writer::NonEmptyArray 0179 : Arguments::Writer::WriteTypesOfEmptyArray); 0180 break; } 0181 case Arguments::EndDict: 0182 reader->endDict(); 0183 writer->endDict(); 0184 break; 0185 #ifdef WITH_DICT_ENTRY 0186 case Arguments::BeginDictEntry: 0187 reader->beginDictEntry(); 0188 writer->beginDictEntry(); 0189 break; 0190 case Arguments::EndDictEntry: 0191 reader->endDictEntry(); 0192 writer->endDictEntry(); 0193 break; 0194 #endif 0195 case Arguments::Byte: 0196 writer->writeByte(reader->readByte()); 0197 break; 0198 case Arguments::Boolean: 0199 writer->writeBoolean(reader->readBoolean()); 0200 break; 0201 case Arguments::Int16: 0202 writer->writeInt16(reader->readInt16()); 0203 break; 0204 case Arguments::Uint16: 0205 writer->writeUint16(reader->readUint16()); 0206 break; 0207 case Arguments::Int32: 0208 writer->writeInt32(reader->readInt32()); 0209 break; 0210 case Arguments::Uint32: 0211 writer->writeUint32(reader->readUint32()); 0212 break; 0213 case Arguments::Int64: 0214 writer->writeInt64(reader->readInt64()); 0215 break; 0216 case Arguments::Uint64: 0217 writer->writeUint64(reader->readUint64()); 0218 break; 0219 case Arguments::Double: 0220 writer->writeDouble(reader->readDouble()); 0221 break; 0222 case Arguments::String: { 0223 const cstring s = reader->readString(); 0224 writer->writeString(s); 0225 break; } 0226 case Arguments::ObjectPath: { 0227 const cstring objectPath = reader->readObjectPath(); 0228 writer->writeObjectPath(objectPath); 0229 break; } 0230 case Arguments::Signature: { 0231 const cstring signature = reader->readSignature(); 0232 writer->writeSignature(signature); 0233 break; } 0234 case Arguments::UnixFd: 0235 writer->writeUnixFd(reader->readUnixFd()); 0236 break; 0237 // special cases follow 0238 case Arguments::Finished: 0239 break; // You *probably* want to handle that one in the caller, but you don't have to 0240 case Arguments::NeedMoreData: 0241 break; // No way to handle that one here 0242 default: 0243 break; // dito 0244 } 0245 } 0246 0247 thread_local static MallocCache<sizeof(Arguments::Private), 4> allocCache; 0248 0249 Arguments::Private::Private(const Private &other) 0250 { 0251 initFrom(other); 0252 } 0253 0254 Arguments::Private &Arguments::Private::operator=(const Private &other) 0255 { 0256 if (this != &other) { 0257 initFrom(other); 0258 } 0259 return *this; 0260 } 0261 0262 void Arguments::Private::initFrom(const Private &other) 0263 { 0264 m_isByteSwapped = other.m_isByteSwapped; 0265 0266 // make a deep copy 0267 // use only one malloced block for signature and main data - this saves one malloc and free 0268 // and also saves a pointer 0269 // (if it weren't for the Arguments(..., cstring signature, chunk data, ...) constructor 0270 // we could save more size, and it would be very ugly, if we stored m_signature and m_data 0271 // as offsets to m_memOwnership) 0272 m_memOwnership = nullptr; 0273 m_signature.length = other.m_signature.length; 0274 m_data.length = other.m_data.length; 0275 0276 m_fileDescriptors = other.m_fileDescriptors; 0277 m_error = other.m_error; 0278 0279 const uint32 alignedSigLength = other.m_signature.length ? align(other.m_signature.length + 1, 8) : 0; 0280 const uint32 fullLength = alignedSigLength + other.m_data.length; 0281 0282 if (fullLength != 0) { 0283 // deep copy if there is any data 0284 m_memOwnership = reinterpret_cast<byte *>(malloc(fullLength)); 0285 0286 m_signature.ptr = reinterpret_cast<char *>(m_memOwnership); 0287 memcpy(m_signature.ptr, other.m_signature.ptr, other.m_signature.length + 1); 0288 uint32 bufferPos = other.m_signature.length + 1; 0289 zeroPad(reinterpret_cast<byte *>(m_signature.ptr), 8, &bufferPos); 0290 assert(bufferPos == alignedSigLength); 0291 0292 if (other.m_data.length) { 0293 m_data.ptr = m_memOwnership + alignedSigLength; 0294 memcpy(m_data.ptr, other.m_data.ptr, other.m_data.length); 0295 } else { 0296 m_data.ptr = nullptr; 0297 } 0298 } else { 0299 m_signature.ptr = nullptr; 0300 m_data.ptr = nullptr; 0301 } 0302 } 0303 0304 Arguments::Private::~Private() 0305 { 0306 if (m_memOwnership) { 0307 free(m_memOwnership); 0308 } 0309 } 0310 0311 Arguments::Arguments() 0312 : d(new(allocCache.allocate()) Private) 0313 { 0314 } 0315 0316 Arguments::Arguments(byte *memOwnership, cstring signature, chunk data, bool isByteSwapped) 0317 : d(new(allocCache.allocate()) Private) 0318 { 0319 d->m_isByteSwapped = isByteSwapped; 0320 d->m_memOwnership = memOwnership; 0321 d->m_signature = signature; 0322 d->m_data = data; 0323 } 0324 0325 Arguments::Arguments(byte *memOwnership, cstring signature, chunk data, 0326 std::vector<int> fileDescriptors, bool isByteSwapped) 0327 : d(new(allocCache.allocate()) Private) 0328 { 0329 d->m_isByteSwapped = isByteSwapped; 0330 d->m_memOwnership = memOwnership; 0331 d->m_signature = signature; 0332 d->m_data = data; 0333 d->m_fileDescriptors = std::move(fileDescriptors); 0334 } 0335 0336 Arguments::Arguments(Arguments &&other) 0337 : d(other.d) 0338 { 0339 other.d = nullptr; 0340 } 0341 0342 Arguments &Arguments::operator=(Arguments &&other) 0343 { 0344 Arguments temp(std::move(other)); 0345 std::swap(d, temp.d); 0346 return *this; 0347 } 0348 0349 Arguments::Arguments(const Arguments &other) 0350 : d(nullptr) 0351 { 0352 if (other.d) { 0353 d = new(allocCache.allocate()) Private(*other.d); 0354 } 0355 } 0356 0357 Arguments &Arguments::operator=(const Arguments &other) 0358 { 0359 if (d && other.d) { 0360 *d = *other.d; 0361 } else { 0362 Arguments temp(other); 0363 std::swap(d, temp.d); 0364 } 0365 return *this; 0366 } 0367 0368 Arguments::~Arguments() 0369 { 0370 if (d) { 0371 d->~Private(); 0372 allocCache.free(d); 0373 d = nullptr; 0374 } 0375 } 0376 0377 Error Arguments::error() const 0378 { 0379 return d->m_error; 0380 } 0381 0382 cstring Arguments::signature() const 0383 { 0384 return d->m_signature; 0385 } 0386 0387 chunk Arguments::data() const 0388 { 0389 return d->m_data; 0390 } 0391 0392 const std::vector<int> &Arguments::fileDescriptors() const 0393 { 0394 return d->m_fileDescriptors; 0395 } 0396 0397 bool Arguments::isByteSwapped() const 0398 { 0399 return d->m_isByteSwapped; 0400 } 0401 0402 static void printMaybeNilProlog(std::stringstream *out, const std::string &nestingPrefix, bool isNil, 0403 const char *typeName) 0404 { 0405 *out << nestingPrefix << typeName << ": "; 0406 if (isNil) { 0407 *out << "<nil>\n"; 0408 } 0409 } 0410 0411 template<typename T> 0412 void printMaybeNil(std::stringstream *out, const std::string &nestingPrefix, bool isNil, 0413 T value, const char *typeName) 0414 { 0415 printMaybeNilProlog(out, nestingPrefix, isNil, typeName); 0416 if (!isNil) { 0417 *out << value << '\n'; 0418 } 0419 } 0420 0421 template<> 0422 void printMaybeNil<cstring>(std::stringstream *out, const std::string &nestingPrefix, bool isNil, 0423 cstring cstr, const char *typeName) 0424 { 0425 printMaybeNilProlog(out, nestingPrefix, isNil, typeName); 0426 if (!isNil) { 0427 *out << '"' << toStdString(cstr) << "\"\n"; 0428 } 0429 } 0430 0431 static bool strEndsWith(const std::string &str, const std::string &ending) 0432 { 0433 if (str.length() >= ending.length()) { 0434 return str.compare(str.length() - ending.length(), ending.length(), ending) == 0; 0435 } else { 0436 return false; 0437 } 0438 } 0439 0440 std::string Arguments::prettyPrint() const 0441 { 0442 Reader reader(*this); 0443 if (!reader.isValid()) { 0444 return std::string(); 0445 } 0446 std::stringstream ret; 0447 std::string nestingPrefix; 0448 0449 bool isDone = false; 0450 0451 // Cache it, don't call Reader::isInsideEmptyArray() on every data element. This isn't really 0452 // a big deal for performance here, but in other situations it is, so set a good example :) 0453 bool inEmptyArray = false; 0454 0455 while (!isDone) { 0456 // HACK use nestingPrefix to determine when we're switching from key to value - this can be done 0457 // more cleanly with an aggregate stack if translation or similar makes this approach too ugly 0458 if (reader.isDictKey()) { 0459 if (strEndsWith(nestingPrefix, "V ")) { 0460 nestingPrefix.resize(nestingPrefix.size() - strlen("V ")); 0461 assert(strEndsWith(nestingPrefix, "{ ")); 0462 } 0463 } 0464 if (strEndsWith(nestingPrefix, "{ ")) { 0465 nestingPrefix += "K "; 0466 } else if (strEndsWith(nestingPrefix, "K ")) { 0467 nestingPrefix.replace(nestingPrefix.size() - strlen("K "), strlen("V "), "V "); 0468 } 0469 switch(reader.state()) { 0470 case Arguments::Finished: 0471 assert(nestingPrefix.empty()); 0472 isDone = true; 0473 break; 0474 case Arguments::BeginStruct: 0475 reader.beginStruct(); 0476 ret << nestingPrefix << "begin struct\n"; 0477 nestingPrefix += "( "; 0478 break; 0479 case Arguments::EndStruct: 0480 reader.endStruct(); 0481 nestingPrefix.resize(nestingPrefix.size() - 2); 0482 ret << nestingPrefix << "end struct\n"; 0483 break; 0484 case Arguments::BeginVariant: 0485 reader.beginVariant(); 0486 ret << nestingPrefix << "begin variant\n"; 0487 nestingPrefix += "* "; 0488 break; 0489 case Arguments::EndVariant: 0490 reader.endVariant(); 0491 nestingPrefix.resize(nestingPrefix.size() - 2); 0492 ret << nestingPrefix << "end variant\n"; 0493 break; 0494 case Arguments::BeginArray: 0495 if (reader.peekPrimitiveArray() == Arguments::Byte) { 0496 // print byte arrays in a more space-efficient format 0497 const std::pair<Arguments::IoState, chunk> bytes = reader.readPrimitiveArray(); 0498 assert(bytes.first == Arguments::Byte); 0499 assert(bytes.second.length > 0); 0500 inEmptyArray = reader.isInsideEmptyArray(); // Maybe not necessary, but safe 0501 ret << nestingPrefix << "array of bytes [ " << uint(bytes.second.ptr[0]); 0502 for (uint32 i = 1; i < bytes.second.length; i++) { 0503 ret << ", " << uint(bytes.second.ptr[i]); 0504 } 0505 ret << " ]\n"; 0506 } else { 0507 inEmptyArray = !reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); 0508 ret << nestingPrefix << "begin array\n"; 0509 nestingPrefix += "[ "; 0510 } 0511 break; 0512 case Arguments::EndArray: 0513 reader.endArray(); 0514 inEmptyArray = reader.isInsideEmptyArray(); 0515 nestingPrefix.resize(nestingPrefix.size() - 2); 0516 ret << nestingPrefix << "end array\n"; 0517 break; 0518 case Arguments::BeginDict: { 0519 inEmptyArray = !reader.beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty); 0520 ret << nestingPrefix << "begin dict\n"; 0521 nestingPrefix += "{ "; 0522 break; } 0523 #ifdef WITH_DICT_ENTRY 0524 // We *could* use those states to be a bit more efficient than with calling isDictKey() all 0525 // the time, but let's keep it simple, and WITH_DICT_ENTRY as a non-default configuration. 0526 case Arguments::BeginDictEntry: 0527 reader.beginDictEntry(); 0528 break; 0529 case Arguments::EndDictEntry: 0530 reader.endDictEntry(); 0531 break; 0532 #endif 0533 case Arguments::EndDict: 0534 reader.endDict(); 0535 inEmptyArray = reader.isInsideEmptyArray(); 0536 nestingPrefix.resize(nestingPrefix.size() - strlen("{ V ")); 0537 ret << nestingPrefix << "end dict\n"; 0538 break; 0539 case Arguments::Boolean: { 0540 bool b = reader.readBoolean(); 0541 ret << nestingPrefix << "bool: "; 0542 if (inEmptyArray) { 0543 ret << "<nil>"; 0544 } else { 0545 ret << (b ? "true" : "false"); 0546 } 0547 ret << '\n'; 0548 break; } 0549 case Arguments::Byte: 0550 printMaybeNil(&ret, nestingPrefix, inEmptyArray, int(reader.readByte()), "byte"); 0551 break; 0552 case Arguments::Int16: 0553 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readInt16(), "int16"); 0554 break; 0555 case Arguments::Uint16: 0556 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readUint16(), "uint16"); 0557 break; 0558 case Arguments::Int32: 0559 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readInt32(), "int32"); 0560 break; 0561 case Arguments::Uint32: 0562 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readUint32(), "uint32"); 0563 break; 0564 case Arguments::Int64: 0565 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readInt64(), "int64"); 0566 break; 0567 case Arguments::Uint64: 0568 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readUint64(), "uint64"); 0569 break; 0570 case Arguments::Double: 0571 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readDouble(), "double"); 0572 break; 0573 case Arguments::String: 0574 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readString(), "string"); 0575 break; 0576 case Arguments::ObjectPath: 0577 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readObjectPath(), "object path"); 0578 break; 0579 case Arguments::Signature: 0580 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readSignature(), "type signature"); 0581 break; 0582 case Arguments::UnixFd: 0583 printMaybeNil(&ret, nestingPrefix, inEmptyArray, reader.readUnixFd(), "file descriptor"); 0584 break; 0585 case Arguments::InvalidData: 0586 case Arguments::NeedMoreData: 0587 default: { 0588 return std::string("<error: ") + 0589 toStdString(reader.stateString()) + ">\n"; 0590 break; } 0591 } 0592 } 0593 return ret.str(); 0594 } 0595 0596 static void chopFirst(cstring *s) 0597 { 0598 s->ptr++; 0599 s->length--; 0600 } 0601 0602 // static 0603 bool Arguments::isStringValid(cstring string) 0604 { 0605 if (!string.ptr || string.length + 1 >= MaxArrayLength || string.ptr[string.length] != 0) { 0606 return false; 0607 } 0608 // check that there are no embedded nulls, exploiting the highly optimized strlen... 0609 return strlen(string.ptr) == string.length; 0610 } 0611 0612 static inline bool isObjectNameLetter(char c) 0613 { 0614 return likely((c >= 'a' && c <= 'z') || c == '_' || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')); 0615 } 0616 0617 // static 0618 bool Arguments::isObjectPathValid(cstring path) 0619 { 0620 if (!path.ptr || path.length + 1 >= MaxArrayLength || path.ptr[path.length] != 0) { 0621 return false; 0622 } 0623 char prevLetter = path.ptr[0]; 0624 if (prevLetter != '/') { 0625 return false; 0626 } 0627 if (path.length == 1) { 0628 return true; // "/" special case 0629 } 0630 for (uint32 i = 1; i < path.length; i++) { 0631 char currentLetter = path.ptr[i]; 0632 if (prevLetter == '/') { 0633 if (!isObjectNameLetter(currentLetter)) { 0634 return false; 0635 } 0636 } else { 0637 if (currentLetter != '/' && !isObjectNameLetter(currentLetter)) { 0638 return false; 0639 } 0640 } 0641 prevLetter = currentLetter; 0642 } 0643 return prevLetter != '/'; 0644 } 0645 0646 // static 0647 bool Arguments::isObjectPathElementValid(cstring pathElement) 0648 { 0649 if (!pathElement.length) { 0650 return false; 0651 } 0652 for (uint32 i = 0; i < pathElement.length; i++) { 0653 if (!isObjectNameLetter(pathElement.ptr[i])) { 0654 return false; 0655 } 0656 } 0657 return true; 0658 } 0659 0660 static bool parseBasicType(cstring *s) 0661 { 0662 // ### not checking if zero-terminated 0663 assert(s->ptr); 0664 if (s->length == 0) { 0665 return false; 0666 } 0667 switch (*s->ptr) { 0668 case 'y': 0669 case 'b': 0670 case 'n': 0671 case 'q': 0672 case 'i': 0673 case 'u': 0674 case 'x': 0675 case 't': 0676 case 'd': 0677 case 's': 0678 case 'o': 0679 case 'g': 0680 case 'h': 0681 chopFirst(s); 0682 return true; 0683 default: 0684 return false; 0685 } 0686 } 0687 0688 bool parseSingleCompleteType(cstring *s, Nesting *nest) 0689 { 0690 assert(s->ptr); 0691 // ### not cheching if zero-terminated 0692 0693 switch (*s->ptr) { 0694 case 'y': 0695 case 'b': 0696 case 'n': 0697 case 'q': 0698 case 'i': 0699 case 'u': 0700 case 'x': 0701 case 't': 0702 case 'd': 0703 case 's': 0704 case 'o': 0705 case 'g': 0706 case 'h': 0707 chopFirst(s); 0708 return true; 0709 case 'v': 0710 if (!nest->beginVariant()) { 0711 return false; 0712 } 0713 chopFirst(s); 0714 nest->endVariant(); 0715 return true; 0716 case '(': { 0717 if (!nest->beginParen()) { 0718 return false; 0719 } 0720 chopFirst(s); 0721 bool isEmptyStruct = true; 0722 while (parseSingleCompleteType(s, nest)) { 0723 isEmptyStruct = false; 0724 } 0725 if (!s->length || *s->ptr != ')' || isEmptyStruct) { 0726 return false; 0727 } 0728 chopFirst(s); 0729 nest->endParen(); 0730 return true; } 0731 case 'a': 0732 if (!nest->beginArray()) { 0733 return false; 0734 } 0735 chopFirst(s); 0736 if (*s->ptr == '{') { // an "array of dict entries", i.e. a dict 0737 if (!nest->beginParen() || s->length < 4) { 0738 return false; 0739 } 0740 chopFirst(s); 0741 // key must be a basic type 0742 if (!parseBasicType(s)) { 0743 return false; 0744 } 0745 // value can be any type 0746 if (!parseSingleCompleteType(s, nest)) { 0747 return false; 0748 } 0749 if (!s->length || *s->ptr != '}') { 0750 return false; 0751 } 0752 chopFirst(s); 0753 nest->endParen(); 0754 } else { // regular array 0755 if (!parseSingleCompleteType(s, nest)) { 0756 return false; 0757 } 0758 } 0759 nest->endArray(); 0760 return true; 0761 default: 0762 return false; 0763 } 0764 } 0765 0766 //static 0767 bool Arguments::isSignatureValid(cstring signature, SignatureType type) 0768 { 0769 Nesting nest; 0770 if (!signature.ptr || signature.ptr[signature.length] != 0) { 0771 return false; 0772 } 0773 if (type == VariantSignature) { 0774 if (!signature.length) { 0775 return false; 0776 } 0777 if (!parseSingleCompleteType(&signature, &nest)) { 0778 return false; 0779 } 0780 if (signature.length) { 0781 return false; 0782 } 0783 } else { 0784 while (signature.length) { 0785 if (!parseSingleCompleteType(&signature, &nest)) { 0786 return false; 0787 } 0788 } 0789 } 0790 // all aggregates must be closed at the end; if those asserts trigger the parsing code is not correct 0791 assert(!nest.array); 0792 assert(!nest.paren); 0793 assert(!nest.variant); 0794 return true; 0795 }