| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIBBRILLO_BRILLO_DBUS_DATA_SERIALIZATION_H_ |
| #define LIBBRILLO_BRILLO_DBUS_DATA_SERIALIZATION_H_ |
| |
| // The main functionality provided by this header file is methods to serialize |
| // native C++ data over D-Bus. This includes three major parts: |
| // - Methods to get the D-Bus signature for a given C++ type: |
| // std::string GetDBusSignature<T>(); |
| // - Methods to write arbitrary C++ data to D-Bus MessageWriter: |
| // void AppendValueToWriter(dbus::MessageWriter* writer, const T& value); |
| // void AppendValueToWriterAsVariant(dbus::MessageWriter*, const T&); |
| // - Methods to read arbitrary C++ data from D-Bus MessageReader: |
| // bool PopValueFromReader(dbus::MessageReader* reader, T* value); |
| // bool PopVariantValueFromReader(dbus::MessageReader* reader, T* value); |
| // |
| // There are a number of overloads to handle C++ equivalents of basic D-Bus |
| // types: |
| // D-Bus Type | D-Bus Signature | Native C++ type |
| // -------------------------------------------------- |
| // BYTE | y | uint8_t |
| // BOOL | b | bool |
| // INT16 | n | int16_t |
| // UINT16 | q | uint16_t |
| // INT32 | i | int32_t (int) |
| // UINT32 | u | uint32_t (unsigned) |
| // INT64 | x | int64_t |
| // UINT64 | t | uint64_t |
| // DOUBLE | d | double |
| // STRING | s | std::string |
| // OBJECT_PATH | o | dbus::ObjectPath |
| // ARRAY | aT | std::vector<T> |
| // STRUCT | (UV) | std::pair<U,V> |
| // | (UVW...) | std::tuple<U,V,W,...> |
| // DICT | a{KV} | std::map<K,V> |
| // VARIANT | v | brillo::Any |
| // UNIX_FD | h | brillo::dbus_utils::FileDescriptor (write) |
| // | | base::ScopedFD (read) |
| // SIGNATURE | g | (unsupported) |
| // |
| // Additional overloads/specialization can be provided for custom types. |
| // In order to do that, provide overloads of AppendValueToWriter() and |
| // PopValueFromReader() functions in brillo::dbus_utils namespace for the |
| // CustomType. As well as a template specialization of DBusType<> for the same |
| // CustomType. This specialization must provide three static functions: |
| // - static std::string GetSignature(); |
| // - static void Write(dbus::MessageWriter* writer, const CustomType& value); |
| // - static bool Read(dbus::MessageReader* reader, CustomType* value); |
| // See an example in DBusUtils.CustomStruct unit test in |
| // brillo/dbus/data_serialization_test.cc. |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/files/scoped_file.h> |
| #include <base/logging.h> |
| #include <brillo/brillo_export.h> |
| #include <brillo/dbus/file_descriptor.h> |
| #include <brillo/type_name_undecorate.h> |
| #include <dbus/message.h> |
| |
| namespace google { |
| namespace protobuf { |
| class MessageLite; |
| } // namespace protobuf |
| } // namespace google |
| |
| namespace brillo { |
| |
| // Forward-declare only. Can't include any.h right now because it needs |
| // AppendValueToWriter() declared below. |
| class Any; |
| |
| namespace dbus_utils { |
| |
| // Base class for DBusType<T> for T not supported by D-Bus. This used to |
| // implement IsTypeSupported<> below. |
| struct Unsupported {}; |
| |
| // Generic definition of DBusType<T> which will be specialized for particular |
| // types later. |
| // The second template parameter is used only in SFINAE situations to resolve |
| // class hierarchy chains for protobuf-derived classes. This type is defaulted |
| // to be 'void' in all other cases and simply ignored. |
| // See DBusType specialization for google::protobuf::MessageLite below for more |
| // detailed information. |
| template<typename T, typename = void> |
| struct DBusType : public Unsupported {}; |
| |
| // A helper type trait to determine if all of the types listed in Types... are |
| // supported by D-Bus. This is a generic forward-declaration which will be |
| // specialized for different type combinations. |
| template<typename... Types> |
| struct IsTypeSupported; |
| |
| // Both T and the Types... must be supported for the complete set to be |
| // supported. |
| template<typename T, typename... Types> |
| struct IsTypeSupported<T, Types...> |
| : public std::integral_constant< |
| bool, |
| IsTypeSupported<T>::value && IsTypeSupported<Types...>::value> {}; |
| |
| // For a single type T, check if DBusType<T> derives from Unsupported. |
| // If it does, then the type is not supported by the D-Bus. |
| template<typename T> |
| struct IsTypeSupported<T> |
| : public std::integral_constant< |
| bool, |
| !std::is_base_of<Unsupported, DBusType<T>>::value> {}; |
| |
| // Empty set is not supported. |
| template<> |
| struct IsTypeSupported<> : public std::false_type {}; |
| |
| //---------------------------------------------------------------------------- |
| // AppendValueToWriter<T>(dbus::MessageWriter* writer, const T& value) |
| // Write the |value| of type T to D-Bus message. |
| // Explicitly delete the overloads for scalar types that are not supported by |
| // D-Bus. |
| void AppendValueToWriter(dbus::MessageWriter* writer, char value) = delete; |
| void AppendValueToWriter(dbus::MessageWriter* writer, float value) = delete; |
| |
| //---------------------------------------------------------------------------- |
| // PopValueFromReader<T>(dbus::MessageWriter* writer, T* value) |
| // Reads the |value| of type T from D-Bus message. |
| // Explicitly delete the overloads for scalar types that are not supported by |
| // D-Bus. |
| void PopValueFromReader(dbus::MessageReader* reader, char* value) = delete; |
| void PopValueFromReader(dbus::MessageReader* reader, float* value) = delete; |
| |
| //---------------------------------------------------------------------------- |
| // Get D-Bus data signature from C++ data types. |
| // Specializations of a generic GetDBusSignature<T>() provide signature strings |
| // for native C++ types. This function is available only for type supported |
| // by D-Bus. |
| template<typename T> |
| inline typename std::enable_if<IsTypeSupported<T>::value, std::string>::type |
| GetDBusSignature() { |
| return DBusType<T>::GetSignature(); |
| } |
| |
| namespace details { |
| // Helper method used by the many overloads of PopValueFromReader(). |
| // If the current value in the reader is of Variant type, the method descends |
| // into the Variant and updates the |*reader_ref| with the transient |
| // |variant_reader| MessageReader instance passed in. |
| // Returns false if it fails to descend into the Variant. |
| inline bool DescendIntoVariantIfPresent(dbus::MessageReader** reader_ref, |
| dbus::MessageReader* variant_reader) { |
| if ((*reader_ref)->GetDataType() != dbus::Message::VARIANT) |
| return true; |
| if (!(*reader_ref)->PopVariant(variant_reader)) |
| return false; |
| *reader_ref = variant_reader; |
| return true; |
| } |
| |
| // Helper method to format the type string of an array. |
| // Essentially it adds "a" in front of |element_signature|. |
| inline std::string GetArrayDBusSignature(const std::string& element_signature) { |
| return DBUS_TYPE_ARRAY_AS_STRING + element_signature; |
| } |
| |
| // Helper method to get a signature string for DICT_ENTRY. |
| // Returns "{KV}", where "K" and "V" are the type signatures for types |
| // KEY/VALUE. For example, GetDBusDictEntryType<std::string, int>() would return |
| // "{si}". |
| template<typename KEY, typename VALUE> |
| inline std::string GetDBusDictEntryType() { |
| return DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + |
| GetDBusSignature<KEY>() + GetDBusSignature<VALUE>() + |
| DBUS_DICT_ENTRY_END_CHAR_AS_STRING; |
| } |
| |
| } // namespace details |
| |
| //============================================================================= |
| // Specializations/overloads for AppendValueToWriter, PopValueFromReader and |
| // DBusType<T> for various C++ types that can be serialized over D-Bus. |
| |
| // bool ----------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| bool value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| bool* value); |
| |
| template<> |
| struct DBusType<bool> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_BOOLEAN_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, bool value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, bool* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // uint8_t -------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| uint8_t value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| uint8_t* value); |
| |
| template<> |
| struct DBusType<uint8_t> { |
| inline static std::string GetSignature() { return DBUS_TYPE_BYTE_AS_STRING; } |
| inline static void Write(dbus::MessageWriter* writer, uint8_t value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, uint8_t* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // int16_t -------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| int16_t value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| int16_t* value); |
| |
| template<> |
| struct DBusType<int16_t> { |
| inline static std::string GetSignature() { return DBUS_TYPE_INT16_AS_STRING; } |
| inline static void Write(dbus::MessageWriter* writer, int16_t value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, int16_t* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // uint16_t ------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| uint16_t value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| uint16_t* value); |
| |
| template<> |
| struct DBusType<uint16_t> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_UINT16_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, uint16_t value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, uint16_t* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // int32_t -------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| int32_t value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| int32_t* value); |
| |
| template<> |
| struct DBusType<int32_t> { |
| inline static std::string GetSignature() { return DBUS_TYPE_INT32_AS_STRING; } |
| inline static void Write(dbus::MessageWriter* writer, int32_t value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, int32_t* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // uint32_t ------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| uint32_t value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| uint32_t* value); |
| |
| template<> |
| struct DBusType<uint32_t> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_UINT32_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, uint32_t value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, uint32_t* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // int64_t -------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| int64_t value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| int64_t* value); |
| |
| template<> |
| struct DBusType<int64_t> { |
| inline static std::string GetSignature() { return DBUS_TYPE_INT64_AS_STRING; } |
| inline static void Write(dbus::MessageWriter* writer, int64_t value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, int64_t* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // uint64_t ------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| uint64_t value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| uint64_t* value); |
| |
| template<> |
| struct DBusType<uint64_t> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_UINT64_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, uint64_t value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, uint64_t* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // double --------------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| double value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| double* value); |
| |
| template<> |
| struct DBusType<double> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_DOUBLE_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, double value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, double* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // std::string ---------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| const std::string& value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| std::string* value); |
| |
| template<> |
| struct DBusType<std::string> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_STRING_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, |
| const std::string& value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, std::string* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // const char* |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| const char* value); |
| |
| template<> |
| struct DBusType<const char*> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_STRING_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, const char* value) { |
| AppendValueToWriter(writer, value); |
| } |
| }; |
| |
| // const char[] |
| template<> |
| struct DBusType<const char[]> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_STRING_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, const char* value) { |
| AppendValueToWriter(writer, value); |
| } |
| }; |
| |
| // dbus::ObjectPath ----------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| const dbus::ObjectPath& value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| dbus::ObjectPath* value); |
| |
| template<> |
| struct DBusType<dbus::ObjectPath> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_OBJECT_PATH_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, |
| const dbus::ObjectPath& value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, |
| dbus::ObjectPath* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // brillo::dbus_utils::FileDescriptor/base::ScopedFD -------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| const FileDescriptor& value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| base::ScopedFD* value); |
| |
| template<> |
| struct DBusType<FileDescriptor> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_UNIX_FD_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, |
| const FileDescriptor& value) { |
| AppendValueToWriter(writer, value); |
| } |
| }; |
| |
| template<> |
| struct DBusType<base::ScopedFD> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_UNIX_FD_AS_STRING; |
| } |
| inline static bool Read(dbus::MessageReader* reader, |
| base::ScopedFD* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // brillo::Any -------------------------------------------------------------- |
| BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, |
| const brillo::Any& value); |
| BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, |
| brillo::Any* value); |
| |
| template<> |
| struct DBusType<brillo::Any> { |
| inline static std::string GetSignature() { |
| return DBUS_TYPE_VARIANT_AS_STRING; |
| } |
| inline static void Write(dbus::MessageWriter* writer, |
| const brillo::Any& value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, brillo::Any* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // std::vector = D-Bus ARRAY. ------------------------------------------------- |
| template<typename T, typename ALLOC> |
| typename std::enable_if<IsTypeSupported<T>::value>::type AppendValueToWriter( |
| dbus::MessageWriter* writer, |
| const std::vector<T, ALLOC>& value) { |
| dbus::MessageWriter array_writer(nullptr); |
| writer->OpenArray(GetDBusSignature<T>(), &array_writer); |
| for (const auto& element : value) { |
| // Use DBusType<T>::Write() instead of AppendValueToWriter() to delay |
| // binding to AppendValueToWriter() to the point of instantiation of this |
| // template. |
| DBusType<T>::Write(&array_writer, element); |
| } |
| writer->CloseContainer(&array_writer); |
| } |
| |
| template<typename T, typename ALLOC> |
| typename std::enable_if<IsTypeSupported<T>::value, bool>::type |
| PopValueFromReader(dbus::MessageReader* reader, std::vector<T, ALLOC>* value) { |
| dbus::MessageReader variant_reader(nullptr); |
| dbus::MessageReader array_reader(nullptr); |
| if (!details::DescendIntoVariantIfPresent(&reader, &variant_reader) || |
| !reader->PopArray(&array_reader)) |
| return false; |
| value->clear(); |
| while (array_reader.HasMoreData()) { |
| T data; |
| // Use DBusType<T>::Read() instead of PopValueFromReader() to delay |
| // binding to PopValueFromReader() to the point of instantiation of this |
| // template. |
| if (!DBusType<T>::Read(&array_reader, &data)) |
| return false; |
| value->push_back(std::move(data)); |
| } |
| return true; |
| } |
| |
| namespace details { |
| // DBusArrayType<> is a helper base class for DBusType<vector<T>> that provides |
| // GetSignature/Write/Read methods for T types that are supported by D-Bus |
| // and not having those methods for types that are not supported by D-Bus. |
| template<bool inner_type_supported, typename T, typename ALLOC> |
| struct DBusArrayType { |
| // Returns "aT", where "T" is the signature string for type T. |
| inline static std::string GetSignature() { |
| return GetArrayDBusSignature(GetDBusSignature<T>()); |
| } |
| inline static void Write(dbus::MessageWriter* writer, |
| const std::vector<T, ALLOC>& value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, |
| std::vector<T, ALLOC>* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // Explicit specialization for unsupported type T. |
| template<typename T, typename ALLOC> |
| struct DBusArrayType<false, T, ALLOC> : public Unsupported {}; |
| |
| } // namespace details |
| |
| template<typename T, typename ALLOC> |
| struct DBusType<std::vector<T, ALLOC>> |
| : public details::DBusArrayType<IsTypeSupported<T>::value, T, ALLOC> {}; |
| |
| // std::pair = D-Bus STRUCT with two elements. -------------------------------- |
| namespace details { |
| |
| // Helper class to get a D-Bus signature of a list of types. |
| // For example, TupleTraits<int32_t, bool, std::string>::GetSignature() will |
| // return "ibs". |
| template<typename... Types> |
| struct TupleTraits; |
| |
| template<typename FirstType, typename... RestOfTypes> |
| struct TupleTraits<FirstType, RestOfTypes...> { |
| static std::string GetSignature() { |
| return GetDBusSignature<FirstType>() + |
| TupleTraits<RestOfTypes...>::GetSignature(); |
| } |
| }; |
| |
| template<> |
| struct TupleTraits<> { |
| static std::string GetSignature() { return std::string{}; } |
| }; |
| |
| } // namespace details |
| |
| template<typename... Types> |
| inline std::string GetStructDBusSignature() { |
| // Returns "(T...)", where "T..." is the signature strings for types T... |
| return DBUS_STRUCT_BEGIN_CHAR_AS_STRING + |
| details::TupleTraits<Types...>::GetSignature() + |
| DBUS_STRUCT_END_CHAR_AS_STRING; |
| } |
| |
| template<typename U, typename V> |
| typename std::enable_if<IsTypeSupported<U, V>::value>::type AppendValueToWriter( |
| dbus::MessageWriter* writer, |
| const std::pair<U, V>& value) { |
| dbus::MessageWriter struct_writer(nullptr); |
| writer->OpenStruct(&struct_writer); |
| // Use DBusType<T>::Write() instead of AppendValueToWriter() to delay |
| // binding to AppendValueToWriter() to the point of instantiation of this |
| // template. |
| DBusType<U>::Write(&struct_writer, value.first); |
| DBusType<V>::Write(&struct_writer, value.second); |
| writer->CloseContainer(&struct_writer); |
| } |
| |
| template<typename U, typename V> |
| typename std::enable_if<IsTypeSupported<U, V>::value, bool>::type |
| PopValueFromReader(dbus::MessageReader* reader, std::pair<U, V>* value) { |
| dbus::MessageReader variant_reader(nullptr); |
| dbus::MessageReader struct_reader(nullptr); |
| if (!details::DescendIntoVariantIfPresent(&reader, &variant_reader) || |
| !reader->PopStruct(&struct_reader)) |
| return false; |
| // Use DBusType<T>::Read() instead of PopValueFromReader() to delay |
| // binding to PopValueFromReader() to the point of instantiation of this |
| // template. |
| return DBusType<U>::Read(&struct_reader, &value->first) && |
| DBusType<V>::Read(&struct_reader, &value->second); |
| } |
| |
| namespace details { |
| |
| // DBusArrayType<> is a helper base class for DBusType<pair<U, V>> that provides |
| // GetSignature/Write/Read methods for types that are supported by D-Bus |
| // and not having those methods for types that are not supported by D-Bus. |
| template<bool inner_type_supported, typename U, typename V> |
| struct DBusPairType { |
| // Returns "(UV)", where "U" and "V" are the signature strings for types U, V. |
| inline static std::string GetSignature() { |
| return GetStructDBusSignature<U, V>(); |
| } |
| inline static void Write(dbus::MessageWriter* writer, |
| const std::pair<U, V>& value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, std::pair<U, V>* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // Either U, or V, or both are not supported by D-Bus. |
| template<typename U, typename V> |
| struct DBusPairType<false, U, V> : public Unsupported {}; |
| |
| } // namespace details |
| |
| template<typename U, typename V> |
| struct DBusType<std::pair<U, V>> |
| : public details::DBusPairType<IsTypeSupported<U, V>::value, U, V> {}; |
| |
| // std::tuple = D-Bus STRUCT with arbitrary number of members. ---------------- |
| namespace details { |
| |
| // TupleIterator<I, N, T...> is a helper class to iterate over all the elements |
| // of a tuple<T...> from index I to N. TupleIterator<>::Read and ::Write methods |
| // are called for each element of the tuple and iteration continues until I == N |
| // in which case the specialization for I==N below stops the recursion. |
| template<size_t I, size_t N, typename... T> |
| struct TupleIterator { |
| // Tuple is just a convenience alias to a tuple containing elements of type T. |
| using Tuple = std::tuple<T...>; |
| // ValueType is the type of the element at index I. |
| using ValueType = typename std::tuple_element<I, Tuple>::type; |
| |
| // Write the tuple element at index I to D-Bus message. |
| static void Write(dbus::MessageWriter* writer, const Tuple& value) { |
| // Use DBusType<T>::Write() instead of AppendValueToWriter() to delay |
| // binding to AppendValueToWriter() to the point of instantiation of this |
| // template. |
| DBusType<ValueType>::Write(writer, std::get<I>(value)); |
| TupleIterator<I + 1, N, T...>::Write(writer, value); |
| } |
| |
| // Read the tuple element at index I from D-Bus message. |
| static bool Read(dbus::MessageReader* reader, Tuple* value) { |
| // Use DBusType<T>::Read() instead of PopValueFromReader() to delay |
| // binding to PopValueFromReader() to the point of instantiation of this |
| // template. |
| return DBusType<ValueType>::Read(reader, &std::get<I>(*value)) && |
| TupleIterator<I + 1, N, T...>::Read(reader, value); |
| } |
| }; |
| |
| // Specialization to end the iteration when the index reaches the last element. |
| template<size_t N, typename... T> |
| struct TupleIterator<N, N, T...> { |
| using Tuple = std::tuple<T...>; |
| static void Write(dbus::MessageWriter* /* writer */, |
| const Tuple& /* value */) {} |
| static bool Read(dbus::MessageReader* /* reader */, |
| Tuple* /* value */) { return true; } |
| }; |
| |
| } // namespace details |
| |
| template<typename... T> |
| typename std::enable_if<IsTypeSupported<T...>::value>::type AppendValueToWriter( |
| dbus::MessageWriter* writer, |
| const std::tuple<T...>& value) { |
| dbus::MessageWriter struct_writer(nullptr); |
| writer->OpenStruct(&struct_writer); |
| details::TupleIterator<0, sizeof...(T), T...>::Write(&struct_writer, value); |
| writer->CloseContainer(&struct_writer); |
| } |
| |
| template<typename... T> |
| typename std::enable_if<IsTypeSupported<T...>::value, bool>::type |
| PopValueFromReader(dbus::MessageReader* reader, std::tuple<T...>* value) { |
| dbus::MessageReader variant_reader(nullptr); |
| dbus::MessageReader struct_reader(nullptr); |
| if (!details::DescendIntoVariantIfPresent(&reader, &variant_reader) || |
| !reader->PopStruct(&struct_reader)) |
| return false; |
| return details::TupleIterator<0, sizeof...(T), T...>::Read(&struct_reader, |
| value); |
| } |
| |
| namespace details { |
| |
| // DBusTupleType<> is a helper base class for DBusType<tuple<T...>> that |
| // provides GetSignature/Write/Read methods for types that are supported by |
| // D-Bus and not having those methods for types that are not supported by D-Bus. |
| template<bool inner_type_supported, typename... T> |
| struct DBusTupleType { |
| // Returns "(T...)", where "T..." are the signature strings for types T... |
| inline static std::string GetSignature() { |
| return GetStructDBusSignature<T...>(); |
| } |
| inline static void Write(dbus::MessageWriter* writer, |
| const std::tuple<T...>& value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, |
| std::tuple<T...>* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // Some/all of types T... are not supported by D-Bus. |
| template<typename... T> |
| struct DBusTupleType<false, T...> : public Unsupported {}; |
| |
| } // namespace details |
| |
| template<typename... T> |
| struct DBusType<std::tuple<T...>> |
| : public details::DBusTupleType<IsTypeSupported<T...>::value, T...> {}; |
| |
| // std::map = D-Bus ARRAY of DICT_ENTRY. -------------------------------------- |
| template<typename KEY, typename VALUE, typename PRED, typename ALLOC> |
| typename std::enable_if<IsTypeSupported<KEY, VALUE>::value>::type |
| AppendValueToWriter(dbus::MessageWriter* writer, |
| const std::map<KEY, VALUE, PRED, ALLOC>& value) { |
| dbus::MessageWriter dict_writer(nullptr); |
| writer->OpenArray(details::GetDBusDictEntryType<KEY, VALUE>(), &dict_writer); |
| for (const auto& pair : value) { |
| dbus::MessageWriter entry_writer(nullptr); |
| dict_writer.OpenDictEntry(&entry_writer); |
| // Use DBusType<T>::Write() instead of AppendValueToWriter() to delay |
| // binding to AppendValueToWriter() to the point of instantiation of this |
| // template. |
| DBusType<KEY>::Write(&entry_writer, pair.first); |
| DBusType<VALUE>::Write(&entry_writer, pair.second); |
| dict_writer.CloseContainer(&entry_writer); |
| } |
| writer->CloseContainer(&dict_writer); |
| } |
| |
| template<typename KEY, typename VALUE, typename PRED, typename ALLOC> |
| typename std::enable_if<IsTypeSupported<KEY, VALUE>::value, bool>::type |
| PopValueFromReader(dbus::MessageReader* reader, |
| std::map<KEY, VALUE, PRED, ALLOC>* value) { |
| dbus::MessageReader variant_reader(nullptr); |
| dbus::MessageReader array_reader(nullptr); |
| if (!details::DescendIntoVariantIfPresent(&reader, &variant_reader) || |
| !reader->PopArray(&array_reader)) |
| return false; |
| value->clear(); |
| while (array_reader.HasMoreData()) { |
| dbus::MessageReader dict_entry_reader(nullptr); |
| if (!array_reader.PopDictEntry(&dict_entry_reader)) |
| return false; |
| KEY key; |
| VALUE data; |
| // Use DBusType<T>::Read() instead of PopValueFromReader() to delay |
| // binding to PopValueFromReader() to the point of instantiation of this |
| // template. |
| if (!DBusType<KEY>::Read(&dict_entry_reader, &key) || |
| !DBusType<VALUE>::Read(&dict_entry_reader, &data)) |
| return false; |
| value->emplace(std::move(key), std::move(data)); |
| } |
| return true; |
| } |
| |
| namespace details { |
| |
| // DBusArrayType<> is a helper base class for DBusType<map<K, V>> that provides |
| // GetSignature/Write/Read methods for T types that are supported by D-Bus |
| // and not having those methods for types that are not supported by D-Bus. |
| template<bool inner_types_supported, |
| typename KEY, |
| typename VALUE, |
| typename PRED, |
| typename ALLOC> |
| struct DBusMapType { |
| // Returns "a{KV}", where "K" and "V" are the signature strings for types |
| // KEY/VALUE. |
| inline static std::string GetSignature() { |
| return GetArrayDBusSignature(GetDBusDictEntryType<KEY, VALUE>()); |
| } |
| inline static void Write(dbus::MessageWriter* writer, |
| const std::map<KEY, VALUE, PRED, ALLOC>& value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, |
| std::map<KEY, VALUE, PRED, ALLOC>* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| // Types KEY, VALUE or both are not supported by D-Bus. |
| template<typename KEY, typename VALUE, typename PRED, typename ALLOC> |
| struct DBusMapType<false, KEY, VALUE, PRED, ALLOC> : public Unsupported {}; |
| |
| } // namespace details |
| |
| template<typename KEY, typename VALUE, typename PRED, typename ALLOC> |
| struct DBusType<std::map<KEY, VALUE, PRED, ALLOC>> |
| : public details::DBusMapType<IsTypeSupported<KEY, VALUE>::value, |
| KEY, |
| VALUE, |
| PRED, |
| ALLOC> {}; |
| |
| // google::protobuf::MessageLite = D-Bus ARRAY of BYTE ------------------------ |
| inline void AppendValueToWriter(dbus::MessageWriter* writer, |
| const google::protobuf::MessageLite& value) { |
| writer->AppendProtoAsArrayOfBytes(value); |
| } |
| |
| inline bool PopValueFromReader(dbus::MessageReader* reader, |
| google::protobuf::MessageLite* value) { |
| return reader->PopArrayOfBytesAsProto(value); |
| } |
| |
| // is_protobuf_t<T> is a helper type trait to determine if type T derives from |
| // google::protobuf::MessageLite. |
| template<typename T> |
| using is_protobuf = std::is_base_of<google::protobuf::MessageLite, T>; |
| |
| // Specialize DBusType<T> for classes that derive from protobuf::MessageLite. |
| // Here we perform a partial specialization of DBusType<T> only for types |
| // that derive from google::protobuf::MessageLite. This is done by employing |
| // the second template parameter in DBusType and this basically relies on C++ |
| // SFINAE rules. "typename std::enable_if<is_protobuf<T>::value>::type" will |
| // evaluate to "void" for classes T that descend from MessageLite and will be |
| // an invalid construct for other types/classes which will automatically |
| // remove this particular specialization from name resolution context. |
| template<typename T> |
| struct DBusType<T, typename std::enable_if<is_protobuf<T>::value>::type> { |
| inline static std::string GetSignature() { |
| return GetDBusSignature<std::vector<uint8_t>>(); |
| } |
| inline static void Write(dbus::MessageWriter* writer, const T& value) { |
| AppendValueToWriter(writer, value); |
| } |
| inline static bool Read(dbus::MessageReader* reader, T* value) { |
| return PopValueFromReader(reader, value); |
| } |
| }; |
| |
| //---------------------------------------------------------------------------- |
| // AppendValueToWriterAsVariant<T>(dbus::MessageWriter* writer, const T& value) |
| // Write the |value| of type T to D-Bus message as a VARIANT. |
| // This overload is provided only if T is supported by D-Bus. |
| template<typename T> |
| typename std::enable_if<IsTypeSupported<T>::value>::type |
| AppendValueToWriterAsVariant(dbus::MessageWriter* writer, const T& value) { |
| std::string data_type = GetDBusSignature<T>(); |
| dbus::MessageWriter variant_writer(nullptr); |
| writer->OpenVariant(data_type, &variant_writer); |
| // Use DBusType<T>::Write() instead of AppendValueToWriter() to delay |
| // binding to AppendValueToWriter() to the point of instantiation of this |
| // template. |
| DBusType<T>::Write(&variant_writer, value); |
| writer->CloseContainer(&variant_writer); |
| } |
| |
| // Special case: do not allow to write a Variant containing a Variant. |
| // Just redirect to normal AppendValueToWriter(). |
| inline void AppendValueToWriterAsVariant(dbus::MessageWriter* writer, |
| const brillo::Any& value) { |
| return AppendValueToWriter(writer, value); |
| } |
| |
| //---------------------------------------------------------------------------- |
| // PopVariantValueFromReader<T>(dbus::MessageWriter* writer, T* value) |
| // Reads a Variant containing the |value| of type T from D-Bus message. |
| // Note that the generic PopValueFromReader<T>(...) can do this too. |
| // This method is provided for two reasons: |
| // 1. For API symmetry with AppendValueToWriter/AppendValueToWriterAsVariant. |
| // 2. To be used when it is important to assert that the data was sent |
| // specifically as a Variant. |
| // This overload is provided only if T is supported by D-Bus. |
| template<typename T> |
| typename std::enable_if<IsTypeSupported<T>::value, bool>::type |
| PopVariantValueFromReader(dbus::MessageReader* reader, T* value) { |
| dbus::MessageReader variant_reader(nullptr); |
| if (!reader->PopVariant(&variant_reader)) |
| return false; |
| // Use DBusType<T>::Read() instead of PopValueFromReader() to delay |
| // binding to PopValueFromReader() to the point of instantiation of this |
| // template. |
| return DBusType<T>::Read(&variant_reader, value); |
| } |
| |
| // Special handling of request to read a Variant of Variant. |
| inline bool PopVariantValueFromReader(dbus::MessageReader* reader, Any* value) { |
| return PopValueFromReader(reader, value); |
| } |
| |
| } // namespace dbus_utils |
| } // namespace brillo |
| |
| #endif // LIBBRILLO_BRILLO_DBUS_DATA_SERIALIZATION_H_ |