Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support serialization of array, span and unordered_map #342

Merged
merged 3 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
280 changes: 232 additions & 48 deletions include/sdbus-c++/Message.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@
#include <sdbus-c++/Error.h>
#include <string>
#include <vector>
#include <array>
#if __cplusplus >= 202002L
#include <span>
#endif
#include <map>
#include <unordered_map>
#include <utility>
#include <cstdint>
#include <cassert>
Expand Down Expand Up @@ -85,10 +90,23 @@ namespace sdbus {
Message& operator<<(const ObjectPath &item);
Message& operator<<(const Signature &item);
Message& operator<<(const UnixFd &item);
template <typename _Element> Message& operator<<(const std::vector<_Element>& items);
template <typename _Key, typename _Value> Message& operator<<(const std::map<_Key, _Value>& items);
template <typename... _ValueTypes> Message& operator<<(const Struct<_ValueTypes...>& item);
template <typename... _ValueTypes> Message& operator<<(const std::tuple<_ValueTypes...>& item);

template <typename _Element, typename _Allocator>
Message& operator<<(const std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator<<(const std::array<_Element, _Size>& items);
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator<<(const std::span<_Element, _Extent>& items);
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
Message& operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
template <typename... _ValueTypes>
Message& operator<<(const Struct<_ValueTypes...>& item);
template <typename... _ValueTypes>
Message& operator<<(const std::tuple<_ValueTypes...>& item);
sangelovic marked this conversation as resolved.
Show resolved Hide resolved

Message& operator>>(bool& item);
Message& operator>>(int16_t& item);
Expand All @@ -105,10 +123,22 @@ namespace sdbus {
Message& operator>>(ObjectPath &item);
Message& operator>>(Signature &item);
Message& operator>>(UnixFd &item);
template <typename _Element> Message& operator>>(std::vector<_Element>& items);
template <typename _Key, typename _Value> Message& operator>>(std::map<_Key, _Value>& items);
template <typename... _ValueTypes> Message& operator>>(Struct<_ValueTypes...>& item);
template <typename... _ValueTypes> Message& operator>>(std::tuple<_ValueTypes...>& item);
template <typename _Element, typename _Allocator>
Message& operator>>(std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator>>(std::array<_Element, _Size>& items);
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator>>(std::span<_Element, _Extent>& items);
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
Message& operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
template <typename... _ValueTypes>
Message& operator>>(Struct<_ValueTypes...>& item);
template <typename... _ValueTypes>
Message& operator>>(std::tuple<_ValueTypes...>& item);
sangelovic marked this conversation as resolved.
Show resolved Hide resolved

Message& openContainer(const std::string& signature);
Message& closeContainer();
Expand Down Expand Up @@ -139,6 +169,7 @@ namespace sdbus {
void peekType(std::string& type, std::string& contents) const;
bool isValid() const;
bool isEmpty() const;
bool isAtEnd(bool complete) const;

void copyTo(Message& destination, bool complete) const;
void seal();
Expand All @@ -155,9 +186,27 @@ namespace sdbus {
class Factory;

private:
template <typename _Array>
void serializeArray(const _Array& items);
template <typename _Array>
void deserializeArray(_Array& items);
template <typename _Array>
void deserializeArrayFast(_Array& items);
template <typename _Element, typename _Allocator>
void deserializeArrayFast(std::vector<_Element, _Allocator>& items);
template <typename _Array>
void deserializeArraySlow(_Array& items);
template <typename _Element, typename _Allocator>
void deserializeArraySlow(std::vector<_Element, _Allocator>& items);

void appendArray(char type, const void *ptr, size_t size);
void readArray(char type, const void **ptr, size_t *size);

template <typename _Dictionary>
void serializeDictionary(const _Dictionary& items);
template <typename _Dictionary>
void deserializeDictionary(_Dictionary& items);

protected:
Message() = default;
explicit Message(internal::ISdBus* sdbus) noexcept;
Expand Down Expand Up @@ -256,32 +305,77 @@ namespace sdbus {
PlainMessage() = default;
};

template <typename _Element>
inline Message& Message::operator<<(const std::vector<_Element>& items)
template <typename _Element, typename _Allocator>
inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items)
{
serializeArray(items);

return *this;
}

template <typename _Element, std::size_t _Size>
inline Message& Message::operator<<(const std::array<_Element, _Size>& items)
{
serializeArray(items);

return *this;
}

#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator<<(const std::span<_Element, _Extent>& items)
{
serializeArray(items);

return *this;
}
#endif

template <typename _Array>
inline void Message::serializeArray(const _Array& items)
{
using ElementType = typename _Array::value_type;

// Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool,
// otherwise use step-by-step serialization of individual elements.
if constexpr (signature_of<_Element>::is_trivial_dbus_type && !std::is_same_v<_Element, bool>)
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
appendArray(*signature_of<_Element>::str().c_str(), items.data(), items.size() * sizeof(_Element));
appendArray(*signature_of<ElementType>::str().c_str(), items.data(), items.size() * sizeof(ElementType));
}
else
{
openContainer(signature_of<_Element>::str());
openContainer(signature_of<ElementType>::str());

for (const auto& item : items)
*this << item;

closeContainer();
}
}

template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items)
{
serializeDictionary(items);

return *this;
}

template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
inline Message& Message::operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
{
serializeDictionary(items);

return *this;
}

template <typename _Key, typename _Value>
inline Message& Message::operator<<(const std::map<_Key, _Value>& items)
template <typename _Dictionary>
inline void Message::serializeDictionary(const _Dictionary& items)
{
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;

const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";

openContainer(arraySignature);
Expand All @@ -295,8 +389,6 @@ namespace sdbus {
}

closeContainer();

return *this;
}

namespace detail
Expand All @@ -322,7 +414,7 @@ namespace sdbus {
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
assert(structSignature.size() > 2);
// Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
auto structContentSignature = structSignature.substr(1, structSignature.size() - 2);

openStruct(structContentSignature);
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
Expand All @@ -338,58 +430,152 @@ namespace sdbus {
return *this;
}

template <typename _Element>
inline Message& Message::operator>>(std::vector<_Element>& items)
template <typename _Element, typename _Allocator>
inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items)
{
deserializeArray(items);

return *this;
}

template <typename _Element, std::size_t _Size>
inline Message& Message::operator>>(std::array<_Element, _Size>& items)
{
deserializeArray(items);

return *this;
}

#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator>>(std::span<_Element, _Extent>& items)
{
deserializeArray(items);

return *this;
}
#endif

template <typename _Array>
inline void Message::deserializeArray(_Array& items)
{
using ElementType = typename _Array::value_type;

// Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool,
// otherwise use step-by-step deserialization of individual elements.
if constexpr (signature_of<_Element>::is_trivial_dbus_type && !std::is_same_v<_Element, bool>)
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
size_t arraySize{};
const _Element* arrayPtr{};

readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize);

items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
deserializeArrayFast(items);
}
else
{
if(!enterContainer(signature_of<_Element>::str()))
return *this;
deserializeArraySlow(items);
}
}

template <typename _Array>
inline void Message::deserializeArrayFast(_Array& items)
{
using ElementType = typename _Array::value_type;

size_t arraySize{};
const ElementType* arrayPtr{};

readArray(*signature_of<ElementType>::str().c_str(), (const void**)&arrayPtr, &arraySize);

size_t elementsInMsg = arraySize / sizeof(ElementType);
bool notEnoughSpace = items.size() < elementsInMsg;
SDBUS_THROW_ERROR_IF(notEnoughSpace, "Failed to deserialize array: not enough space in destination sequence", EINVAL);

std::copy_n(arrayPtr, elementsInMsg, items.begin());
}

template <typename _Element, typename _Allocator>
void Message::deserializeArrayFast(std::vector<_Element, _Allocator>& items)
{
size_t arraySize{};
const _Element* arrayPtr{};

readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize);

items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
}

