diff options
Diffstat (limited to 'include/CommonAPI/DBus/DBusOutputStream.hpp')
-rw-r--r-- | include/CommonAPI/DBus/DBusOutputStream.hpp | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/include/CommonAPI/DBus/DBusOutputStream.hpp b/include/CommonAPI/DBus/DBusOutputStream.hpp new file mode 100644 index 0000000..1148f3e --- /dev/null +++ b/include/CommonAPI/DBus/DBusOutputStream.hpp @@ -0,0 +1,398 @@ +// 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 <CommonAPI/CommonAPI.hpp> can be included directly, this file may disappear or change contents." +#endif + +#ifndef COMMONAPI_DBUS_DBUSOUTPUTSTREAM_HPP_ +#define COMMONAPI_DBUS_DBUSOUTPUTSTREAM_HPP_ + +#include <cassert> +#include <cstring> +#include <memory> +#include <stack> +#include <string> +#include <vector> + +#include <CommonAPI/Export.hpp> +#include <CommonAPI/Logger.hpp> +#include <CommonAPI/OutputStream.hpp> +#include <CommonAPI/DBus/DBusDeployment.hpp> +#include <CommonAPI/DBus/DBusError.hpp> +#include <CommonAPI/DBus/DBusHelper.hpp> +#include <CommonAPI/DBus/DBusMessage.hpp> +#include <CommonAPI/DBus/DBusTypeOutputStream.hpp> + +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<DBusOutputStream> { +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 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<double>(_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<class _Deployment, typename _Base> + COMMONAPI_EXPORT OutputStream &writeValue(const Enumeration<_Base> &_value, const _Deployment *_depl = nullptr) { + return writeValue(static_cast<_Base>(_value), _depl); + } + + template<class _Deployment, typename... _Types> + COMMONAPI_EXPORT OutputStream &writeValue(const Struct<_Types...> &_value, const _Deployment *_depl = nullptr) { + align(8); + + const auto itsSize(std::tuple_size<std::tuple<_Types...>>::value); + StructWriter<itsSize-1, DBusOutputStream, Struct<_Types...>, _Deployment>{}((*this), _value, _depl); + + return (*this); + } + + template<class _Deployment, class _PolymorphicStruct> + 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<typename... _Types> + COMMONAPI_EXPORT OutputStream &writeValue(const Variant<_Types...> &_value, const CommonAPI::EmptyDeployment *_depl = nullptr) { + align(8); + writeValue(_value.getValueType(), static_cast<EmptyDeployment *>(nullptr)); + + DBusTypeOutputStream typeOutput; + TypeOutputStreamWriteVisitor<DBusTypeOutputStream> typeVisitor(typeOutput); + ApplyVoidVisitor<TypeOutputStreamWriteVisitor<DBusTypeOutputStream>, + Variant<_Types...>, _Types...>::visit(typeVisitor, _value); + writeSignature(typeOutput.getSignature()); + + OutputStreamWriteVisitor<DBusOutputStream> valueVisitor(*this); + ApplyVoidVisitor<OutputStreamWriteVisitor<DBusOutputStream>, + Variant<_Types...>, _Types...>::visit(valueVisitor, _value); + + return (*this); + } + + template<typename _Deployment, typename... _Types> + 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<EmptyDeployment *>(nullptr)); + } + + DBusTypeOutputStream typeOutput; + TypeOutputStreamWriteVisitor<DBusTypeOutputStream> typeVisitor(typeOutput); + ApplyVoidVisitor<TypeOutputStreamWriteVisitor<DBusTypeOutputStream>, + Variant<_Types...>, _Types...>::visit(typeVisitor, _value); + writeSignature(typeOutput.getSignature()); + + OutputStreamWriteVisitor<DBusOutputStream> valueVisitor(*this); + ApplyVoidVisitor<OutputStreamWriteVisitor<DBusOutputStream>, + Variant<_Types...>, _Types...>::visit(valueVisitor, _value); + + return (*this); + } + + template<typename _ElementType> + COMMONAPI_EXPORT OutputStream &writeValue(const std::vector<_ElementType> &_value, + const EmptyDeployment *_depl) { + align(sizeof(uint32_t)); + pushPosition(); + _writeValue(static_cast<uint32_t>(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<class _Deployment, typename _ElementType> + COMMONAPI_EXPORT OutputStream &writeValue(const std::vector<_ElementType> &_value, + const _Deployment *_depl) { + align(sizeof(uint32_t)); + pushPosition(); + _writeValue(static_cast<uint32_t>(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<typename _KeyType, typename _ValueType, typename _HasherType> + COMMONAPI_EXPORT OutputStream &writeValue(const std::unordered_map<_KeyType, _ValueType, _HasherType> &_value, + const EmptyDeployment *_depl) { + align(sizeof(uint32_t)); + pushPosition(); + _writeValue(static_cast<uint32_t>(0)); // Placeholder + + align(8); + pushPosition(); // Start of map data + + for (auto v : _value) { + align(8); + writeValue(v.first, static_cast<EmptyDeployment *>(nullptr)); + writeValue(v.second, static_cast<EmptyDeployment *>(nullptr)); + + if (hasError()) { + return (*this); + } + } + + // Write number of written bytes to placeholder position + uint32_t length = getPosition() - popPosition(); + _writeValueAt(popPosition(), length); + return (*this); + } + + template<class _Deployment, typename _KeyType, typename _ValueType, typename _HasherType> + COMMONAPI_EXPORT OutputStream &writeValue(const std::unordered_map<_KeyType, _ValueType, _HasherType> &_value, + const _Deployment *_depl) { + align(sizeof(uint32_t)); + pushPosition(); + _writeValue(static_cast<uint32_t>(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<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<!std::is_class<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_vector<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_unordered_map<_Type>::value>::type * = nullptr) { + if (4 < sizeof(_Type)) align(8); + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<!std::is_same<_Type, std::string>::value>::type * = nullptr, + typename std::enable_if<std::is_class<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_vector<_Type>::value>::type * = nullptr, + typename std::enable_if<!is_std_unordered_map<_Type>::value>::type * = nullptr) { + align(8); + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<std::is_same<_Type, std::string>::value>::type * = nullptr) { + // Intentionally do nothing + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<is_std_vector<_Type>::value>::type * = nullptr) { + // Intentionally do nothing + } + + template<typename _Type> + COMMONAPI_EXPORT void alignVector(typename std::enable_if<is_std_unordered_map<_Type>::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<typename _Type> + COMMONAPI_EXPORT DBusOutputStream &_writeValue(const _Type &_value) { + if (sizeof(_Type) > 1) + align(sizeof(_Type)); + + _writeRaw(reinterpret_cast<const char*>(&_value), sizeof(_Type)); + return (*this); + } + + template<typename _Type> + COMMONAPI_EXPORT void _writeValueAt(size_t _position, const _Type &_value) { + assert(_position + sizeof(_Type) <= payload_.size()); + _writeRawAt(reinterpret_cast<const char *>(&_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<const char*>(&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<size_t> positions_; +}; + +} // namespace DBus +} // namespace CommonAPI + +#endif // COMMONAPI_DBUS_DBUSOUTPUTSTREAM_PPH_ |