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 }