template <typename _Array>
inline void Message::deserializeArraySlow(_Array& items)
{
using ElementType = typename _Array::value_type;

if(!enterContainer(signature_of<ElementType>::str()))
return;

for (auto& elem : items)
if (!(*this >> elem))
break; // Keep the rest in the destination sequence untouched

SDBUS_THROW_ERROR_IF(!isAtEnd(false), "Failed to deserialize array: not enough space in destination sequence", EINVAL);

clearFlags();

while (true)
{
_Element elem;
if (*this >> elem)
items.emplace_back(std::move(elem));
else
break;
}
exitContainer();
}

clearFlags();
template <typename _Element, typename _Allocator>
void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items)
{
if(!enterContainer(signature_of<_Element>::str()))
return;

exitContainer();
while (true)
{
_Element elem;
// TODO: Is there a way to find D-Bus message container size upfront? We could reserve space in vector upfront.
if (*this >> elem)
items.emplace_back(std::move(elem));
else
break;
}

clearFlags();

exitContainer();
}

template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items)
{
deserializeDictionary(items);

return *this;
}

template <typename _Key, typename _Value>
inline Message& Message::operator>>(std::map<_Key, _Value>& items)
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
inline Message& Message::operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
{
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
deserializeDictionary(items);

return *this;
}

template <typename _Dictionary>
inline void Message::deserializeDictionary(_Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;

const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";

if (!enterContainer(arraySignature))
return *this;
return;

while (true)
{
if (!enterDictEntry(dictEntrySignature))
break;

_Key key;
_Value value;
KeyType key;
ValueType value;
*this >> key >> value;

items.emplace(std::move(key), std::move(value));
Expand All @@ -400,8 +586,6 @@ namespace sdbus {
clearFlags();

exitContainer();

return *this;
}

namespace detail
Expand Down
Loading