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
src/attachable.cpp
src/behavior.cpp
src/binary_deserializer.cpp
src/binary_serializer.cpp
src/blocking_actor.cpp
src/config_option.cpp
src/config_option_adder.cpp
src/config_option_set.cpp
src/config_value.cpp
src/decorator/sequencer.cpp
src/decorator/splitter.cpp
src/default_attachable.cpp
src/defaults.cpp
src/deserializer.cpp
......@@ -61,8 +61,6 @@ set(LIBCAF_CORE_SRCS
src/detail/behavior_impl.cpp
src/detail/behavior_stack.cpp
src/detail/blocking_behavior.cpp
src/detail/concatenated_tuple.cpp
src/detail/decorated_tuple.cpp
src/detail/dynamic_message_data.cpp
src/detail/fnv_hash.cpp
src/detail/get_mac_addresses.cpp
......@@ -70,14 +68,12 @@ set(LIBCAF_CORE_SRCS
src/detail/get_root_uuid.cpp
src/detail/ini_consumer.cpp
src/detail/invoke_result_visitor.cpp
src/detail/merged_tuple.cpp
src/detail/message_data.cpp
src/detail/parse.cpp
src/detail/parser/chars.cpp
src/detail/pretty_type_name.cpp
src/detail/private_thread.cpp
src/detail/ripemd_160.cpp
src/detail/serialized_size.cpp
src/detail/set_thread_name.cpp
src/detail/shared_spinlock.cpp
src/detail/simple_actor_clock.cpp
......@@ -92,6 +88,7 @@ set(LIBCAF_CORE_SRCS
src/downstream_manager_base.cpp
src/duration.cpp
src/error.cpp
src/error_code.cpp
src/event_based_actor.cpp
src/execution_unit.cpp
src/exit_reason.cpp
......
......@@ -47,6 +47,9 @@ public:
/// Serialize this group to `sink`.
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
/// or `false` if `who` is already subscribed.
virtual bool subscribe(strong_actor_ptr who) = 0;
......
......@@ -133,8 +133,6 @@ public:
intptr_t compare(const strong_actor_ptr&) const noexcept;
static actor splice_impl(std::initializer_list<actor> xs);
actor(actor_control_block*, bool);
/// @endcond
......@@ -175,12 +173,6 @@ private:
/// Combine `f` and `g` so that `(f*g)(x) = f(g(x))`.
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
bool operator==(const actor& lhs, abstract_actor* rhs);
......
......@@ -395,8 +395,6 @@ public:
settings& result);
protected:
virtual std::string make_help_text(const std::vector<message::cli_arg>&);
config_option_set custom_options_;
private:
......
......@@ -46,6 +46,7 @@
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/blocking_actor.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/composable_behavior.hpp"
#include "caf/composed_behavior.hpp"
#include "caf/config_option.hpp"
......@@ -103,10 +104,7 @@
#include "caf/spawn_options.hpp"
#include "caf/stateful_actor.hpp"
#include "caf/stream.hpp"
#include "caf/stream_deserializer.hpp"
#include "caf/stream_serializer.hpp"
#include "caf/stream_slot.hpp"
#include "caf/streambuf.hpp"
#include "caf/system_messages.hpp"
#include "caf/term.hpp"
#include "caf/thread_hook.hpp"
......
......@@ -43,6 +43,10 @@ template <class T>
struct is_allowed_unsafe_message_type<const 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
#define CAF_ALLOW_UNSAFE_MESSAGE_TYPE(type_name) \
......
......@@ -25,15 +25,12 @@
#include "caf/detail/atom_val.hpp"
#include "caf/fwd.hpp"
#include "caf/meta/load_callback.hpp"
namespace caf {
/// The value type of atoms.
enum class atom_value : uint64_t {
/// @cond PRIVATE
dirty_little_hack = 31337
/// @endcond
};
enum class atom_value : uint64_t {};
/// @relates atom_value
std::string to_string(atom_value x);
......
......@@ -19,64 +19,44 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <vector>
#include "caf/byte.hpp"
#include "caf/deserializer.hpp"
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include "caf/error_code.hpp"
#include "caf/fwd.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
#include "caf/write_inspector.hpp"
namespace caf {
/// Implements the deserializer interface with a binary serialization protocol.
class binary_deserializer final : public deserializer {
class binary_deserializer : public write_inspector<binary_deserializer> {
public:
// -- member types -----------------------------------------------------------
using super = deserializer;
// -- constructors, destructors, and assignment operators --------------------
using result_type = error;
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>
binary_deserializer(actor_system& sys, const std::vector<T>& buf)
: binary_deserializer(sys, as_bytes(make_span(buf))) {
// nop
template <class Container>
binary_deserializer(actor_system& sys, const Container& input) noexcept
: binary_deserializer(sys) {
reset(as_bytes(make_span(input)));
}
template <class T>
binary_deserializer(execution_unit* ctx, const std::vector<T>& buf)
: binary_deserializer(ctx, as_bytes(make_span(buf))) {
// nop
template <class Container>
binary_deserializer(execution_unit* ctx, const Container& input) noexcept
: context_(ctx) {
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 -------------------------------------------------------------
/// 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.
size_t remaining() const noexcept {
return static_cast<size_t>(end_ - current_);
......@@ -87,49 +67,99 @@ public:
return make_span(current_, end_);
}
/// Returns the current execution unit.
execution_unit* context() const noexcept {
return context_;
}
/// Jumps `num_bytes` forward.
/// @pre `num_bytes <= remaining()`
void skip(size_t num_bytes);
void skip(size_t num_bytes) noexcept;
/// 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:
error apply_impl(int8_t&) override;
apply_result apply(byte&) noexcept;
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:
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_;
}
/// Points to the current read position.
const byte* current_;
/// Points to the end of the assigned memory block.
const byte* end_;
/// Provides access to the ::proxy_registry and to the ::actor_system.
execution_unit* context_;
};
} // namespace caf
......@@ -18,12 +18,133 @@
#pragma once
#include <cstddef>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#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 {
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
......@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* 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 *
......@@ -16,52 +16,15 @@
* 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 "caf/all.hpp"
using namespace caf;
#include "caf/byte.hpp"
using std::string;
namespace caf {
CAF_TEST(type_sequences) {
auto _64 = uint64_t{64};
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)));
}
/// A buffer for storing binary data.
using byte_buffer = std::vector<byte>;
CAF_TEST(cli_args) {
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, "");
}
} // namespace caf
This diff is collapsed.
......@@ -18,62 +18,134 @@
#pragma once
#include <string>
#include <cstddef>
#include <utility>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include "caf/data_processor.hpp"
#include "caf/byte.hpp"
#include "caf/fwd.hpp"
#include "caf/raise_error.hpp"
#include "caf/span.hpp"
#include "caf/write_inspector.hpp"
namespace caf {
/// @ingroup TypeSystem
/// Technology-independent deserialization interface.
class deserializer : public data_processor<deserializer> {
class deserializer : public write_inspector<deserializer> {
public:
~deserializer() override;
// -- member types -----------------------------------------------------------
using super = data_processor<deserializer>;
using result_type = error;
static constexpr bool reads_state = false;
static constexpr bool writes_state = true;
// -- constructors, destructors, and assignment operators --------------------
// Boost Serialization compatibility
using is_saving = std::false_type;
using is_loading = std::true_type;
explicit deserializer(actor_system& sys) noexcept;
explicit deserializer(actor_system& x);
explicit deserializer(execution_unit* ctx = nullptr) noexcept;
explicit deserializer(execution_unit* x = nullptr);
};
virtual ~deserializer();
template <class T>
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;
}
// -- properties -------------------------------------------------------------
} // 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 @@
#include <vector>
#include "caf/type_erased_value.hpp"
#include "caf/detail/message_data.hpp"
#include "caf/type_erased_value.hpp"
namespace caf::detail {
......@@ -52,6 +51,8 @@ public:
error load(size_t pos, deserializer& source) override;
error load(size_t pos, binary_deserializer& source) override;
// -- overridden observers of type_erased_tuple ------------------------------
size_t size() const noexcept override;
......@@ -68,6 +69,8 @@ public:
error save(size_t pos, serializer& sink) const override;
error save(size_t pos, binary_serializer& sink) const override;
// -- modifiers --------------------------------------------------------------
void clear();
......
......@@ -55,5 +55,3 @@ template <class T>
using squashed_int_t = typename squashed_int<T>::type;
} // namespace caf
......@@ -21,19 +21,20 @@
#include <tuple>
#include <stdexcept>
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/deep_to_string.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/rtti_pair.hpp"
#include "caf/serializer.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) \
case x: \
return tuple_inspect_delegate<x, sizeof...(Ts)-1>(data_, f)
......@@ -137,6 +138,10 @@ public:
return dispatch(pos, source);
}
error load(size_t pos, binary_deserializer& source) override {
return dispatch(pos, source);
}
uint32_t type_token() const noexcept override {
return make_type_token<Ts...>();
}
......@@ -149,6 +154,10 @@ public:
return mptr()->dispatch(pos, sink);
}
error save(size_t pos, binary_serializer& sink) const override {
return mptr()->dispatch(pos, sink);
}
private:
template <class F>
auto dispatch(size_t pos, F& f) -> decltype(f(std::declval<int&>())) {
......
......@@ -70,6 +70,10 @@ public:
return ptrs_[pos]->load(source);
}
error load(size_t pos, binary_deserializer& source) override {
return ptrs_[pos]->load(source);
}
// -- overridden observers ---------------------------------------------------
size_t size() const noexcept override {
......@@ -100,6 +104,10 @@ public:
return ptrs_[pos]->save(sink);
}
error save(size_t pos, binary_serializer& sink) const override {
return ptrs_[pos]->save(sink);
}
// -- member variables access ------------------------------------------------
tuple_type& data() {
......
......@@ -22,11 +22,14 @@
#include <typeinfo>
#include <functional>
#include "caf/error.hpp"
#include "caf/type_erased_value.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/safe_equal.hpp"
#include "caf/detail/try_serialize.hpp"
#include "caf/error.hpp"
#include "caf/serializer.hpp"
#include "caf/type_erased_value.hpp"
namespace caf::detail {
......@@ -78,6 +81,10 @@ public:
return source(*addr_of(x_));
}
error load(binary_deserializer& source) override {
return source(*addr_of(x_));
}
// -- overridden observers ---------------------------------------------------
static rtti_pair type(std::integral_constant<uint16_t, 0>) {
......@@ -103,6 +110,10 @@ public:
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 {
return deep_to_string(x_);
}
......
......@@ -18,13 +18,14 @@
#pragma once
#include <tuple>
#include <array>
#include <chrono>
#include <string>
#include <vector>
#include <utility>
#include <functional>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "caf/fwd.hpp"
#include "caf/timestamp.hpp"
......@@ -273,19 +274,6 @@ public:
template <class T>
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
/// serialization. The checks test whether both serialization and
/// deserialization can succeed. The meta function tests the following
......@@ -771,10 +759,12 @@ using deconst_kvp_t = typename deconst_kvp<T>::type;
template <class T>
struct is_pair : std::false_type {};
/// Utility trait for checking whether T is a `std::pair`.
template <class First, class Second>
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 -------------------------------
CAF_HAS_ALIAS_TRAIT(value_type);
......@@ -806,6 +796,9 @@ struct is_map_like {
&& 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`.
template <class T>
struct is_list_like {
......@@ -814,6 +807,9 @@ struct is_list_like {
&& !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>
struct is_invocable {
private:
......@@ -846,7 +842,58 @@ public:
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_ALIAS_TRAIT
......@@ -23,13 +23,12 @@
#include <utility>
#include "caf/atom.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/error_code.hpp"
#include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/none.hpp"
namespace caf {
......@@ -99,15 +98,19 @@ public:
// -- constructors, destructors, and assignment operators --------------------
error() noexcept;
error(none_t) noexcept;
error(error&&) noexcept;
error& operator=(error&&) noexcept;
error(const error&);
error& operator=(const error&);
error(uint8_t x, atom_value y);
error(uint8_t x, atom_value y, message z);
template <class E, class = enable_if_has_make_error_t<E>>
......@@ -115,6 +118,11 @@ public:
// nop
}
template <class E>
error(error_code<E> code) : error(code.value()) {
// nop
}
template <class E, class = enable_if_has_make_error_t<E>>
error& operator=(E error_value) {
auto tmp = make_error(error_value);
......@@ -122,6 +130,13 @@ public:
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();
// -- observers --------------------------------------------------------------
......
......@@ -18,68 +18,59 @@
#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:
using super = serializer;
using enum_type = Enum;
using super::super;
using underlying_type = std::underlying_type_t<enum_type>;
size_t result() const noexcept {
return result_;
constexpr error_code() noexcept : value_(static_cast<Enum>(0)) {
// nop
}
error begin_object(uint16_t& nr, 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;
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;
constexpr error_code(none_t) noexcept : value_(static_cast<Enum>(0)) {
// nop
}
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:
size_t result_ = 0;
enum_type value_;
};
template <class T>
size_t serialized_size(actor_system& sys, const T& x) {
serialized_size_inspector f{sys};
f(const_cast<T&>(x));
return f.result();
/// Converts `x` to a string if `Enum` provides a `to_string` function.
template <class Enum>
auto to_string(error_code<Enum> x) -> decltype(to_string(x.value())) {
return to_string(x.value());
}
} // namespace caf
......@@ -37,12 +37,12 @@ namespace caf {
template <class> class behavior_type_of;
template <class> class dictionary;
template <class> class downstream;
template <class> class error_code;
template <class> class expected;
template <class> class intrusive_cow_ptr;
template <class> class intrusive_ptr;
template <class> class optional;
template <class> class param;
template <class> class serializer_impl;
template <class> class span;
template <class> class stream;
template <class> class stream_sink;
......@@ -95,6 +95,7 @@ class actor_system;
class actor_system_config;
class behavior;
class binary_deserializer;
class binary_serializer;
class blocking_actor;
class config_option;
class config_option_adder;
......@@ -183,7 +184,6 @@ enum class invoke_message_result;
// -- aliases ------------------------------------------------------------------
using actor_id = uint64_t;
using binary_serializer = serializer_impl<std::vector<char>>;
using ip_address = ipv6_address;
using ip_endpoint = ipv6_endpoint;
using ip_subnet = ipv6_subnet;
......@@ -195,6 +195,9 @@ using stream_slot = uint16_t;
/// @relates actor_system_config
const settings& content(const actor_system_config&);
template <class T, class... Ts>
message make_message(T&& x, Ts&&... xs);
// -- intrusive containers -----------------------------------------------------
namespace intrusive {
......
......@@ -80,24 +80,14 @@ public:
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(binary_serializer&, group&);
friend error inspect(deserializer&, group&);
friend error inspect(binary_deserializer&, group&);
abstract_group* get() const noexcept {
return ptr_.get();
}
......
......@@ -50,6 +50,9 @@ public:
/// Loads a group of this module from `source` and stores it in `storage`.
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 --------------------------------------------------------------
/// Returns the hosting actor system.
......
......@@ -62,23 +62,18 @@ struct is_serializable_or_whitelisted {
/// Returns a new `message` containing the values `(x, xs...)`.
/// @relates message
template <class T, class... Ts>
typename std::enable_if<
!std::is_same<message, typename std::decay<T>::type>::value
|| (sizeof...(Ts) > 0),
message
>::type
make_message(T&& x, Ts&&... xs) {
message make_message(T&& x, Ts&&... xs) {
if constexpr (sizeof...(Ts) == 0
&& std::is_same<message, std::decay_t<T>>::value) {
return std::forward<T>(x);
} else {
using namespace caf::detail;
using stored_types =
type_list<
using stored_types = type_list<
typename unbox_message_element<typename strip_and_convert<T>::type>::type,
typename unbox_message_element<
typename strip_and_convert<T>::type
>::type,
typename unbox_message_element<
typename strip_and_convert<Ts>::type
>::type...
>;
static_assert(tl_forall<stored_types, is_serializable_or_whitelisted>::value,
typename strip_and_convert<Ts>::type>::type...>;
static_assert(tl_forall<stored_types,
is_serializable_or_whitelisted>::value,
"at least one type is neither inspectable via "
"inspect(Inspector&, T&) nor serializable via "
"'serialize(Processor&, T&, const unsigned int)' or "
......@@ -87,14 +82,10 @@ make_message(T&& x, Ts&&... xs) {
"specializing `caf::allowed_unsafe_message_type<T>` "
"or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
using storage = typename tl_apply<stored_types, tuple_vals>::type;
auto ptr = make_counted<storage>(std::forward<T>(x), std::forward<Ts>(xs)...);
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`.
......@@ -118,4 +109,3 @@ message make_message_from_tuple(std::tuple<Ts...> xs) {
}
} // namespace caf
This diff is collapsed.
......@@ -106,16 +106,6 @@ public:
/// is undefined behavior (dereferencing a `nullptr`)
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
optional<message> apply(message_handler handler);
......
......@@ -43,5 +43,7 @@ struct is_annotation<const T&> : is_annotation<T> {};
template <class 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 {};
template <class F>
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
/// user-defined code after performing load operations.
template <class F>
......
......@@ -43,6 +43,9 @@ struct is_save_callback : std::false_type {};
template <class F>
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
/// user-defined code after performing save operations.
template <class F>
......
......@@ -56,6 +56,10 @@ public:
virtual error serialize(serializer& sink) const = 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.
......@@ -115,6 +119,10 @@ public:
error deserialize(deserializer& source) override;
error serialize(binary_serializer& sink) const override;
error deserialize(binary_deserializer& source) override;
private:
// -- member variables -----------------------------------------------------
......@@ -159,6 +167,10 @@ public:
error deserialize(deserializer& source) override;
error serialize(binary_serializer& sink) const override;
error deserialize(binary_deserializer& source) override;
private:
// -- member variables -----------------------------------------------------
......@@ -199,9 +211,6 @@ public:
/// @cond PRIVATE
error serialize(serializer& sink) const;
error deserialize(deserializer& source);
data* operator->() noexcept {
return data_.get();
......@@ -221,6 +230,14 @@ public:
/// @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:
intrusive_ptr<data> data_;
};
......@@ -281,12 +298,6 @@ inline bool operator!=(const none_t&, const node_id& x) noexcept {
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`.
/// @relates node_id
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 @@
#pragma once
#include "caf/error.hpp"
#include "caf/make_message.hpp"
#include <string>
#include "caf/fwd.hpp"
namespace caf {
......@@ -134,12 +135,14 @@ std::string to_string(sec);
/// @relates sec
error make_error(sec);
/// @relates sec
error make_error(sec, message);
/// @relates sec
template <class T, class... Ts>
error make_error(sec code, T&& x, Ts&&... xs) {
return {static_cast<uint8_t>(code), atom("system"),
make_message(std::forward<T>(x), std::forward<Ts>(xs)...)};
auto make_error(sec code, T&& x, Ts&&... xs) {
return make_error(code,
make_message(std::forward<T>(x), std::forward<Ts>(xs)...));
}
} // namespace caf
......@@ -18,62 +18,136 @@
#pragma once
#include <string>
#include <cstddef> // size_t
#include <type_traits>
#include <cstddef>
#include <cstdint>
#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/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 {
/// @ingroup TypeSystem
/// Technology-independent serialization interface.
class serializer : public data_processor<serializer> {
class serializer : public read_inspector<serializer> {
public:
using super = data_processor<serializer>;
// -- member types -----------------------------------------------------------
static constexpr bool reads_state = true;
static constexpr bool writes_state = false;
using result_type = error;
// Boost Serialization compatibility
using is_saving = std::true_type;
using is_loading = std::false_type;
// -- constructors, destructors, and assignment operators --------------------
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>
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;
}
// -- properties -------------------------------------------------------------
} // 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:
}
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()) {
// nop
}
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()) {
// 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:
/// Load the content for the element at position `pos` from `source`.
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 --------------------------------------------------------------
/// Load the content for the tuple from `source`.
virtual error load(deserializer& source);
/// Load the content for the tuple from `source`.
virtual error load(binary_deserializer& source);
// -- pure virtual observers -------------------------------------------------
/// Returns the size of this tuple.
......@@ -82,6 +88,9 @@ public:
/// Saves the element at position `pos` to `sink`.
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 --------------------------------------------------------------
/// Returns whether multiple references to this tuple exist.
......@@ -97,6 +106,9 @@ public:
/// Saves the content of the tuple to `sink`.
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`
/// matches type number `n` and run-time type information `p`.
bool matches(size_t pos, uint16_t nr,
......@@ -219,17 +231,23 @@ private:
};
/// @relates type_erased_tuple
template <class Processor>
typename std::enable_if<Processor::reads_state>::type
serialize(Processor& proc, type_erased_tuple& x) {
x.save(proc);
inline error inspect(serializer& sink, const type_erased_tuple& x) {
return x.save(sink);
}
/// @relates type_erased_tuple
template <class Processor>
typename std::enable_if<Processor::writes_state>::type
serialize(Processor& proc, type_erased_tuple& x) {
x.load(proc);
inline error inspect(binary_serializer& sink, const type_erased_tuple& x) {
return x.save(sink);
}
/// @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
......@@ -249,6 +267,8 @@ public:
error load(size_t pos, deserializer& source) override;
error load(size_t pos, binary_deserializer& source) override;
size_t size() const noexcept override;
uint32_t type_token() const noexcept override;
......@@ -262,7 +282,8 @@ public:
type_erased_value_ptr copy(size_t pos) const override;
error save(size_t pos, serializer& sink) const override;
error save(size_t pos, binary_serializer& sink) const override;
};
} // namespace caf
......@@ -44,6 +44,9 @@ public:
/// Load the content for the stored value from `source`.
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 -------------------------------------------------
/// Returns the type number and type information object for the stored value.
......@@ -55,6 +58,9 @@ public:
/// Saves the content of the stored value to `sink`.
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.
virtual std::string stringify() const = 0;
......@@ -97,11 +103,21 @@ inline error inspect(serializer& f, type_erased_value& x) {
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
inline error inspect(deserializer& f, type_erased_value& x) {
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
inline std::string to_string(const type_erased_value& x) {
return x.stringify();
......
......@@ -25,13 +25,13 @@
#include <cstdint>
#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/timespan.hpp"
#include "caf/timestamp.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/squashed_int.hpp"
namespace caf {
/// Compile-time list of all built-in types.
......@@ -43,6 +43,7 @@ using sorted_builtin_types =
actor_addr, // @addr
std::vector<actor_addr>, // @addrvec
atom_value, // @atom
std::vector<byte>, // @bytebuf
std::vector<char>, // @charbuf
config_value, // @config_value
down_msg, // @down
......
......@@ -20,22 +20,20 @@
#include <cstddef>
#include "caf/abstract_actor.hpp"
#include "caf/actor.hpp"
#include "caf/make_actor.hpp"
#include "caf/actor_cast.hpp"
#include "caf/replies_to.hpp"
#include "caf/actor_system.hpp"
#include "caf/intrusive_ptr.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/typed_behavior.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 {
template <class... Sigs>
......@@ -322,29 +320,6 @@ operator*(typed_actor<Xs...> f, typed_actor<Ys...> g) {
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
// allow typed_actor to be used in hash maps
......
......@@ -123,8 +123,12 @@ public:
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::binary_deserializer& src, uri& x);
private:
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 @@
#include <utility>
#include "caf/actor_addr.hpp"
#include "caf/make_actor.hpp"
#include "caf/serializer.hpp"
#include "caf/actor_proxy.hpp"
#include "caf/local_actor.hpp"
#include "caf/decorator/sequencer.hpp"
#include "caf/deserializer.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/decorator/splitter.hpp"
#include "caf/decorator/sequencer.hpp"
#include "caf/local_actor.hpp"
#include "caf/make_actor.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/serializer.hpp"
namespace caf {
......@@ -89,21 +87,6 @@ actor operator*(actor f, actor g) {
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) {
return lhs ? actor_cast<abstract_actor*>(lhs) == rhs : rhs == nullptr;
}
......
......@@ -215,41 +215,6 @@ settings actor_system_config::dump_content() const {
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,
const char* ini_file_cstr) {
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 @@
namespace caf {
deserializer::~deserializer() {
deserializer::deserializer(actor_system& x) noexcept
: context_(x.dummy_execution_unit()) {
// nop
}
deserializer::deserializer(actor_system& x) : super(x.dummy_execution_unit()) {
deserializer::deserializer(execution_unit* x) noexcept : context_(x) {
// nop
}
deserializer::deserializer(execution_unit* x) : super(x) {
deserializer::~deserializer() {
// 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
......@@ -60,6 +60,11 @@ error dynamic_message_data::load(size_t pos, deserializer& 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 {
return elements_.size();
}
......@@ -93,6 +98,11 @@ error dynamic_message_data::save(size_t pos, serializer& sink) const {
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() {
elements_.clear();
type_token_ = 0xFFFFFFFF;
......
......@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* 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 *
......@@ -16,9 +16,7 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <vector>
#include "caf/error_code.hpp"
namespace caf::io::basp {
......@@ -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 {
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;
auto ptr = x.get();
if (ptr == nullptr)
return f(mod_name);
return sink(mod_name);
mod_name = ptr->module().name();
auto e = f(mod_name);
return e ? e : ptr->save(f);
if (auto err = sink(mod_name))
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;
f(module_name);
if (auto err = source(module_name))
return err;
if (module_name.empty()) {
x = invalid_group;
return none;
}
if (f.context() == nullptr)
if (source.context() == nullptr)
return sec::no_context;
auto& sys = f.context()->system();
auto& sys = source.context()->system();
auto mod = sys.groups().get_module(module_name);
if (!mod)
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) {
......
......@@ -111,6 +111,8 @@ public:
error save(serializer& sink) const override;
error save(binary_serializer& sink) const override;
void stop() override {
CAF_LOG_TRACE("");
await_all_locals_down(system(), {broker_});
......@@ -348,7 +350,9 @@ public:
return group{result};
}
error load(deserializer& source, group& storage) override {
template <class Deserializer>
error load_impl(Deserializer& source, group& storage) {
CAF_LOG_TRACE("");
// deserialize identifier and broker
std::string identifier;
......@@ -382,7 +386,16 @@ public:
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_LOG_TRACE("");
auto bro = actor_cast<strong_actor_ptr>(ptr->broker());
......@@ -390,6 +403,14 @@ public:
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 {
CAF_LOG_TRACE("");
std::map<std::string, local_group_ptr> imap;
......@@ -431,6 +452,11 @@ error local_group::save(serializer& sink) const {
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;
} // namespace
......
This diff is collapsed.
......@@ -69,10 +69,6 @@ message message_builder::move_to_message() {
return message{std::move(data_)};
}
message message_builder::extract(message_handler f) const {
return to_message().extract(std::move(f));
}
optional<message> message_builder::apply(message_handler handler) {
// Avoid detaching of data_ by moving the data to a message object,
// calling message::apply and moving the data back.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -59,6 +59,7 @@ const char* numbered_type_names[] = {
"@addr",
"@addrvec",
"@atom",
"@bytebuf",
"@charbuf",
"@config_value",
"@down",
......
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