Commit d3b18572 authored by Dominik Charousset's avatar Dominik Charousset

Enable config_value to parse its to_string output

parent ab7535c7
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
namespace caf { namespace caf {
/// Deserializes objects from sequence of bytes. /// Deserializes objects from sequence of bytes. Does not perform run-time type
/// checks.
class CAF_CORE_EXPORT binary_deserializer class CAF_CORE_EXPORT binary_deserializer
: public load_inspector_base<binary_deserializer> { : public load_inspector_base<binary_deserializer> {
public: public:
...@@ -112,7 +113,7 @@ public: ...@@ -112,7 +113,7 @@ public:
bool fetch_next_object_type(type_id_t& type) noexcept; bool fetch_next_object_type(type_id_t& type) noexcept;
constexpr bool begin_object(string_view) noexcept { constexpr bool begin_object(type_id_t, string_view) noexcept {
return true; return true;
} }
......
...@@ -33,7 +33,10 @@ ...@@ -33,7 +33,10 @@
namespace caf { namespace caf {
/// Serializes objects into a sequence of bytes. /// Serializes C++ objects into a sequence of bytes.
/// @note The binary data format may change between CAF versions and does not
/// perform any type checking at run-time. Thus the output of this
/// serializer is unsuitable for persistence layers.
class CAF_CORE_EXPORT binary_serializer class CAF_CORE_EXPORT binary_serializer
: public save_inspector_base<binary_serializer> { : public save_inspector_base<binary_serializer> {
public: public:
...@@ -95,9 +98,7 @@ public: ...@@ -95,9 +98,7 @@ public:
// -- interface functions ---------------------------------------------------- // -- interface functions ----------------------------------------------------
bool inject_next_object_type(type_id_t type); constexpr bool begin_object(type_id_t, string_view) noexcept {
constexpr bool begin_object(string_view) {
return true; return true;
} }
......
...@@ -172,6 +172,9 @@ public: ...@@ -172,6 +172,9 @@ public:
// -- utility ---------------------------------------------------------------- // -- utility ----------------------------------------------------------------
/// @private
type_id_t type_id() const noexcept;
/// @private /// @private
error_code<sec> default_construct(type_id_t); error_code<sec> default_construct(type_id_t);
...@@ -207,6 +210,16 @@ public: ...@@ -207,6 +210,16 @@ public:
return {reader.move_error()}; return {reader.move_error()};
} }
/// @experimental
template <class T>
error assign(const T& x) {
config_value_writer writer{this};
if (detail::save(writer, x))
return {};
else
return {writer.move_error()};
}
private: private:
// -- properties ------------------------------------------------------------- // -- properties -------------------------------------------------------------
......
...@@ -99,7 +99,7 @@ public: ...@@ -99,7 +99,7 @@ public:
bool fetch_next_object_type(type_id_t& type) override; bool fetch_next_object_type(type_id_t& type) override;
bool begin_object(string_view name) override; bool begin_object(type_id_t type, string_view name) override;
bool end_object() override; bool end_object() override;
......
...@@ -68,9 +68,7 @@ public: ...@@ -68,9 +68,7 @@ public:
// -- interface functions ---------------------------------------------------- // -- interface functions ----------------------------------------------------
bool inject_next_object_type(type_id_t type) override; bool begin_object(type_id_t type, string_view name) override;
bool begin_object(string_view name) override;
bool end_object() override; bool end_object() override;
...@@ -140,7 +138,6 @@ private: ...@@ -140,7 +138,6 @@ private:
bool push(config_value&& x); bool push(config_value&& x);
stack_type st_; stack_type st_;
string_view type_hint_;
}; };
} // namespace caf } // namespace caf
...@@ -62,13 +62,15 @@ public: ...@@ -62,13 +62,15 @@ public:
// -- interface functions ---------------------------------------------------- // -- interface functions ----------------------------------------------------
/// Reads run-time-type information for the next object. Requires that the /// Reads run-time-type information for the next object if available.
/// @ref serializer provided this information via
/// @ref serializer::inject_next_object_type.
virtual bool fetch_next_object_type(type_id_t& type) = 0; virtual bool fetch_next_object_type(type_id_t& type) = 0;
/// Begins processing of an object. /// Begins processing of an object, may perform a type check depending on the
virtual bool begin_object(string_view type) = 0; /// data format.
/// @param type 16-bit ID for known types, @ref invalid_type_id otherwise.
/// @param pretty_class_name Either the output of @ref type_name_or_anonymous
/// or the optionally defined pretty name.
virtual bool begin_object(type_id_t type, string_view pretty_class_name) = 0;
/// Ends processing of an object. /// Ends processing of an object.
virtual bool end_object() = 0; virtual bool end_object() = 0;
......
...@@ -32,9 +32,7 @@ public: ...@@ -32,9 +32,7 @@ public:
size_t result = 0; size_t result = 0;
bool inject_next_object_type(type_id_t type) override; bool begin_object(type_id_t, string_view) override;
bool begin_object(string_view) override;
bool end_object() override; bool end_object() override;
......
...@@ -58,7 +58,7 @@ public: ...@@ -58,7 +58,7 @@ public:
// -- serializer interface --------------------------------------------------- // -- serializer interface ---------------------------------------------------
bool begin_object(string_view name); bool begin_object(type_id_t, string_view name);
bool end_object(); bool end_object();
......
...@@ -53,7 +53,7 @@ public: ...@@ -53,7 +53,7 @@ public:
return false; return false;
} }
constexpr bool begin_object(string_view) { constexpr bool begin_object(type_id_t, string_view) {
return true; return true;
} }
......
...@@ -49,7 +49,7 @@ public: ...@@ -49,7 +49,7 @@ public:
return false; return false;
} }
constexpr bool begin_object(string_view) { constexpr bool begin_object(type_id_t, string_view) {
return true; return true;
} }
......
...@@ -275,6 +275,7 @@ public: ...@@ -275,6 +275,7 @@ public:
template <class Inspector, class LoadCallback> template <class Inspector, class LoadCallback>
struct object_with_load_callback_t { struct object_with_load_callback_t {
type_id_t object_type;
string_view object_name; string_view object_name;
Inspector* f; Inspector* f;
LoadCallback load_callback; LoadCallback load_callback;
...@@ -282,7 +283,7 @@ public: ...@@ -282,7 +283,7 @@ public:
template <class... Fields> template <class... Fields>
bool fields(Fields&&... fs) { bool fields(Fields&&... fs) {
using load_callback_result = decltype(load_callback()); using load_callback_result = decltype(load_callback());
if (!(f->begin_object(object_name) && (fs(*f) && ...))) if (!(f->begin_object(object_type, object_name) && (fs(*f) && ...)))
return false; return false;
if constexpr (std::is_same<load_callback_result, bool>::value) { if constexpr (std::is_same<load_callback_result, bool>::value) {
if (!load_callback()) { if (!load_callback()) {
...@@ -299,7 +300,7 @@ public: ...@@ -299,7 +300,7 @@ public:
} }
auto pretty_name(string_view name) && { auto pretty_name(string_view name) && {
return object_t{name, f}; return object_t{object_type, name, f};
} }
template <class F> template <class F>
...@@ -310,16 +311,19 @@ public: ...@@ -310,16 +311,19 @@ public:
template <class Inspector> template <class Inspector>
struct object_t { struct object_t {
type_id_t object_type;
string_view object_name; string_view object_name;
Inspector* f; Inspector* f;
template <class... Fields> template <class... Fields>
bool fields(Fields&&... fs) { bool fields(Fields&&... fs) {
return f->begin_object(object_name) && (fs(*f) && ...) && f->end_object(); return f->begin_object(object_type, object_name) //
&& (fs(*f) && ...) //
&& f->end_object();
} }
auto pretty_name(string_view name) && { auto pretty_name(string_view name) && {
return object_t{name, f}; return object_t{object_type, name, f};
} }
template <class F> template <class F>
...@@ -330,6 +334,7 @@ public: ...@@ -330,6 +334,7 @@ public:
template <class F> template <class F>
auto on_load(F fun) && { auto on_load(F fun) && {
return object_with_load_callback_t<Inspector, F>{ return object_with_load_callback_t<Inspector, F>{
object_type,
object_name, object_name,
f, f,
std::move(fun), std::move(fun),
......
...@@ -38,7 +38,8 @@ public: ...@@ -38,7 +38,8 @@ public:
template <class T> template <class T>
constexpr auto object(T&) noexcept { constexpr auto object(T&) noexcept {
return super::object_t<Subtype>{type_name_or_anonymous<T>(), dptr()}; return super::object_t<Subtype>{type_id_or_invalid<T>(),
type_name_or_anonymous<T>(), dptr()};
} }
template <class T> template <class T>
......
...@@ -171,6 +171,7 @@ public: ...@@ -171,6 +171,7 @@ public:
template <class Inspector, class SaveCallback> template <class Inspector, class SaveCallback>
struct object_with_save_callback_t { struct object_with_save_callback_t {
type_id_t object_type;
string_view object_name; string_view object_name;
Inspector* f; Inspector* f;
SaveCallback save_callback; SaveCallback save_callback;
...@@ -178,7 +179,7 @@ public: ...@@ -178,7 +179,7 @@ public:
template <class... Fields> template <class... Fields>
bool fields(Fields&&... fs) { bool fields(Fields&&... fs) {
using save_callback_result = decltype(save_callback()); using save_callback_result = decltype(save_callback());
if (!(f->begin_object(object_name) && (fs(*f) && ...))) if (!(f->begin_object(object_type, object_name) && (fs(*f) && ...)))
return false; return false;
if constexpr (std::is_same<save_callback_result, bool>::value) { if constexpr (std::is_same<save_callback_result, bool>::value) {
if (!save_callback()) { if (!save_callback()) {
...@@ -206,16 +207,19 @@ public: ...@@ -206,16 +207,19 @@ public:
template <class Inspector> template <class Inspector>
struct object_t { struct object_t {
type_id_t object_type;
string_view object_name; string_view object_name;
Inspector* f; Inspector* f;
template <class... Fields> template <class... Fields>
bool fields(Fields&&... fs) { bool fields(Fields&&... fs) {
return f->begin_object(object_name) && (fs(*f) && ...) && f->end_object(); return f->begin_object(object_type, object_name) //
&& (fs(*f) && ...) //
&& f->end_object();
} }
auto pretty_name(string_view name) && { auto pretty_name(string_view name) && {
return object_t{name, f}; return object_t{object_type, name, f};
} }
template <class F> template <class F>
...@@ -226,6 +230,7 @@ public: ...@@ -226,6 +230,7 @@ public:
template <class F> template <class F>
auto on_save(F fun) && { auto on_save(F fun) && {
return object_with_save_callback_t<Inspector, F>{ return object_with_save_callback_t<Inspector, F>{
object_type,
object_name, object_name,
f, f,
std::move(fun), std::move(fun),
......
...@@ -34,7 +34,8 @@ public: ...@@ -34,7 +34,8 @@ public:
template <class T> template <class T>
constexpr auto object(T&) noexcept { constexpr auto object(T&) noexcept {
return super::object_t<Subtype>{type_name_or_anonymous<T>(), dptr()}; return super::object_t<Subtype>{type_id_or_invalid<T>(),
type_name_or_anonymous<T>(), dptr()};
} }
template <class T> template <class T>
......
...@@ -164,6 +164,12 @@ enum class sec : uint8_t { ...@@ -164,6 +164,12 @@ enum class sec : uint8_t {
conversion_failed, conversion_failed,
/// A network connection was closed by the remote side. /// A network connection was closed by the remote side.
connection_closed, connection_closed,
/// An operation failed because run-time type information diverged from the
/// expected type.
type_clash,
/// An operation failed because the callee does not implement this
/// functionality.
unsupported_operation,
}; };
/// @relates sec /// @relates sec
......
...@@ -66,15 +66,10 @@ public: ...@@ -66,15 +66,10 @@ public:
// -- interface functions ---------------------------------------------------- // -- interface functions ----------------------------------------------------
/// Injects run-time-type information for the *next* object, i.e., causes the /// Begins processing of an object. May save the type information to the
/// next call to `begin_object` to write additional meta information. Allows a /// underlying storage to allow a @ref deserializer to retrieve and check the
/// @ref deserializer to retrieve the type for the next object via /// type information for data formats that provide deserialization.
/// @ref deserializer::fetch_next_object_type. virtual bool begin_object(type_id_t type, string_view name) = 0;
virtual bool inject_next_object_type(type_id_t type) = 0;
/// Begins processing of an object. Saves the type information
/// to the underlying storage.
virtual bool begin_object(string_view name) = 0;
/// Ends processing of an object. /// Ends processing of an object.
virtual bool end_object() = 0; virtual bool end_object() = 0;
......
...@@ -108,6 +108,15 @@ string_view type_name_or_anonymous() { ...@@ -108,6 +108,15 @@ string_view type_name_or_anonymous() {
return "anonymous"; return "anonymous";
} }
/// Returns `type_id_v<T>` if available, `invalid_type_id` otherwise.
template <class T>
type_id_t type_id_or_invalid() {
if constexpr (detail::is_complete<type_id<T>>)
return type_id<T>::value;
else
return invalid_type_id;
}
/// Returns the type name of given `type` or an empty string if `type` is an /// Returns the type name of given `type` or an empty string if `type` is an
/// invalid ID. /// invalid ID.
CAF_CORE_EXPORT string_view query_type_name(type_id_t type); CAF_CORE_EXPORT string_view query_type_name(type_id_t type);
......
...@@ -35,19 +35,23 @@ namespace { ...@@ -35,19 +35,23 @@ namespace {
template <class T> template <class T>
bool int_value(binary_deserializer& source, T& x) { bool int_value(binary_deserializer& source, T& x) {
auto tmp = std::make_unsigned_t<T>{}; auto tmp = std::make_unsigned_t<T>{};
if (!source.value(as_writable_bytes(make_span(&tmp, 1)))) if (source.value(as_writable_bytes(make_span(&tmp, 1)))) {
x = static_cast<T>(detail::from_network_order(tmp));
return true;
} else {
return false; return false;
x = static_cast<T>(detail::from_network_order(tmp)); }
return true;
} }
template <class T> template <class T>
bool float_value(binary_deserializer& source, T& x) { bool float_value(binary_deserializer& source, T& x) {
auto tmp = typename detail::ieee_754_trait<T>::packed_type{}; auto tmp = typename detail::ieee_754_trait<T>::packed_type{};
if (!int_value(source, tmp)) if (int_value(source, tmp)) {
x = detail::unpack754(tmp);
return true;
} else {
return false; return false;
x = detail::unpack754(tmp); }
return true;
} }
// Does not perform any range checks. // Does not perform any range checks.
...@@ -67,7 +71,10 @@ binary_deserializer::binary_deserializer(actor_system& sys) noexcept ...@@ -67,7 +71,10 @@ binary_deserializer::binary_deserializer(actor_system& sys) noexcept
} }
bool binary_deserializer::fetch_next_object_type(type_id_t& type) noexcept { bool binary_deserializer::fetch_next_object_type(type_id_t& type) noexcept {
return value(type); type = invalid_type_id;
emplace_error(sec::unsupported_operation,
"the default binary format does not embed type information");
return false;
} }
bool binary_deserializer::begin_sequence(size_t& list_size) noexcept { bool binary_deserializer::begin_sequence(size_t& list_size) noexcept {
......
...@@ -51,10 +51,6 @@ void binary_serializer::skip(size_t num_bytes) { ...@@ -51,10 +51,6 @@ void binary_serializer::skip(size_t num_bytes) {
write_pos_ += num_bytes; write_pos_ += num_bytes;
} }
bool binary_serializer::inject_next_object_type(type_id_t type) {
return value(type);
}
bool binary_serializer::begin_sequence(size_t list_size) { bool binary_serializer::begin_sequence(size_t list_size) {
// Use varbyte encoding to compress sequence size on the wire. // Use varbyte encoding to compress sequence size on the wire.
// For 64-bit values, the encoded representation cannot get larger than 10 // For 64-bit values, the encoded representation cannot get larger than 10
......
...@@ -149,6 +149,21 @@ const char* config_value::type_name_at_index(size_t index) noexcept { ...@@ -149,6 +149,21 @@ const char* config_value::type_name_at_index(size_t index) noexcept {
// -- utility ------------------------------------------------------------------ // -- utility ------------------------------------------------------------------
type_id_t config_value::type_id() const noexcept {
static constexpr type_id_t allowed_types[] = {
type_id_v<none_t>,
type_id_v<config_value::integer>,
type_id_v<config_value::boolean>,
type_id_v<config_value::real>,
type_id_v<timespan>,
type_id_v<uri>,
type_id_v<config_value::string>,
type_id_v<config_value::list>,
type_id_v<config_value::dictionary>,
};
return allowed_types[data_.index()];
}
error_code<sec> config_value::default_construct(type_id_t id) { error_code<sec> config_value::default_construct(type_id_t id) {
switch (id) { switch (id) {
case type_id_v<bool>: case type_id_v<bool>:
......
...@@ -122,11 +122,12 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) { ...@@ -122,11 +122,12 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) {
return false; return false;
}, },
[this, &type](const config_value* val) { [this, &type](const config_value* val) {
if (auto obj = get_if<settings>(val); obj == nullptr) { auto tid = val->type_id();
emplace_error(sec::conversion_failed, "cannot read input as object"); if (tid != type_id_v<config_value::dictionary>) {
return false; type = tid;
return true;
} else { } else {
return fetch_object_type(obj, type); return fetch_object_type(get_if<settings>(val), type);
} }
}, },
[this](key_ptr) { [this](key_ptr) {
...@@ -146,11 +147,13 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) { ...@@ -146,11 +147,13 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) {
emplace_error(sec::runtime_error, "list index out of bounds"); emplace_error(sec::runtime_error, "list index out of bounds");
return false; return false;
} }
if (auto obj = get_if<settings>(std::addressof(seq.current())); !obj) { auto& val = seq.current();
emplace_error(sec::conversion_failed, "cannot read input as object"); auto tid = val.type_id();
return false; if (tid != type_id_v<config_value::dictionary>) {
type = tid;
return true;
} else { } else {
return fetch_object_type(obj, type); return fetch_object_type(get_if<settings>(&val), type);
} }
}, },
[this](associative_array&) { [this](associative_array&) {
...@@ -162,7 +165,7 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) { ...@@ -162,7 +165,7 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) {
} }
} }
bool config_value_reader::begin_object(string_view) { bool config_value_reader::begin_object(type_id_t type, string_view) {
if (st_.empty()) { if (st_.empty()) {
emplace_error(sec::runtime_error, emplace_error(sec::runtime_error,
"tried to read multiple objects from the root object"); "tried to read multiple objects from the root object");
...@@ -216,7 +219,27 @@ bool config_value_reader::begin_object(string_view) { ...@@ -216,7 +219,27 @@ bool config_value_reader::begin_object(string_view) {
"fetch_next_object_type called inside associative array"); "fetch_next_object_type called inside associative array");
return false; return false;
}); });
return visit(f, st_.top()); if (visit(f, st_.top())) {
// Perform a type check if type is a valid ID and the object contains an
// "@type" field.
if (type != invalid_type_id) {
CAF_ASSERT(holds_alternative<const settings*>(st_.top()));
auto obj = get<const settings*>(st_.top());
auto want = query_type_name(type);
if (auto i = obj->find("@type"); i != obj->end()) {
if (auto got = get_if<std::string>(std::addressof(i->second))) {
if (want != *got) {
emplace_error(sec::type_clash, "expected type: " + to_string(want),
"found type: " + *got);
return false;
}
}
}
}
return true;
} else {
return false;
}
} }
bool config_value_reader::end_object() { bool config_value_reader::end_object() {
...@@ -563,15 +586,14 @@ bool config_value_reader::value(span<byte> bytes) { ...@@ -563,15 +586,14 @@ bool config_value_reader::value(span<byte> bytes) {
bool config_value_reader::fetch_object_type(const settings* obj, bool config_value_reader::fetch_object_type(const settings* obj,
type_id_t& type) { type_id_t& type) {
if (auto str = get_if<std::string>(obj, "@type"); str == nullptr) { if (auto str = get_if<std::string>(obj, "@type"); str == nullptr) {
emplace_error(sec::runtime_error, type = type_id_v<config_value::dictionary>;
"cannot fetch object type: no '@type' entry found"); return true;
return false; } else if (auto id = query_type_id(*str); id != invalid_type_id) {
} else if (auto id = query_type_id(*str); id == invalid_type_id) {
emplace_error(sec::runtime_error, "no such type: " + *str);
return false;
} else {
type = id; type = id;
return true; return true;
} else {
emplace_error(sec::runtime_error, "unknown type: " + *str);
return false;
} }
} }
......
...@@ -66,18 +66,7 @@ config_value_writer::~config_value_writer() { ...@@ -66,18 +66,7 @@ config_value_writer::~config_value_writer() {
// -- interface functions ------------------------------------------------------ // -- interface functions ------------------------------------------------------
bool config_value_writer::inject_next_object_type(type_id_t type) { bool config_value_writer::begin_object(type_id_t type, string_view) {
CHECK_NOT_EMPTY();
type_hint_ = query_type_name(type);
if (type_hint_.empty()) {
emplace_error(sec::runtime_error,
"query_type_name returned an empty string for type ID");
return false;
}
return true;
}
bool config_value_writer::begin_object(string_view) {
CHECK_NOT_EMPTY(); CHECK_NOT_EMPTY();
auto f = detail::make_overload( auto f = detail::make_overload(
[this](config_value* x) { [this](config_value* x) {
...@@ -118,10 +107,8 @@ bool config_value_writer::begin_object(string_view) { ...@@ -118,10 +107,8 @@ bool config_value_writer::begin_object(string_view) {
}); });
if (!visit(f, st_.top())) if (!visit(f, st_.top()))
return false; return false;
if (!type_hint_.empty()) { if (type != invalid_type_id)
put(*get<settings*>(st_.top()), "@type", type_hint_); put(*get<settings*>(st_.top()), "@type", query_type_name(type));
type_hint_ = string_view{};
}
return true; return true;
} }
......
...@@ -26,11 +26,7 @@ ...@@ -26,11 +26,7 @@
namespace caf::detail { namespace caf::detail {
bool serialized_size_inspector::inject_next_object_type(type_id_t type) { bool serialized_size_inspector::begin_object(type_id_t, string_view) {
return value(type);
}
bool serialized_size_inspector::begin_object(string_view) {
return true; return true;
} }
......
...@@ -49,7 +49,7 @@ void escape(std::string& result, char c) { ...@@ -49,7 +49,7 @@ void escape(std::string& result, char c) {
namespace caf::detail { namespace caf::detail {
bool stringification_inspector::begin_object(string_view name) { bool stringification_inspector::begin_object(type_id_t, string_view name) {
sep(); sep();
if (name != "std::string") { if (name != "std::string") {
result_.insert(result_.end(), name.begin(), name.end()); result_.insert(result_.end(), name.begin(), name.end());
......
...@@ -59,7 +59,7 @@ template <class Deserializer> ...@@ -59,7 +59,7 @@ template <class Deserializer>
bool load_data(Deserializer& source, message::data_ptr& data) { bool load_data(Deserializer& source, message::data_ptr& data) {
// For machine-to-machine data formats, we prefix the type information. // For machine-to-machine data formats, we prefix the type information.
if (!source.has_human_readable_format()) { if (!source.has_human_readable_format()) {
GUARDED(source.begin_object("message")); GUARDED(source.begin_object(type_id_v<message>, "message"));
GUARDED(source.begin_field("types")); GUARDED(source.begin_field("types"));
size_t msg_size = 0; size_t msg_size = 0;
GUARDED(source.begin_sequence(msg_size)); GUARDED(source.begin_sequence(msg_size));
...@@ -159,8 +159,6 @@ bool load_data(Deserializer& source, message::data_ptr& data) { ...@@ -159,8 +159,6 @@ bool load_data(Deserializer& source, message::data_ptr& data) {
using unique_void_ptr = std::unique_ptr<void, free_t>; using unique_void_ptr = std::unique_ptr<void, free_t>;
auto msg_size = size_t{0}; auto msg_size = size_t{0};
std::vector<object_ptr> objects; std::vector<object_ptr> objects;
GUARDED(source.begin_object("message"));
GUARDED(source.begin_field("values"));
GUARDED(source.begin_sequence(msg_size)); GUARDED(source.begin_sequence(msg_size));
if (msg_size > 0) { if (msg_size > 0) {
// Deserialize message elements individually. // Deserialize message elements individually.
...@@ -185,7 +183,7 @@ bool load_data(Deserializer& source, message::data_ptr& data) { ...@@ -185,7 +183,7 @@ bool load_data(Deserializer& source, message::data_ptr& data) {
STOP(sec::unknown_type); STOP(sec::unknown_type);
} }
} }
GUARDED(source.end_field() && source.end_sequence()); GUARDED(source.end_sequence());
// Merge elements into a single message data object. // Merge elements into a single message data object.
intrusive_ptr<detail::message_data> ptr; intrusive_ptr<detail::message_data> ptr;
if (auto vptr = malloc(sizeof(detail::message_data) + data_size)) { if (auto vptr = malloc(sizeof(detail::message_data) + data_size)) {
...@@ -203,13 +201,11 @@ bool load_data(Deserializer& source, message::data_ptr& data) { ...@@ -203,13 +201,11 @@ bool load_data(Deserializer& source, message::data_ptr& data) {
pos += x.meta->padded_size; pos += x.meta->padded_size;
} }
data.reset(ptr.release(), false); data.reset(ptr.release(), false);
return source.end_object(); return true;
} else { } else {
data.reset(); data.reset();
return source.end_sequence();
} }
return source.end_sequence() //
&& source.end_field() //
&& source.end_object();
} }
} // namespace } // namespace
...@@ -242,18 +238,18 @@ save_data(Serializer& sink, const message::data_ptr& data) { ...@@ -242,18 +238,18 @@ save_data(Serializer& sink, const message::data_ptr& data) {
if (!sink.has_human_readable_format()) { if (!sink.has_human_readable_format()) {
if (data == nullptr) { if (data == nullptr) {
// Short-circuit empty tuples. // Short-circuit empty tuples.
return sink.begin_object("message") // return sink.begin_object(type_id_v<message>, "message") //
&& sink.begin_field("types") // && sink.begin_field("types") //
&& sink.begin_sequence(0) // && sink.begin_sequence(0) //
&& sink.end_sequence() // && sink.end_sequence() //
&& sink.end_field() // && sink.end_field() //
&& sink.begin_field("values") // && sink.begin_field("values") //
&& sink.begin_tuple(0) // && sink.begin_tuple(0) //
&& sink.end_tuple() // && sink.end_tuple() //
&& sink.end_field() // && sink.end_field() //
&& sink.end_object(); && sink.end_object();
} }
GUARDED(sink.begin_object("message")); GUARDED(sink.begin_object(type_id_v<message>, "message"));
auto type_ids = data->types(); auto type_ids = data->types();
// Write type information. // Write type information.
GUARDED(sink.begin_field("types") && sink.begin_sequence(type_ids.size())); GUARDED(sink.begin_field("types") && sink.begin_sequence(type_ids.size()));
...@@ -274,25 +270,17 @@ save_data(Serializer& sink, const message::data_ptr& data) { ...@@ -274,25 +270,17 @@ save_data(Serializer& sink, const message::data_ptr& data) {
// dynamically-typed objects. // dynamically-typed objects.
if (data == nullptr) { if (data == nullptr) {
// Short-circuit empty tuples. // Short-circuit empty tuples.
return sink.begin_object("message") // return sink.begin_sequence(0) && sink.end_sequence();
&& sink.begin_field("values") //
&& sink.begin_sequence(0) //
&& sink.end_sequence() //
&& sink.end_field() //
&& sink.end_object();
} }
auto type_ids = data->types(); auto type_ids = data->types();
GUARDED(sink.begin_object("message") // GUARDED(sink.begin_sequence(type_ids.size()));
&& sink.begin_field("values") //
&& sink.begin_sequence(type_ids.size()));
auto storage = data->storage(); auto storage = data->storage();
for (auto id : type_ids) { for (auto id : type_ids) {
auto& meta = gmos[id]; auto& meta = gmos[id];
GUARDED(sink.inject_next_object_type(id) // GUARDED(save(meta, sink, storage));
&& save(meta, sink, storage));
storage += meta.padded_size; storage += meta.padded_size;
} }
return sink.end_sequence() && sink.end_field() && sink.end_object(); return sink.end_sequence();
} }
} // namespace } // namespace
......
...@@ -41,22 +41,22 @@ namespace { ...@@ -41,22 +41,22 @@ namespace {
template <class Serializer> template <class Serializer>
bool serialize_impl(Serializer& sink, const tracing_data_ptr& x) { bool serialize_impl(Serializer& sink, const tracing_data_ptr& x) {
if (!x) { if (!x) {
return sink.begin_object("tracing_data") // return sink.begin_object(invalid_type_id, "tracing_data") //
&& sink.begin_field("value", false) // && sink.begin_field("value", false) //
&& sink.end_field() // && sink.end_field() //
&& sink.end_object(); && sink.end_object();
} }
return sink.begin_object("tracing_data") // return sink.begin_object(invalid_type_id, "tracing_data") //
&& sink.begin_field("value", true) // && sink.begin_field("value", true) //
&& x->serialize(sink) // && x->serialize(sink) //
&& sink.end_field() // && sink.end_field() //
&& sink.end_object(); && sink.end_object();
} }
template <class Deserializer> template <class Deserializer>
bool deserialize_impl(Deserializer& source, tracing_data_ptr& x) { bool deserialize_impl(Deserializer& source, tracing_data_ptr& x) {
bool is_present = false; bool is_present = false;
if (!source.begin_object("tracing_data") if (!source.begin_object(invalid_type_id, "tracing_data")
|| !source.begin_field("value", is_present)) || !source.begin_field("value", is_present))
return false; return false;
if (!is_present) if (!is_present)
......
...@@ -672,7 +672,9 @@ SCENARIO("config values can default-construct all registered types") { ...@@ -672,7 +672,9 @@ SCENARIO("config values can default-construct all registered types") {
auto val = from(type_id_v<my_request>); auto val = from(type_id_v<my_request>);
CHECK_EQ(val.get_data().index(), 8u); CHECK_EQ(val.get_data().index(), 8u);
auto& dict = val.as_dictionary(); auto& dict = val.as_dictionary();
CHECK_EQ(keys(dict), std::vector<std::string>({"a", "b"})); CHECK_EQ(keys(dict), std::vector<std::string>({"@type", "a", "b"}));
CHECK_EQ(dict["@type"].get_data().index(), 6u);
CHECK_EQ(get_as<std::string>(dict["@type"]), "my_request"s);
CHECK_EQ(dict["a"].get_data().index(), 1u); CHECK_EQ(dict["a"].get_data().index(), 1u);
CHECK_EQ(get_as<int32_t>(dict["a"]), 0); CHECK_EQ(get_as<int32_t>(dict["a"]), 0);
CHECK_EQ(dict["b"].get_data().index(), 1u); CHECK_EQ(dict["b"].get_data().index(), 1u);
...@@ -682,6 +684,41 @@ SCENARIO("config values can default-construct all registered types") { ...@@ -682,6 +684,41 @@ SCENARIO("config values can default-construct all registered types") {
} }
} }
#define CHECK_ROUNDTRIP(init_val, expected_str) \
do { \
config_value x; \
if (auto assign_failed = x.assign(init_val); CHECK(!assign_failed)) { \
auto str = to_string(x); \
CHECK_EQ(str, expected_str); \
auto parsed = config_value::parse(str); \
using init_val_type = decltype(init_val); \
if (CHECK(parsed)) { \
if constexpr (!std::is_same<init_val_type, message>::value) \
CHECK_EQ(get_as<init_val_type>(*parsed), init_val); \
else \
CHECK_EQ(to_string(*parsed), str); \
} \
} \
} while (false)
SCENARIO("config values can parse their own to_string output") {
GIVEN("a config value") {
WHEN("assigning a value and then calling to_string on it") {
THEN("then config_value::parse reconstitutes the original value") {
CHECK_ROUNDTRIP(0, "0");
CHECK_ROUNDTRIP("hello world"s, "hello world");
CHECK_ROUNDTRIP(std::vector<int>({1, 2, 3}), "[1, 2, 3]");
CHECK_ROUNDTRIP(my_request(1, 2),
R"_({"@type" = "my_request", a = 1, b = 2})_");
CHECK_ROUNDTRIP(std::make_tuple(add_atom_v, 1, 2),
R"_([{"@type" = "caf::add_atom"}, 1, 2])_");
CHECK_ROUNDTRIP(make_message(add_atom_v, 1, 2),
R"_([{"@type" = "caf::add_atom"}, 1, 2])_");
}
}
}
}
CAF_TEST(default_constructed) { CAF_TEST(default_constructed) {
config_value x; config_value x;
CAF_CHECK_EQUAL(holds_alternative<none_t>(x), true); CAF_CHECK_EQUAL(holds_alternative<none_t>(x), true);
......
...@@ -58,7 +58,7 @@ struct testee : deserializer { ...@@ -58,7 +58,7 @@ struct testee : deserializer {
return false; return false;
} }
bool begin_object(string_view object_name) override { bool begin_object(type_id_t, string_view object_name) override {
new_line(); new_line();
indent += 2; indent += 2;
log += "begin object "; log += "begin object ";
......
...@@ -49,15 +49,7 @@ struct testee : serializer { ...@@ -49,15 +49,7 @@ struct testee : serializer {
log.insert(log.end(), indent, ' '); log.insert(log.end(), indent, ' ');
} }
bool inject_next_object_type(type_id_t type) override { bool begin_object(type_id_t, string_view object_name) override {
new_line();
log += "next object type: ";
auto tn = detail::global_meta_object(type)->type_name;
log.insert(log.end(), tn.begin(), tn.end());
return true;
}
bool begin_object(string_view object_name) override {
new_line(); new_line();
indent += 2; indent += 2;
log += "begin object "; log += "begin object ";
...@@ -734,18 +726,11 @@ end object)_"); ...@@ -734,18 +726,11 @@ end object)_");
f.set_has_human_readable_format(true); f.set_has_human_readable_format(true);
CAF_CHECK(inspect(f, x)); CAF_CHECK(inspect(f, x));
CAF_CHECK_EQUAL(f.log, R"_( CAF_CHECK_EQUAL(f.log, R"_(
begin object message begin sequence of size 3
begin field values int32_t value
begin sequence of size 3 std::string value
next object type: int32_t double value
int32_t value end sequence)_");
next object type: std::string
std::string value
next object type: double
double value
end sequence
end field
end object)_");
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment