Commit 55153b94 authored by Dominik Charousset's avatar Dominik Charousset

Redesign serialization classes

- Remove `data_processor`. This class unified serializer and
  deserializer interfaces to reduce code duplication. However, the
  implementation is quite complex and enforces virtual dispatch for all
  derived types.
- Add `write_inspector` and `read_inspector` utility classes that inject
  an `operator()`. The previous recursive argument unrolling in the
  `data_processor` no longer has its place in C++17. The two new classes
  dispatch on `apply` member functions of their subtype via CRTP and use
  `if constexpr` to dispatch to the correct functions.
- Refactor `serializer` and `deserializer` classes. These two classes
  still serve as base types for abstract inspectors, but no longer are
  connected through a common base.
- Add `binary_serializer` and `binary_deserializer` classes. These two
  classes are the workhorses in CAF that run in performance-critical
  code. Hence, these implementations must reduce overhead as much as
  possible. Because of this, they do not inherit from the abstract
  interfaces and internally use the new `error_code` class over the
  generic `error` class.
- Add handlers for `binary_serializer` and `binary_deserializer` to all
  CAF classes in addition to the generic versions.
parent 2cb70865
...@@ -44,13 +44,13 @@ set(LIBCAF_CORE_SRCS ...@@ -44,13 +44,13 @@ set(LIBCAF_CORE_SRCS
src/attachable.cpp src/attachable.cpp
src/behavior.cpp src/behavior.cpp
src/binary_deserializer.cpp src/binary_deserializer.cpp
src/binary_serializer.cpp
src/blocking_actor.cpp src/blocking_actor.cpp
src/config_option.cpp src/config_option.cpp
src/config_option_adder.cpp src/config_option_adder.cpp
src/config_option_set.cpp src/config_option_set.cpp
src/config_value.cpp src/config_value.cpp
src/decorator/sequencer.cpp src/decorator/sequencer.cpp
src/decorator/splitter.cpp
src/default_attachable.cpp src/default_attachable.cpp
src/defaults.cpp src/defaults.cpp
src/deserializer.cpp src/deserializer.cpp
...@@ -61,8 +61,6 @@ set(LIBCAF_CORE_SRCS ...@@ -61,8 +61,6 @@ set(LIBCAF_CORE_SRCS
src/detail/behavior_impl.cpp src/detail/behavior_impl.cpp
src/detail/behavior_stack.cpp src/detail/behavior_stack.cpp
src/detail/blocking_behavior.cpp src/detail/blocking_behavior.cpp
src/detail/concatenated_tuple.cpp
src/detail/decorated_tuple.cpp
src/detail/dynamic_message_data.cpp src/detail/dynamic_message_data.cpp
src/detail/fnv_hash.cpp src/detail/fnv_hash.cpp
src/detail/get_mac_addresses.cpp src/detail/get_mac_addresses.cpp
...@@ -70,14 +68,12 @@ set(LIBCAF_CORE_SRCS ...@@ -70,14 +68,12 @@ set(LIBCAF_CORE_SRCS
src/detail/get_root_uuid.cpp src/detail/get_root_uuid.cpp
src/detail/ini_consumer.cpp src/detail/ini_consumer.cpp
src/detail/invoke_result_visitor.cpp src/detail/invoke_result_visitor.cpp
src/detail/merged_tuple.cpp
src/detail/message_data.cpp src/detail/message_data.cpp
src/detail/parse.cpp src/detail/parse.cpp
src/detail/parser/chars.cpp src/detail/parser/chars.cpp
src/detail/pretty_type_name.cpp src/detail/pretty_type_name.cpp
src/detail/private_thread.cpp src/detail/private_thread.cpp
src/detail/ripemd_160.cpp src/detail/ripemd_160.cpp
src/detail/serialized_size.cpp
src/detail/set_thread_name.cpp src/detail/set_thread_name.cpp
src/detail/shared_spinlock.cpp src/detail/shared_spinlock.cpp
src/detail/simple_actor_clock.cpp src/detail/simple_actor_clock.cpp
...@@ -92,6 +88,7 @@ set(LIBCAF_CORE_SRCS ...@@ -92,6 +88,7 @@ set(LIBCAF_CORE_SRCS
src/downstream_manager_base.cpp src/downstream_manager_base.cpp
src/duration.cpp src/duration.cpp
src/error.cpp src/error.cpp
src/error_code.cpp
src/event_based_actor.cpp src/event_based_actor.cpp
src/execution_unit.cpp src/execution_unit.cpp
src/exit_reason.cpp src/exit_reason.cpp
......
...@@ -47,6 +47,9 @@ public: ...@@ -47,6 +47,9 @@ public:
/// Serialize this group to `sink`. /// Serialize this group to `sink`.
virtual error save(serializer& sink) const = 0; virtual error save(serializer& sink) const = 0;
/// Serialize this group to `sink`.
virtual error save(binary_serializer& sink) const = 0;
/// Subscribes `who` to this group and returns `true` on success /// Subscribes `who` to this group and returns `true` on success
/// or `false` if `who` is already subscribed. /// or `false` if `who` is already subscribed.
virtual bool subscribe(strong_actor_ptr who) = 0; virtual bool subscribe(strong_actor_ptr who) = 0;
......
...@@ -133,8 +133,6 @@ public: ...@@ -133,8 +133,6 @@ public:
intptr_t compare(const strong_actor_ptr&) const noexcept; intptr_t compare(const strong_actor_ptr&) const noexcept;
static actor splice_impl(std::initializer_list<actor> xs);
actor(actor_control_block*, bool); actor(actor_control_block*, bool);
/// @endcond /// @endcond
...@@ -175,12 +173,6 @@ private: ...@@ -175,12 +173,6 @@ private:
/// Combine `f` and `g` so that `(f*g)(x) = f(g(x))`. /// Combine `f` and `g` so that `(f*g)(x) = f(g(x))`.
actor operator*(actor f, actor g); actor operator*(actor f, actor g);
/// @relates actor
template <class... Ts>
actor splice(const actor& x, const actor& y, const Ts&... zs) {
return actor::splice_impl({x, y, zs...});
}
/// @relates actor /// @relates actor
bool operator==(const actor& lhs, abstract_actor* rhs); bool operator==(const actor& lhs, abstract_actor* rhs);
......
...@@ -395,8 +395,6 @@ public: ...@@ -395,8 +395,6 @@ public:
settings& result); settings& result);
protected: protected:
virtual std::string make_help_text(const std::vector<message::cli_arg>&);
config_option_set custom_options_; config_option_set custom_options_;
private: private:
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include "caf/binary_deserializer.hpp" #include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp" #include "caf/binary_serializer.hpp"
#include "caf/blocking_actor.hpp" #include "caf/blocking_actor.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/composable_behavior.hpp" #include "caf/composable_behavior.hpp"
#include "caf/composed_behavior.hpp" #include "caf/composed_behavior.hpp"
#include "caf/config_option.hpp" #include "caf/config_option.hpp"
...@@ -103,10 +104,7 @@ ...@@ -103,10 +104,7 @@
#include "caf/spawn_options.hpp" #include "caf/spawn_options.hpp"
#include "caf/stateful_actor.hpp" #include "caf/stateful_actor.hpp"
#include "caf/stream.hpp" #include "caf/stream.hpp"
#include "caf/stream_deserializer.hpp"
#include "caf/stream_serializer.hpp"
#include "caf/stream_slot.hpp" #include "caf/stream_slot.hpp"
#include "caf/streambuf.hpp"
#include "caf/system_messages.hpp" #include "caf/system_messages.hpp"
#include "caf/term.hpp" #include "caf/term.hpp"
#include "caf/thread_hook.hpp" #include "caf/thread_hook.hpp"
......
...@@ -43,6 +43,10 @@ template <class T> ...@@ -43,6 +43,10 @@ template <class T>
struct is_allowed_unsafe_message_type<const T&> struct is_allowed_unsafe_message_type<const T&>
: allowed_unsafe_message_type<T> {}; : allowed_unsafe_message_type<T> {};
template <class T>
constexpr bool is_allowed_unsafe_message_type_v
= allowed_unsafe_message_type<T>::value;
} // namespace caf } // namespace caf
#define CAF_ALLOW_UNSAFE_MESSAGE_TYPE(type_name) \ #define CAF_ALLOW_UNSAFE_MESSAGE_TYPE(type_name) \
......
...@@ -25,15 +25,12 @@ ...@@ -25,15 +25,12 @@
#include "caf/detail/atom_val.hpp" #include "caf/detail/atom_val.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/meta/load_callback.hpp"
namespace caf { namespace caf {
/// The value type of atoms. /// The value type of atoms.
enum class atom_value : uint64_t { enum class atom_value : uint64_t {};
/// @cond PRIVATE
dirty_little_hack = 31337
/// @endcond
};
/// @relates atom_value /// @relates atom_value
std::string to_string(atom_value x); std::string to_string(atom_value x);
......
...@@ -19,64 +19,44 @@ ...@@ -19,64 +19,44 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <cstdint> #include <string>
#include <vector> #include <string_view>
#include <tuple>
#include "caf/byte.hpp" #include <utility>
#include "caf/deserializer.hpp"
#include "caf/error_code.hpp"
#include "caf/fwd.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp" #include "caf/span.hpp"
#include "caf/write_inspector.hpp"
namespace caf { namespace caf {
/// Implements the deserializer interface with a binary serialization protocol. /// Implements the deserializer interface with a binary serialization protocol.
class binary_deserializer final : public deserializer { class binary_deserializer : public write_inspector<binary_deserializer> {
public: public:
// -- member types ----------------------------------------------------------- // -- member types -----------------------------------------------------------
using super = deserializer; using result_type = error;
// -- constructors, destructors, and assignment operators --------------------
binary_deserializer(actor_system& sys, span<const byte> bytes); using apply_result = error_code<sec>;
binary_deserializer(execution_unit* ctx, span<const byte> bytes); // -- constructors, destructors, and assignment operators --------------------
template <class T> template <class Container>
binary_deserializer(actor_system& sys, const std::vector<T>& buf) binary_deserializer(actor_system& sys, const Container& input) noexcept
: binary_deserializer(sys, as_bytes(make_span(buf))) { : binary_deserializer(sys) {
// nop reset(as_bytes(make_span(input)));
} }
template <class T> template <class Container>
binary_deserializer(execution_unit* ctx, const std::vector<T>& buf) binary_deserializer(execution_unit* ctx, const Container& input) noexcept
: binary_deserializer(ctx, as_bytes(make_span(buf))) { : context_(ctx) {
// nop reset(as_bytes(make_span(input)));
} }
binary_deserializer(actor_system& sys, const char* buf, size_t buf_size);
binary_deserializer(execution_unit* ctx, const char* buf, size_t buf_size);
// -- overridden member functions --------------------------------------------
error begin_object(uint16_t& typenr, std::string& name) override;
error end_object() override;
error begin_sequence(size_t& list_size) override;
error end_sequence() override;
error apply_raw(size_t num_bytes, void* data) override;
// -- properties ------------------------------------------------------------- // -- properties -------------------------------------------------------------
/// Returns the current read position.
const char* current() const CAF_DEPRECATED_MSG("use remaining() instead");
/// Returns the past-the-end iterator.
const char* end() const CAF_DEPRECATED_MSG("use remaining() instead");
/// Returns how many bytes are still available to read. /// Returns how many bytes are still available to read.
size_t remaining() const noexcept { size_t remaining() const noexcept {
return static_cast<size_t>(end_ - current_); return static_cast<size_t>(end_ - current_);
...@@ -87,49 +67,99 @@ public: ...@@ -87,49 +67,99 @@ public:
return make_span(current_, end_); return make_span(current_, end_);
} }
/// Returns the current execution unit.
execution_unit* context() const noexcept {
return context_;
}
/// Jumps `num_bytes` forward. /// Jumps `num_bytes` forward.
/// @pre `num_bytes <= remaining()` /// @pre `num_bytes <= remaining()`
void skip(size_t num_bytes); void skip(size_t num_bytes) noexcept;
/// Assings a new input. /// Assings a new input.
void reset(span<const byte> bytes); void reset(span<const byte> bytes) noexcept;
/// Returns the current read position.
const byte* current() const noexcept {
return current_;
}
/// Returns the end of the assigned memory block.
const byte* end() const noexcept {
return end_;
}
// -- overridden member functions --------------------------------------------
apply_result begin_object(uint16_t& typenr, std::string& name);
apply_result end_object() noexcept;
apply_result begin_sequence(size_t& list_size) noexcept;
apply_result end_sequence() noexcept;
apply_result apply(bool&) noexcept;
protected: apply_result apply(byte&) noexcept;
error apply_impl(int8_t&) override;
error apply_impl(uint8_t&) override; apply_result apply(int8_t&) noexcept;
error apply_impl(int16_t&) override; apply_result apply(uint8_t&) noexcept;
error apply_impl(uint16_t&) override; apply_result apply(int16_t&) noexcept;
error apply_impl(int32_t&) override; apply_result apply(uint16_t&) noexcept;
error apply_impl(uint32_t&) override; apply_result apply(int32_t&) noexcept;
error apply_impl(int64_t&) override; apply_result apply(uint32_t&) noexcept;
error apply_impl(uint64_t&) override; apply_result apply(int64_t&) noexcept;
error apply_impl(float&) override; apply_result apply(uint64_t&) noexcept;
error apply_impl(double&) override; apply_result apply(float&) noexcept;
error apply_impl(long double&) override; apply_result apply(double&) noexcept;
error apply_impl(std::string&) override; apply_result apply(long double&);
error apply_impl(std::u16string&) override; apply_result apply(timespan& x) noexcept;
error apply_impl(std::u32string&) override; apply_result apply(timestamp& x) noexcept;
apply_result apply(span<byte>) noexcept;
apply_result apply(std::string&);
apply_result apply(std::u16string&);
apply_result apply(std::u32string&);
template <class Enum, class = std::enable_if_t<std::is_enum<Enum>::value>>
auto apply(Enum& x) noexcept {
return apply(reinterpret_cast<std::underlying_type_t<Enum>&>(x));
}
apply_result apply(std::vector<bool>& xs);
private: private:
bool range_check(size_t read_size) { explicit binary_deserializer(actor_system& sys) noexcept;
/// Checks whether we can read `read_size` more bytes.
bool range_check(size_t read_size) const noexcept {
return current_ + read_size <= end_; return current_ + read_size <= end_;
} }
/// Points to the current read position.
const byte* current_; const byte* current_;
/// Points to the end of the assigned memory block.
const byte* end_; const byte* end_;
/// Provides access to the ::proxy_registry and to the ::actor_system.
execution_unit* context_;
}; };
} // namespace caf } // namespace caf
...@@ -18,12 +18,133 @@ ...@@ -18,12 +18,133 @@
#pragma once #pragma once
#include <cstddef>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector> #include <vector>
#include "caf/serializer_impl.hpp" #include "caf/byte.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/error_code.hpp"
#include "caf/fwd.hpp"
#include "caf/read_inspector.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
namespace caf { namespace caf {
using binary_serializer = serializer_impl<std::vector<char>>; class binary_serializer : public read_inspector<binary_serializer> {
public:
// -- member types -----------------------------------------------------------
using result_type = error;
// -- constructors, destructors, and assignment operators --------------------
binary_serializer(actor_system& sys, byte_buffer& buf) noexcept;
binary_serializer(execution_unit* ctx, byte_buffer& buf) noexcept
: buf_(buf), write_pos_(buf.size()), context_(ctx) {
// nop
}
binary_serializer(const binary_serializer&) = delete;
binary_serializer& operator=(const binary_serializer&) = delete;
// -- properties -------------------------------------------------------------
/// Returns the current execution unit.
execution_unit* context() const noexcept {
return context_;
}
byte_buffer& buf() noexcept {
return buf_;
}
const byte_buffer& buf() const noexcept {
return buf_;
}
size_t write_pos() const noexcept {
return write_pos_;
}
// -- position management ----------------------------------------------------
/// Sets the write position to `offset`.
/// @pre `offset <= buf.size()`
void seek(size_t offset) noexcept {
write_pos_ = offset;
}
/// Jumps `num_bytes` forward. Resizes the buffer (filling it with zeros)
/// when skipping past the end.
void skip(size_t num_bytes);
// -- interface functions ----------------------------------------------------
error_code<sec> begin_object(uint16_t nr, string_view name);
error_code<sec> end_object();
error_code<sec> begin_sequence(size_t list_size);
error_code<sec> end_sequence();
void apply(byte x);
void apply(uint8_t x);
void apply(uint16_t x);
void apply(uint32_t x);
void apply(uint64_t x);
void apply(float x);
void apply(double x);
void apply(long double x);
void apply(timespan x);
void apply(timestamp x);
void apply(string_view x);
void apply(std::u16string_view x);
void apply(std::u32string_view x);
void apply(span<const byte> x);
template <class T>
std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>> apply(T x) {
return apply(static_cast<std::make_unsigned_t<T>>(x));
}
template <class Enum>
std::enable_if_t<std::is_enum<Enum>::value> apply(Enum x) {
return apply(static_cast<std::underlying_type_t<Enum>>(x));
}
void apply(const std::vector<bool>& x);
private:
/// Stores the serialized output.
byte_buffer& buf_;
/// Stores the current offset for writing.
size_t write_pos_;
/// Provides access to the ::proxy_registry and to the ::actor_system.
execution_unit* context_;
};
} // namespace caf } // namespace caf
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework * * | |___ / ___ \| _| Framework *
* \____/_/ \_|_| * * \____/_/ \_|_| *
* * * *
* Copyright 2011-2018 Dominik Charousset * * Copyright 2011-2019 Dominik Charousset *
* * * *
* Distributed under the terms and conditions of the BSD 3-Clause License or * * Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software * * (at your option) under the terms and conditions of the Boost Software *
...@@ -16,52 +16,15 @@ ...@@ -16,52 +16,15 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/config.hpp" #pragma once
#define CAF_SUITE extract
#include "caf/test/unit_test.hpp"
#include <string>
#include <vector> #include <vector>
#include "caf/all.hpp" #include "caf/byte.hpp"
using namespace caf;
using std::string; namespace caf {
CAF_TEST(type_sequences) { /// A buffer for storing binary data.
auto _64 = uint64_t{64}; using byte_buffer = std::vector<byte>;
std::string str = "str";
auto msg = make_message(1.0, 2.f, str, 42, _64);
auto df = [](double, float) { };
auto fs = [](float, const string&) { };
auto iu = [](int, uint64_t) { };
CAF_CHECK_EQUAL(to_string(msg.extract(df)),
to_string(make_message(str, 42, _64)));
CAF_CHECK_EQUAL(to_string(msg.extract(fs)),
to_string(make_message(1.0, 42, _64)));
CAF_CHECK_EQUAL(to_string(msg.extract(iu)),
to_string(make_message(1.0, 2.f, str)));
}
CAF_TEST(cli_args) { } // namespace caf
std::vector<string> args{"-n", "-v", "5", "--out-file=/dev/null"};
int verbosity = 0;
string output_file;
string input_file;
auto res = message_builder(args.begin(), args.end()).extract_opts({
{"no-colors,n", "disable colors"},
{"out-file,o", "redirect output", output_file},
{"in-file,i", "read from file", input_file},
{"verbosity,v", "1-5", verbosity}
});
CAF_CHECK_EQUAL(res.remainder.size(), 0u);
CAF_CHECK(res.remainder.empty());
CAF_CHECK_EQUAL(res.opts.count("no-colors"), 1u);
CAF_CHECK_EQUAL(res.opts.count("verbosity"), 1u);
CAF_CHECK_EQUAL(res.opts.count("out-file"), 1u);
CAF_CHECK_EQUAL(res.opts.count("in-file"), 0u);
CAF_CHECK_EQUAL(output_file, "/dev/null");
CAF_CHECK_EQUAL(input_file, "");
}
This diff is collapsed.
...@@ -18,62 +18,134 @@ ...@@ -18,62 +18,134 @@
#pragma once #pragma once
#include <string>
#include <cstddef> #include <cstddef>
#include <utility> #include <string>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <utility>
#include "caf/data_processor.hpp" #include "caf/byte.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/raise_error.hpp" #include "caf/span.hpp"
#include "caf/write_inspector.hpp"
namespace caf { namespace caf {
/// @ingroup TypeSystem /// @ingroup TypeSystem
/// Technology-independent deserialization interface. /// Technology-independent deserialization interface.
class deserializer : public data_processor<deserializer> { class deserializer : public write_inspector<deserializer> {
public: public:
~deserializer() override; // -- member types -----------------------------------------------------------
using super = data_processor<deserializer>; using result_type = error;
static constexpr bool reads_state = false; // -- constructors, destructors, and assignment operators --------------------
static constexpr bool writes_state = true;
// Boost Serialization compatibility explicit deserializer(actor_system& sys) noexcept;
using is_saving = std::false_type;
using is_loading = std::true_type;
explicit deserializer(actor_system& x); explicit deserializer(execution_unit* ctx = nullptr) noexcept;
explicit deserializer(execution_unit* x = nullptr); virtual ~deserializer();
};
template <class T> // -- properties -------------------------------------------------------------
typename std::enable_if<
std::is_same<
error,
decltype(std::declval<deserializer&>().apply(std::declval<T&>()))
>::value
>::type
operator&(deserializer& source, T& x) {
auto e = source.apply(x);
if (e)
CAF_RAISE_ERROR("error during serialization (using operator&)");
}
template <class T>
typename std::enable_if<
std::is_same<
error,
decltype(std::declval<deserializer&>().apply(std::declval<T&>()))
>::value,
deserializer&
>::type
operator>>(deserializer& source, T& x) {
source & x;
return source;
}
} // namespace caf auto context() const noexcept {
return context_;
}
// -- interface functions ----------------------------------------------------
/// Begins processing of an object. Saves the type information
/// to the underlying storage.
virtual result_type begin_object(uint16_t& typenr, std::string& type_name)
= 0;
/// Ends processing of an object.
virtual result_type end_object() = 0;
/// Begins processing of a sequence. Saves the size
/// to the underlying storage when in saving mode, otherwise
/// sets `num` accordingly.
virtual result_type begin_sequence(size_t& num) = 0;
/// Ends processing of a sequence.
virtual result_type end_sequence() = 0;
/// Reads primitive value from the input.
/// @param x The primitive value.
/// @returns A non-zero error code on failure, `sec::success` otherwise.
virtual result_type apply(bool& x) = 0;
/// @copydoc apply
virtual result_type apply(int8_t&) = 0;
/// @copydoc apply
virtual result_type apply(uint8_t&) = 0;
/// @copydoc apply
virtual result_type apply(int16_t&) = 0;
/// @copydoc apply
virtual result_type apply(uint16_t&) = 0;
/// @copydoc apply
virtual result_type apply(int32_t&) = 0;
/// @copydoc apply
virtual result_type apply(uint32_t&) = 0;
/// @copydoc apply
virtual result_type apply(int64_t&) = 0;
/// @copydoc apply
virtual result_type apply(uint64_t&) = 0;
/// @copydoc apply
virtual result_type apply(float&) = 0;
/// @copydoc apply
virtual result_type apply(double&) = 0;
/// @copydoc apply
virtual result_type apply(long double&) = 0;
/// @copydoc apply
virtual result_type apply(timespan x) = 0;
/// @copydoc apply
virtual result_type apply(timestamp x) = 0;
/// @copydoc apply
virtual result_type apply(atom_value x) = 0;
/// @copydoc apply
virtual result_type apply(std::string&) = 0;
/// @copydoc apply
virtual result_type apply(std::u16string&) = 0;
/// @copydoc apply
virtual result_type apply(std::u32string&) = 0;
/// @copydoc apply
template <class Enum, class = std::enable_if_t<std::is_enum<Enum>::value>>
auto apply(Enum& x) {
return apply(reinterpret_cast<std::underlying_type_t<Enum>&>(x));
}
/// Reads a byte sequence from the input.
/// @param x The byte sequence.
/// @returns A non-zero error code on failure, `sec::success` otherwise.
virtual result_type apply_raw(span<byte> x) = 0;
/// Adds each boolean in `xs` to the output. Derived classes can override this
/// member function to pack the booleans, for example to avoid using one byte
/// for each value in a binary output format.
virtual result_type apply(std::vector<bool>& xs) noexcept;
protected:
/// Provides access to the ::proxy_registry and to the ::actor_system.
execution_unit* context_;
};
} // namespace caf
...@@ -20,9 +20,8 @@ ...@@ -20,9 +20,8 @@
#include <vector> #include <vector>
#include "caf/type_erased_value.hpp"
#include "caf/detail/message_data.hpp" #include "caf/detail/message_data.hpp"
#include "caf/type_erased_value.hpp"
namespace caf::detail { namespace caf::detail {
...@@ -52,6 +51,8 @@ public: ...@@ -52,6 +51,8 @@ public:
error load(size_t pos, deserializer& source) override; error load(size_t pos, deserializer& source) override;
error load(size_t pos, binary_deserializer& source) override;
// -- overridden observers of type_erased_tuple ------------------------------ // -- overridden observers of type_erased_tuple ------------------------------
size_t size() const noexcept override; size_t size() const noexcept override;
...@@ -68,6 +69,8 @@ public: ...@@ -68,6 +69,8 @@ public:
error save(size_t pos, serializer& sink) const override; error save(size_t pos, serializer& sink) const override;
error save(size_t pos, binary_serializer& sink) const override;
// -- modifiers -------------------------------------------------------------- // -- modifiers --------------------------------------------------------------
void clear(); void clear();
......
...@@ -43,7 +43,7 @@ using int_types_by_size = ...@@ -43,7 +43,7 @@ using int_types_by_size =
template <class T> template <class T>
struct squashed_int { struct squashed_int {
using tpair = typename detail::tl_at<int_types_by_size, sizeof(T)>::type; using tpair = typename detail::tl_at<int_types_by_size, sizeof(T)>::type;
using type = using type =
typename std::conditional< typename std::conditional<
std::is_signed<T>::value, std::is_signed<T>::value,
typename tpair::first, typename tpair::first,
...@@ -55,5 +55,3 @@ template <class T> ...@@ -55,5 +55,3 @@ template <class T>
using squashed_int_t = typename squashed_int<T>::type; using squashed_int_t = typename squashed_int<T>::type;
} // namespace caf } // namespace caf
...@@ -21,19 +21,20 @@ ...@@ -21,19 +21,20 @@
#include <tuple> #include <tuple>
#include <stdexcept> #include <stdexcept>
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/detail/message_data.hpp"
#include "caf/detail/safe_equal.hpp"
#include "caf/detail/stringification_inspector.hpp"
#include "caf/detail/try_serialize.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/make_type_erased_value.hpp" #include "caf/make_type_erased_value.hpp"
#include "caf/rtti_pair.hpp" #include "caf/rtti_pair.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
#include "caf/type_nr.hpp" #include "caf/type_nr.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/safe_equal.hpp"
#include "caf/detail/message_data.hpp"
#include "caf/detail/try_serialize.hpp"
#include "caf/detail/stringification_inspector.hpp"
#define CAF_TUPLE_VALS_DISPATCH(x) \ #define CAF_TUPLE_VALS_DISPATCH(x) \
case x: \ case x: \
return tuple_inspect_delegate<x, sizeof...(Ts)-1>(data_, f) return tuple_inspect_delegate<x, sizeof...(Ts)-1>(data_, f)
...@@ -137,6 +138,10 @@ public: ...@@ -137,6 +138,10 @@ public:
return dispatch(pos, source); return dispatch(pos, source);
} }
error load(size_t pos, binary_deserializer& source) override {
return dispatch(pos, source);
}
uint32_t type_token() const noexcept override { uint32_t type_token() const noexcept override {
return make_type_token<Ts...>(); return make_type_token<Ts...>();
} }
...@@ -149,6 +154,10 @@ public: ...@@ -149,6 +154,10 @@ public:
return mptr()->dispatch(pos, sink); return mptr()->dispatch(pos, sink);
} }
error save(size_t pos, binary_serializer& sink) const override {
return mptr()->dispatch(pos, sink);
}
private: private:
template <class F> template <class F>
auto dispatch(size_t pos, F& f) -> decltype(f(std::declval<int&>())) { auto dispatch(size_t pos, F& f) -> decltype(f(std::declval<int&>())) {
......
...@@ -70,6 +70,10 @@ public: ...@@ -70,6 +70,10 @@ public:
return ptrs_[pos]->load(source); return ptrs_[pos]->load(source);
} }
error load(size_t pos, binary_deserializer& source) override {
return ptrs_[pos]->load(source);
}
// -- overridden observers --------------------------------------------------- // -- overridden observers ---------------------------------------------------
size_t size() const noexcept override { size_t size() const noexcept override {
...@@ -100,6 +104,10 @@ public: ...@@ -100,6 +104,10 @@ public:
return ptrs_[pos]->save(sink); return ptrs_[pos]->save(sink);
} }
error save(size_t pos, binary_serializer& sink) const override {
return ptrs_[pos]->save(sink);
}
// -- member variables access ------------------------------------------------ // -- member variables access ------------------------------------------------
tuple_type& data() { tuple_type& data() {
......
...@@ -22,11 +22,14 @@ ...@@ -22,11 +22,14 @@
#include <typeinfo> #include <typeinfo>
#include <functional> #include <functional>
#include "caf/error.hpp" #include "caf/binary_deserializer.hpp"
#include "caf/type_erased_value.hpp" #include "caf/binary_serializer.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/safe_equal.hpp" #include "caf/detail/safe_equal.hpp"
#include "caf/detail/try_serialize.hpp" #include "caf/detail/try_serialize.hpp"
#include "caf/error.hpp"
#include "caf/serializer.hpp"
#include "caf/type_erased_value.hpp"
namespace caf::detail { namespace caf::detail {
...@@ -78,6 +81,10 @@ public: ...@@ -78,6 +81,10 @@ public:
return source(*addr_of(x_)); return source(*addr_of(x_));
} }
error load(binary_deserializer& source) override {
return source(*addr_of(x_));
}
// -- overridden observers --------------------------------------------------- // -- overridden observers ---------------------------------------------------
static rtti_pair type(std::integral_constant<uint16_t, 0>) { static rtti_pair type(std::integral_constant<uint16_t, 0>) {
...@@ -103,6 +110,10 @@ public: ...@@ -103,6 +110,10 @@ public:
return sink(*addr_of(const_cast<T&>(x_))); return sink(*addr_of(const_cast<T&>(x_)));
} }
error save(binary_serializer& sink) const override {
return sink(*addr_of(const_cast<T&>(x_)));
}
std::string stringify() const override { std::string stringify() const override {
return deep_to_string(x_); return deep_to_string(x_);
} }
......
...@@ -18,13 +18,14 @@ ...@@ -18,13 +18,14 @@
#pragma once #pragma once
#include <tuple> #include <array>
#include <chrono> #include <chrono>
#include <string>
#include <vector>
#include <utility>
#include <functional> #include <functional>
#include <string>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <utility>
#include <vector>
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/timestamp.hpp" #include "caf/timestamp.hpp"
...@@ -273,19 +274,6 @@ public: ...@@ -273,19 +274,6 @@ public:
template <class T> template <class T>
constexpr bool is_iterable<T>::value; constexpr bool is_iterable<T>::value;
/// Checks whether T is a contiguous sequence of byte.
template <class T>
struct is_byte_sequence : std::false_type { };
template <>
struct is_byte_sequence<std::vector<char>> : std::true_type { };
template <>
struct is_byte_sequence<std::vector<unsigned char>> : std::true_type { };
template <>
struct is_byte_sequence<std::string> : std::true_type { };
/// Checks whether `T` provides either a free function or a member function for /// Checks whether `T` provides either a free function or a member function for
/// serialization. The checks test whether both serialization and /// serialization. The checks test whether both serialization and
/// deserialization can succeed. The meta function tests the following /// deserialization can succeed. The meta function tests the following
...@@ -771,10 +759,12 @@ using deconst_kvp_t = typename deconst_kvp<T>::type; ...@@ -771,10 +759,12 @@ using deconst_kvp_t = typename deconst_kvp<T>::type;
template <class T> template <class T>
struct is_pair : std::false_type {}; struct is_pair : std::false_type {};
/// Utility trait for checking whether T is a `std::pair`.
template <class First, class Second> template <class First, class Second>
struct is_pair<std::pair<First, Second>> : std::true_type {}; struct is_pair<std::pair<First, Second>> : std::true_type {};
template <class T>
constexpr bool is_pair_v = is_pair<T>::value;
// -- traits to check for STL-style type aliases ------------------------------- // -- traits to check for STL-style type aliases -------------------------------
CAF_HAS_ALIAS_TRAIT(value_type); CAF_HAS_ALIAS_TRAIT(value_type);
...@@ -806,6 +796,9 @@ struct is_map_like { ...@@ -806,6 +796,9 @@ struct is_map_like {
&& has_mapped_type_alias<T>::value; && has_mapped_type_alias<T>::value;
}; };
template <class T>
constexpr bool is_map_like_v = is_map_like<T>::value;
/// Checks whether T behaves like `std::vector`, `std::list`, or `std::set`. /// Checks whether T behaves like `std::vector`, `std::list`, or `std::set`.
template <class T> template <class T>
struct is_list_like { struct is_list_like {
...@@ -814,6 +807,9 @@ struct is_list_like { ...@@ -814,6 +807,9 @@ struct is_list_like {
&& !has_mapped_type_alias<T>::value; && !has_mapped_type_alias<T>::value;
}; };
template <class T>
constexpr bool is_list_like_v = is_list_like<T>::value;
template <class F, class... Ts> template <class F, class... Ts>
struct is_invocable { struct is_invocable {
private: private:
...@@ -846,7 +842,58 @@ public: ...@@ -846,7 +842,58 @@ public:
static constexpr bool value = sfinae_type::value; static constexpr bool value = sfinae_type::value;
}; };
} // namespace caf template <class T, class To>
class has_convertible_data_member {
private:
template <class U>
static auto sfinae(U* x)
-> decltype(std::declval<To*>() = x->data(), std::true_type());
template <class U>
static auto sfinae(...) -> std::false_type;
using sfinae_type = decltype(sfinae<T>(nullptr));
public:
static constexpr bool value = sfinae_type::value;
};
template <class T, class Arg>
struct can_apply {
template <class U>
static auto sfinae(U* x)
-> decltype(x->apply(std::declval<Arg>()), std::true_type{});
template <class U>
static auto sfinae(...) -> std::false_type;
using type = decltype(sfinae<T>(nullptr));
static constexpr bool value = type::value;
};
template <class T, class Arg>
constexpr bool can_apply_v = can_apply<T, Arg>::value;
/// Evaluates to `true` for all types that specialize `std::tuple_size`, i.e.,
/// `std::tuple`, `std::pair`, and `std::array`.
template <class T>
struct is_stl_tuple_type {
template <class U>
static auto sfinae(U*)
-> decltype(std::bool_constant<std::tuple_size<U>::value >= 0>{});
template <class U>
static auto sfinae(...) -> std::false_type;
using type = decltype(sfinae<T>(nullptr));
static constexpr bool value = type::value;
};
template <class T>
constexpr bool is_stl_tuple_type_v = is_stl_tuple_type<T>::value;
} // namespace caf::detail
#undef CAF_HAS_MEMBER_TRAIT #undef CAF_HAS_MEMBER_TRAIT
#undef CAF_HAS_ALIAS_TRAIT #undef CAF_HAS_ALIAS_TRAIT
...@@ -23,13 +23,12 @@ ...@@ -23,13 +23,12 @@
#include <utility> #include <utility>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/error_code.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/meta/omittable_if_empty.hpp" #include "caf/meta/omittable_if_empty.hpp"
#include "caf/meta/type_name.hpp" #include "caf/meta/type_name.hpp"
#include "caf/none.hpp"
#include "caf/detail/comparable.hpp"
namespace caf { namespace caf {
...@@ -99,15 +98,19 @@ public: ...@@ -99,15 +98,19 @@ public:
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
error() noexcept; error() noexcept;
error(none_t) noexcept; error(none_t) noexcept;
error(error&&) noexcept; error(error&&) noexcept;
error& operator=(error&&) noexcept; error& operator=(error&&) noexcept;
error(const error&); error(const error&);
error& operator=(const error&); error& operator=(const error&);
error(uint8_t x, atom_value y); error(uint8_t x, atom_value y);
error(uint8_t x, atom_value y, message z); error(uint8_t x, atom_value y, message z);
template <class E, class = enable_if_has_make_error_t<E>> template <class E, class = enable_if_has_make_error_t<E>>
...@@ -115,6 +118,11 @@ public: ...@@ -115,6 +118,11 @@ public:
// nop // nop
} }
template <class E>
error(error_code<E> code) : error(code.value()) {
// nop
}
template <class E, class = enable_if_has_make_error_t<E>> template <class E, class = enable_if_has_make_error_t<E>>
error& operator=(E error_value) { error& operator=(E error_value) {
auto tmp = make_error(error_value); auto tmp = make_error(error_value);
...@@ -122,6 +130,13 @@ public: ...@@ -122,6 +130,13 @@ public:
return *this; return *this;
} }
template <class E>
error& operator=(error_code<E> code) {
auto tmp = make_error(code.value());
std::swap(data_, tmp.data_);
return *this;
}
~error(); ~error();
// -- observers -------------------------------------------------------------- // -- observers --------------------------------------------------------------
......
...@@ -18,68 +18,59 @@ ...@@ -18,68 +18,59 @@
#pragma once #pragma once
#include "caf/serializer.hpp" #include <string>
#include <type_traits>
namespace caf::detail { #include "caf/fwd.hpp"
#include "caf/none.hpp"
class serialized_size_inspector final : public serializer { namespace caf {
/// A lightweight wrapper around an error code enum.
template <class Enum>
class error_code {
public: public:
using super = serializer; using enum_type = Enum;
using super::super; using underlying_type = std::underlying_type_t<enum_type>;
size_t result() const noexcept { constexpr error_code() noexcept : value_(static_cast<Enum>(0)) {
return result_; // nop
} }
error begin_object(uint16_t& nr, std::string& name) override; constexpr error_code(none_t) noexcept : value_(static_cast<Enum>(0)) {
// nop
error end_object() override; }
error begin_sequence(size_t& list_size) override;
error end_sequence() override;
error apply_raw(size_t num_bytes, void* data) override;
protected:
error apply_impl(int8_t& x) override;
error apply_impl(uint8_t& x) override;
error apply_impl(int16_t& x) override;
error apply_impl(uint16_t& x) override;
error apply_impl(int32_t& x) override;
error apply_impl(uint32_t& x) override;
error apply_impl(int64_t& x) override;
error apply_impl(uint64_t& x) override;
error apply_impl(float& x) override; constexpr error_code(enum_type value) noexcept : value_(value) {
// nop
}
error apply_impl(double& x) override; constexpr error_code(const error_code&) noexcept = default;
error apply_impl(long double& x) override; constexpr error_code& operator=(const error_code&) noexcept = default;
error apply_impl(std::string& x) override; error_code& operator=(Enum value) noexcept {
value_ = value;
return *this;
}
error apply_impl(std::u16string& x) override; constexpr explicit operator bool() const noexcept {
return static_cast<underlying_type>(value_) != 0;
}
error apply_impl(std::u32string& x) override; constexpr enum_type value() const noexcept {
return value_;
}
private: private:
size_t result_ = 0; enum_type value_;
}; };
template <class T> /// Converts `x` to a string if `Enum` provides a `to_string` function.
size_t serialized_size(actor_system& sys, const T& x) { template <class Enum>
serialized_size_inspector f{sys}; auto to_string(error_code<Enum> x) -> decltype(to_string(x.value())) {
f(const_cast<T&>(x)); return to_string(x.value());
return f.result();
} }
} // namespace caf } // namespace caf
...@@ -37,12 +37,12 @@ namespace caf { ...@@ -37,12 +37,12 @@ namespace caf {
template <class> class behavior_type_of; template <class> class behavior_type_of;
template <class> class dictionary; template <class> class dictionary;
template <class> class downstream; template <class> class downstream;
template <class> class error_code;
template <class> class expected; template <class> class expected;
template <class> class intrusive_cow_ptr; template <class> class intrusive_cow_ptr;
template <class> class intrusive_ptr; template <class> class intrusive_ptr;
template <class> class optional; template <class> class optional;
template <class> class param; template <class> class param;
template <class> class serializer_impl;
template <class> class span; template <class> class span;
template <class> class stream; template <class> class stream;
template <class> class stream_sink; template <class> class stream_sink;
...@@ -95,6 +95,7 @@ class actor_system; ...@@ -95,6 +95,7 @@ class actor_system;
class actor_system_config; class actor_system_config;
class behavior; class behavior;
class binary_deserializer; class binary_deserializer;
class binary_serializer;
class blocking_actor; class blocking_actor;
class config_option; class config_option;
class config_option_adder; class config_option_adder;
...@@ -183,7 +184,6 @@ enum class invoke_message_result; ...@@ -183,7 +184,6 @@ enum class invoke_message_result;
// -- aliases ------------------------------------------------------------------ // -- aliases ------------------------------------------------------------------
using actor_id = uint64_t; using actor_id = uint64_t;
using binary_serializer = serializer_impl<std::vector<char>>;
using ip_address = ipv6_address; using ip_address = ipv6_address;
using ip_endpoint = ipv6_endpoint; using ip_endpoint = ipv6_endpoint;
using ip_subnet = ipv6_subnet; using ip_subnet = ipv6_subnet;
...@@ -195,6 +195,9 @@ using stream_slot = uint16_t; ...@@ -195,6 +195,9 @@ using stream_slot = uint16_t;
/// @relates actor_system_config /// @relates actor_system_config
const settings& content(const actor_system_config&); const settings& content(const actor_system_config&);
template <class T, class... Ts>
message make_message(T&& x, Ts&&... xs);
// -- intrusive containers ----------------------------------------------------- // -- intrusive containers -----------------------------------------------------
namespace intrusive { namespace intrusive {
......
...@@ -80,24 +80,14 @@ public: ...@@ -80,24 +80,14 @@ public:
return ptr_ ? 1 : 0; return ptr_ ? 1 : 0;
} }
template <class Inspector>
friend typename Inspector::result_type inspect(Inspector& f, group& x) {
std::string x_id;
std::string x_mod;
auto ptr = x.get();
if (ptr) {
x_id = ptr->identifier();
x_mod = ptr->module().name();
}
return f(meta::type_name("group"),
meta::omittable_if_empty(), x_id,
meta::omittable_if_empty(), x_mod);
}
friend error inspect(serializer&, group&); friend error inspect(serializer&, group&);
friend error inspect(binary_serializer&, group&);
friend error inspect(deserializer&, group&); friend error inspect(deserializer&, group&);
friend error inspect(binary_deserializer&, group&);
abstract_group* get() const noexcept { abstract_group* get() const noexcept {
return ptr_.get(); return ptr_.get();
} }
......
...@@ -50,6 +50,9 @@ public: ...@@ -50,6 +50,9 @@ public:
/// Loads a group of this module from `source` and stores it in `storage`. /// Loads a group of this module from `source` and stores it in `storage`.
virtual error load(deserializer& source, group& storage) = 0; virtual error load(deserializer& source, group& storage) = 0;
/// Loads a group of this module from `source` and stores it in `storage`.
virtual error load(binary_deserializer& source, group& storage) = 0;
// -- observers -------------------------------------------------------------- // -- observers --------------------------------------------------------------
/// Returns the hosting actor system. /// Returns the hosting actor system.
......
...@@ -62,39 +62,30 @@ struct is_serializable_or_whitelisted { ...@@ -62,39 +62,30 @@ struct is_serializable_or_whitelisted {
/// Returns a new `message` containing the values `(x, xs...)`. /// Returns a new `message` containing the values `(x, xs...)`.
/// @relates message /// @relates message
template <class T, class... Ts> template <class T, class... Ts>
typename std::enable_if< message make_message(T&& x, Ts&&... xs) {
!std::is_same<message, typename std::decay<T>::type>::value if constexpr (sizeof...(Ts) == 0
|| (sizeof...(Ts) > 0), && std::is_same<message, std::decay_t<T>>::value) {
message return std::forward<T>(x);
>::type } else {
make_message(T&& x, Ts&&... xs) { using namespace caf::detail;
using namespace caf::detail; using stored_types = type_list<
using stored_types = typename unbox_message_element<typename strip_and_convert<T>::type>::type,
type_list<
typename unbox_message_element< typename unbox_message_element<
typename strip_and_convert<T>::type typename strip_and_convert<Ts>::type>::type...>;
>::type, static_assert(tl_forall<stored_types,
typename unbox_message_element< is_serializable_or_whitelisted>::value,
typename strip_and_convert<Ts>::type "at least one type is neither inspectable via "
>::type... "inspect(Inspector&, T&) nor serializable via "
>; "'serialize(Processor&, T&, const unsigned int)' or "
static_assert(tl_forall<stored_types, is_serializable_or_whitelisted>::value, "`T::serialize(Processor&, const unsigned int)`; "
"at least one type is neither inspectable via " "you can whitelist individual types by "
"inspect(Inspector&, T&) nor serializable via " "specializing `caf::allowed_unsafe_message_type<T>` "
"'serialize(Processor&, T&, const unsigned int)' or " "or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
"`T::serialize(Processor&, const unsigned int)`; " using storage = typename tl_apply<stored_types, tuple_vals>::type;
"you can whitelist individual types by " auto ptr = make_counted<storage>(std::forward<T>(x),
"specializing `caf::allowed_unsafe_message_type<T>` " std::forward<Ts>(xs)...);
"or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE"); return message{detail::message_data::cow_ptr{std::move(ptr)}};
using storage = typename tl_apply<stored_types, tuple_vals>::type; }
auto ptr = make_counted<storage>(std::forward<T>(x), std::forward<Ts>(xs)...);
return message{detail::message_data::cow_ptr{std::move(ptr)}};
}
/// Returns a copy of @p other.
/// @relates message
inline message make_message(message other) {
return other;
} }
/// Returns an empty `message`. /// Returns an empty `message`.
...@@ -118,4 +109,3 @@ message make_message_from_tuple(std::tuple<Ts...> xs) { ...@@ -118,4 +109,3 @@ message make_message_from_tuple(std::tuple<Ts...> xs) {
} }
} // namespace caf } // namespace caf
This diff is collapsed.
...@@ -106,16 +106,6 @@ public: ...@@ -106,16 +106,6 @@ public:
/// is undefined behavior (dereferencing a `nullptr`) /// is undefined behavior (dereferencing a `nullptr`)
message move_to_message(); message move_to_message();
/// @copydoc message::extract
message extract(message_handler f) const;
/// @copydoc message::extract_opts
inline message::cli_res extract_opts(std::vector<message::cli_arg> xs,
message::help_factory f = nullptr,
bool no_help = false) const {
return to_message().extract_opts(std::move(xs), std::move(f), no_help);
}
/// @copydoc message::apply /// @copydoc message::apply
optional<message> apply(message_handler handler); optional<message> apply(message_handler handler);
......
...@@ -43,5 +43,7 @@ struct is_annotation<const T&> : is_annotation<T> {}; ...@@ -43,5 +43,7 @@ struct is_annotation<const T&> : is_annotation<T> {};
template <class T> template <class T>
struct is_annotation<T&&> : is_annotation<T> {}; struct is_annotation<T&&> : is_annotation<T> {};
} // namespace caf template <class T>
constexpr bool is_annotation_v = is_annotation<T>::value;
} // namespace caf::meta
...@@ -43,6 +43,9 @@ struct is_load_callback : std::false_type {}; ...@@ -43,6 +43,9 @@ struct is_load_callback : std::false_type {};
template <class F> template <class F>
struct is_load_callback<load_callback_t<F>> : std::true_type {}; struct is_load_callback<load_callback_t<F>> : std::true_type {};
template <class F>
constexpr bool is_load_callback_v = is_load_callback<F>::value;
/// Returns an annotation that allows inspectors to call /// Returns an annotation that allows inspectors to call
/// user-defined code after performing load operations. /// user-defined code after performing load operations.
template <class F> template <class F>
......
...@@ -43,6 +43,9 @@ struct is_save_callback : std::false_type {}; ...@@ -43,6 +43,9 @@ struct is_save_callback : std::false_type {};
template <class F> template <class F>
struct is_save_callback<save_callback_t<F>> : std::true_type {}; struct is_save_callback<save_callback_t<F>> : std::true_type {};
template <class F>
constexpr bool is_save_callback_v = is_save_callback<F>::value;
/// Returns an annotation that allows inspectors to call /// Returns an annotation that allows inspectors to call
/// user-defined code after performing save operations. /// user-defined code after performing save operations.
template <class F> template <class F>
......
...@@ -56,6 +56,10 @@ public: ...@@ -56,6 +56,10 @@ public:
virtual error serialize(serializer& sink) const = 0; virtual error serialize(serializer& sink) const = 0;
virtual error deserialize(deserializer& source) = 0; virtual error deserialize(deserializer& source) = 0;
virtual error serialize(binary_serializer& sink) const = 0;
virtual error deserialize(binary_deserializer& source) = 0;
}; };
// A technology-agnostic node identifier with process ID and hash value. // A technology-agnostic node identifier with process ID and hash value.
...@@ -115,6 +119,10 @@ public: ...@@ -115,6 +119,10 @@ public:
error deserialize(deserializer& source) override; error deserialize(deserializer& source) override;
error serialize(binary_serializer& sink) const override;
error deserialize(binary_deserializer& source) override;
private: private:
// -- member variables ----------------------------------------------------- // -- member variables -----------------------------------------------------
...@@ -159,6 +167,10 @@ public: ...@@ -159,6 +167,10 @@ public:
error deserialize(deserializer& source) override; error deserialize(deserializer& source) override;
error serialize(binary_serializer& sink) const override;
error deserialize(binary_deserializer& source) override;
private: private:
// -- member variables ----------------------------------------------------- // -- member variables -----------------------------------------------------
...@@ -199,9 +211,6 @@ public: ...@@ -199,9 +211,6 @@ public:
/// @cond PRIVATE /// @cond PRIVATE
error serialize(serializer& sink) const;
error deserialize(deserializer& source);
data* operator->() noexcept { data* operator->() noexcept {
return data_.get(); return data_.get();
...@@ -221,6 +230,14 @@ public: ...@@ -221,6 +230,14 @@ public:
/// @endcond /// @endcond
friend error inspect(serializer& sink, node_id& x);
friend error inspect(binary_serializer& sink, node_id& x);
friend error inspect(deserializer& source, node_id& x);
friend error inspect(binary_deserializer& source, node_id& x);
private: private:
intrusive_ptr<data> data_; intrusive_ptr<data> data_;
}; };
...@@ -281,12 +298,6 @@ inline bool operator!=(const none_t&, const node_id& x) noexcept { ...@@ -281,12 +298,6 @@ inline bool operator!=(const none_t&, const node_id& x) noexcept {
return static_cast<bool>(x); return static_cast<bool>(x);
} }
/// @relates node_id
error inspect(serializer& sink, const node_id& x);
/// @relates node_id
error inspect(deserializer& source, node_id& x);
/// Appends `x` in human-readable string representation to `str`. /// Appends `x` in human-readable string representation to `str`.
/// @relates node_id /// @relates node_id
void append_to_string(std::string& str, const node_id& x); void append_to_string(std::string& str, const node_id& x);
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <type_traits>
#include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/meta/save_callback.hpp"
#include "caf/sec.hpp"
#include "caf/unifyn.hpp"
#define CAF_READ_INSPECTOR_TRY(statement) \
using CAF_UNIFYN(statement_result) = decltype(statement); \
if constexpr (std::is_same<CAF_UNIFYN(statement_result), void>::value) { \
statement; \
} else if constexpr (std::is_same<CAF_UNIFYN(statement_result), \
bool>::value) { \
if (!statement) \
return false; \
} else if (auto err = statement) { \
result = err; \
return false; \
}
namespace caf {
/// Injects an `operator()` that dispatches to `Subtype::apply`.
template <class Subtype>
class read_inspector {
public:
static constexpr bool reads_state = true;
static constexpr bool writes_state = false;
template <class... Ts>
[[nodiscard]] auto operator()(Ts&&... xs) {
auto& dref = *static_cast<Subtype*>(this);
typename Subtype::result_type result;
auto f = [&result, &dref](auto&& x) {
using type = std::remove_const_t<std::remove_reference_t<decltype(x)>>;
if constexpr (std::is_empty<type>::value) {
// nop
} else if constexpr (meta::is_save_callback_v<type>) {
CAF_READ_INSPECTOR_TRY(x.fun())
} else if constexpr (meta::is_annotation_v<type> //
|| is_allowed_unsafe_message_type_v<type>) {
// skip element
} else if constexpr (detail::can_apply_v<Subtype, decltype(x)>) {
using apply_type = decltype(dref.apply(x));
if constexpr (std::is_same<apply_type, void>::value) {
dref.apply(x);
} else if constexpr (std::is_same<apply_type, bool>::value) {
if (!dref.apply(x)) {
result = sec::end_of_stream;
return false;
}
} else {
CAF_READ_INSPECTOR_TRY(dref.apply(x))
}
} else if constexpr (std::is_integral_v<type>) {
using squashed_type = detail::squashed_int_t<type>;
CAF_READ_INSPECTOR_TRY(dref.apply(static_cast<squashed_type>(x)))
} else if constexpr (std::is_array<type>::value) {
CAF_READ_INSPECTOR_TRY(apply_array(dref, x))
} else if constexpr (detail::is_stl_tuple_type_v<type>) {
std::make_index_sequence<std::tuple_size_v<type>> seq;
CAF_READ_INSPECTOR_TRY(apply_tuple(dref, x, seq))
} else if constexpr (detail::is_map_like_v<type>) {
CAF_READ_INSPECTOR_TRY(dref.begin_sequence(x.size()))
for (const auto& [key, value] : x) {
CAF_READ_INSPECTOR_TRY(dref(key, value))
}
CAF_READ_INSPECTOR_TRY(dref.end_sequence())
} else if constexpr (detail::is_list_like_v<type>) {
CAF_READ_INSPECTOR_TRY(dref.begin_sequence(x.size()))
for (const auto& value : x) {
CAF_READ_INSPECTOR_TRY(dref(value))
}
CAF_READ_INSPECTOR_TRY(dref.end_sequence())
} else {
static_assert(detail::is_inspectable<Subtype, type>::value);
// We require that the implementation for `inspect` does not modify its
// arguments when passing a reading inspector.
CAF_READ_INSPECTOR_TRY(inspect(dref, const_cast<type&>(x)));
}
return true;
};
static_cast<void>((f(std::forward<Ts>(xs)) && ...));
return result;
}
private:
template <class Tuple, size_t... Is>
static auto apply_tuple(Subtype& dref, const Tuple& xs,
std::index_sequence<Is...>) {
return dref(std::get<Is>(xs)...);
}
template <class T, size_t... Is>
static auto apply_array(Subtype& dref, T* xs, std::index_sequence<Is...>) {
return dref(xs[Is]...);
}
template <class T, size_t N>
static auto apply_array(Subtype& dref, T (&xs)[N]) {
std::make_index_sequence<N> seq;
return apply_array(dref, xs, seq);
}
};
} // namespace caf
#undef CAF_READ_INSPECTOR_TRY
...@@ -22,8 +22,9 @@ ...@@ -22,8 +22,9 @@
#pragma once #pragma once
#include "caf/error.hpp" #include <string>
#include "caf/make_message.hpp"
#include "caf/fwd.hpp"
namespace caf { namespace caf {
...@@ -134,12 +135,14 @@ std::string to_string(sec); ...@@ -134,12 +135,14 @@ std::string to_string(sec);
/// @relates sec /// @relates sec
error make_error(sec); error make_error(sec);
/// @relates sec
error make_error(sec, message);
/// @relates sec /// @relates sec
template <class T, class... Ts> template <class T, class... Ts>
error make_error(sec code, T&& x, Ts&&... xs) { auto make_error(sec code, T&& x, Ts&&... xs) {
return {static_cast<uint8_t>(code), atom("system"), return make_error(code,
make_message(std::forward<T>(x), std::forward<Ts>(xs)...)}; make_message(std::forward<T>(x), std::forward<Ts>(xs)...));
} }
} // namespace caf } // namespace caf
...@@ -18,62 +18,136 @@ ...@@ -18,62 +18,136 @@
#pragma once #pragma once
#include <string> #include <cstddef>
#include <cstddef> // size_t #include <cstdint>
#include <type_traits> #include <string_view>
#include <tuple>
#include <utility>
#include "caf/data_processor.hpp" #include "caf/byte.hpp"
#include "caf/error.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/raise_error.hpp" #include "caf/meta/annotation.hpp"
#include "caf/meta/save_callback.hpp"
#include "caf/read_inspector.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
namespace caf { namespace caf {
/// @ingroup TypeSystem /// @ingroup TypeSystem
/// Technology-independent serialization interface. /// Technology-independent serialization interface.
class serializer : public data_processor<serializer> { class serializer : public read_inspector<serializer> {
public: public:
using super = data_processor<serializer>; // -- member types -----------------------------------------------------------
static constexpr bool reads_state = true; using result_type = error;
static constexpr bool writes_state = false;
// Boost Serialization compatibility // -- constructors, destructors, and assignment operators --------------------
using is_saving = std::true_type;
using is_loading = std::false_type;
explicit serializer(actor_system& sys); explicit serializer(actor_system& sys) noexcept;
explicit serializer(execution_unit* ctx = nullptr); explicit serializer(execution_unit* ctx = nullptr) noexcept;
~serializer() override; virtual ~serializer();
};
template <class T> // -- properties -------------------------------------------------------------
typename std::enable_if<
std::is_same<
error,
decltype(std::declval<serializer&>().apply(std::declval<T&>()))
>::value
>::type
operator&(serializer& sink, const T& x) {
// implementations are required to never modify `x` while saving
auto e = sink.apply(const_cast<T&>(x));
if (e)
CAF_RAISE_ERROR("error during serialization (using operator&)");
}
template <class T>
typename std::enable_if<
std::is_same<
error,
decltype(std::declval<serializer&>().apply(std::declval<T&>()))
>::value,
serializer&
>::type
operator<<(serializer& sink, const T& x) {
sink & x;
return sink;
}
} // namespace caf auto context() const noexcept {
return context_;
}
// -- interface functions ----------------------------------------------------
/// Begins processing of an object. Saves the type information
/// to the underlying storage.
virtual result_type begin_object(uint16_t typenr, string_view type_name) = 0;
/// Ends processing of an object.
virtual result_type end_object() = 0;
/// Begins processing of a sequence. Saves the size
/// to the underlying storage when in saving mode, otherwise
/// sets `num` accordingly.
virtual result_type begin_sequence(size_t num) = 0;
/// Ends processing of a sequence.
virtual result_type end_sequence() = 0;
/// Adds the primitive type `x` to the output.
/// @param x The primitive value.
/// @returns A non-zero error code on failure, `sec::success` otherwise.
virtual result_type apply(bool x) = 0;
/// @copydoc apply
virtual result_type apply(int8_t x) = 0;
/// @copydoc apply
virtual result_type apply(uint8_t x) = 0;
/// @copydoc apply
virtual result_type apply(int16_t x) = 0;
/// @copydoc apply
virtual result_type apply(uint16_t x) = 0;
/// @copydoc apply
virtual result_type apply(int32_t x) = 0;
/// @copydoc apply
virtual result_type apply(uint32_t x) = 0;
/// @copydoc apply
virtual result_type apply(int64_t x) = 0;
/// @copydoc apply
virtual result_type apply(uint64_t x) = 0;
/// @copydoc apply
virtual result_type apply(float x) = 0;
/// @copydoc apply
virtual result_type apply(double x) = 0;
/// @copydoc apply
virtual result_type apply(long double x) = 0;
/// @copydoc apply
virtual result_type apply(timespan x) = 0;
/// @copydoc apply
virtual result_type apply(timestamp x) = 0;
/// @copydoc apply
virtual result_type apply(atom_value x) = 0;
/// @copydoc apply
virtual result_type apply(string_view x) = 0;
/// @copydoc apply
virtual result_type apply(std::u16string_view x) = 0;
/// @copydoc apply
virtual result_type apply(std::u32string_view x) = 0;
template <class Enum, class = std::enable_if_t<std::is_enum<Enum>::value>>
result_type apply(Enum x) {
return apply(static_cast<std::underlying_type_t<Enum>>(x));
}
/// Adds `x` as raw byte block to the output.
/// @param x The byte sequence.
/// @returns A non-zero error code on failure, `sec::success` otherwise.
virtual result_type apply(span<const byte> x) = 0;
/// Adds each boolean in `xs` to the output. Derived classes can override this
/// member function to pack the booleans, for example to avoid using one byte
/// for each value in a binary output format.
virtual result_type apply(const std::vector<bool>& xs);
protected:
/// Provides access to the ::proxy_registry and to the ::actor_system.
execution_unit* context_;
};
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <vector>
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp"
#include "caf/serializer.hpp"
namespace caf {
/// Implements the serializer interface with a binary serialization protocol.
template <class Container>
class serializer_impl final : public serializer {
public:
// -- member types -----------------------------------------------------------
using super = serializer;
using container_type = Container;
using value_type = typename container_type::value_type;
// -- invariants -------------------------------------------------------------
static_assert(sizeof(value_type) == 1,
"container must have a byte-sized value type");
// -- constructors, destructors, and assignment operators --------------------
serializer_impl(actor_system& sys, container_type& buf)
: super(sys), buf_(buf), write_pos_(buf_.size()) {
// nop
}
serializer_impl(execution_unit* ctx, container_type& buf)
: super(ctx), buf_(buf), write_pos_(buf_.size()) {
// nop
}
// -- position management ----------------------------------------------------
/// Sets the write position to given offset.
/// @pre `offset <= buf.size()`
void seek(size_t offset) {
write_pos_ = offset;
}
/// Jumps `num_bytes` forward. Resizes the buffer (filling it with zeros)
/// when skipping past the end.
void skip(size_t num_bytes) {
auto remaining = buf_.size() - write_pos_;
if (remaining < num_bytes)
buf_.insert(buf_.end(), num_bytes - remaining, 0);
write_pos_ += num_bytes;
}
// -- overridden member functions --------------------------------------------
error begin_object(uint16_t& nr, std::string& name) override {
if (nr != 0)
return apply(nr);
if (auto err = apply(nr))
return err;
return apply(name);
}
error end_object() override {
return none;
}
error begin_sequence(size_t& list_size) override {
// Use varbyte encoding to compress sequence size on the wire.
// For 64-bit values, the encoded representation cannot get larger than 10
// bytes. A scratch space of 16 bytes suffices as upper bound.
uint8_t buf[16];
auto i = buf;
auto x = static_cast<uint32_t>(list_size);
while (x > 0x7f) {
*i++ = (static_cast<uint8_t>(x) & 0x7f) | 0x80;
x >>= 7;
}
*i++ = static_cast<uint8_t>(x) & 0x7f;
apply_raw(static_cast<size_t>(i - buf), buf);
return none;
}
error end_sequence() override {
return none;
}
error apply_raw(size_t num_bytes, void* data) override {
CAF_ASSERT(write_pos_ <= buf_.size());
static_assert((sizeof(value_type) == 1), "sizeof(value_type) > 1");
auto ptr = reinterpret_cast<value_type*>(data);
auto buf_size = buf_.size();
if (write_pos_ == buf_size) {
buf_.insert(buf_.end(), ptr, ptr + num_bytes);
} else if (write_pos_ + num_bytes <= buf_size) {
memcpy(buf_.data() + write_pos_, ptr, num_bytes);
} else {
auto remaining = buf_size - write_pos_;
CAF_ASSERT(remaining < num_bytes);
memcpy(buf_.data() + write_pos_, ptr, remaining);
buf_.insert(buf_.end(), ptr + remaining, ptr + num_bytes);
}
write_pos_ += num_bytes;
CAF_ASSERT(write_pos_ <= buf_.size());
return none;
}
// -- properties -------------------------------------------------------------
container_type& buf() {
return buf_;
}
const container_type& buf() const {
return buf_;
}
size_t write_pos() const noexcept {
return write_pos_;
}
protected:
error apply_impl(int8_t& x) override {
return apply_raw(sizeof(int8_t), &x);
}
error apply_impl(uint8_t& x) override {
return apply_raw(sizeof(uint8_t), &x);
}
error apply_impl(int16_t& x) override {
return apply_int(*this, static_cast<uint16_t>(x));
}
error apply_impl(uint16_t& x) override {
return apply_int(*this, x);
}
error apply_impl(int32_t& x) override {
return apply_int(*this, static_cast<uint32_t>(x));
}
error apply_impl(uint32_t& x) override {
return apply_int(*this, x);
}
error apply_impl(int64_t& x) override {
return apply_int(*this, static_cast<uint64_t>(x));
}
error apply_impl(uint64_t& x) override {
return apply_int(*this, x);
}
error apply_impl(float& x) override {
return apply_int(*this, detail::pack754(x));
}
error apply_impl(double& x) override {
return apply_int(*this, detail::pack754(x));
}
error apply_impl(long double& x) override {
// The IEEE-754 conversion does not work for long double
// => fall back to string serialization (event though it sucks).
std::ostringstream oss;
oss << std::setprecision(std::numeric_limits<long double>::digits) << x;
auto tmp = oss.str();
return apply_impl(tmp);
}
error apply_impl(std::string& x) override {
auto str_size = x.size();
if (str_size == 0)
return error::eval([&] { return begin_sequence(str_size); },
[&] { return end_sequence(); });
auto data = const_cast<char*>(x.c_str());
return error::eval([&] { return begin_sequence(str_size); },
[&] { return apply_raw(str_size, data); },
[&] { return end_sequence(); });
}
error apply_impl(std::u16string& x) override {
auto str_size = x.size();
if (auto err = begin_sequence(str_size))
return err;
for (auto c : x) {
// The standard does not guarantee that char16_t is exactly 16 bits.
if (auto err = apply_int(*this, static_cast<uint16_t>(c)))
return err;
}
return none;
}
error apply_impl(std::u32string& x) override {
auto str_size = x.size();
if (auto err = begin_sequence(str_size))
return err;
for (auto c : x) {
// The standard does not guarantee that char32_t is exactly 32 bits.
if (auto err = apply_int(*this, static_cast<uint32_t>(c)))
return err;
}
return none;
}
private:
template <class T>
error apply_int(serializer_impl<Container>& bs, T x) {
auto y = detail::to_network_order(x);
return bs.apply_raw(sizeof(T), &y);
}
container_type& buf_;
size_t write_pos_;
};
} // namespace caf
...@@ -79,13 +79,15 @@ public: ...@@ -79,13 +79,15 @@ public:
} }
template <class C, template <class C,
class E = detail::enable_if_t<detail::has_data_member<C>::value>> class = std::enable_if_t<
detail::has_convertible_data_member<C, value_type>::value>>
span(C& xs) noexcept : begin_(xs.data()), size_(xs.size()) { span(C& xs) noexcept : begin_(xs.data()), size_(xs.size()) {
// nop // nop
} }
template <class C, template <class C,
class E = detail::enable_if_t<detail::has_data_member<C>::value>> class = std::enable_if_t<
detail::has_convertible_data_member<C, value_type>::value>>
span(const C& xs) noexcept : begin_(xs.data()), size_(xs.size()) { span(const C& xs) noexcept : begin_(xs.data()), size_(xs.size()) {
// nop // nop
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include "caf/deserializer.hpp"
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp"
#include "caf/logger.hpp"
#include "caf/sec.hpp"
namespace caf {
/// Implements the deserializer interface with a binary serialization protocol.
template <class Streambuf>
class stream_deserializer : public deserializer {
using streambuf_type = typename std::remove_reference<Streambuf>::type;
using char_type = typename streambuf_type::char_type;
using streambuf_base = std::basic_streambuf<char_type>;
using streamsize = std::streamsize;
static_assert(std::is_base_of<streambuf_base, streambuf_type>::value,
"Streambuf must inherit from std::streambuf");
public:
template <class... Ts>
explicit stream_deserializer(actor_system& sys, Ts&&... xs)
: deserializer(sys),
streambuf_(std::forward<Ts>(xs)...) {
}
template <class... Ts>
explicit stream_deserializer(execution_unit* ctx, Ts&&... xs)
: deserializer(ctx),
streambuf_(std::forward<Ts>(xs)...) {
}
template <
class S,
class = typename std::enable_if<
std::is_same<
typename std::remove_reference<S>::type,
typename std::remove_reference<Streambuf>::type
>::value
>::type
>
explicit stream_deserializer(S&& sb)
: deserializer(nullptr),
streambuf_(std::forward<S>(sb)) {
}
error begin_object(uint16_t& typenr, std::string& name) override {
return error::eval([&] { return apply_int(typenr); },
[&] { return typenr == 0 ? apply(name) : error{}; });
}
error end_object() override {
return none;
}
error begin_sequence(size_t& num_elements) override {
// We serialize a `size_t` always in 32-bit, to guarantee compatibility
// with 32-bit nodes in the network.
// TODO: protect with `if constexpr (sizeof(size_t) > sizeof(uint32_t))`
// when switching to C++17 and pass `num_elements` directly to
// `varbyte_decode` in the `else` case
uint32_t x;
auto result = varbyte_decode(x);
if (!result)
num_elements = static_cast<size_t>(x);
return result;
}
error end_sequence() override {
return none;
}
error apply_raw(size_t num_bytes, void* data) override {
return range_check(streambuf_.sgetn(reinterpret_cast<char_type*>(data),
static_cast<streamsize>(num_bytes)),
num_bytes);
}
protected:
// Decode an unsigned integral type as variable-byte-encoded byte sequence.
template <class T>
error varbyte_decode(T& x) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned type");
auto n = 0;
x = 0;
uint8_t low7;
do {
auto c = streambuf_.sbumpc();
using traits = typename streambuf_type::traits_type;
if (traits::eq_int_type(c, traits::eof()))
return sec::end_of_stream;
low7 = static_cast<uint8_t>(traits::to_char_type(c));
x |= static_cast<T>((low7 & 0x7F)) << (7 * n);
++n;
} while (low7 & 0x80);
return none;
}
error apply_impl(int8_t& x) override {
return apply_raw(sizeof(int8_t), &x);
}
error apply_impl(uint8_t& x) override {
return apply_raw(sizeof(uint8_t), &x);
}
error apply_impl(int16_t& x) override {
return apply_int(x);
}
error apply_impl(uint16_t& x) override {
return apply_int(x);
}
error apply_impl(int32_t& x) override {
return apply_int(x);
}
error apply_impl(uint32_t& x) override {
return apply_int(x);
}
error apply_impl(int64_t& x) override {
return apply_int(x);
}
error apply_impl(uint64_t& x) override {
return apply_int(x);
}
error apply_impl(float& x) override {
return apply_float(x);
}
error apply_impl(double& x) override {
return apply_float(x);
}
error apply_impl(long double& x) override {
// The IEEE-754 conversion does not work for long double
// => fall back to string serialization (even though it sucks).
std::string tmp;
if (auto err = apply(tmp))
return err;
std::istringstream iss{std::move(tmp)};
iss >> x;
return none;
}
error apply_impl(std::string& x) override {
size_t str_size;
if (auto err = begin_sequence(str_size))
return err;
x.resize(str_size);
auto s = static_cast<streamsize>(str_size);
auto data = reinterpret_cast<char_type*>(&x[0]);
if (auto err = range_check(streambuf_.sgetn(data, s), str_size))
return err;
return end_sequence();
}
error apply_impl(std::u16string& x) override {
size_t str_size;
return error::eval([&] { return begin_sequence(str_size); },
[&] { return fill_range_c<uint16_t>(x, str_size); },
[&] { return end_sequence(); });
}
error apply_impl(std::u32string& x) override {
size_t str_size;
return error::eval([&] { return begin_sequence(str_size); },
[&] { return fill_range_c<uint32_t>(x, str_size); },
[&] { return end_sequence(); });
}
error range_check(std::streamsize got, size_t need) {
if (got >= 0 && static_cast<size_t>(got) == need)
return none;
CAF_LOG_ERROR("range_check failed");
return sec::end_of_stream;
}
template <class T>
error apply_int(T& x) {
typename std::make_unsigned<T>::type tmp = 0;
if (auto err = apply_raw(sizeof(T), &tmp))
return err;
x = static_cast<T>(detail::from_network_order(tmp));
return none;
}
template <class T>
error apply_float(T& x) {
typename detail::ieee_754_trait<T>::packed_type tmp = 0;
if (auto err = apply_int(tmp))
return err;
x = detail::unpack754(tmp);
return none;
}
private:
Streambuf streambuf_;
};
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <string>
#include <limits>
#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <sstream>
#include <streambuf>
#include <type_traits>
#include "caf/sec.hpp"
#include "caf/config.hpp"
#include "caf/streambuf.hpp"
#include "caf/serializer.hpp"
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp"
namespace caf {
/// Implements the serializer interface with a binary serialization protocol.
template <class Streambuf>
class stream_serializer : public serializer {
using streambuf_type = typename std::remove_reference<Streambuf>::type;
using char_type = typename streambuf_type::char_type;
using streambuf_base = std::basic_streambuf<char_type>;
static_assert(std::is_base_of<streambuf_base, streambuf_type>::value,
"Streambuf must inherit from std::streambuf");
public:
template <class... Ts>
explicit stream_serializer(actor_system& sys, Ts&&... xs)
: serializer(sys),
streambuf_{std::forward<Ts>(xs)...} {
}
template <class... Ts>
explicit stream_serializer(execution_unit* ctx, Ts&&... xs)
: serializer(ctx),
streambuf_{std::forward<Ts>(xs)...} {
}
template <
class S,
class = typename std::enable_if<
std::is_same<
typename std::remove_reference<S>::type,
typename std::remove_reference<Streambuf>::type
>::value
>::type
>
explicit stream_serializer(S&& sb)
: serializer(nullptr),
streambuf_(std::forward<S>(sb)) {
}
error begin_object(uint16_t& typenr, std::string& name) override {
return error::eval([&] { return apply(typenr); },
[&] { return typenr == 0 ? apply(name) : error{}; });
}
error end_object() override {
return none;
}
error begin_sequence(size_t& list_size) override {
// TODO: protect with `if constexpr (sizeof(size_t) > sizeof(uint32_t))`
// when switching to C++17
CAF_ASSERT(list_size <= std::numeric_limits<uint32_t>::max());
// Serialize a `size_t` always in 32-bit, to guarantee compatibility with
// 32-bit nodes in the network.
return varbyte_encode(static_cast<uint32_t>(list_size));
}
error end_sequence() override {
return none;
}
error apply_raw(size_t num_bytes, void* data) override {
auto ssize = static_cast<std::streamsize>(num_bytes);
auto n = streambuf_.sputn(reinterpret_cast<char_type*>(data), ssize);
if (n != ssize)
return sec::end_of_stream;
return none;
}
protected:
// Encode an unsigned integral type as variable-byte sequence.
template <class T>
error varbyte_encode(T x) {
static_assert(std::is_unsigned<T>::value, "T must be an unsigned type");
// For 64-bit values, the encoded representation cannot get larger than 10
// bytes. A scratch space of 16 bytes suffices as upper bound.
uint8_t buf[16];
auto i = buf;
while (x > 0x7f) {
*i++ = (static_cast<uint8_t>(x) & 0x7f) | 0x80;
x >>= 7;
}
*i++ = static_cast<uint8_t>(x) & 0x7f;
auto res = streambuf_.sputn(reinterpret_cast<char_type*>(buf),
static_cast<std::streamsize>(i - buf));
if (res != (i - buf))
return sec::end_of_stream;
return none;
}
error apply_impl(int8_t& x) override {
return apply_raw(sizeof(int8_t), &x);
}
error apply_impl(uint8_t& x) override {
return apply_raw(sizeof(uint8_t), &x);
}
error apply_impl(int16_t& x) override {
return apply_int(x);
}
error apply_impl(uint16_t& x) override {
return apply_int(x);
}
error apply_impl(int32_t& x) override {
return apply_int(x);
}
error apply_impl(uint32_t& x) override {
return apply_int(x);
}
error apply_impl(int64_t& x) override {
return apply_int(x);
}
error apply_impl(uint64_t& x) override {
return apply_int(x);
}
error apply_impl(float& x) override {
return apply_int(detail::pack754(x));
}
error apply_impl(double& x) override {
return apply_int(detail::pack754(x));
}
error apply_impl(long double& x) override {
// The IEEE-754 conversion does not work for long double
// => fall back to string serialization (event though it sucks).
std::ostringstream oss;
oss << std::setprecision(std::numeric_limits<long double>::digits) << x;
auto tmp = oss.str();
return apply_impl(tmp);
}
error apply_impl(std::string& x) override {
auto str_size = x.size();
auto data = const_cast<char*>(x.c_str());
return error::eval([&] { return begin_sequence(str_size); },
[&] { return apply_raw(x.size(), data); },
[&] { return end_sequence(); });
}
error apply_impl(std::u16string& x) override {
auto str_size = x.size();
return error::eval([&] { return begin_sequence(str_size); },
[&] { return consume_range_c<uint16_t>(x); },
[&] { return end_sequence(); });
}
error apply_impl(std::u32string& x) override {
auto str_size = x.size();
return error::eval([&] { return begin_sequence(str_size); },
[&] { return consume_range_c<uint32_t>(x); },
[&] { return end_sequence(); });
}
template <class T>
error apply_int(T x) {
using unsigned_type = typename std::make_unsigned<T>::type;
auto y = detail::to_network_order(static_cast<unsigned_type>(x));
return apply_raw(sizeof(T), &y);
}
private:
Streambuf streambuf_;
};
} // namespace caf
This diff is collapsed.
...@@ -53,11 +53,17 @@ public: ...@@ -53,11 +53,17 @@ public:
/// Load the content for the element at position `pos` from `source`. /// Load the content for the element at position `pos` from `source`.
virtual error load(size_t pos, deserializer& source) = 0; virtual error load(size_t pos, deserializer& source) = 0;
/// Load the content for the element at position `pos` from `source`.
virtual error load(size_t pos, binary_deserializer& source) = 0;
// -- modifiers -------------------------------------------------------------- // -- modifiers --------------------------------------------------------------
/// Load the content for the tuple from `source`. /// Load the content for the tuple from `source`.
virtual error load(deserializer& source); virtual error load(deserializer& source);
/// Load the content for the tuple from `source`.
virtual error load(binary_deserializer& source);
// -- pure virtual observers ------------------------------------------------- // -- pure virtual observers -------------------------------------------------
/// Returns the size of this tuple. /// Returns the size of this tuple.
...@@ -82,6 +88,9 @@ public: ...@@ -82,6 +88,9 @@ public:
/// Saves the element at position `pos` to `sink`. /// Saves the element at position `pos` to `sink`.
virtual error save(size_t pos, serializer& sink) const = 0; virtual error save(size_t pos, serializer& sink) const = 0;
/// Saves the element at position `pos` to `sink`.
virtual error save(size_t pos, binary_serializer& sink) const = 0;
// -- observers -------------------------------------------------------------- // -- observers --------------------------------------------------------------
/// Returns whether multiple references to this tuple exist. /// Returns whether multiple references to this tuple exist.
...@@ -97,6 +106,9 @@ public: ...@@ -97,6 +106,9 @@ public:
/// Saves the content of the tuple to `sink`. /// Saves the content of the tuple to `sink`.
virtual error save(serializer& sink) const; virtual error save(serializer& sink) const;
/// Saves the content of the tuple to `sink`.
virtual error save(binary_serializer& sink) const;
/// Checks whether the type of the stored value at position `pos` /// Checks whether the type of the stored value at position `pos`
/// matches type number `n` and run-time type information `p`. /// matches type number `n` and run-time type information `p`.
bool matches(size_t pos, uint16_t nr, bool matches(size_t pos, uint16_t nr,
...@@ -219,17 +231,23 @@ private: ...@@ -219,17 +231,23 @@ private:
}; };
/// @relates type_erased_tuple /// @relates type_erased_tuple
template <class Processor> inline error inspect(serializer& sink, const type_erased_tuple& x) {
typename std::enable_if<Processor::reads_state>::type return x.save(sink);
serialize(Processor& proc, type_erased_tuple& x) {
x.save(proc);
} }
/// @relates type_erased_tuple /// @relates type_erased_tuple
template <class Processor> inline error inspect(binary_serializer& sink, const type_erased_tuple& x) {
typename std::enable_if<Processor::writes_state>::type return x.save(sink);
serialize(Processor& proc, type_erased_tuple& x) { }
x.load(proc);
/// @relates type_erased_tuple
inline error inspect(deserializer& source, type_erased_tuple& x) {
return x.load(source);
}
/// @relates type_erased_tuple
inline error inspect(binary_deserializer& source, type_erased_tuple& x) {
return x.load(source);
} }
/// @relates type_erased_tuple /// @relates type_erased_tuple
...@@ -249,6 +267,8 @@ public: ...@@ -249,6 +267,8 @@ public:
error load(size_t pos, deserializer& source) override; error load(size_t pos, deserializer& source) override;
error load(size_t pos, binary_deserializer& source) override;
size_t size() const noexcept override; size_t size() const noexcept override;
uint32_t type_token() const noexcept override; uint32_t type_token() const noexcept override;
...@@ -262,7 +282,8 @@ public: ...@@ -262,7 +282,8 @@ public:
type_erased_value_ptr copy(size_t pos) const override; type_erased_value_ptr copy(size_t pos) const override;
error save(size_t pos, serializer& sink) const override; error save(size_t pos, serializer& sink) const override;
error save(size_t pos, binary_serializer& sink) const override;
}; };
} // namespace caf } // namespace caf
...@@ -44,6 +44,9 @@ public: ...@@ -44,6 +44,9 @@ public:
/// Load the content for the stored value from `source`. /// Load the content for the stored value from `source`.
virtual error load(deserializer& source) = 0; virtual error load(deserializer& source) = 0;
/// Load the content for the stored value from `source`.
virtual error load(binary_deserializer& source) = 0;
// -- pure virtual observers ------------------------------------------------- // -- pure virtual observers -------------------------------------------------
/// Returns the type number and type information object for the stored value. /// Returns the type number and type information object for the stored value.
...@@ -55,6 +58,9 @@ public: ...@@ -55,6 +58,9 @@ public:
/// Saves the content of the stored value to `sink`. /// Saves the content of the stored value to `sink`.
virtual error save(serializer& sink) const = 0; virtual error save(serializer& sink) const = 0;
/// Saves the content of the stored value to `sink`.
virtual error save(binary_serializer& sink) const = 0;
/// Converts the stored value to a string. /// Converts the stored value to a string.
virtual std::string stringify() const = 0; virtual std::string stringify() const = 0;
...@@ -97,11 +103,21 @@ inline error inspect(serializer& f, type_erased_value& x) { ...@@ -97,11 +103,21 @@ inline error inspect(serializer& f, type_erased_value& x) {
return x.save(f); return x.save(f);
} }
/// @relates type_erased_value_impl
inline error inspect(binary_serializer& f, type_erased_value& x) {
return x.save(f);
}
/// @relates type_erased_value_impl /// @relates type_erased_value_impl
inline error inspect(deserializer& f, type_erased_value& x) { inline error inspect(deserializer& f, type_erased_value& x) {
return x.load(f); return x.load(f);
} }
/// @relates type_erased_value_impl
inline error inspect(binary_deserializer& f, type_erased_value& x) {
return x.load(f);
}
/// @relates type_erased_value_impl /// @relates type_erased_value_impl
inline std::string to_string(const type_erased_value& x) { inline std::string to_string(const type_erased_value& x) {
return x.stringify(); return x.stringify();
......
...@@ -25,13 +25,13 @@ ...@@ -25,13 +25,13 @@
#include <cstdint> #include <cstdint>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/byte.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/timespan.hpp" #include "caf/timespan.hpp"
#include "caf/timestamp.hpp" #include "caf/timestamp.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/squashed_int.hpp"
namespace caf { namespace caf {
/// Compile-time list of all built-in types. /// Compile-time list of all built-in types.
...@@ -43,6 +43,7 @@ using sorted_builtin_types = ...@@ -43,6 +43,7 @@ using sorted_builtin_types =
actor_addr, // @addr actor_addr, // @addr
std::vector<actor_addr>, // @addrvec std::vector<actor_addr>, // @addrvec
atom_value, // @atom atom_value, // @atom
std::vector<byte>, // @bytebuf
std::vector<char>, // @charbuf std::vector<char>, // @charbuf
config_value, // @config_value config_value, // @config_value
down_msg, // @down down_msg, // @down
......
...@@ -20,22 +20,20 @@ ...@@ -20,22 +20,20 @@
#include <cstddef> #include <cstddef>
#include "caf/abstract_actor.hpp"
#include "caf/actor.hpp" #include "caf/actor.hpp"
#include "caf/make_actor.hpp"
#include "caf/actor_cast.hpp" #include "caf/actor_cast.hpp"
#include "caf/replies_to.hpp"
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/composed_type.hpp" #include "caf/composed_type.hpp"
#include "caf/abstract_actor.hpp" #include "caf/decorator/sequencer.hpp"
#include "caf/detail/mpi_splice.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/make_actor.hpp"
#include "caf/replies_to.hpp"
#include "caf/stateful_actor.hpp" #include "caf/stateful_actor.hpp"
#include "caf/typed_behavior.hpp" #include "caf/typed_behavior.hpp"
#include "caf/typed_response_promise.hpp" #include "caf/typed_response_promise.hpp"
#include "caf/detail/mpi_splice.hpp"
#include "caf/decorator/splitter.hpp"
#include "caf/decorator/sequencer.hpp"
namespace caf { namespace caf {
template <class... Sigs> template <class... Sigs>
...@@ -322,29 +320,6 @@ operator*(typed_actor<Xs...> f, typed_actor<Ys...> g) { ...@@ -322,29 +320,6 @@ operator*(typed_actor<Xs...> f, typed_actor<Ys...> g) {
actor_cast<strong_actor_ptr>(std::move(g)), std::move(mts)); actor_cast<strong_actor_ptr>(std::move(g)), std::move(mts));
} }
template <class... Xs, class... Ts>
typename detail::mpi_splice<
typed_actor,
detail::type_list<Xs...>,
typename Ts::signatures...
>::type
splice(const typed_actor<Xs...>& x, const Ts&... xs) {
using result =
typename detail::mpi_splice<
typed_actor,
detail::type_list<Xs...>,
typename Ts::signatures...
>::type;
std::vector<strong_actor_ptr> tmp{actor_cast<strong_actor_ptr>(x),
actor_cast<strong_actor_ptr>(xs)...};
auto& sys = x->home_system();
auto mts = sys.message_types(detail::type_list<result>{});
return make_actor<decorator::splitter, result>(sys.next_actor_id(),
sys.node(), &sys,
std::move(tmp),
std::move(mts));
}
} // namespace caf } // namespace caf
// allow typed_actor to be used in hash maps // allow typed_actor to be used in hash maps
......
...@@ -123,8 +123,12 @@ public: ...@@ -123,8 +123,12 @@ public:
friend error inspect(caf::serializer& dst, uri& x); friend error inspect(caf::serializer& dst, uri& x);
friend error inspect(caf::binary_serializer& dst, uri& x);
friend error inspect(caf::deserializer& src, uri& x); friend error inspect(caf::deserializer& src, uri& x);
friend error inspect(caf::binary_deserializer& src, uri& x);
private: private:
impl_ptr impl_; impl_ptr impl_;
}; };
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <array>
#include <tuple>
#include <type_traits>
#include <utility>
#include "caf/allowed_unsafe_message_type.hpp"
#include "caf/detail/squashed_int.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/meta/annotation.hpp"
#include "caf/meta/load_callback.hpp"
#include "caf/unifyn.hpp"
#define CAF_WRITE_INSPECTOR_TRY(statement) \
if constexpr (std::is_same<decltype(statement), void>::value) { \
statement; \
} else if (auto err = statement) { \
result = err; \
return false; \
}
namespace caf {
/// Injects an `operator()` that dispatches to `Subtype::apply`. The `Subtype`
/// shall overload `apply` for:
/// - all fixed-size integer types from `<cstdint>`
/// - floating point numbers
/// - enum types
/// - `timespan`, `timestamp`, and `atom_value`
/// - `std::string`, `std::u16string`, and `std::u32string`
template <class Subtype>
class write_inspector {
public:
static constexpr bool reads_state = false;
static constexpr bool writes_state = true;
template <class... Ts>
[[nodiscard]] auto operator()(Ts&&... xs) {
auto& dref = *static_cast<Subtype*>(this);
typename Subtype::result_type result;
auto f = [&result, &dref](auto&& x) {
using type = std::remove_reference_t<decltype(x)>;
if constexpr (std::is_empty<type>::value) {
// nop
} else if constexpr (meta::is_load_callback_v<type>) {
CAF_WRITE_INSPECTOR_TRY(x.fun())
} else if constexpr (meta::is_annotation_v<type> //
|| is_allowed_unsafe_message_type_v<type>) {
// skip element
} else if constexpr (detail::can_apply_v<Subtype, decltype(x)>) {
CAF_WRITE_INSPECTOR_TRY(dref.apply(x))
} else if constexpr (std::is_integral_v<type>) {
using squashed_type = detail::squashed_int_t<type>;
CAF_WRITE_INSPECTOR_TRY(dref.apply(reinterpret_cast<squashed_type&>(x)))
} else if constexpr (std::is_array<type>::value) {
CAF_WRITE_INSPECTOR_TRY(apply_array(dref, x))
} else if constexpr (detail::is_stl_tuple_type_v<type>) {
std::make_index_sequence<std::tuple_size_v<type>> seq;
CAF_WRITE_INSPECTOR_TRY(apply_tuple(dref, x, seq))
} else if constexpr (detail::is_map_like_v<type>) {
x.clear();
size_t size = 0;
CAF_WRITE_INSPECTOR_TRY(dref.begin_sequence(size))
for (size_t i = 0; i < size; ++i) {
auto key = typename type::key_type{};
auto val = typename type::mapped_type{};
CAF_WRITE_INSPECTOR_TRY(dref(key, val))
x.emplace(std::move(key), std::move(val));
}
CAF_WRITE_INSPECTOR_TRY(dref.end_sequence())
} else if constexpr (detail::is_list_like_v<type>) {
x.clear();
size_t size = 0;
CAF_WRITE_INSPECTOR_TRY(dref.begin_sequence(size))
for (size_t i = 0; i < size; ++i) {
auto tmp = typename type::value_type{};
CAF_WRITE_INSPECTOR_TRY(dref(tmp))
x.insert(x.end(), std::move(tmp));
}
CAF_WRITE_INSPECTOR_TRY(dref.end_sequence())
} else {
static_assert(std::is_lvalue_reference_v<decltype(x)> //
&& !std::is_const_v<decltype(x)>);
static_assert(detail::is_inspectable<Subtype, type>::value);
CAF_WRITE_INSPECTOR_TRY(inspect(dref, x));
}
return true;
};
static_cast<void>((f(std::forward<Ts>(xs)) && ...));
return result;
}
private:
template <class Tuple, size_t... Is>
static auto apply_tuple(Subtype& dref, Tuple& xs,
std::index_sequence<Is...>) {
return dref(std::get<Is>(xs)...);
}
template <class T, size_t... Is>
static auto apply_array(Subtype& dref, T* xs, std::index_sequence<Is...>) {
return dref(xs[Is]...);
}
template <class T, size_t N>
static auto apply_array(Subtype& dref, T (&xs)[N]) {
std::make_index_sequence<N> seq;
return apply_array(dref, xs, seq);
}
};
} // namespace caf
#undef CAF_WRITE_INSPECTOR_TRY
...@@ -22,16 +22,14 @@ ...@@ -22,16 +22,14 @@
#include <utility> #include <utility>
#include "caf/actor_addr.hpp" #include "caf/actor_addr.hpp"
#include "caf/make_actor.hpp"
#include "caf/serializer.hpp"
#include "caf/actor_proxy.hpp" #include "caf/actor_proxy.hpp"
#include "caf/local_actor.hpp" #include "caf/decorator/sequencer.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
#include "caf/local_actor.hpp"
#include "caf/decorator/splitter.hpp" #include "caf/make_actor.hpp"
#include "caf/decorator/sequencer.hpp" #include "caf/scoped_actor.hpp"
#include "caf/serializer.hpp"
namespace caf { namespace caf {
...@@ -89,21 +87,6 @@ actor operator*(actor f, actor g) { ...@@ -89,21 +87,6 @@ actor operator*(actor f, actor g) {
actor_cast<strong_actor_ptr>(std::move(g)), std::set<std::string>{}); actor_cast<strong_actor_ptr>(std::move(g)), std::set<std::string>{});
} }
actor actor::splice_impl(std::initializer_list<actor> xs) {
assert(xs.size() >= 2);
actor_system* sys = nullptr;
std::vector<strong_actor_ptr> tmp;
for (auto& x : xs) {
if (sys == nullptr)
sys = &(x->home_system());
tmp.push_back(actor_cast<strong_actor_ptr>(x));
}
return make_actor<decorator::splitter, actor>(sys->next_actor_id(),
sys->node(), sys,
std::move(tmp),
std::set<std::string>{});
}
bool operator==(const actor& lhs, abstract_actor* rhs) { bool operator==(const actor& lhs, abstract_actor* rhs) {
return lhs ? actor_cast<abstract_actor*>(lhs) == rhs : rhs == nullptr; return lhs ? actor_cast<abstract_actor*>(lhs) == rhs : rhs == nullptr;
} }
......
...@@ -215,41 +215,6 @@ settings actor_system_config::dump_content() const { ...@@ -215,41 +215,6 @@ settings actor_system_config::dump_content() const {
return result; return result;
} }
std::string
actor_system_config::make_help_text(const std::vector<message::cli_arg>& xs) {
auto is_no_caf_option = [](const message::cli_arg& arg) {
return arg.name.compare(0, 4, "caf#") != 0;
};
auto op = [](size_t tmp, const message::cli_arg& arg) {
return std::max(tmp, arg.helptext.size());
};
// maximum string lenght of all options
auto name_width = std::accumulate(xs.begin(), xs.end(), size_t{0}, op);
// iterators to the vector with respect to partition point
auto first = xs.begin();
auto last = xs.end();
auto sep = std::find_if(first, last, is_no_caf_option);
// output stream
std::ostringstream oss;
oss << std::left;
oss << "CAF Options:" << std::endl;
for (auto i = first; i != sep; ++i) {
oss << " ";
oss.width(static_cast<std::streamsize>(name_width));
oss << i->helptext << " : " << i->text << std::endl;
}
if (sep != last) {
oss << std::endl;
oss << "Application Options:" << std::endl;
for (auto i = sep; i != last; ++i) {
oss << " ";
oss.width(static_cast<std::streamsize>(name_width));
oss << i->helptext << " : " << i->text << std::endl;
}
}
return oss.str();
}
error actor_system_config::parse(int argc, char** argv, error actor_system_config::parse(int argc, char** argv,
const char* ini_file_cstr) { const char* ini_file_cstr) {
string_list args; string_list args;
......
This diff is collapsed.
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/binary_serializer.hpp"
#include "caf/actor_system.hpp"
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp"
namespace caf {
namespace {
template <class T>
auto apply_int(binary_serializer& sink, T x) {
auto y = detail::to_network_order(x);
sink.apply(as_bytes(make_span(&y, 1)));
}
} // namespace
binary_serializer::binary_serializer(actor_system& sys,
byte_buffer& buf) noexcept
: binary_serializer(sys.dummy_execution_unit(), buf) {
// nop
}
void binary_serializer::skip(size_t num_bytes) {
auto remaining = buf_.size() - write_pos_;
if (remaining < num_bytes)
buf_.insert(buf_.end(), num_bytes - remaining, byte{0});
write_pos_ += num_bytes;
}
error_code<sec> binary_serializer::begin_object(uint16_t nr, string_view name) {
apply(nr);
if (nr == 0)
apply(name);
return none;
}
error_code<sec> binary_serializer::end_object() {
return none;
}
error_code<sec> binary_serializer::begin_sequence(size_t list_size) {
// Use varbyte encoding to compress sequence size on the wire.
// For 64-bit values, the encoded representation cannot get larger than 10
// bytes. A scratch space of 16 bytes suffices as upper bound.
uint8_t buf[16];
auto i = buf;
auto x = static_cast<uint32_t>(list_size);
while (x > 0x7f) {
*i++ = (static_cast<uint8_t>(x) & 0x7f) | 0x80;
x >>= 7;
}
*i++ = static_cast<uint8_t>(x) & 0x7f;
apply(as_bytes(make_span(buf, static_cast<size_t>(i - buf))));
return none;
}
error_code<sec> binary_serializer::end_sequence() {
return none;
}
void binary_serializer::apply(span<const byte> x) {
CAF_ASSERT(write_pos_ <= buf_.size());
auto buf_size = buf_.size();
if (write_pos_ == buf_size) {
buf_.insert(buf_.end(), x.begin(), x.end());
} else if (write_pos_ + x.size() <= buf_size) {
memcpy(buf_.data() + write_pos_, x.data(), x.size());
} else {
auto remaining = buf_size - write_pos_;
CAF_ASSERT(remaining < x.size());
auto first = x.begin();
auto mid = first + remaining;
auto last = x.end();
memcpy(buf_.data() + write_pos_, first, remaining);
buf_.insert(buf_.end(), mid, last);
}
write_pos_ += x.size();
CAF_ASSERT(write_pos_ <= buf_.size());
}
void binary_serializer::apply(byte x) {
if (write_pos_ == buf_.size())
buf_.emplace_back(x);
else
buf_[write_pos_] = x;
++write_pos_;
}
void binary_serializer::apply(uint8_t x) {
apply(static_cast<byte>(x));
}
void binary_serializer::apply(uint16_t x) {
apply_int(*this, x);
}
void binary_serializer::apply(uint32_t x) {
apply_int(*this, x);
}
void binary_serializer::apply(uint64_t x) {
apply_int(*this, x);
}
void binary_serializer::apply(float x) {
apply_int(*this, detail::pack754(x));
}
void binary_serializer::apply(double x) {
apply_int(*this, detail::pack754(x));
}
void binary_serializer::apply(long double x) {
// TODO: Our IEEE-754 conversion currently does not work for long double. The
// standard does not guarantee a fixed representation for this type, but
// on X86 we can usually rely on 80-bit precision. For now, we fall back
// to string conversion.
std::ostringstream oss;
oss << std::setprecision(std::numeric_limits<long double>::digits) << x;
auto tmp = oss.str();
apply(tmp);
}
void binary_serializer::apply(timespan x) {
apply(x.count());
}
void binary_serializer::apply(timestamp x) {
apply(x.time_since_epoch().count());
}
void binary_serializer::apply(string_view x) {
begin_sequence(x.size());
apply(as_bytes(make_span(x)));
end_sequence();
}
void binary_serializer::apply(std::u16string_view x) {
auto str_size = x.size();
begin_sequence(str_size);
// The standard does not guarantee that char16_t is exactly 16 bits.
for (auto c : x)
apply_int(*this, static_cast<uint16_t>(c));
end_sequence();
}
void binary_serializer::apply(std::u32string_view x) {
auto str_size = x.size();
begin_sequence(str_size);
// The standard does not guarantee that char32_t is exactly 32 bits.
for (auto c : x)
apply_int(*this, static_cast<uint32_t>(c));
end_sequence();
}
void binary_serializer::apply(const std::vector<bool>& x) {
auto len = x.size();
begin_sequence(len);
if (len == 0) {
end_sequence();
return;
}
size_t pos = 0;
size_t blocks = len / 8;
for (size_t block = 0; block < blocks; ++block) {
uint8_t tmp = 0;
if (x[pos++])
tmp |= 0b1000'0000;
if (x[pos++])
tmp |= 0b0100'0000;
if (x[pos++])
tmp |= 0b0010'0000;
if (x[pos++])
tmp |= 0b0001'0000;
if (x[pos++])
tmp |= 0b0000'1000;
if (x[pos++])
tmp |= 0b0000'0100;
if (x[pos++])
tmp |= 0b0000'0010;
if (x[pos++])
tmp |= 0b0000'0001;
apply(tmp);
}
auto trailing_block_size = len % 8;
if (trailing_block_size > 0) {
uint8_t tmp = 0;
switch (trailing_block_size) {
case 7:
if (x[pos++])
tmp |= 0b0100'0000;
[[fallthrough]];
case 6:
if (x[pos++])
tmp |= 0b0010'0000;
[[fallthrough]];
case 5:
if (x[pos++])
tmp |= 0b0001'0000;
[[fallthrough]];
case 4:
if (x[pos++])
tmp |= 0b0000'1000;
[[fallthrough]];
case 3:
if (x[pos++])
tmp |= 0b0000'0100;
[[fallthrough]];
case 2:
if (x[pos++])
tmp |= 0b0000'0010;
[[fallthrough]];
case 1:
if (x[pos++])
tmp |= 0b0000'0001;
[[fallthrough]];
default:
break;
}
apply(tmp);
}
end_sequence();
}
} // namespace caf
...@@ -22,16 +22,31 @@ ...@@ -22,16 +22,31 @@
namespace caf { namespace caf {
deserializer::~deserializer() { deserializer::deserializer(actor_system& x) noexcept
: context_(x.dummy_execution_unit()) {
// nop // nop
} }
deserializer::deserializer(actor_system& x) : super(x.dummy_execution_unit()) { deserializer::deserializer(execution_unit* x) noexcept : context_(x) {
// nop // nop
} }
deserializer::deserializer(execution_unit* x) : super(x) { deserializer::~deserializer() {
// nop // nop
} }
auto deserializer::apply(std::vector<bool>& x) noexcept -> result_type {
x.clear();
size_t size = 0;
if (auto err = begin_sequence(size))
return err;
for (size_t i = 0; i < size; ++i) {
bool tmp = false;
if (auto err = apply(tmp))
return err;
x.emplace_back(tmp);
}
return end_sequence();
}
} // namespace caf } // namespace caf
...@@ -60,6 +60,11 @@ error dynamic_message_data::load(size_t pos, deserializer& source) { ...@@ -60,6 +60,11 @@ error dynamic_message_data::load(size_t pos, deserializer& source) {
return elements_[pos]->load(source); return elements_[pos]->load(source);
} }
error dynamic_message_data::load(size_t pos, binary_deserializer& source) {
CAF_ASSERT(pos < size());
return elements_[pos]->load(source);
}
size_t dynamic_message_data::size() const noexcept { size_t dynamic_message_data::size() const noexcept {
return elements_.size(); return elements_.size();
} }
...@@ -93,6 +98,11 @@ error dynamic_message_data::save(size_t pos, serializer& sink) const { ...@@ -93,6 +98,11 @@ error dynamic_message_data::save(size_t pos, serializer& sink) const {
return elements_[pos]->save(sink); return elements_[pos]->save(sink);
} }
error dynamic_message_data::save(size_t pos, binary_serializer& sink) const {
CAF_ASSERT(pos < size());
return elements_[pos]->save(sink);
}
void dynamic_message_data::clear() { void dynamic_message_data::clear() {
elements_.clear(); elements_.clear();
type_token_ = 0xFFFFFFFF; type_token_ = 0xFFFFFFFF;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework * * | |___ / ___ \| _| Framework *
* \____/_/ \_|_| * * \____/_/ \_|_| *
* * * *
* Copyright 2011-2018 Dominik Charousset * * Copyright 2011-2019 Dominik Charousset *
* * * *
* Distributed under the terms and conditions of the BSD 3-Clause License or * * Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software * * (at your option) under the terms and conditions of the Boost Software *
...@@ -16,9 +16,7 @@ ...@@ -16,9 +16,7 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#pragma once #include "caf/error_code.hpp"
#include <vector>
namespace caf::io::basp { namespace caf::io::basp {
...@@ -29,6 +27,4 @@ using buffer_type = std::vector<char>; ...@@ -29,6 +27,4 @@ using buffer_type = std::vector<char>;
/// @} /// @}
} // namespace caf } // namespace caf::io::basp
...@@ -54,30 +54,58 @@ intptr_t group::compare(const group& other) const noexcept { ...@@ -54,30 +54,58 @@ intptr_t group::compare(const group& other) const noexcept {
return compare(ptr_.get(), other.ptr_.get()); return compare(ptr_.get(), other.ptr_.get());
} }
error inspect(serializer& f, group& x) { namespace {
template <class Serializer>
auto save_group(Serializer& sink, group& x) {
std::string mod_name; std::string mod_name;
auto ptr = x.get(); auto ptr = x.get();
if (ptr == nullptr) if (ptr == nullptr)
return f(mod_name); return sink(mod_name);
mod_name = ptr->module().name(); mod_name = ptr->module().name();
auto e = f(mod_name); if (auto err = sink(mod_name))
return e ? e : ptr->save(f); return err;
return ptr->save(sink);
}
} // namespace
error inspect(serializer& sink, group& x) {
return save_group(sink, x);
}
error inspect(binary_serializer& sink, group& x) {
return save_group(sink, x);
} }
error inspect(deserializer& f, group& x) { namespace {
template <class Deserializer>
typename Deserializer::result_type load_group(Deserializer& source, group& x) {
std::string module_name; std::string module_name;
f(module_name); if (auto err = source(module_name))
return err;
if (module_name.empty()) { if (module_name.empty()) {
x = invalid_group; x = invalid_group;
return none; return none;
} }
if (f.context() == nullptr) if (source.context() == nullptr)
return sec::no_context; return sec::no_context;
auto& sys = f.context()->system(); auto& sys = source.context()->system();
auto mod = sys.groups().get_module(module_name); auto mod = sys.groups().get_module(module_name);
if (!mod) if (!mod)
return sec::no_such_group_module; return sec::no_such_group_module;
return mod->load(f, x); return mod->load(source, x);
}
} // namespace
error inspect(deserializer& source, group& x) {
return load_group(source, x);
}
error inspect(binary_deserializer& source, group& x) {
return load_group(source, x);
} }
std::string to_string(const group& x) { std::string to_string(const group& x) {
......
...@@ -111,6 +111,8 @@ public: ...@@ -111,6 +111,8 @@ public:
error save(serializer& sink) const override; error save(serializer& sink) const override;
error save(binary_serializer& sink) const override;
void stop() override { void stop() override {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
await_all_locals_down(system(), {broker_}); await_all_locals_down(system(), {broker_});
...@@ -348,7 +350,9 @@ public: ...@@ -348,7 +350,9 @@ public:
return group{result}; return group{result};
} }
error load(deserializer& source, group& storage) override {
template <class Deserializer>
error load_impl(Deserializer& source, group& storage) {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
// deserialize identifier and broker // deserialize identifier and broker
std::string identifier; std::string identifier;
...@@ -382,7 +386,16 @@ public: ...@@ -382,7 +386,16 @@ public:
return none; return none;
} }
error save(const local_group* ptr, serializer& sink) const { error load(deserializer& source, group& storage) override {
return load_impl(source, storage);
}
error load(binary_deserializer& source, group& storage) override {
return load_impl(source, storage);
}
template <class Serializer>
auto save_impl(const local_group* ptr, Serializer& sink) const {
CAF_ASSERT(ptr != nullptr); CAF_ASSERT(ptr != nullptr);
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
auto bro = actor_cast<strong_actor_ptr>(ptr->broker()); auto bro = actor_cast<strong_actor_ptr>(ptr->broker());
...@@ -390,6 +403,14 @@ public: ...@@ -390,6 +403,14 @@ public:
return sink(id, bro); return sink(id, bro);
} }
error save(const local_group* ptr, serializer& sink) const {
return save_impl(ptr, sink);
}
error save(const local_group* ptr, binary_serializer& sink) const {
return save_impl(ptr, sink);
}
void stop() override { void stop() override {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
std::map<std::string, local_group_ptr> imap; std::map<std::string, local_group_ptr> imap;
...@@ -431,6 +452,11 @@ error local_group::save(serializer& sink) const { ...@@ -431,6 +452,11 @@ error local_group::save(serializer& sink) const {
return static_cast<local_group_module&>(parent_).save(this, sink); return static_cast<local_group_module&>(parent_).save(this, sink);
} }
error local_group::save(binary_serializer& sink) const {
CAF_LOG_TRACE("");
return static_cast<local_group_module&>(parent_).save(this, sink);
}
std::atomic<size_t> s_ad_hoc_id; std::atomic<size_t> s_ad_hoc_id;
} // namespace } // namespace
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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