// Copyright (C) 2014-2015 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #if !defined (COMMONAPI_INTERNAL_COMPILATION) #error "Only can be included directly, this file may disappear or change contents." #endif #ifndef COMMONAPI_DBUS_DBUSOUTPUTSTREAM_HPP_ #define COMMONAPI_DBUS_DBUSOUTPUTSTREAM_HPP_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CommonAPI { namespace DBus { /** * @class DBusOutputMessageStream * * Used to serialize and write data into a #DBusMessage. For all data types that may be written to a #DBusMessage, a "<<"-operator should be defined to handle the writing * (this operator is predefined for all basic data types and for vectors). The signature that has to be written to the #DBusMessage separately is assumed * to match the actual data that is inserted via the #DBusOutputMessageStream. */ class DBusOutputStream: public OutputStream { public: /** * Creates a #DBusOutputMessageStream which can be used to serialize and write data into the given #DBusMessage. Any data written is buffered within the stream. * Remember to call flush() when you are done with writing: Only then the data actually is written to the #DBusMessage. * * @param dbusMessage The #DBusMessage any data pushed into this stream should be written to. */ COMMONAPI_EXPORT DBusOutputStream(DBusMessage dbusMessage); COMMONAPI_EXPORT void beginWriteVectorOfSerializableStructs() { align(sizeof(uint32_t)); pushPosition(); _writeValue(static_cast(0)); // Placeholder align(8); pushPosition(); // Start of map data } COMMONAPI_EXPORT void endWriteVector() { // Write number of written bytes to placeholder position const uint32_t length = getPosition() - popPosition(); _writeValueAt(popPosition(), length); } COMMONAPI_EXPORT OutputStream &writeValue(const bool &_value, const EmptyDeployment *_depl) { uint32_t tmp = (_value ? 1 : 0); return _writeValue(tmp); } COMMONAPI_EXPORT OutputStream &writeValue(const int8_t &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const int16_t &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const int32_t &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const int64_t &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const uint8_t &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const uint16_t &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const uint32_t &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const uint64_t &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const float &_value, const EmptyDeployment *_depl) { return _writeValue(static_cast(_value)); } COMMONAPI_EXPORT OutputStream &writeValue(const double &_value, const EmptyDeployment *_depl) { return _writeValue(_value); } COMMONAPI_EXPORT OutputStream &writeValue(const std::string &_value, const EmptyDeployment * = nullptr) { return writeString(_value.c_str(), _value.length()); } COMMONAPI_EXPORT OutputStream &writeValue(const Version &_value, const EmptyDeployment *_depl = nullptr) { align(8); writeValue(_value.Major, _depl); writeValue(_value.Minor, _depl); return (*this); } template COMMONAPI_EXPORT OutputStream &writeValue(const Enumeration<_Base> &_value, const _Deployment *_depl = nullptr) { return writeValue(static_cast<_Base>(_value), _depl); } template COMMONAPI_EXPORT OutputStream &writeValue(const Struct<_Types...> &_value, const _Deployment *_depl = nullptr) { align(8); const auto itsSize(std::tuple_size>::value); StructWriter, _Deployment>{}((*this), _value, _depl); return (*this); } template COMMONAPI_EXPORT OutputStream &writeValue(const std::shared_ptr<_PolymorphicStruct> &_value, const _Deployment *_depl = nullptr) { align(8); _writeValue(_value->getSerial()); DBusTypeOutputStream typeOutput; typeOutput.writeType(_value); writeSignature(typeOutput.getSignature()); align(8); _value->template writeValue<>((*this), _depl); return (*this); } template COMMONAPI_EXPORT OutputStream &writeValue(const Variant<_Types...> &_value, const CommonAPI::EmptyDeployment *_depl = nullptr) { align(8); writeValue(_value.getValueType(), static_cast(nullptr)); DBusTypeOutputStream typeOutput; TypeOutputStreamWriteVisitor typeVisitor(typeOutput); ApplyVoidVisitor, Variant<_Types...>, _Types...>::visit(typeVisitor, _value); writeSignature(typeOutput.getSignature()); OutputStreamWriteVisitor valueVisitor(*this); ApplyVoidVisitor, Variant<_Types...>, _Types...>::visit(valueVisitor, _value); return (*this); } template COMMONAPI_EXPORT OutputStream &writeValue(const Variant<_Types...> &_value, const _Deployment *_depl = nullptr) { if (_depl != nullptr && _depl->isFreeDesktop_) { align(1); } else { align(8); writeValue(_value.getValueType(), static_cast(nullptr)); } DBusTypeOutputStream typeOutput; TypeOutputStreamWriteVisitor typeVisitor(typeOutput); ApplyVoidVisitor, Variant<_Types...>, _Types...>::visit(typeVisitor, _value); writeSignature(typeOutput.getSignature()); OutputStreamWriteVisitor valueVisitor(*this); ApplyVoidVisitor, Variant<_Types...>, _Types...>::visit(valueVisitor, _value); return (*this); } template COMMONAPI_EXPORT OutputStream &writeValue(const std::vector<_ElementType> &_value, const EmptyDeployment *_depl) { align(sizeof(uint32_t)); pushPosition(); _writeValue(static_cast(0)); // Placeholder alignVector<_ElementType>(); pushPosition(); // Start of vector data for (auto i : _value) { writeValue(i, _depl); if (hasError()) { break; } } // Write number of written bytes to placeholder position uint32_t length = getPosition() - popPosition(); _writeValueAt(popPosition(), length); return (*this); } template COMMONAPI_EXPORT OutputStream &writeValue(const std::vector<_ElementType> &_value, const _Deployment *_depl) { align(sizeof(uint32_t)); pushPosition(); _writeValue(static_cast(0)); // Placeholder alignVector<_ElementType>(); pushPosition(); // Start of vector data for (auto i : _value) { writeValue(i, _depl->elementDepl_); if (hasError()) { break; } } // Write number of written bytes to placeholder position uint32_t length = getPosition() - popPosition(); _writeValueAt(popPosition(), length); return (*this); } template COMMONAPI_EXPORT OutputStream &writeValue(const std::unordered_map<_KeyType, _ValueType, _HasherType> &_value, const EmptyDeployment *_depl) { align(sizeof(uint32_t)); pushPosition(); _writeValue(static_cast(0)); // Placeholder align(8); pushPosition(); // Start of map data for (auto v : _value) { align(8); writeValue(v.first, static_cast(nullptr)); writeValue(v.second, static_cast(nullptr)); if (hasError()) { return (*this); } } // Write number of written bytes to placeholder position uint32_t length = getPosition() - popPosition(); _writeValueAt(popPosition(), length); return (*this); } template COMMONAPI_EXPORT OutputStream &writeValue(const std::unordered_map<_KeyType, _ValueType, _HasherType> &_value, const _Deployment *_depl) { align(sizeof(uint32_t)); pushPosition(); _writeValue(static_cast(0)); // Placeholder align(8); pushPosition(); // Start of map data for (auto v : _value) { align(8); writeValue(v.first, _depl->key_); writeValue(v.second, _depl->value_); if (hasError()) { return (*this); } } // Write number of written bytes to placeholder position uint32_t length = getPosition() - popPosition(); _writeValueAt(popPosition(), length); return (*this); } /** * Writes the data that was buffered within this #DBusOutputMessageStream to the #DBusMessage that was given to the constructor. Each call to flush() * will completely override the data that currently is contained in the #DBusMessage. The data that is buffered in this #DBusOutputMessageStream is * not deleted by calling flush(). */ COMMONAPI_EXPORT void flush(); COMMONAPI_EXPORT bool hasError() const; private: COMMONAPI_EXPORT size_t getPosition(); COMMONAPI_EXPORT void pushPosition(); COMMONAPI_EXPORT size_t popPosition(); template COMMONAPI_EXPORT void alignVector(typename std::enable_if::value>::type * = nullptr, typename std::enable_if::value>::type * = nullptr, typename std::enable_if::value>::type * = nullptr) { if (4 < sizeof(_Type)) align(8); } template COMMONAPI_EXPORT void alignVector(typename std::enable_if::value>::type * = nullptr, typename std::enable_if::value>::type * = nullptr, typename std::enable_if::value>::type * = nullptr, typename std::enable_if::value>::type * = nullptr) { align(8); } template COMMONAPI_EXPORT void alignVector(typename std::enable_if::value>::type * = nullptr) { // Intentionally do nothing } template COMMONAPI_EXPORT void alignVector(typename std::enable_if::value>::type * = nullptr) { // Intentionally do nothing } template COMMONAPI_EXPORT void alignVector(typename std::enable_if::value>::type * = nullptr) { align(4); } COMMONAPI_EXPORT void setError(); /** * Reserves the given number of bytes for writing, thereby negating the need to dynamically allocate memory while writing. * Use this method for optimization: If possible, reserve as many bytes as you need for your data before doing any writing. * * @param numOfBytes The number of bytes that should be reserved for writing. */ COMMONAPI_EXPORT void reserveMemory(size_t numOfBytes); template COMMONAPI_EXPORT DBusOutputStream &_writeValue(const _Type &_value) { if (sizeof(_Type) > 1) align(sizeof(_Type)); _writeRaw(reinterpret_cast(&_value), sizeof(_Type)); return (*this); } template COMMONAPI_EXPORT void _writeValueAt(size_t _position, const _Type &_value) { assert(_position + sizeof(_Type) <= payload_.size()); _writeRawAt(reinterpret_cast(&_value), sizeof(_Type), _position); } COMMONAPI_EXPORT DBusOutputStream &writeString(const char *_data, const uint32_t &_length); /** * Fills the stream with 0-bytes to make the next value be aligned to the boundary given. * This means that as many 0-bytes are written to the buffer as are necessary * to make the next value start with the given alignment. * * @param alignBoundary The byte-boundary to which the next value should be aligned. */ COMMONAPI_EXPORT void align(const size_t _boundary); /** * Takes sizeInByte characters, starting from the character which val points to, and stores them for later writing. * When calling flush(), all values that were written to this stream are copied into the payload of the #DBusMessage. * * The array of characters might be created from a pointer to a given value by using a reinterpret_cast. Example: * @code * ... * int32_t val = 15; * outputMessageStream.alignForBasicType(sizeof(int32_t)); * const char* const reinterpreted = reinterpret_cast(&val); * outputMessageStream.writeValue(reinterpreted, sizeof(int32_t)); * ... * @endcode * * @param _data The array of chars that should serve as input * @param _size The number of bytes that should be written * @return true if writing was successful, false otherwise. * * @see DBusOutputMessageStream() * @see flush() */ COMMONAPI_EXPORT void _writeRaw(const char *_data, const size_t _size); COMMONAPI_EXPORT void _writeRawAt(const char *_data, const size_t _size, size_t _position); protected: std::string payload_; private: COMMONAPI_EXPORT void writeSignature(const std::string& signature); COMMONAPI_EXPORT size_t getCurrentStreamPosition(); DBusError dbusError_; DBusMessage dbusMessage_; std::stack positions_; }; } // namespace DBus } // namespace CommonAPI #endif // COMMONAPI_DBUS_DBUSOUTPUTSTREAM_PPH_