// 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_unittest.cc. #include <map> #include <memory> #include <string> #include <tuple> #include <utility> #include <vector> #include <base/files/scoped_file.h> #include <base/logging.h> #include <base/files/scoped_file.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_