Warning, file /sdk/dferry/serialization/arguments.h 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 #ifndef ARGUMENTS_H
0025 #define ARGUMENTS_H
0026 
0027 #include "export.h"
0028 #include "types.h"
0029 
0030 #include <cassert>
0031 #include <string>
0032 #include <vector>
0033 
0034 class Error;
0035 class Message;
0036 class MessagePrivate;
0037 
0038 //#define WITH_DICT_ENTRY
0039 
0040 class DFERRY_EXPORT Arguments
0041 {
0042 public:
0043     class Reader;
0044     class Writer;
0045 
0046     enum SignatureType
0047     {
0048         MethodSignature = 0,
0049         VariantSignature
0050     };
0051 
0052     enum
0053     {
0054         MaxSignatureLength = 255,
0055         MaxArrayLength = 1 << 26, // 64 MiB
0056         MaxMessageLength = 1 << 27 // 128 MiB
0057         // MaxMessageLength applies to a full Message (and it IS checked in Message when the message is
0058         // complete), which also implies a max size for Arguments. Instead of working out the exact minimum
0059         // size of the header part of a Message (which depends on too many variables), just allow the max
0060         // Message length as max Arguments length.
0061         // The way the limit is defined in the spec seems tied a bit too closely to the design of libdbus-1...
0062     };
0063 
0064     enum IoState
0065     {
0066         // "exceptional" states
0067         NotStarted = 0,
0068         Finished,
0069         NeedMoreData, // recoverable by adding data; should only happen when parsing the not length-prefixed variable message header
0070         InvalidData, // non-recoverable
0071         // Writer states when the next type is still open (not iterating in an array or dict)
0072         // ### it is inconsistent to have DictKey, but nothing for other constraints. The name AnyData is
0073         //     also weird. Remove DictKey and call AnyData InputData?
0074         AnyData, // occurs in Writer when you are free to add any type
0075         DictKey, // occurs in Writer when the next type must be suitable for a dict key -
0076                  // a simple string or numeric type.
0077 
0078         // the following occur in Reader, and in Writer when in the second or higher iteration
0079         // of an array or dict where the types must match the first iteration (except inside variants).
0080 
0081         // states pertaining to aggregates
0082         BeginArray,
0083         EndArray,
0084         BeginDict,
0085         EndDict,
0086         BeginStruct, // 10
0087         EndStruct,
0088         BeginVariant,
0089         EndVariant,
0090         // the next element is plain data
0091         Boolean,
0092         Byte,
0093         Int16,
0094         Uint16,
0095         Int32,
0096         Uint32,
0097         Int64, // 20
0098         Uint64,
0099         Double,
0100         String,
0101         ObjectPath,
0102         Signature,
0103         UnixFd,
0104 #ifdef WITH_DICT_ENTRY
0105         BeginDictEntry,
0106         EndDictEntry,
0107 #endif
0108         LastState
0109     };
0110 
0111     // Constructs an empty argument list
0112     Arguments();
0113 
0114     // Constructs an argument list to deserialize data in @p data with signature @p signature;
0115     // if memOwnership is non-null, this means that signature and data's memory is contained in
0116     // a malloc()ed block starting at memOwnership, and ~Arguments will free it.
0117     // Otherwise, the instance assumes that @p signature and @p data live in "borrowed" memory and
0118     // you need to make sure that the memory lives as long as the Arguments.
0119     // (A notable user of this is Message - you can only get a const ref to its internal Arguments
0120     //  so you need to copy to take the Arguments away from the Message, which copies out of the
0121     //  borrowed memory into heap memory so the copy is safe)
0122     // The copy contructor and assignment operator will always copy the data, so copying is safe
0123     // regarding memory correctness but has a significant performance impact.
0124     Arguments(byte *memOwnership, cstring signature, chunk data, bool isByteSwapped = false);
0125 
0126     // Same thing as above, with file descriptors added
0127     Arguments(byte *memOwnership, cstring signature, chunk data,
0128               std::vector<int> fileDescriptors, bool isByteSwapped = false);
0129 
0130     // use these wherever possible if you care at all about efficiency!!
0131     Arguments(Arguments &&other);
0132     Arguments &operator=(Arguments &&other);
0133 
0134     // copying needs special treatment due to the d-pointer
0135     Arguments(const Arguments &other);
0136     Arguments &operator=(const Arguments &other);
0137 
0138     ~Arguments();
0139 
0140     // error (if any) propagates to Message, so it is still available later
0141     Error error() const;
0142 
0143     std::string prettyPrint() const;
0144 
0145     cstring signature() const;
0146     chunk data() const;
0147     const std::vector<int> &fileDescriptors() const;
0148     bool isByteSwapped() const;
0149 
0150     static bool isStringValid(cstring string);
0151     static bool isObjectPathValid(cstring objectPath);
0152     static bool isObjectPathElementValid(cstring pathElement);
0153     static bool isSignatureValid(cstring signature, SignatureType type = MethodSignature);
0154 
0155     static void copyOneElement(Reader *reader, Writer *writer);
0156 
0157 private:
0158     struct podCstring // Same as cstring but without ctor.
0159                       // Can't put the cstring type into a union because it has a constructor :/
0160     {
0161         char *ptr;
0162         uint32 length;
0163     };
0164 
0165     typedef union
0166     {
0167         byte Byte;
0168         bool Boolean;
0169         int16 Int16;
0170         uint16 Uint16;
0171         int32 Int32;
0172         uint32 Uint32;
0173         int64 Int64;
0174         uint64 Uint64;
0175         double Double;
0176         podCstring String; // also for ObjectPath and Signature
0177     } DataUnion;
0178 
0179 public:
0180     // error handling is done by asking state() or isError(), not by method return values.
0181     // occasionally looking at isError() is less work than checking every call.
0182     class DFERRY_EXPORT Reader
0183     {
0184     public:
0185         explicit Reader(const Arguments &al);
0186         explicit Reader(const Message &msg);
0187         Reader(Reader &&other);
0188         void operator=(Reader &&other);
0189         // TODO unit-test copy and assignment
0190         Reader(const Reader &other);
0191         void operator=(const Reader &other);
0192 
0193         ~Reader();
0194 
0195         bool isValid() const;
0196         Error error() const; // see also: aggregateStack()
0197 
0198         IoState state() const { return m_state; }
0199         cstring stateString() const;
0200         bool isInsideEmptyArray() const;
0201         cstring currentSignature() const; // current signature, either main signature or current variant
0202         uint32 currentSignaturePosition() const;
0203         cstring currentSingleCompleteTypeSignature() const;
0204         // HACK call this in NeedMoreData state when more data has been added; this replaces m_data
0205         // WARNING: calling replaceData() invalidates copies (if any) of this Reader
0206         void replaceData(chunk data);
0207 
0208         bool isFinished() const { return m_state == Finished; }
0209         bool isError() const { return m_state == InvalidData || m_state == NeedMoreData; } // TODO remove
0210 
0211         enum EmptyArrayOption
0212         {
0213             SkipIfEmpty = 0,
0214             ReadTypesOnlyIfEmpty
0215         };
0216 
0217         // Start reading an array. @p option changes behavior in case the array is empty, i.e. it has
0218         // zero elements. Empty arrays still contain types, which may be of interest.
0219         // If @p option == SkipIfEmpty, empty arrays will work according to the usual rules:
0220         // you call nextArrayEntry() and it returns false, you call endArray() and proceed to the next
0221         // value or aggregate.
0222         // If @p option == ReadTypesOnlyIfEmpty, you will be taken on a single iteration through the array
0223         // if it is empty, which makes it possible to extract the type(s) of data inside the array. In
0224         // that mode, all data returned from read...() is undefined and should be discarded. Only use state()
0225         // to get the types and call read...() purely to move from one type to the next.
0226         // Empty arrays are handled that way for symmetry with regular data extraction code so that very
0227         // similar code can handle empty and nonempty arrays.
0228         //
0229         // The return value is false if the array is empty (has 0 elements), true if it has >= 1 elements.
0230         // The return value is not affected by @p option.
0231         bool beginArray(EmptyArrayOption option = SkipIfEmpty);
0232         void skipArray(); // skips the current array; only  call this in state BeginArray!
0233         void endArray(); // leaves the current array; only  call this in state EndArray!
0234 
0235         bool beginDict(EmptyArrayOption option = SkipIfEmpty);
0236         void skipDict(); // like skipArray()
0237         bool isDictKey() const; // this can be used to track whether the current value is a dict key or value, e.g.
0238                                 // for pretty-printing purposes (it is usually clear in marshalling code).
0239         void endDict(); // like endArray()
0240 
0241         void beginStruct();
0242         void skipStruct(); // like skipArray()
0243         void endStruct(); // like endArray()
0244 
0245         void beginVariant();
0246         void skipVariant(); // like skipArray();
0247         void endVariant(); // like endArray()
0248 
0249         std::vector<IoState> aggregateStack() const; // the aggregates the reader is currently in
0250         uint32 aggregateDepth() const; // like calling aggregateStack().size() but much faster
0251         IoState currentAggregate() const; // the innermost aggregate, NotStarted if not in an aggregate
0252 
0253         // reading a type that is not indicated by state() will cause undefined behavior and at
0254         // least return garbage.
0255         byte readByte() { byte ret = m_u.Byte; advanceState(); return ret; }
0256         bool readBoolean() { bool ret = m_u.Boolean; advanceState(); return ret; }
0257         int16 readInt16() { int ret = m_u.Int16; advanceState(); return ret; }
0258         uint16 readUint16() { uint16 ret = m_u.Uint16; advanceState(); return ret; }
0259         int32 readInt32() { int32 ret = m_u.Int32; advanceState(); return ret; }
0260         uint32 readUint32() { uint32 ret = m_u.Uint32; advanceState(); return ret; }
0261         int64 readInt64() { int64 ret = m_u.Int64; advanceState(); return ret; }
0262         uint64 readUint64() { uint64 ret = m_u.Uint64; advanceState(); return ret; }
0263         double readDouble() { double ret = m_u.Double; advanceState(); return ret; }
0264         cstring readString() { cstring ret(m_u.String.ptr, m_u.String.length); advanceState(); return ret; }
0265         cstring readObjectPath() { cstring ret(m_u.String.ptr, m_u.String.length); advanceState(); return ret; }
0266         cstring readSignature() { cstring ret(m_u.String.ptr, m_u.String.length); advanceState(); return ret; }
0267         int32 readUnixFd() { int32 ret = m_u.Int32; advanceState(); return ret; }
0268 
0269         void skipCurrentElement(); // works on single values and Begin... states. In the Begin... states,
0270                                    // skips the whole aggregate.
0271 
0272         // Returns primitive type and the raw array data if in BeginArray state of an array containing only a
0273         // primitive type. You must copy the data before destroying the Reader or changing its backing store
0274         // with replaceData().
0275         // If the array is empty, that does not constitute a special case with this function: It will return
0276         // the type in the first return value as usual and an empty chunk in the second return value.
0277         // (### it might be possible to extend this feature to all fixed-length types including structs)
0278         std::pair<Arguments::IoState, chunk> readPrimitiveArray();
0279         // In state BeginArray, check if the array is a primitive array, in order to check whether to use
0280         // readPrimitiveArray(). Returns a primitive type if readPrimitiveArray() will succeed, BeginArray
0281         // if the array is not primitive, InvalidData if state is not BeginArray. The latter will not put
0282         // the reader in InvalidData state.
0283         // If option is SkipIfEmpty, an empty array of primitives will result in a return value of BeginArray
0284         // instead of the type of primitive.
0285         Arguments::IoState peekPrimitiveArray(EmptyArrayOption option = SkipIfEmpty) const;
0286 
0287 #ifdef WITH_DICT_ENTRY
0288         void beginDictEntry();
0289         void endDictEntry();
0290 #endif
0291 
0292         class Private;
0293         friend class Private;
0294 
0295     private:
0296         void beginRead();
0297         void doReadPrimitiveType();
0298         void doReadString(uint32 lengthPrefixSize);
0299         void advanceState();
0300         void beginArrayOrDict(bool isDict, EmptyArrayOption option);
0301         void skipArrayOrDictSignature(bool isDict);
0302         void skipArrayOrDict(bool isDict);
0303 
0304         Private *d;
0305 
0306         // two data members not behind d-pointer for performance reasons, especially inlining
0307         IoState m_state;
0308 
0309         // it is more efficient, in code size and performance, to read the data in advanceState()
0310         // and store the result for later retrieval in readFoo()
0311         DataUnion m_u;
0312     };
0313 
0314     class DFERRY_EXPORT Writer
0315     {
0316     public:
0317         explicit Writer();
0318         Writer(Writer &&other);
0319         void operator=(Writer &&other);
0320         // TODO unit-test copy and assignment
0321         Writer(const Writer &other);
0322         void operator=(const Writer &other);
0323 
0324         ~Writer();
0325 
0326         bool isValid() const;
0327         // error propagates to Arguments (if the error wasn't that the Arguments is not writable),
0328         // so it is still available later
0329         Error error() const; // see also: aggregateStack()
0330 
0331         IoState state() const { return m_state; }
0332         cstring stateString() const;
0333         bool isInsideEmptyArray() const;
0334         cstring currentSignature() const; // current signature, either main signature or current variant
0335         uint32 currentSignaturePosition() const;
0336 
0337         enum ArrayOption
0338         {
0339             NonEmptyArray = 0,
0340             WriteTypesOfEmptyArray,
0341             RestartEmptyArrayToWriteTypes
0342         };
0343 
0344         void beginArray(ArrayOption option = NonEmptyArray);
0345         void endArray();
0346 
0347         void beginDict(ArrayOption option = NonEmptyArray);
0348         void endDict();
0349 
0350         void beginStruct();
0351         void endStruct();
0352 
0353         void beginVariant();
0354         void endVariant();
0355 
0356         Arguments finish();
0357 
0358         std::vector<IoState> aggregateStack() const; // the aggregates the writer is currently in
0359         uint32 aggregateDepth() const; // like calling aggregateStack().size() but much faster
0360         IoState currentAggregate() const; // the innermost aggregate, NotStarted if not in an aggregate
0361 
0362         void writeByte(byte b);
0363         void writeBoolean(bool b);
0364         void writeInt16(int16 i);
0365         void writeUint16(uint16 i);
0366         void writeInt32(int32 i);
0367         void writeUint32(uint32 i);
0368         void writeInt64(int64 i);
0369         void writeUint64(uint64 i);
0370         void writeDouble(double d);
0371         void writeString(cstring string);
0372         void writeObjectPath(cstring objectPath);
0373         void writeSignature(cstring signature);
0374         void writeUnixFd(int32 fd);
0375 
0376         void writePrimitiveArray(IoState type, chunk data);
0377 
0378         // Return the current serialized data; if the current state of writing has any aggregates open
0379         // OR is in an error state, return an empty chunk (instead of invalid serialized data).
0380         // After (or before - this method is const!) an empty chunk is returned, you can find out why
0381         // using state(), isValid(), and currentAggregate().
0382         // The returned memory is only valid as long as the Writer is not mutated in any way!
0383         // If successful, the returned data can be used together with currentSignature() and
0384         // fileDescriptors() to construct a temporary Arguments as a strucrured view into the data.
0385         chunk peekSerializedData() const;
0386         const std::vector<int> &fileDescriptors() const;
0387 
0388 #ifdef WITH_DICT_ENTRY
0389         void beginDictEntry();
0390         void endDictEntry();
0391 #endif
0392 
0393         class Private;
0394         friend class Private;
0395 
0396     private:
0397         friend class MessagePrivate;
0398         void writeVariantForMessageHeader(char sig); // faster variant for typical message headers;
0399         // does not work for nested variants which aren't needed for message headers. Also does not
0400         // change the aggregate stack, but Message knows how to handle it.
0401         void fixupAfterWriteVariantForMessageHeader();
0402 
0403         void doWritePrimitiveType(IoState type, uint32 alignAndSize);
0404         void doWriteString(IoState type, uint32 lengthPrefixSize);
0405         void advanceState(cstring signatureFragment, IoState newState);
0406         void beginArrayOrDict(IoState beginWhat, ArrayOption option);
0407         void flushQueuedData();
0408 
0409         Private *d;
0410 
0411         // two data members not behind d-pointer for performance reasons
0412         IoState m_state;
0413 
0414         // ### check if it makes any performance difference to have this here (writeFoo() should benefit)
0415         DataUnion m_u;
0416     };
0417 
0418     class Private;
0419 
0420 private:
0421     Private *d;
0422 };
0423 
0424 #endif // ARGUMENTS_H