diff options
Diffstat (limited to 'brillo/dbus/data_serialization.h')
-rw-r--r-- | brillo/dbus/data_serialization.h | 886 |
1 files changed, 886 insertions, 0 deletions
diff --git a/brillo/dbus/data_serialization.h b/brillo/dbus/data_serialization.h new file mode 100644 index 0000000..dbd96ac --- /dev/null +++ b/brillo/dbus/data_serialization.h @@ -0,0 +1,886 @@ +// 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 LIBCHROMEOS_BRILLO_DBUS_DATA_SERIALIZATION_H_ +#define LIBCHROMEOS_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 | dbus::FileDescriptor +// 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/logging.h> +#include <brillo/brillo_export.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); + } +}; + +// dbus::FileDescriptor ------------------------------------------------------- +BRILLO_EXPORT void AppendValueToWriter(dbus::MessageWriter* writer, + const dbus::FileDescriptor& value); +BRILLO_EXPORT bool PopValueFromReader(dbus::MessageReader* reader, + dbus::FileDescriptor* value); + +template<> +struct DBusType<dbus::FileDescriptor> { + inline static std::string GetSignature() { + return DBUS_TYPE_UNIX_FD_AS_STRING; + } + inline static void Write(dbus::MessageWriter* writer, + const dbus::FileDescriptor& value) { + AppendValueToWriter(writer, value); + } + inline static bool Read(dbus::MessageReader* reader, + dbus::FileDescriptor* 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 // LIBCHROMEOS_BRILLO_DBUS_DATA_SERIALIZATION_H_ |