Unverified Commit 3e3c9479 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #975

Redesign serialization classes
parents 6869e4ad 3c0459bb
......@@ -90,7 +90,7 @@ void caf_main(actor_system& system, const config&) {
f1.b.resize(1);
f1.b.back().push_back(42);
// I/O buffer
vector<char> buf;
binary_serializer::container_type buf;
// write f1 to buffer
binary_serializer bs{system, buf};
auto e = bs(f1);
......
......@@ -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
......
......@@ -47,6 +47,9 @@ public:
/// Serialize this group to `sink`.
virtual error save(serializer& sink) const = 0;
/// Serialize this group to `sink`.
virtual error_code<sec> 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);
......
......@@ -183,10 +183,10 @@ inline bool operator!=(const abstract_actor* x, const strong_actor_ptr& y) {
/// @relates actor_control_block
using weak_actor_ptr = weak_intrusive_ptr<actor_control_block>;
error load_actor(strong_actor_ptr& storage, execution_unit*,
error_code<sec> load_actor(strong_actor_ptr& storage, execution_unit*,
actor_id aid, const node_id& nid);
error save_actor(strong_actor_ptr& storage, execution_unit*,
error_code<sec> save_actor(strong_actor_ptr& storage, execution_unit*,
actor_id aid, const node_id& nid);
template <class Inspector>
......@@ -225,7 +225,7 @@ template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, weak_actor_ptr& x) {
// inspect as strong pointer, then write back to weak pointer on save
auto tmp = x.lock();
auto load = [&]() -> error { x.reset(tmp.get()); return none; };
auto load = [&] { x.reset(tmp.get()); };
return f(tmp, meta::load_callback(load));
}
......
......@@ -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,55 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <vector>
#include <string>
#include <tuple>
#include <utility>
#include "caf/byte.hpp"
#include "caf/deserializer.hpp"
#include "caf/error_code.hpp"
#include "caf/fwd.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
#include "caf/write_inspector.hpp"
namespace caf {
/// Implements the deserializer interface with a binary serialization protocol.
class binary_deserializer final : public deserializer {
/// Deserializes objects from sequence of bytes.
class binary_deserializer : public write_inspector<binary_deserializer> {
public:
// -- member types -----------------------------------------------------------
using super = deserializer;
using result_type = error_code<sec>;
// -- constructors, destructors, and assignment operators --------------------
binary_deserializer(actor_system& sys, span<const byte> bytes);
template <class Container>
binary_deserializer(actor_system& sys, const Container& input) noexcept
: binary_deserializer(sys) {
reset(as_bytes(make_span(input)));
}
binary_deserializer(execution_unit* ctx, span<const byte> bytes);
template <class Container>
binary_deserializer(execution_unit* ctx, const Container& input) noexcept
: context_(ctx) {
reset(as_bytes(make_span(input)));
}
template <class T>
binary_deserializer(actor_system& sys, const std::vector<T>& buf)
: binary_deserializer(sys, as_bytes(make_span(buf))) {
binary_deserializer(execution_unit* ctx, const void* buf,
size_t size) noexcept
: binary_deserializer(ctx,
make_span(reinterpret_cast<const byte*>(buf), size)) {
// nop
}
template <class T>
binary_deserializer(execution_unit* ctx, const std::vector<T>& buf)
: binary_deserializer(ctx, as_bytes(make_span(buf))) {
binary_deserializer(actor_system& sys, const void* buf, size_t size) noexcept
: binary_deserializer(sys,
make_span(reinterpret_cast<const byte*>(buf), size)) {
// nop
}
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 +78,95 @@ 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;
/// Assigns 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 --------------------------------------------
result_type begin_object(uint16_t& typenr, std::string& name);
result_type end_object() noexcept;
result_type begin_sequence(size_t& list_size) noexcept;
result_type end_sequence() noexcept;
protected:
error apply_impl(int8_t&) override;
result_type apply(bool&) noexcept;
error apply_impl(uint8_t&) override;
result_type apply(byte&) noexcept;
error apply_impl(int16_t&) override;
result_type apply(int8_t&) noexcept;
error apply_impl(uint16_t&) override;
result_type apply(uint8_t&) noexcept;
error apply_impl(int32_t&) override;
result_type apply(int16_t&) noexcept;
error apply_impl(uint32_t&) override;
result_type apply(uint16_t&) noexcept;
error apply_impl(int64_t&) override;
result_type apply(int32_t&) noexcept;
error apply_impl(uint64_t&) override;
result_type apply(uint32_t&) noexcept;
error apply_impl(float&) override;
result_type apply(int64_t&) noexcept;
error apply_impl(double&) override;
result_type apply(uint64_t&) noexcept;
error apply_impl(long double&) override;
result_type apply(float&) noexcept;
error apply_impl(std::string&) override;
result_type apply(double&) noexcept;
error apply_impl(std::u16string&) override;
result_type apply(long double&);
error apply_impl(std::u32string&) override;
result_type apply(span<byte>) noexcept;
result_type apply(std::string&);
result_type apply(std::u16string&);
result_type 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));
}
result_type 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,134 @@
#pragma once
#include <cstddef>
#include <string>
#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>>;
/// Serializes objects into a sequence of bytes.
class binary_serializer : public read_inspector<binary_serializer> {
public:
// -- member types -----------------------------------------------------------
using result_type = error_code<sec>;
using container_type = byte_buffer;
using value_type = byte;
// -- 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(string_view x);
void apply(const std::u16string& x);
void apply(const std::u32string& x);
void apply(span<const byte> x);
template <class T>
std::enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value>
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 *
......@@ -20,15 +20,11 @@
#include <vector>
namespace caf::io::basp {
#include "caf/byte.hpp"
/// @addtogroup BASP
namespace caf {
/// Storage type for raw bytes.
using buffer_type = std::vector<char>;
/// @}
/// A buffer for storing binary data.
using byte_buffer = std::vector<byte>;
} // namespace caf
......@@ -36,24 +36,31 @@ namespace caf {
/// requires a heap allocation. With the callback implementation of CAF,
/// the object remains on the stack and does not cause more overhead
/// than necessary.
template <class... Ts>
class callback {
template <class Signature>
class callback;
template <class Result, class... Ts>
class callback<Result(Ts...)> {
public:
virtual error operator()(Ts...) = 0;
virtual Result operator()(Ts...) = 0;
};
/// Utility class for wrapping a function object of type `Base`.
template <class F, class... Ts>
class callback_impl : public callback<Ts...> {
/// Utility class for wrapping a function object of type `F`.
template <class F, class Signature>
class callback_impl;
template <class F, class Result, class... Ts>
class callback_impl<F, Result(Ts...)> : public callback<Result(Ts...)> {
public:
callback_impl(F&& f) : f_(std::move(f)) {
// nop
}
callback_impl(callback_impl&&) = default;
callback_impl& operator=(callback_impl&&) = default;
error operator()(Ts... xs) override {
Result operator()(Ts... xs) override {
return f_(std::forward<Ts>(xs)...);
}
......@@ -61,24 +68,14 @@ private:
F f_;
};
/// Utility class for selecting a `callback_impl`.
template <class F,
class Args = typename detail::get_callable_trait<F>::arg_types>
struct select_callback;
template <class F, class... Ts>
struct select_callback<F, detail::type_list<Ts...>> {
using type = callback_impl<F, Ts...>;
};
/// Creates a callback from a lambda expression.
/// Creates a ::callback from the function object `fun`.
/// @relates callback
template <class F>
typename select_callback<F>::type make_callback(F fun) {
return {std::move(fun)};
auto make_callback(F fun) {
using signature = typename detail::get_callable_trait<F>::fun_sig;
return callback_impl<F, signature>{std::move(fun)};
}
} // namespace caf
CAF_POP_WARNINGS
......@@ -18,62 +18,131 @@
#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.
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.
virtual result_type begin_sequence(size_t& size) = 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_code<sec> 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_code<sec> save(size_t pos, binary_serializer& sink) const override;
// -- modifiers --------------------------------------------------------------
void clear();
......
......@@ -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,59 @@
* 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 <chrono>
#include <string>
#include <vector>
#include "caf/meta/load_callback.hpp"
#include "caf/all.hpp"
namespace caf::detail {
using namespace caf;
// -- inject `inspect` overloads for some STL types ----------------------------
using std::string;
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)));
template <class Inspector, class Rep, class Period>
auto inspect(Inspector& f, std::chrono::duration<Rep, Period>& x) {
if constexpr (Inspector::reads_state) {
return f(x.count());
} else {
auto tmp = Rep{};
auto cb = [&] { x = std::chrono::duration<Rep, Period>{tmp}; };
return f(tmp, meta::load_callback(cb));
}
}
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, "");
template <class Inspector, class Clock, class Duration>
auto inspect(Inspector& f, std::chrono::time_point<Clock, Duration>& x) {
if constexpr (Inspector::reads_state) {
return f(x.time_since_epoch());
} else {
auto tmp = Duration{};
auto cb = [&] { x = std::chrono::time_point<Clock, Duration>{tmp}; };
return f(tmp, meta::load_callback(cb));
}
}
// -- provide `is_inspectable` trait for metaprogramming -----------------------
/// Checks whether `T` is inspectable by `Inspector`.
template <class Inspector, class T>
class is_inspectable {
private:
template <class U>
static auto sfinae(Inspector& x, U& y) -> decltype(inspect(x, y));
static std::false_type sfinae(Inspector&, ...);
using result_type
= decltype(sfinae(std::declval<Inspector&>(), std::declval<T&>()));
public:
static constexpr bool value
= !std::is_same<result_type, std::false_type>::value;
};
// Pointers are never inspectable.
template <class Inspector, class T>
struct is_inspectable<Inspector, T*> : std::false_type {};
} // namespace caf::detail
......@@ -26,8 +26,7 @@
namespace caf::detail {
/// Compile-time list of integer types types.
using int_types_by_size =
detail::type_list< // bytes
using int_types_by_size = detail::type_list< // bytes
void, // 0
detail::type_pair<int8_t, uint8_t>, // 1
detail::type_pair<int16_t, uint16_t>, // 2
......@@ -39,21 +38,16 @@ using int_types_by_size =
detail::type_pair<int64_t, uint64_t> // 8
>;
/// Squashes integer types into [u]int_[8|16|32|64]_t equivalents
/// Squashes integer types into [u]int_[8|16|32|64]_t equivalents.
template <class T>
struct squashed_int {
using tpair = typename detail::tl_at<int_types_by_size, sizeof(T)>::type;
using type =
typename std::conditional<
std::is_signed<T>::value,
typename tpair::first,
typename tpair::second
>::type;
using type = std::conditional_t<std::is_signed<T>::value, //
typename tpair::first, //
typename tpair::second>;
};
template <class T>
using squashed_int_t = typename squashed_int<T>::type;
} // namespace caf
} // namespace caf::detail
......@@ -26,6 +26,7 @@
#include "caf/detail/append_hex.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/fwd.hpp"
#include "caf/meta/annotation.hpp"
......
......@@ -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_code<sec> 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_code<sec> 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_code<sec> 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_code<sec> 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_code<sec> 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_code<sec> 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"
......@@ -85,22 +86,6 @@ using enable_if_tt = typename std::enable_if<Trait::value, T>::type;
template <class T>
using remove_reference_t = typename std::remove_reference<T>::type;
/// Checks whether `T` is inspectable by `Inspector`.
template <class Inspector, class T>
class is_inspectable {
private:
template <class U>
static auto sfinae(Inspector& x, U& y) -> decltype(inspect(x, y));
static std::false_type sfinae(Inspector&, ...);
using result_type = decltype(sfinae(std::declval<Inspector&>(),
std::declval<T&>()));
public:
static constexpr bool value = !std::is_same<result_type, std::false_type>::value;
};
/// Checks whether `T` defines a free function `to_string`.
template <class T>
class has_to_string {
......@@ -116,10 +101,6 @@ public:
static constexpr bool value = std::is_convertible<result, std::string>::value;
};
// pointers are never inspectable
template <class Inspector, class T>
struct is_inspectable<Inspector, T*> : std::false_type {};
template <bool X>
using bool_token = std::integral_constant<bool, X>;
......@@ -273,150 +254,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
/// functions with `Processor` being both `serializer` and `deserializer` and
/// returns an integral constant if and only if the test succeeds for both.
///
/// - `serialize(Processor&, T&, const unsigned int)`
/// - `serialize(Processor&, T&)`
/// - `T::serialize(Processor&, const unsigned int)`.
/// - `T::serialize(Processor&)`.
template <class T,
bool Ignore = std::is_pointer<T>::value
|| std::is_function<T>::value>
struct has_serialize {
template <class U>
static auto test_serialize(caf::serializer* sink, U* x, unsigned int y = 0)
-> decltype(serialize(*sink, *x, y));
template <class U>
static auto test_serialize(caf::serializer* sink, U* x)
-> decltype(serialize(*sink, *x));
template <class>
static auto test_serialize(...) -> std::false_type;
template <class U>
static auto test_deserialize(caf::deserializer* source, U* x, unsigned int y = 0)
-> decltype(serialize(*source, *x, y));
template <class U>
static auto test_deserialize(caf::deserializer* source, U* x)
-> decltype(serialize(*source, *x));
template <class>
static auto test_deserialize(...) -> std::false_type;
using serialize_type = decltype(test_serialize<T>(nullptr, nullptr));
using deserialize_type = decltype(test_deserialize<T>(nullptr, nullptr));
using type = std::integral_constant<
bool,
std::is_same<serialize_type, void>::value
&& std::is_same<deserialize_type, void>::value
>;
static constexpr bool value = type::value;
};
template <class T>
struct has_serialize<T, true> {
static constexpr bool value = false;
};
/// Any inspectable type is considered to be serializable.
template <class T>
struct is_serializable;
template <class T,
bool IsIterable = is_iterable<T>::value,
bool Ignore = std::is_pointer<T>::value
|| std::is_function<T>::value>
struct is_serializable_impl;
/// Checks whether `T` is builtin or provides a `serialize`
/// (free or member) function.
template <class T>
struct is_serializable_impl<T, false, false> {
static constexpr bool value = has_serialize<T>::value
|| is_inspectable<serializer, T>::value
|| is_builtin<T>::value;
};
template <class F, class S>
struct is_serializable_impl<std::pair<F, S>, false, false> {
static constexpr bool value = is_serializable<F>::value
&& is_serializable<S>::value;
};
template <class... Ts>
struct is_serializable_impl<std::tuple<Ts...>, false, false> {
static constexpr bool value = conjunction<
is_serializable<Ts>::value...
>::value;
};
template <class T>
struct is_serializable_impl<T, true, false> {
using value_type = typename T::value_type;
static constexpr bool value = is_serializable<value_type>::value;
};
template <class T, size_t S>
struct is_serializable_impl<T[S], false, false> {
static constexpr bool value = is_serializable<T>::value;
};
template <class T, bool IsIterable>
struct is_serializable_impl<T, IsIterable, true> {
static constexpr bool value = false;
};
/// Checks whether `T` is builtin or provides a `serialize`
/// (free or member) function.
template <class T>
struct is_serializable {
static constexpr bool value = is_serializable_impl<T>::value
|| is_inspectable<serializer, T>::value
|| std::is_empty<T>::value
|| std::is_enum<T>::value;
};
template <>
struct is_serializable<bool> : std::true_type {
// nop
};
template <class T>
struct is_serializable<T&> : is_serializable<T> {
// nop
};
template <class T>
struct is_serializable<const T> : is_serializable<T> {
// nop
};
template <class T>
struct is_serializable<const T&> : is_serializable<T> {
// nop
};
/// Checks whether `T` is a non-const reference.
template <class T>
struct is_mutable_ref : std::false_type { };
......@@ -771,10 +608,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 +645,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 +656,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 +691,59 @@ 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::integral_constant<bool, 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
......@@ -92,11 +92,10 @@ private:
/// @relates uri_impl
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, uri_impl& x) {
auto load = [&]() -> error {
auto load = [&] {
x.str.clear();
if (x.valid())
x.assemble_str();
return none;
};
return f(x.scheme, x.authority, x.path, x.query, x.fragment,
meta::load_callback(load));
......
......@@ -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 --------------------------------------------------------------
......@@ -180,17 +195,40 @@ public:
// -- friend functions -------------------------------------------------------
template <class Inspector>
friend typename Inspector::result_type inspect(Inspector& f, error& x) {
auto fun = [&](meta::type_name_t x0, uint8_t& x1, atom_value& x2,
meta::omittable_if_empty_t x3,
message& x4) -> error { return f(x0, x1, x2, x3, x4); };
return x.apply(fun);
friend auto inspect(Inspector& f, error& x) {
using result_type = typename Inspector::result_type;
if constexpr (Inspector::reads_state) {
if (!x) {
uint8_t code = 0;
return f(code);
}
return f(x.code(), x.category(), x.context());
} else {
uint8_t code = 0;
auto cb = meta::load_callback([&] {
if (code == 0) {
x.clear();
if constexpr (std::is_same<result_type, void>::value)
return;
else
return result_type{};
}
x.init();
x.code_ref() = code;
return f(x.category_ref(), x.context());
});
return f(code, cb);
}
}
private:
// -- inspection support -----------------------------------------------------
error apply(const inspect_fun& f);
uint8_t& code_ref() noexcept;
atom_value& category_ref() noexcept;
void init();
// -- nested classes ---------------------------------------------------------
......
......@@ -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_code<sec> inspect(binary_serializer&, group&);
friend error inspect(deserializer&, group&);
friend error_code<sec> 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_code<sec> load(binary_deserializer& source, group& storage) = 0;
// -- observers --------------------------------------------------------------
/// Returns the hosting actor system.
......
......@@ -18,15 +18,20 @@
#pragma once
#include <string>
#include <tuple>
#include <sstream>
#include <type_traits>
#include "caf/message.hpp"
#include "caf/allowed_unsafe_message_type.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/inspect.hpp"
#include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/message.hpp"
#include "caf/serializer.hpp"
namespace caf {
......@@ -55,46 +60,56 @@ struct unbox_message_element<actor_control_block*, 0> {
///
template <class T>
struct is_serializable_or_whitelisted {
static constexpr bool value = detail::is_serializable<T>::value
static constexpr bool value
= std::is_arithmetic<T>::value //
|| std::is_empty<T>::value //
|| std::is_enum<T>::value //
|| detail::is_stl_tuple_type<T>::value //
|| detail::is_map_like<T>::value //
|| detail::is_list_like<T>::value
|| (detail::is_inspectable<binary_serializer, T>::value
&& detail::is_inspectable<binary_deserializer, T>::value
&& detail::is_inspectable<serializer, T>::value
&& detail::is_inspectable<deserializer, T>::value)
|| allowed_unsafe_message_type<T>::value;
};
template <>
struct is_serializable_or_whitelisted<byte> : std::true_type {};
template <>
struct is_serializable_or_whitelisted<std::string> : std::true_type {};
template <>
struct is_serializable_or_whitelisted<std::u16string> : std::true_type {};
template <>
struct is_serializable_or_whitelisted<std::u32string> : std::true_type {};
/// 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,
"at least one type is neither inspectable via "
"inspect(Inspector&, T&) nor serializable via "
"'serialize(Processor&, T&, const unsigned int)' or "
"`T::serialize(Processor&, const unsigned int)`; "
"you can whitelist individual types by "
"specializing `caf::allowed_unsafe_message_type<T>` "
"or using the macro CAF_ALLOW_UNSAFE_MESSAGE_TYPE");
typename strip_and_convert<Ts>::type>::type...>;
static_assert(
tl_forall<stored_types, is_serializable_or_whitelisted>::value,
"at least one type is not inspectable via inspect(Inspector&, T&). If "
"you are not sending this type over the network, you can whitelist "
"individual types by specializing caf::allowed_unsafe_message_type<T> "
"or by 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 +133,3 @@ message make_message_from_tuple(std::tuple<Ts...> xs) {
}
} // namespace caf
......@@ -46,12 +46,6 @@ class message_handler;
/// tuple with elements of any type.
class message : public type_erased_tuple {
public:
// -- nested types -----------------------------------------------------------
struct cli_arg;
struct cli_res;
// -- member types -----------------------------------------------------------
/// Raw pointer to content.
......@@ -60,9 +54,6 @@ public:
/// Copy-on-write pointer to content.
using data_ptr = detail::message_data::cow_ptr;
/// Function object for generating CLI argument help text.
using help_factory = std::function<std::string (const std::vector<cli_arg>&)>;
// -- constructors, destructors, and assignment operators --------------------
message() noexcept = default;
......@@ -82,6 +73,8 @@ public:
error load(size_t pos, deserializer& source) override;
error_code<sec> load(size_t pos, binary_deserializer& source) override;
size_t size() const noexcept override;
uint32_t type_token() const noexcept override;
......@@ -96,28 +89,25 @@ public:
error save(size_t pos, serializer& sink) const override;
error_code<sec> save(size_t pos, binary_serializer& sink) const override;
bool shared() const noexcept override;
error load(deserializer& source) override;
error_code<sec> load(binary_deserializer& source) override;
error save(serializer& sink) const override;
// -- factories --------------------------------------------------------------
error_code<sec> save(binary_serializer& sink) const override;
/// Creates a new message by concatenating `xs...`.
template <class... Ts>
static message concat(const Ts&... xs) {
return concat_impl({xs.vals()...});
}
// -- factories --------------------------------------------------------------
/// Creates a new message by copying all elements in a type-erased tuple.
static message copy(const type_erased_tuple& xs);
// -- modifiers --------------------------------------------------------------
/// Concatenates `*this` and `x`.
message& operator+=(const message& x);
/// Returns `handler(*this)`.
optional<message> apply(message_handler handler);
......@@ -139,91 +129,6 @@ public:
/// Assigns new content.
void reset(raw_ptr new_ptr = nullptr, bool add_ref = true) noexcept;
// -- observers --------------------------------------------------------------
/// Creates a new message with all but the first n values.
message drop(size_t n) const;
/// Creates a new message with all but the last n values.
message drop_right(size_t n) const;
/// Creates a new message of size `n` starting at the element at position `p`.
message slice(size_t pos, size_t n) const;
/// Filters this message by applying slices of it to `handler` and returns
/// the remaining elements of this operation. Slices are generated in the
/// sequence `[0, size)`, `[0, size-1)`, `...` , `[1, size-1)`, `...`,
/// `[size-1, size)`. Whenever a slice matches, it is removed from the message
/// and the next slice starts at the *same* index on the reduced message.
///
/// For example:
///
/// ~~~
/// auto msg = make_message(1, 2.f, 3.f, 4);
/// // extract float and integer pairs
/// auto msg2 = msg.extract({
/// [](float, float) { },
/// [](int, int) { }
/// });
/// assert(msg2 == make_message(1, 4));
/// ~~~
///
/// Step-by-step explanation:
/// - Slice 1: `(1, 2.f, 3.f, 4)`, no match
/// - Slice 2: `(1, 2.f, 3.f)`, no match
/// - Slice 3: `(1, 2.f)`, no match
/// - Slice 4: `(1)`, no match
/// - Slice 5: `(2.f, 3.f, 4)`, no match
/// - Slice 6: `(2.f, 3.f)`, *match*; new message is `(1, 4)`
/// - Slice 7: `(4)`, no match
///
/// Slice 7 is `(4)`, i.e., does not contain the first element, because the
/// match on slice 6 occurred at index position 1. The function `extract`
/// iterates a message only once, from left to right.
message extract(message_handler handler) const;
/// A simplistic interface for using `extract` to parse command line options.
/// Usage example:
///
/// ~~~
/// int main(int argc, char** argv) {
/// uint16_t port;
/// string host = "localhost";
/// auto res = message_builder(argv + 1, argv + argc).extract_opts({
/// {"port,p", "set port", port},
/// {"host,H", "set host (default: localhost)", host},
/// {"verbose,v", "enable verbose mode"}
/// });
/// if (!res.error.empty()) {
/// cerr << res.error << endl;
/// return 1;
/// }
/// if (res.opts.count("help") > 0) {
/// // CLI arguments contained "-h", "--help", or "-?" (builtin);
/// cout << res.helptext << endl;
/// return 0;
/// }
/// if (!res.remainder.empty()) {
/// // ... extract did not consume all CLI arguments ...
/// }
/// if (res.opts.count("verbose") > 0) {
/// // ... enable verbose mode ...
/// }
/// // ...
/// }
/// ~~~
/// @param xs List of argument descriptors.
/// @param f Optional factory function to generate help text
/// (overrides the default generator).
/// @param no_help Suppress generation of default-generated help option.
/// @returns A struct containing remainder
/// (i.e. unmatched elements), a set containing the names of all
/// used arguments, and the generated help text.
/// @throws std::invalid_argument if no name or more than one long name is set
cli_res extract_opts(std::vector<cli_arg> xs,
const help_factory& f = nullptr,
bool no_help = false) const;
// -- inline observers -------------------------------------------------------
/// Returns a const pointer to the element at position `p`.
......@@ -242,17 +147,6 @@ public:
return vals_;
}
/// Returns the size of this message.
/// Creates a new message from the first n values.
inline message take(size_t n) const {
return n >= size() ? *this : drop_right(size() - n);
}
/// Creates a new message from the last n values.
inline message take_right(size_t n) const {
return n >= size() ? *this : drop(size() - n);
}
/// @cond PRIVATE
/// @pre `!empty()`
......@@ -271,6 +165,9 @@ public:
/// even if the serialized object had a different type.
static error save(serializer& sink, const type_erased_tuple& x);
static error_code<sec>
save(binary_serializer& sink, const type_erased_tuple& x);
/// @endcond
private:
......@@ -290,127 +187,26 @@ private:
return match_element<T>(P) && match_elements_impl(next_p, next_list);
}
message extract_impl(size_t start, message_handler handler) const;
static message concat_impl(std::initializer_list<data_ptr> xs);
// -- member functions -------------------------------------------------------
data_ptr vals_;
};
// -- nested types -------------------------------------------------------------
/// Stores the result of `message::extract_opts`.
struct message::cli_res {
/// Stores the remaining (unmatched) arguments.
message remainder;
/// Stores the names of all active options.
std::set<std::string> opts;
/// Stores the automatically generated help text.
std::string helptext;
/// Stores errors during option parsing.
std::string error;
};
/// Stores the name of a command line option ("<long name>[,<short name>]")
/// along with a description and a callback.
struct message::cli_arg {
/// Returns `true` on a match, `false` otherwise.
using consumer = std::function<bool (const std::string&)>;
/// Full name of this CLI argument using format "<long name>[,<short name>]"
std::string name;
/// Description of this CLI argument for the auto-generated help text.
std::string text;
/// Auto-generated helptext for this item.
std::string helptext;
/// Evaluates option arguments.
consumer fun;
/// Set to true for zero-argument options.
bool* flag;
/// Creates a CLI argument without data.
cli_arg(std::string nstr, std::string tstr);
/// Creates a CLI flag option. The `flag` is set to `true` if the option
/// was set, otherwise it is `false`.
cli_arg(std::string nstr, std::string tstr, bool& arg);
/// Creates a CLI argument storing its matched argument in `dest`.
cli_arg(std::string nstr, std::string tstr, atom_value& arg);
/// Creates a CLI argument storing its matched argument in `dest`.
cli_arg(std::string nstr, std::string tstr, timespan& arg);
/// Creates a CLI argument storing its matched argument in `dest`.
cli_arg(std::string nstr, std::string tstr, std::string& arg);
/// Creates a CLI argument appending matched arguments to `dest`.
cli_arg(std::string nstr, std::string tstr, std::vector<std::string>& arg);
/// Creates a CLI argument using the function object `f`.
cli_arg(std::string nstr, std::string tstr, consumer f);
/// Creates a CLI argument for converting from strings,
/// storing its matched argument in `dest`.
template <class T>
cli_arg(std::string nstr, std::string tstr, T& arg)
: name(std::move(nstr)),
text(std::move(tstr)),
flag(nullptr) {
fun = [&arg](const std::string& str) -> bool {
T x;
// TODO: using this stream is a workaround for the missing
// from_string<T>() interface and has downsides such as
// not performing overflow/underflow checks etc.
std::istringstream iss{str};
if (iss >> x) {
arg = x;
return true;
}
return false;
};
}
/// Creates a CLI argument for converting from strings,
/// appending matched arguments to `dest`.
template <class T>
cli_arg(std::string nstr, std::string tstr, std::vector<T>& arg)
: name(std::move(nstr)),
text(std::move(tstr)),
flag(nullptr) {
fun = [&arg](const std::string& str) -> bool {
T x;
std::istringstream iss{str};
if (iss >> x) {
arg.emplace_back(std::move(x));
return true;
}
return false;
};
}
};
// -- related non-members ------------------------------------------------------
/// @relates message
error inspect(serializer& sink, message& msg);
/// @relates message
error_code<sec> inspect(binary_serializer& sink, message& msg);
/// @relates message
error inspect(deserializer& source, message& msg);
/// @relates message
std::string to_string(const message& msg);
error_code<sec> inspect(binary_deserializer& source, message& msg);
/// @relates message
inline message operator+(const message& lhs, const message& rhs) {
return message::concat(lhs, rhs);
}
std::string to_string(const message& msg);
} // namespace caf
......@@ -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_code<sec> serialize(binary_serializer& sink) const = 0;
virtual error_code<sec> 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_code<sec> serialize(binary_serializer& sink) const override;
error_code<sec> deserialize(binary_deserializer& source) override;
private:
// -- member variables -----------------------------------------------------
......@@ -159,6 +167,10 @@ public:
error deserialize(deserializer& source) override;
error_code<sec> serialize(binary_serializer& sink) const override;
error_code<sec> 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_code<sec> inspect(binary_serializer& sink, node_id& x);
friend error inspect(deserializer& source, node_id& x);
friend error_code<sec> 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/inspect.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) \
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`.
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) {
typename Subtype::result_type result;
static_cast<void>((try_apply(result, 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, const T* xs, std::index_sequence<Is...>) {
return dref(xs[Is]...);
}
template <class R, class T>
std::enable_if_t<meta::is_annotation_v<T>, bool> try_apply(R& result, T& x) {
if constexpr (meta::is_save_callback_v<T>) {
CAF_READ_INSPECTOR_TRY(x.fun())
}
return true;
}
template <class R, class T>
std::enable_if_t<!meta::is_annotation_v<T>, bool>
try_apply(R& result, const T& x) {
Subtype& dref = *static_cast<Subtype*>(this);
if constexpr (std::is_empty<T>::value
|| is_allowed_unsafe_message_type_v<T>) {
// skip element
} else if constexpr (detail::can_apply_v<Subtype, decltype(x)>) {
CAF_READ_INSPECTOR_TRY(dref.apply(x))
} else if constexpr (std::is_integral<T>::value) {
using squashed_type = detail::squashed_int_t<T>;
auto squashed_x = static_cast<squashed_type>(x);
CAF_READ_INSPECTOR_TRY(dref.apply(squashed_x))
} else if constexpr (std::is_array<T>::value) {
std::make_index_sequence<std::extent<T>::value> seq;
CAF_READ_INSPECTOR_TRY(apply_array(dref, x, seq))
} else if constexpr (detail::is_stl_tuple_type<T>::value) {
std::make_index_sequence<std::tuple_size<T>::value> seq;
CAF_READ_INSPECTOR_TRY(apply_tuple(dref, x, seq))
} else if constexpr (detail::is_map_like<T>::value) {
CAF_READ_INSPECTOR_TRY(dref.begin_sequence(x.size()))
for (const auto& kvp : x) {
CAF_READ_INSPECTOR_TRY(dref(kvp.first, kvp.second))
}
CAF_READ_INSPECTOR_TRY(dref.end_sequence())
} else if constexpr (detail::is_list_like<T>::value) {
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, T>::value);
using caf::detail::inspect;
// We require that the implementation for `inspect` does not modify its
// arguments when passing a reading inspector.
auto& mutable_x = const_cast<T&>(x);
CAF_READ_INSPECTOR_TRY(inspect(dref, mutable_x));
}
return true;
}
};
} // 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,137 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include <cstddef> // size_t
#include <type_traits>
#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"
#include "caf/string_view.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(const std::u16string& x) = 0;
/// @copydoc apply
virtual result_type apply(const std::u32string& 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
}
......
......@@ -42,8 +42,6 @@ public:
explicit stateful_actor(actor_config& cfg, Ts&&... xs)
: Base(cfg, std::forward<Ts>(xs)...),
state(state_) {
if (detail::is_serializable<State>::value)
this->setf(Base::is_serializable_flag);
cr_state(this);
}
......@@ -61,14 +59,6 @@ public:
return get_name(state_);
}
error save_state(serializer& sink, unsigned int version) override {
return serialize_state(&sink, state, version);
}
error load_state(deserializer& source, unsigned int version) override {
return serialize_state(&source, state, version);
}
/// A reference to the actor's state.
State& state;
......@@ -81,17 +71,6 @@ public:
/// @endcond
private:
template <class Inspector, class T>
auto serialize_state(Inspector* f, T& x, unsigned int)
-> decltype(inspect(*f, x)) {
return inspect(*f, x);
}
template <class T>
error serialize_state(void*, T&, unsigned int) {
return sec::invalid_argument;
}
template <class T>
typename std::enable_if<std::is_constructible<State, T>::value>::type
cr_state(T arg) {
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 <algorithm>
#include <cstddef>
#include <cstring>
#include <limits>
#include <streambuf>
#include <type_traits>
#include <vector>
#include "caf/config.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
/// The base class for all stream buffer implementations.
template <class CharT = char, class Traits = std::char_traits<CharT>>
class stream_buffer : public std::basic_streambuf<CharT, Traits> {
public:
using base = std::basic_streambuf<CharT, Traits>;
using pos_type = typename base::pos_type;
using off_type = typename base::off_type;
protected:
/// The standard only defines pbump(int), which can overflow on 64-bit
/// architectures. All stream buffer implementations should therefore use
/// these function instead. For a detailed discussion, see:
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47921
template <class T = int>
typename std::enable_if<sizeof(T) == 4>::type
safe_pbump(std::streamsize n) {
while (n > std::numeric_limits<int>::max()) {
this->pbump(std::numeric_limits<int>::max());
n -= std::numeric_limits<int>::max();
}
this->pbump(static_cast<int>(n));
}
template <class T = int>
typename std::enable_if<sizeof(T) == 8>::type
safe_pbump(std::streamsize n) {
this->pbump(static_cast<int>(n));
}
// As above, but for the get area.
template <class T = int>
typename std::enable_if<sizeof(T) == 4>::type
safe_gbump(std::streamsize n) {
while (n > std::numeric_limits<int>::max()) {
this->gbump(std::numeric_limits<int>::max());
n -= std::numeric_limits<int>::max();
}
this->gbump(static_cast<int>(n));
}
template <class T = int>
typename std::enable_if<sizeof(T) == 8>::type
safe_gbump(std::streamsize n) {
this->gbump(static_cast<int>(n));
}
pos_type default_seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which) {
auto new_off = pos_type(off_type(-1));
auto get = (which & std::ios_base::in) == std::ios_base::in;
auto put = (which & std::ios_base::out) == std::ios_base::out;
if (!(get || put))
return new_off; // nothing to do
if (get) {
switch (dir) {
default:
return pos_type(off_type(-1));
case std::ios_base::beg:
new_off = 0;
break;
case std::ios_base::cur:
new_off = this->gptr() - this->eback();
break;
case std::ios_base::end:
new_off = this->egptr() - this->eback();
break;
}
new_off += off;
this->setg(this->eback(), this->eback() + new_off, this->egptr());
}
if (put) {
switch (dir) {
default:
return pos_type(off_type(-1));
case std::ios_base::beg:
new_off = 0;
break;
case std::ios_base::cur:
new_off = this->pptr() - this->pbase();
break;
case std::ios_base::end:
new_off = this->egptr() - this->pbase();
break;
}
new_off += off;
this->setp(this->pbase(), this->epptr());
safe_pbump(new_off);
}
return new_off;
}
pos_type default_seekpos(pos_type pos, std::ios_base::openmode which) {
auto get = (which & std::ios_base::in) == std::ios_base::in;
auto put = (which & std::ios_base::out) == std::ios_base::out;
if (!(get || put))
return pos_type(off_type(-1)); // nothing to do
if (get)
this->setg(this->eback(), this->eback() + pos, this->egptr());
if (put) {
this->setp(this->pbase(), this->epptr());
safe_pbump(pos);
}
return pos;
}
};
/// A streambuffer abstraction over a fixed array of bytes. This streambuffer
/// cannot overflow/underflow. Once it has reached its end, attempts to read
/// characters will return `trait_type::eof`.
template <class CharT = char, class Traits = std::char_traits<CharT>>
class arraybuf : public stream_buffer<CharT, Traits> {
public:
using base = std::basic_streambuf<CharT, Traits>;
using char_type = typename base::char_type;
using traits_type = typename base::traits_type;
using int_type = typename base::int_type;
using pos_type = typename base::pos_type;
using off_type = typename base::off_type;
/// Constructs an array streambuffer from a container.
/// @param c A contiguous container.
/// @pre `c.data()` must point to a contiguous sequence of characters having
/// length `c.size()`.
template <
class Container,
class = typename std::enable_if<
detail::has_data_member<Container>::value
&& detail::has_size_member<Container>::value
>::type
>
arraybuf(Container& c)
: arraybuf(const_cast<char_type*>(c.data()), c.size()) {
// nop
}
/// Constructs an array streambuffer from a raw character sequence.
/// @param data A pointer to the first character.
/// @param size The length of the character sequence.
arraybuf(char_type* data, size_t size) {
setbuf(data, static_cast<std::streamsize>(size));
}
// There exists a bug in libstdc++ version < 5: the implementation does not
// provide the necessary move constructors, so we have to roll our own :-/.
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 for details.
// TODO: remove after having raised the minimum GCC version to 5.
arraybuf(arraybuf&& other) {
this->setg(other.eback(), other.gptr(), other.egptr());
this->setp(other.pptr(), other.epptr());
other.setg(nullptr, nullptr, nullptr);
other.setp(nullptr, nullptr);
}
// TODO: remove after having raised the minimum GCC version to 5.
arraybuf& operator=(arraybuf&& other) {
this->setg(other.eback(), other.gptr(), other.egptr());
this->setp(other.pptr(), other.epptr());
other.setg(nullptr, nullptr, nullptr);
other.setp(nullptr, nullptr);
return *this;
}
protected:
// -- positioning ----------------------------------------------------------
std::basic_streambuf<char_type, Traits>*
setbuf(char_type* s, std::streamsize n) override {
this->setg(s, s, s + n);
this->setp(s, s + n);
return this;
}
pos_type seekpos(pos_type pos,
std::ios_base::openmode which
= std::ios_base::in | std::ios_base::out) override {
return this->default_seekpos(pos, which);
}
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which) override {
return this->default_seekoff(off, dir, which);
}
// -- put area -------------------------------------------------------------
std::streamsize xsputn(const char_type* s, std::streamsize n) override {
auto available = this->epptr() - this->pptr();
auto actual = std::min(n, static_cast<std::streamsize>(available));
std::memcpy(this->pptr(), s,
static_cast<size_t>(actual) * sizeof(char_type));
this->safe_pbump(actual);
return actual;
}
// -- get area -------------------------------------------------------------
std::streamsize xsgetn(char_type* s, std::streamsize n) override {
auto available = this->egptr() - this->gptr();
auto actual = std::min(n, static_cast<std::streamsize>(available));
std::memcpy(s, this->gptr(),
static_cast<size_t>(actual) * sizeof(char_type));
this->safe_gbump(actual);
return actual;
}
};
/// A streambuffer abstraction over a contiguous container. It supports
/// reading in the same style as `arraybuf`, but is unbounded for output.
template <class Container>
class containerbuf
: public stream_buffer<
typename Container::value_type,
std::char_traits<typename Container::value_type>
> {
public:
using base = std::basic_streambuf<
typename Container::value_type,
std::char_traits<typename Container::value_type>
>;
using char_type = typename base::char_type;
using traits_type = typename base::traits_type;
using int_type = typename base::int_type;
using pos_type = typename base::pos_type;
using off_type = typename base::off_type;
/// Constructs a container streambuf.
/// @param c A contiguous container.
template <
class C = Container,
class = typename std::enable_if<
detail::has_data_member<C>::value && detail::has_size_member<C>::value
>::type
>
containerbuf(Container& c) : container_(c) {
// We use a const_cast because C++11 doesn't provide a non-const data()
// overload. Using std::data(c) would be the right way to write this.
auto data = const_cast<char_type*>(c.data());
auto size = static_cast<std::streamsize>(c.size());
setbuf(data, size);
}
// See note in arraybuf(arraybuf&&).
// TODO: remove after having raised the minimum GCC version to 5.
containerbuf(containerbuf&& other)
: container_(other.container_) {
this->setg(other.eback(), other.gptr(), other.egptr());
other.setg(nullptr, nullptr, nullptr);
}
// TODO: remove after having raised the minimum GCC version to 5.
containerbuf& operator=(containerbuf&& other) {
this->setg(other.eback(), other.gptr(), other.egptr());
other.setg(nullptr, nullptr, nullptr);
return *this;
}
// Hides base-class implementation to simplify single-character lookup.
int_type sgetc() {
if (this->gptr() == this->egptr())
return traits_type::eof();
return traits_type::to_int_type(*this->gptr());
}
// Hides base-class implementation to simplify single-character insert
// without a put area.
int_type sputc(char_type c) {
container_.push_back(c);
return c;
}
// Hides base-class implementation to simplify multi-character insert without
// a put area.
std::streamsize sputn(const char_type* s, std::streamsize n) {
return xsputn(s, n);
}
protected:
// -- positioning ----------------------------------------------------------
base* setbuf(char_type* s, std::streamsize n) override {
this->setg(s, s, s + n);
return this;
}
pos_type seekpos(pos_type pos,
std::ios_base::openmode which
= std::ios_base::in | std::ios_base::out) override {
// We only have a get area, so no put area (= out) operations.
if ((which & std::ios_base::out) == std::ios_base::out)
return pos_type(off_type(-1));
return this->default_seekpos(pos, which);
}
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which) override {
// We only have a get area, so no put area (= out) operations.
if ((which & std::ios_base::out) == std::ios_base::out)
return pos_type(off_type(-1));
return this->default_seekoff(off, dir, which);
}
// Synchronizes the get area with the underlying buffer.
int sync() override {
// See note in ctor for const_cast
auto data = const_cast<char_type*>(container_.data());
auto size = static_cast<std::streamsize>(container_.size());
setbuf(data, size);
return 0;
}
// -- get area -------------------------------------------------------------
// We can't get obtain more characters on underflow, so we only optimize
// multi-character sequential reads.
std::streamsize xsgetn(char_type* s, std::streamsize n) override {
auto available = this->egptr() - this->gptr();
auto actual = std::min(n, static_cast<std::streamsize>(available));
std::memcpy(s, this->gptr(),
static_cast<size_t>(actual) * sizeof(char_type));
this->safe_gbump(actual);
return actual;
}
// -- put area -------------------------------------------------------------
// Should never get called, because there is always space in the buffer.
// (But just in case, it does the same thing as sputc.)
int_type overflow(int_type c = traits_type::eof()) final {
if (!traits_type::eq_int_type(c, traits_type::eof()))
container_.push_back(traits_type::to_char_type(c));
return c;
}
std::streamsize xsputn(const char_type* s, std::streamsize n) override {
container_.insert(container_.end(), s, s + n);
return n;
}
private:
Container& container_;
};
using charbuf = arraybuf<char>;
using vectorbuf = containerbuf<std::vector<char>>;
} // namespace caf
......@@ -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_code<sec> 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_code<sec> 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_code<sec> 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_code<sec> 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 auto 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 auto inspect(binary_serializer& sink, const type_erased_tuple& x) {
return x.save(sink);
}
/// @relates type_erased_tuple
inline auto inspect(deserializer& source, type_erased_tuple& x) {
return x.load(source);
}
/// @relates type_erased_tuple
inline auto 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_code<sec> 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_code<sec> 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_code<sec> 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_code<sec> save(binary_serializer& sink) const = 0;
/// Converts the stored value to a string.
virtual std::string stringify() const = 0;
......@@ -93,17 +99,27 @@ public:
};
/// @relates type_erased_value_impl
inline error inspect(serializer& f, type_erased_value& x) {
inline auto inspect(serializer& f, const type_erased_value& x) {
return x.save(f);
}
/// @relates type_erased_value_impl
inline auto inspect(binary_serializer& f, const type_erased_value& x) {
return x.save(f);
}
/// @relates type_erased_value_impl
inline error inspect(deserializer& f, type_erased_value& x) {
inline auto inspect(deserializer& f, type_erased_value& x) {
return x.load(f);
}
/// @relates type_erased_value_impl
inline auto 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) {
inline auto to_string(const type_erased_value& x) {
return x.stringify();
}
......
......@@ -25,24 +25,24 @@
#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.
/// @warning Types are sorted by uniform name.
using sorted_builtin_types =
detail::type_list<
using sorted_builtin_types = detail::type_list< // uniform name:
actor, // @actor
std::vector<actor>, // @actorvec
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_code<sec> inspect(caf::binary_serializer& dst, uri& x);
friend error inspect(caf::deserializer& src, uri& x);
friend error_code<sec> 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/inspect.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
/// - `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) {
static_assert(
((std::is_lvalue_reference<Ts>::value || meta::is_annotation_v<Ts>) //
&&...));
typename Subtype::result_type result;
static_cast<void>((try_apply(result, 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 R, class T>
std::enable_if_t<meta::is_annotation_v<T>, bool> try_apply(R& result, T& x) {
if constexpr (meta::is_load_callback_v<T>) {
CAF_WRITE_INSPECTOR_TRY(x.fun())
}
return true;
}
template <class R, class T>
std::enable_if_t<!meta::is_annotation_v<T>, bool> try_apply(R& result, T& x) {
Subtype& dref = *static_cast<Subtype*>(this);
if constexpr (std::is_empty<T>::value
|| is_allowed_unsafe_message_type_v<T>) {
// 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<T>::value) {
using squashed_type = detail::squashed_int_t<T>;
auto& squashed_x = reinterpret_cast<squashed_type&>(x);
CAF_WRITE_INSPECTOR_TRY(dref.apply(squashed_x))
} else if constexpr (std::is_array<T>::value) {
std::make_index_sequence<std::extent<T>::value> seq;
CAF_WRITE_INSPECTOR_TRY(apply_array(dref, x, seq))
} else if constexpr (detail::is_stl_tuple_type_v<T>) {
std::make_index_sequence<std::tuple_size<T>::value> seq;
CAF_WRITE_INSPECTOR_TRY(apply_tuple(dref, x, seq))
} else if constexpr (detail::is_map_like_v<T>) {
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 T::key_type{};
auto val = typename T::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<T>) {
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 T::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(detail::is_inspectable<Subtype, T>::value);
using caf::detail::inspect;
CAF_WRITE_INSPECTOR_TRY(inspect(dref, x));
}
return true;
}
};
} // 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;
}
......
......@@ -80,7 +80,7 @@ bool operator==(const abstract_actor* x, const strong_actor_ptr& y) {
return actor_control_block::from(x) == y.get();
}
error load_actor(strong_actor_ptr& storage, execution_unit* ctx,
error_code<sec> load_actor(strong_actor_ptr& storage, execution_unit* ctx,
actor_id aid, const node_id& nid) {
if (ctx == nullptr)
return sec::no_context;
......@@ -99,7 +99,7 @@ error load_actor(strong_actor_ptr& storage, execution_unit* ctx,
return none;
}
error save_actor(strong_actor_ptr& storage, execution_unit* ctx,
error_code<sec> save_actor(strong_actor_ptr& storage, execution_unit* ctx,
actor_id aid, const node_id& nid) {
if (ctx == nullptr)
return sec::no_context;
......
......@@ -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 length 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;
......
......@@ -22,79 +22,73 @@
#include <sstream>
#include <type_traits>
#include "caf/actor_system.hpp"
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp"
#include "caf/error.hpp"
#include "caf/sec.hpp"
namespace caf {
namespace {
using result_type = binary_deserializer::result_type;
template <class T>
error apply_int(binary_deserializer& bs, T& x) {
typename std::make_unsigned<T>::type tmp;
if (auto err = bs.apply_raw(sizeof(T), &tmp))
result_type apply_int(binary_deserializer& source, T& x) {
auto tmp = std::make_unsigned_t<T>{};
if (auto err = source.apply(as_writable_bytes(make_span(&tmp, 1))))
return err;
x = static_cast<T>(detail::from_network_order(tmp));
return none;
}
template <class T>
error apply_float(binary_deserializer& bs, T& x) {
typename detail::ieee_754_trait<T>::packed_type tmp;
if (auto err = apply_int(bs, tmp))
result_type apply_float(binary_deserializer& source, T& x) {
auto tmp = typename detail::ieee_754_trait<T>::packed_type{};
if (auto err = apply_int(source, tmp))
return err;
x = detail::unpack754(tmp);
return none;
}
} // namespace
binary_deserializer::binary_deserializer(actor_system& sys,
span<const byte> bytes)
: super(sys), current_(bytes.data()), end_(bytes.data() + bytes.size()) {
// nop
}
binary_deserializer::binary_deserializer(execution_unit* ctx,
span<const byte> bytes)
: super(ctx), current_(bytes.data()), end_(bytes.data() + bytes.size()) {
// nop
// Does not perform any range checks.
template <class T>
void unsafe_apply_int(binary_deserializer& source, T& x) {
std::make_unsigned_t<T> tmp;
memcpy(&tmp, source.current(), sizeof(tmp));
source.skip(sizeof(tmp));
x = static_cast<T>(detail::from_network_order(tmp));
}
binary_deserializer::binary_deserializer(actor_system& sys, const char* buf,
size_t buf_size)
: binary_deserializer(sys, make_span(reinterpret_cast<const byte*>(buf),
buf_size)) {
// nop
}
} // namespace
binary_deserializer::binary_deserializer(execution_unit* ctx, const char* buf,
size_t buf_size)
: binary_deserializer(ctx, make_span(reinterpret_cast<const byte*>(buf),
buf_size)) {
binary_deserializer::binary_deserializer(actor_system& sys) noexcept
: context_(sys.dummy_execution_unit()) {
// nop
}
error binary_deserializer::begin_object(uint16_t& nr, std::string& name) {
result_type binary_deserializer::begin_object(uint16_t& nr, std::string& name) {
name.clear();
if (auto err = apply(nr))
return err;
if (nr != 0)
if (nr == 0)
if (auto err = apply(name))
return err;
return none;
return apply(name);
}
error binary_deserializer::end_object() {
result_type binary_deserializer::end_object() noexcept {
return none;
}
error binary_deserializer::begin_sequence(size_t& list_size) {
result_type binary_deserializer::begin_sequence(size_t& list_size) noexcept {
// Use varbyte encoding to compress sequence size on the wire.
uint32_t x = 0;
int n = 0;
uint8_t low7;
do {
if (auto err = apply_impl(low7))
if (auto err = apply(low7))
return err;
x |= static_cast<uint32_t>((low7 & 0x7F)) << (7 * n);
++n;
......@@ -103,88 +97,109 @@ error binary_deserializer::begin_sequence(size_t& list_size) {
return none;
}
error binary_deserializer::end_sequence() {
result_type binary_deserializer::end_sequence() noexcept {
return none;
}
error binary_deserializer::apply_raw(size_t num_bytes, void* storage) {
if (!range_check(num_bytes))
return sec::end_of_stream;
memcpy(storage, current_, num_bytes);
void binary_deserializer::skip(size_t num_bytes) noexcept {
CAF_ASSERT(num_bytes <= remaining());
current_ += num_bytes;
return none;
}
const char* binary_deserializer::current() const {
return reinterpret_cast<const char*>(current_);
}
const char* binary_deserializer::end() const {
return reinterpret_cast<const char*>(end_);
void binary_deserializer::reset(span<const byte> bytes) noexcept {
current_ = bytes.data();
end_ = current_ + bytes.size();
}
void binary_deserializer::skip(size_t num_bytes) {
CAF_ASSERT(num_bytes <= remaining());
current_ += num_bytes;
result_type binary_deserializer::apply(bool& x) noexcept {
int8_t tmp = 0;
if (auto err = apply(tmp))
return err;
x = tmp != 0;
return none;
}
void binary_deserializer::reset(span<const byte> bytes) {
current_ = bytes.data();
end_ = current_ + bytes.size();
result_type binary_deserializer::apply(byte& x) noexcept {
if (range_check(1)) {
x = *current_++;
return none;
}
return sec::end_of_stream;
}
error binary_deserializer::apply_impl(int8_t& x) {
return apply_raw(sizeof(int8_t), &x);
result_type binary_deserializer::apply(int8_t& x) noexcept {
if (range_check(1)) {
x = static_cast<int8_t>(*current_++);
return none;
}
return sec::end_of_stream;
}
error binary_deserializer::apply_impl(uint8_t& x) {
return apply_raw(sizeof(uint8_t), &x);
result_type binary_deserializer::apply(uint8_t& x) noexcept {
if (range_check(1)) {
x = static_cast<uint8_t>(*current_++);
return none;
}
return sec::end_of_stream;
}
error binary_deserializer::apply_impl(int16_t& x) {
result_type binary_deserializer::apply(int16_t& x) noexcept {
return apply_int(*this, x);
}
error binary_deserializer::apply_impl(uint16_t& x) {
result_type binary_deserializer::apply(uint16_t& x) noexcept {
return apply_int(*this, x);
}
error binary_deserializer::apply_impl(int32_t& x) {
result_type binary_deserializer::apply(int32_t& x) noexcept {
return apply_int(*this, x);
}
error binary_deserializer::apply_impl(uint32_t& x) {
result_type binary_deserializer::apply(uint32_t& x) noexcept {
return apply_int(*this, x);
}
error binary_deserializer::apply_impl(int64_t& x) {
result_type binary_deserializer::apply(int64_t& x) noexcept {
return apply_int(*this, x);
}
error binary_deserializer::apply_impl(uint64_t& x) {
result_type binary_deserializer::apply(uint64_t& x) noexcept {
return apply_int(*this, x);
}
error binary_deserializer::apply_impl(float& x) {
result_type binary_deserializer::apply(float& x) noexcept {
return apply_float(*this, x);
}
error binary_deserializer::apply_impl(double& x) {
result_type binary_deserializer::apply(double& x) noexcept {
return apply_float(*this, x);
}
error binary_deserializer::apply_impl(long double& x) {
// The IEEE-754 conversion does not work for long double
// => fall back to string serialization (even though it sucks).
result_type binary_deserializer::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::string tmp;
if (auto err = apply(tmp))
return err;
std::istringstream iss{std::move(tmp)};
iss >> x;
if (iss >> x)
return none;
return sec::invalid_argument;
}
error binary_deserializer::apply_impl(std::string& x) {
size_t str_size;
result_type binary_deserializer::apply(span<byte> x) noexcept {
if (!range_check(x.size()))
return sec::end_of_stream;
memcpy(x.data(), current_, x.size());
current_ += x.size();
return none;
}
result_type binary_deserializer::apply(std::string& x) {
x.clear();
size_t str_size = 0;
if (auto err = begin_sequence(str_size))
return err;
if (!range_check(str_size))
......@@ -194,32 +209,91 @@ error binary_deserializer::apply_impl(std::string& x) {
return end_sequence();
}
error binary_deserializer::apply_impl(std::u16string& x) {
auto str_size = x.size();
result_type binary_deserializer::apply(std::u16string& x) {
x.clear();
size_t str_size = 0;
if (auto err = begin_sequence(str_size))
return err;
if (!range_check(str_size * sizeof(uint16_t)))
return sec::end_of_stream;
for (size_t i = 0; i < str_size; ++i) {
// The standard does not guarantee that char16_t is exactly 16 bits.
uint16_t tmp;
if (auto err = apply_int(*this, tmp))
return err;
unsafe_apply_int(*this, tmp);
x.push_back(static_cast<char16_t>(tmp));
}
return none;
return end_sequence();
}
error binary_deserializer::apply_impl(std::u32string& x) {
auto str_size = x.size();
result_type binary_deserializer::apply(std::u32string& x) {
x.clear();
size_t str_size = 0;
if (auto err = begin_sequence(str_size))
return err;
if (!range_check(str_size * sizeof(uint32_t)))
return sec::end_of_stream;
for (size_t i = 0; i < str_size; ++i) {
// The standard does not guarantee that char32_t is exactly 32 bits.
uint32_t tmp;
if (auto err = apply_int(*this, tmp))
return err;
unsafe_apply_int(*this, tmp);
x.push_back(static_cast<char32_t>(tmp));
}
return none;
return end_sequence();
}
result_type binary_deserializer::apply(std::vector<bool>& x) {
x.clear();
size_t len = 0;
if (auto err = begin_sequence(len))
return err;
if (len == 0)
return end_sequence();
size_t blocks = len / 8;
for (size_t block = 0; block < blocks; ++block) {
uint8_t tmp = 0;
if (auto err = apply(tmp))
return err;
x.emplace_back((tmp & 0b1000'0000) != 0);
x.emplace_back((tmp & 0b0100'0000) != 0);
x.emplace_back((tmp & 0b0010'0000) != 0);
x.emplace_back((tmp & 0b0001'0000) != 0);
x.emplace_back((tmp & 0b0000'1000) != 0);
x.emplace_back((tmp & 0b0000'0100) != 0);
x.emplace_back((tmp & 0b0000'0010) != 0);
x.emplace_back((tmp & 0b0000'0001) != 0);
}
auto trailing_block_size = len % 8;
if (trailing_block_size > 0) {
uint8_t tmp = 0;
if (auto err = apply(tmp))
return err;
switch (trailing_block_size) {
case 7:
x.emplace_back((tmp & 0b0100'0000) != 0);
[[fallthrough]];
case 6:
x.emplace_back((tmp & 0b0010'0000) != 0);
[[fallthrough]];
case 5:
x.emplace_back((tmp & 0b0001'0000) != 0);
[[fallthrough]];
case 4:
x.emplace_back((tmp & 0b0000'1000) != 0);
[[fallthrough]];
case 3:
x.emplace_back((tmp & 0b0000'0100) != 0);
[[fallthrough]];
case 2:
x.emplace_back((tmp & 0b0000'0010) != 0);
[[fallthrough]];
case 1:
x.emplace_back((tmp & 0b0000'0001) != 0);
[[fallthrough]];
default:
break;
}
}
return end_sequence();
}
} // namespace caf
......@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* 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 *
......@@ -16,26 +16,51 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/serialized_size.hpp"
#include "caf/binary_serializer.hpp"
#include <iomanip>
#include <sstream>
namespace caf::detail {
#include "caf/actor_system.hpp"
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/network_order.hpp"
error serialized_size_inspector::begin_object(uint16_t& nr, std::string& name) {
if (nr != 0)
return apply(nr);
if (auto err = apply(nr))
return err;
return apply(name);
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 serialized_size_inspector::end_object() {
error_code<sec> binary_serializer::end_object() {
return none;
}
error serialized_size_inspector::begin_sequence(size_t& list_size) {
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.
......@@ -47,100 +72,168 @@ error serialized_size_inspector::begin_sequence(size_t& list_size) {
x >>= 7;
}
*i++ = static_cast<uint8_t>(x) & 0x7f;
apply_raw(static_cast<size_t>(i - buf), buf);
apply(as_bytes(make_span(buf, static_cast<size_t>(i - buf))));
return none;
}
error serialized_size_inspector::end_sequence() {
error_code<sec> binary_serializer::end_sequence() {
return none;
}
error serialized_size_inspector::apply_raw(size_t num_bytes, void*) {
result_ += num_bytes;
return none;
}
error serialized_size_inspector::apply_impl(int8_t& x) {
result_ += sizeof(x);
return none;
}
error serialized_size_inspector::apply_impl(uint8_t& x) {
result_ += sizeof(x);
return none;
}
error serialized_size_inspector::apply_impl(int16_t& x) {
result_ += sizeof(x);
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());
}
error serialized_size_inspector::apply_impl(uint16_t& x) {
result_ += sizeof(x);
return none;
void binary_serializer::apply(byte x) {
if (write_pos_ == buf_.size())
buf_.emplace_back(x);
else
buf_[write_pos_] = x;
++write_pos_;
}
error serialized_size_inspector::apply_impl(int32_t& x) {
result_ += sizeof(x);
return none;
void binary_serializer::apply(uint8_t x) {
apply(static_cast<byte>(x));
}
error serialized_size_inspector::apply_impl(uint32_t& x) {
result_ += sizeof(x);
return none;
void binary_serializer::apply(uint16_t x) {
apply_int(*this, x);
}
error serialized_size_inspector::apply_impl(int64_t& x) {
result_ += sizeof(x);
return none;
void binary_serializer::apply(uint32_t x) {
apply_int(*this, x);
}
error serialized_size_inspector::apply_impl(uint64_t& x) {
result_ += sizeof(x);
return none;
void binary_serializer::apply(uint64_t x) {
apply_int(*this, x);
}
error serialized_size_inspector::apply_impl(float& x) {
result_ += sizeof(x);
return none;
void binary_serializer::apply(float x) {
apply_int(*this, detail::pack754(x));
}
error serialized_size_inspector::apply_impl(double& x) {
result_ += sizeof(x);
return none;
void binary_serializer::apply(double x) {
apply_int(*this, detail::pack754(x));
}
error serialized_size_inspector::apply_impl(long double& x) {
// The IEEE-754 conversion does not work for long double
// => fall back to string serialization (event though it sucks).
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();
return apply_impl(tmp);
apply(tmp);
}
error serialized_size_inspector::apply_impl(std::string& x) {
size_t str_size = x.size();
begin_sequence(str_size);
result_ += x.size();
void binary_serializer::apply(string_view x) {
begin_sequence(x.size());
apply(as_bytes(make_span(x)));
end_sequence();
return none;
}
error serialized_size_inspector::apply_impl(std::u16string& x) {
size_t str_size = x.size();
void binary_serializer::apply(const std::u16string& x) {
auto str_size = x.size();
begin_sequence(str_size);
result_ += x.size() * sizeof(uint16_t);
// 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();
return none;
}
error serialized_size_inspector::apply_impl(std::u32string& x) {
size_t str_size = x.size();
void binary_serializer::apply(const std::u32string& x) {
auto str_size = x.size();
begin_sequence(str_size);
result_ += x.size() * sizeof(uint32_t);
// 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();
return none;
}
} // 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,12 @@ error dynamic_message_data::load(size_t pos, deserializer& source) {
return elements_[pos]->load(source);
}
error_code<sec>
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 +99,12 @@ error dynamic_message_data::save(size_t pos, serializer& sink) const {
return elements_[pos]->save(sink);
}
error_code<sec>
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;
......
......@@ -153,16 +153,19 @@ void error::clear() noexcept {
// -- inspection support -----------------------------------------------------
error error::apply(const inspect_fun& f) {
data tmp{0, atom(""), message{}};
data& ref = data_ != nullptr ? *data_ : tmp;
auto result = f(meta::type_name("error"), ref.code, ref.category,
meta::omittable_if_empty(), ref.context);
if (ref.code == 0)
clear();
else if (&tmp == &ref)
data_ = new data(std::move(tmp));
return result;
uint8_t& error::code_ref() noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->code;
}
atom_value& error::category_ref() noexcept {
CAF_ASSERT(data_ != nullptr);
return data_->category;
}
void error::init() {
if (data_ == nullptr)
data_ = new data;
}
std::string to_string(const error& x) {
......
......@@ -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_code<sec> 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_code<sec> 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_code<sec> save(binary_serializer& sink) const override;
void stop() override {
CAF_LOG_TRACE("");
await_all_locals_down(system(), {broker_});
......@@ -348,14 +350,15 @@ public:
return group{result};
}
error load(deserializer& source, group& storage) override {
template <class Deserializer>
typename Deserializer::result_type
load_impl(Deserializer& source, group& storage) {
CAF_LOG_TRACE("");
// deserialize identifier and broker
std::string identifier;
strong_actor_ptr broker_ptr;
auto e = source(identifier, broker_ptr);
if (e)
return e;
if (auto err = source(identifier, broker_ptr))
return err;
CAF_LOG_DEBUG(CAF_ARG(identifier) << CAF_ARG(broker_ptr));
if (!broker_ptr) {
storage = invalid_group;
......@@ -382,7 +385,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_code<sec> 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 +402,14 @@ public:
return sink(id, bro);
}
error save(const local_group* ptr, serializer& sink) const {
return save_impl(ptr, sink);
}
error_code<sec> 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 +451,11 @@ error local_group::save(serializer& sink) const {
return static_cast<local_group_module&>(parent_).save(this, sink);
}
error_code<sec> 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
......
......@@ -22,17 +22,14 @@
#include <utility>
#include <utility>
#include "caf/serializer.hpp"
#include "caf/actor_system.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/dynamic_message_data.hpp"
#include "caf/message_builder.hpp"
#include "caf/message_handler.hpp"
#include "caf/serializer.hpp"
#include "caf/string_algorithms.hpp"
#include "caf/detail/decorated_tuple.hpp"
#include "caf/detail/concatenated_tuple.hpp"
#include "caf/detail/dynamic_message_data.hpp"
namespace caf {
message::message(none_t) noexcept {
......@@ -69,6 +66,11 @@ error message::load(size_t pos, deserializer& source) {
return vals_.unshared().load(pos, source);
}
error_code<sec> message::load(size_t pos, binary_deserializer& source) {
CAF_ASSERT(vals_ != nullptr);
return vals_.unshared().load(pos, source);
}
size_t message::size() const noexcept {
return vals_ != nullptr ? vals_->size() : 0;
}
......@@ -102,23 +104,30 @@ error message::save(size_t pos, serializer& sink) const {
return vals_->save(pos, sink);
}
error_code<sec> message::save(size_t pos, binary_serializer& sink) const {
CAF_ASSERT(vals_ != nullptr);
return vals_->save(pos, sink);
}
bool message::shared() const noexcept {
return vals_ != nullptr ? vals_->shared() : false;
}
error message::load(deserializer& source) {
namespace {
template <class Deserializer>
typename Deserializer::result_type
load_vals(Deserializer& source, message::data_ptr& vals) {
if (source.context() == nullptr)
return sec::no_context;
uint16_t zero;
uint16_t zero = 0;
std::string tname;
error err;
err = source.begin_object(zero, tname);
if (err)
if (auto err = source.begin_object(zero, tname))
return err;
if (zero != 0)
return sec::unknown_type;
if (tname == "@<>") {
vals_.reset();
vals.reset();
return none;
}
if (tname.compare(0, 4, "@<>+") != 0)
......@@ -137,10 +146,11 @@ error message::load(deserializer& source) {
auto n = next(i);
tmp.assign(i, n);
auto ptr = types.make_value(tmp);
if (!ptr)
return make_error(sec::unknown_type, tmp);
err = ptr->load(source);
if (err)
if (!ptr) {
CAF_LOG_ERROR("unknown type:" << tmp);
return sec::unknown_type;
}
if (auto err = ptr->load(source))
return err;
dmd->append(std::move(ptr));
if (n != eos)
......@@ -148,23 +158,37 @@ error message::load(deserializer& source) {
else
i = eos;
} while (i != eos);
err = source.end_object();
if (err)
if (auto err = source.end_object())
return err;
message result{detail::message_data::cow_ptr{std::move(dmd)}};
swap(result);
vals = detail::message_data::cow_ptr{std::move(dmd)};
return none;
}
error message::save(serializer& sink, const type_erased_tuple& x) {
} // namespace
error message::load(deserializer& source) {
return load_vals(source, vals_);
}
error_code<sec> message::load(binary_deserializer& source) {
return load_vals(source, vals_);
}
namespace {
template <class Serializer>
typename Serializer::result_type
save_tuple(Serializer& sink, const type_erased_tuple& x) {
if (sink.context() == nullptr)
return sec::no_context;
// build type name
uint16_t zero = 0;
std::string tname = "@<>";
if (x.empty())
return error::eval([&] { return sink.begin_object(zero, tname); },
[&] { return sink.end_object(); });
if (x.empty()) {
if (auto err = sink.begin_object(zero, tname))
return err;
return sink.end_object();
}
auto& types = sink.context()->system().types();
auto n = x.size();
for (size_t i = 0; i < n; ++i) {
......@@ -176,28 +200,36 @@ error message::save(serializer& sink, const type_erased_tuple& x) {
<< (rtti.second != nullptr ? rtti.second->name()
: "-not-available-")
<< std::endl;
return make_error(sec::unknown_type, rtti.second != nullptr
? rtti.second->name()
: "-not-available-");
return sec::unknown_type;
}
tname += '+';
tname += portable_name;
}
auto save_loop = [&]() -> error {
for (size_t i = 0; i < n; ++i) {
auto e = x.save(i, sink);
if (e)
return e;
}
return none;
};
return error::eval([&] { return sink.begin_object(zero, tname); },
[&] { return save_loop(); },
[&] { return sink.end_object(); });
if (auto err = sink.begin_object(zero, tname))
return err;
for (size_t i = 0; i < n; ++i)
if (auto err = x.save(i, sink))
return err;
return sink.end_object();
}
} // namespace
error message::save(serializer& sink, const type_erased_tuple& x) {
return save_tuple(sink, x);
}
error_code<sec>
message::save(binary_serializer& sink, const type_erased_tuple& x) {
return save_tuple(sink, x);
}
error message::save(serializer& sink) const {
return save(sink, *this);
return save_tuple(sink, *this);
}
error_code<sec> message::save(binary_serializer& sink) const {
return save_tuple(sink, *this);
}
// -- factories ----------------------------------------------------------------
......@@ -210,11 +242,6 @@ message message::copy(const type_erased_tuple& xs) {
}
// -- modifiers ----------------------------------------------------------------
message& message::operator+=(const message& x) {
auto tmp = *this + x;
swap(tmp);
return *this;
}
optional<message> message::apply(message_handler handler) {
return handler(*this);
......@@ -228,346 +255,19 @@ void message::reset(raw_ptr new_ptr, bool add_ref) noexcept {
vals_.reset(new_ptr, add_ref);
}
// -- observers ----------------------------------------------------------------
message message::drop(size_t n) const {
CAF_ASSERT(vals_ != nullptr);
if (n == 0)
return *this;
if (n >= size())
return message{};
std::vector<size_t> mapping (size() - n);
size_t i = n;
std::generate(mapping.begin(), mapping.end(), [&] { return i++; });
return message {detail::decorated_tuple::make(vals_, mapping)};
}
message message::drop_right(size_t n) const {
CAF_ASSERT(vals_ != nullptr);
if (n == 0) {
return *this;
}
if (n >= size()) {
return message{};
}
std::vector<size_t> mapping(size() - n);
std::iota(mapping.begin(), mapping.end(), size_t{0});
return message{detail::decorated_tuple::make(vals_, std::move(mapping))};
}
message message::slice(size_t pos, size_t n) const {
auto s = size();
if (pos >= s) {
return message{};
}
std::vector<size_t> mapping(std::min(s - pos, n));
std::iota(mapping.begin(), mapping.end(), pos);
return message{detail::decorated_tuple::make(vals_, std::move(mapping))};
}
message message::extract(message_handler handler) const {
return extract_impl(0, std::move(handler));
}
message::cli_res message::extract_opts(std::vector<cli_arg> xs,
const help_factory& f, bool no_help) const {
std::string helpstr;
auto make_error = [&](std::string err) -> cli_res {
return {*this, std::set<std::string>{}, std::move(helpstr), std::move(err)};
};
// add default help item if user did not specify any help option
auto pred = [](const cli_arg& arg) -> bool {
std::vector<std::string> s;
split(s, arg.name, is_any_of(","), token_compress_on);
if (s.empty())
return false;
auto has_short_help = [](const std::string& opt) {
return opt.find_first_of("h?") != std::string::npos;
};
return s[0] == "help"
|| std::find_if(s.begin() + 1, s.end(), has_short_help) != s.end();
};
if (!no_help && std::none_of(xs.begin(), xs.end(), pred)) {
xs.emplace_back("help,h,?", "print this text");
}
std::map<std::string, cli_arg*> shorts;
std::map<std::string, cli_arg*> longs;
for (auto& cliarg : xs) {
std::vector<std::string> s;
split(s, cliarg.name, is_any_of(","), token_compress_on);
if (s.empty()) {
return make_error("invalid option name: " + cliarg.name);
}
longs["--" + s.front()] = &cliarg;
for (size_t i = 1; i < s.size(); ++i) {
if (s[i].size() != 1) {
return make_error("invalid short option name: " + s[i]);
}
shorts["-" + s[i]] = &cliarg;
}
// generate helptext for this item
auto& ht = cliarg.helptext;
if (s.size() == 1) {
ht += "--";
ht += s.front();
} else {
ht += "-";
ht += s[1];
ht += " [";
for (size_t i = 2; i < s.size(); ++i) {
ht += "-";
ht += s[i];
ht += ",";
}
ht += "--";
ht += s.front();
ht += "]";
}
if (cliarg.fun) {
ht += " arg";
}
}
if (f) {
helpstr = f(xs);
} else {
auto op = [](size_t tmp, const cli_arg& arg) {
return std::max(tmp, arg.helptext.size());
};
auto name_width = std::accumulate(xs.begin(), xs.end(), size_t{0}, op);
std::ostringstream oss;
oss << std::left;
oss << "Allowed options:" << std::endl;
for (auto& ca : xs) {
oss << " ";
oss.width(static_cast<std::streamsize>(name_width));
oss << ca.helptext << " : " << ca.text << std::endl;
}
helpstr = oss.str();
}
std::set<std::string> opts;
auto insert_opt_name = [&](const cli_arg* ptr) {
auto separator = ptr->name.find(',');
if (separator == std::string::npos) {
opts.insert(ptr->name);
} else {
opts.insert(ptr->name.substr(0, separator));
}
};
// we can't `return make_error(...)` from inside `extract`, hence we
// store any occurred error in a temporary variable returned at the end
std::string error;
bool skip_remainder = false;
auto res = extract({
[&](const std::string& arg) -> optional<skip_t> {
if (skip_remainder)
return skip();
if (arg == "--") {
skip_remainder = true;
// drop first remainder indicator
return none;
}
if (arg.empty() || arg.front() != '-') {
return skip();
}
auto i = shorts.find(arg.substr(0, 2));
if (i != shorts.end()) {
if (i->second->fun) {
// this short opt expects two arguments
if (arg.size() > 2) {
// this short opt comes with a value (no space), e.g., -x2
if (!i->second->fun(arg.substr(2))) {
error = "invalid value for " + i->second->name + ": " + arg;
return skip();
}
insert_opt_name(i->second);
return none;
}
// no value given, try two-argument form below
return skip();
}
if (i->second->flag != nullptr)
*i->second->flag = true;
insert_opt_name(i->second);
return none;
}
auto eq_pos = arg.find('=');
auto j = longs.find(arg.substr(0, eq_pos));
if (j != longs.end()) {
if (j->second->fun) {
if (eq_pos == std::string::npos) {
error = "missing argument to " + arg;
return skip();
}
if (!j->second->fun(arg.substr(eq_pos + 1))) {
error = "invalid value for " + j->second->name + ": " + arg;
return skip();
}
insert_opt_name(j->second);
return none;
}
if (j->second->flag != nullptr)
*j->second->flag = true;
insert_opt_name(j->second);
return none;
}
error = "unknown command line option: " + arg;
return skip();
},
[&](const std::string& arg1,
const std::string& arg2) -> optional<skip_t> {
if (arg1 == "--") {
return skip();
}
if (skip_remainder)
return skip();
if (arg1.size() < 2 || arg1[0] != '-' || arg1[1] == '-') {
return skip();
}
auto i = shorts.find(arg1.substr(0, 2));
if (i != shorts.end()) {
if (!i->second->fun || arg1.size() > 2) {
// this short opt either expects no argument or comes with a value
// (no space), e.g., -x2, so we have to parse it with the
// one-argument form above
return skip();
}
CAF_ASSERT(arg1.size() == 2);
if (!i->second->fun(arg2)) {
error = "invalid value for option " + i->second->name + ": " + arg2;
return skip();
}
insert_opt_name(i->second);
return none;
}
error = "unknown command line option: " + arg1;
return skip();
}
});
return {res, std::move(opts), std::move(helpstr), std::move(error)};
}
// -- private helpers ----------------------------------------------------------
message message::extract_impl(size_t start, message_handler handler) const {
auto s = size();
for (size_t i = start; i < s; ++i) {
for (size_t n = (s - i) ; n > 0; --n) {
auto next_slice = slice(i, n);
auto res = handler(next_slice);
if (res) {
std::vector<size_t> mapping(s);
std::iota(mapping.begin(), mapping.end(), size_t{0});
auto first = mapping.begin() + static_cast<ptrdiff_t>(i);
auto last = first + static_cast<ptrdiff_t>(n);
mapping.erase(first, last);
if (mapping.empty()) {
return message{};
}
message next{detail::decorated_tuple::make(vals_, std::move(mapping))};
return next.extract_impl(i, handler);
}
}
}
return *this;
}
message message::concat_impl(std::initializer_list<data_ptr> xs) {
auto not_nullptr = [](const data_ptr& ptr) { return ptr.get() != nullptr; };
switch (std::count_if(xs.begin(), xs.end(), not_nullptr)) {
case 0:
return message{};
case 1:
return message{*std::find_if(xs.begin(), xs.end(), not_nullptr)};
default:
return message{detail::concatenated_tuple::make(xs)};
}
}
// -- nested types -------------------------------------------------------------
message::cli_arg::cli_arg(std::string nstr, std::string tstr)
: name(std::move(nstr)),
text(std::move(tstr)),
flag(nullptr) {
// nop
}
message::cli_arg::cli_arg(std::string nstr, std::string tstr, bool& arg)
: name(std::move(nstr)),
text(std::move(tstr)),
flag(&arg) {
// nop
}
message::cli_arg::cli_arg(std::string nstr, std::string tstr, consumer f)
: name(std::move(nstr)),
text(std::move(tstr)),
fun(std::move(f)),
flag(nullptr) {
// nop
}
message::cli_arg::cli_arg(std::string nstr, std::string tstr, atom_value& arg)
: name(std::move(nstr)),
text(std::move(tstr)),
fun([&arg](const std::string& str) -> bool {
if (str.size() <= 10) {
arg = static_cast<atom_value>(detail::atom_val(str.c_str()));
return true;
}
return false;
}),
flag(nullptr) {
// nop
}
message::cli_arg::cli_arg(std::string nstr, std::string tstr, timespan& arg)
: name(std::move(nstr)),
text(std::move(tstr)),
fun([&arg](const std::string& str) -> bool {
int64_t count;
std::istringstream iss{str};
if (iss >> count) {
arg = timespan{count};
return true;
}
return false;
}),
flag(nullptr) {
// nop
}
message::cli_arg::cli_arg(std::string nstr, std::string tstr, std::string& arg)
: name(std::move(nstr)),
text(std::move(tstr)),
fun([&arg](const std::string& str) -> bool {
arg = str;
return true;
}),
flag(nullptr) {
// nop
error inspect(serializer& sink, message& msg) {
return msg.save(sink);
}
message::cli_arg::cli_arg(std::string nstr, std::string tstr,
std::vector<std::string>& arg)
: name(std::move(nstr)),
text(std::move(tstr)),
fun([&arg](const std::string& str) -> bool {
arg.push_back(str);
return true;
}),
flag(nullptr) {
// nop
error inspect(deserializer& source, message& msg) {
return msg.load(source);
}
// -- related non-members ------------------------------------------------------
error inspect(serializer& sink, message& msg) {
error_code<sec> inspect(binary_serializer& sink, message& msg) {
return msg.save(sink);
}
error inspect(deserializer& source, message& msg) {
error_code<sec> inspect(binary_deserializer& source, message& msg) {
return msg.load(source);
}
......
......@@ -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.
......
......@@ -24,6 +24,8 @@
#include <random>
#include <sstream>
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/config.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/get_mac_addresses.hpp"
......@@ -136,6 +138,16 @@ error node_id::default_data::deserialize(deserializer& source) {
return source(pid_, host_);
}
error_code<sec>
node_id::default_data::serialize(binary_serializer& sink) const {
return sink(pid_, host_);
}
error_code<sec>
node_id::default_data::deserialize(binary_deserializer& source) {
return source(pid_, host_);
}
node_id::uri_data::uri_data(uri value) : value_(std::move(value)) {
// nop
}
......@@ -178,6 +190,14 @@ error node_id::uri_data::deserialize(deserializer& source) {
return source(value_);
}
error_code<sec> node_id::uri_data::serialize(binary_serializer& sink) const {
return sink(value_);
}
error_code<sec> node_id::uri_data::deserialize(binary_deserializer& source) {
return source(value_);
}
node_id& node_id::operator=(const none_t&) {
data_.reset();
return *this;
......@@ -207,42 +227,58 @@ void node_id::swap(node_id& x) {
data_.swap(x.data_);
}
error node_id::serialize(serializer& sink) const {
if (data_ && data_->valid()) {
if (auto err = sink(data_->implementation_id()))
namespace {
template <class Serializer>
auto serialize_data(Serializer& sink, const intrusive_ptr<node_id::data>& ptr) {
if (ptr && ptr->valid()) {
if (auto err = sink(ptr->implementation_id()))
return err;
return data_->serialize(sink);
return ptr->serialize(sink);
}
return sink(atom(""));
}
error node_id::deserialize(deserializer& source) {
template <class Deserializer>
auto deserialize_data(Deserializer& source, intrusive_ptr<node_id::data>& ptr) {
using result_type = typename Deserializer::result_type;
auto impl = static_cast<atom_value>(0);
if (auto err = source(impl))
return err;
if (impl == atom("")) {
data_.reset();
return none;
ptr.reset();
return result_type{};
}
if (impl == default_data::class_id) {
if (data_ == nullptr
|| data_->implementation_id() != default_data::class_id)
data_ = make_counted<default_data>();
return data_->deserialize(source);
} else if (impl == uri_data::class_id) {
if (data_ == nullptr || data_->implementation_id() != uri_data::class_id)
data_ = make_counted<uri_data>();
return data_->deserialize(source);
if (impl == node_id::default_data::class_id) {
if (ptr == nullptr
|| ptr->implementation_id() != node_id::default_data::class_id)
ptr = make_counted<node_id::default_data>();
return ptr->deserialize(source);
} else if (impl == node_id::uri_data::class_id) {
if (ptr == nullptr
|| ptr->implementation_id() != node_id::uri_data::class_id)
ptr = make_counted<node_id::uri_data>();
return ptr->deserialize(source);
}
return sec::unknown_type;
return result_type{sec::unknown_type};
}
error inspect(serializer& sink, const node_id& x) {
return x.serialize(sink);
} // namespace
error inspect(serializer& sink, node_id& x) {
return serialize_data(sink, x.data_);
}
error inspect(deserializer& source, node_id& x) {
return x.deserialize(source);
return deserialize_data(source, x.data_);
}
error_code<sec> inspect(binary_serializer& sink, node_id& x) {
return serialize_data(sink, x.data_);
}
error_code<sec> inspect(binary_deserializer& source, node_id& x) {
return deserialize_data(source, x.data_);
}
void append_to_string(std::string& str, const node_id& x) {
......@@ -276,11 +312,14 @@ optional<node_id> make_node_id(uint32_t process_id,
return none;
detail::parser::ascii_to_int<16, uint8_t> xvalue;
node_data::host_id_type host_id;
for (size_t i = 0; i < node_data::host_id_size; i += 2) {
// Read two characters, each representing 4 bytes.
if (!isxdigit(host_hash[i]) || !isxdigit(host_hash[i + 1]))
auto in = host_hash.begin();
for (auto& byte : host_id) {
if (!isxdigit(*in))
return none;
auto first_nibble = (xvalue(*in++) << 4);
if (!isxdigit(*in))
return none;
host_id[i / 2] = (xvalue(host_hash[i]) << 4) | xvalue(host_hash[i + 1]);
byte = static_cast<uint8_t>(first_nibble | xvalue(*in++));
}
if (!node_data::valid(host_id))
return none;
......
......@@ -18,10 +18,22 @@
#include "caf/sec.hpp"
#include "caf/error.hpp"
#include "caf/make_message.hpp"
#include "caf/message.hpp"
namespace caf {
error make_error(sec x) {
return {static_cast<uint8_t>(x), atom("system")};
}
error make_error(sec x, message msg) {
return {static_cast<uint8_t>(x), atom("system"), std::move(msg)};
}
error make_error(sec x, std::string msg) {
return make_error(x, make_message(std::move(msg)));
}
} // namespace caf
......@@ -22,11 +22,12 @@
namespace caf {
serializer::serializer(actor_system& sys) : super(sys.dummy_execution_unit()) {
serializer::serializer(actor_system& sys) noexcept
: context_(sys.dummy_execution_unit()) {
// nop
}
serializer::serializer(execution_unit* ctx) : super(ctx) {
serializer::serializer(execution_unit* ctx) noexcept : context_(ctx) {
// nop
}
......@@ -34,4 +35,13 @@ serializer::~serializer() {
// nop
}
auto serializer::apply(const std::vector<bool>& xs) -> result_type {
if (auto err = begin_sequence(xs.size()))
return err;
for (bool value : xs)
if (auto err = apply(value))
return err;
return end_sequence();
}
} // namespace caf
......@@ -31,11 +31,16 @@ type_erased_tuple::~type_erased_tuple() {
}
error type_erased_tuple::load(deserializer& source) {
for (size_t i = 0; i < size(); ++i) {
auto e = load(i, source);
if (e)
return e;
}
for (size_t i = 0; i < size(); ++i)
if (auto err = load(i, source))
return err;
return none;
}
error_code<sec> type_erased_tuple::load(binary_deserializer& source) {
for (size_t i = 0; i < size(); ++i)
if (auto err = load(i, source))
return err;
return none;
}
......@@ -69,6 +74,12 @@ error type_erased_tuple::save(serializer& sink) const {
return none;
}
error_code<sec> type_erased_tuple::save(binary_serializer& sink) const {
for (size_t i = 0; i < size(); ++i)
save(i, sink);
return none;
}
bool type_erased_tuple::matches(size_t pos, uint16_t nr,
const std::type_info* ptr) const noexcept {
CAF_ASSERT(pos < size());
......@@ -92,6 +103,10 @@ error empty_type_erased_tuple::load(size_t, deserializer&) {
CAF_RAISE_ERROR("empty_type_erased_tuple::get_mutable");
}
error_code<sec> empty_type_erased_tuple::load(size_t, binary_deserializer&) {
CAF_RAISE_ERROR("empty_type_erased_tuple::load");
}
size_t empty_type_erased_tuple::size() const noexcept {
return 0;
}
......@@ -117,7 +132,12 @@ type_erased_value_ptr empty_type_erased_tuple::copy(size_t) const {
}
error empty_type_erased_tuple::save(size_t, serializer&) const {
CAF_RAISE_ERROR("empty_type_erased_tuple::copy");
CAF_RAISE_ERROR("empty_type_erased_tuple::save");
}
error_code<sec>
empty_type_erased_tuple::save(size_t, binary_serializer&) const {
CAF_RAISE_ERROR("empty_type_erased_tuple::save");
}
} // namespace caf
......@@ -59,6 +59,7 @@ const char* numbered_type_names[] = {
"@addr",
"@addrvec",
"@atom",
"@bytebuf",
"@charbuf",
"@config_value",
"@down",
......@@ -96,7 +97,7 @@ const char* numbered_type_names[] = {
"@weak_actor_ptr",
"bool",
"double",
"float"
"float",
};
namespace {
......
......@@ -18,6 +18,8 @@
#include "caf/uri.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/append_percent_encoded.hpp"
#include "caf/detail/fnv_hash.hpp"
......@@ -110,6 +112,18 @@ error inspect(caf::deserializer& src, uri& x) {
return err;
}
error_code<sec> inspect(caf::binary_serializer& dst, uri& x) {
return inspect(dst, const_cast<detail::uri_impl&>(*x.impl_));
}
error_code<sec> inspect(caf::binary_deserializer& src, uri& x) {
auto impl = make_counted<detail::uri_impl>();
auto err = inspect(src, *impl);
if (err == none)
x = uri{std::move(impl)};
return err;
}
// -- related free functions ---------------------------------------------------
std::string to_string(const uri& x) {
......
......@@ -182,43 +182,4 @@ CAF_TEST(random_actor_pool) {
self->send_exit(pool, exit_reason::user_shutdown);
}
CAF_TEST(split_join_actor_pool) {
auto spawn_split_worker = [&] {
return system.spawn<lazy_init>([]() -> behavior {
return {
[](size_t pos, const std::vector<int>& xs) {
return xs[pos];
}
};
});
};
auto split_fun = [](std::vector<std::pair<actor, message>>& xs, message& y) {
for (size_t i = 0; i < xs.size(); ++i) {
xs[i].second = make_message(i) + y;
}
};
auto join_fun = [](int& res, message& msg) {
msg.apply([&](int x) {
res += x;
});
};
scoped_actor self{system};
CAF_MESSAGE("create actor pool");
auto pool = actor_pool::make(&context, 5, spawn_split_worker,
actor_pool::split_join<int>(join_fun, split_fun));
self->request(pool, infinite, std::vector<int>{1, 2, 3, 4, 5}).receive(
[&](int res) {
CAF_CHECK_EQUAL(res, 15);
},
handle_err
);
self->request(pool, infinite, std::vector<int>{6, 7, 8, 9, 10}).receive(
[&](int res) {
CAF_CHECK_EQUAL(res, 40);
},
handle_err
);
self->send_exit(pool, exit_reason::user_shutdown);
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -22,16 +22,15 @@
#include "caf/test/dsl.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
using namespace caf;
namespace {
behavior dummy() {
return {
[](int i) {
return i;
}
};
return {[](int i) { return i; }};
}
using foo_atom = atom_constant<atom("foo")>;
......@@ -52,4 +51,18 @@ CAF_TEST(erase) {
CAF_CHECK_EQUAL(sys.registry().named_actors().size(), baseline);
}
CAF_TEST(serialization roundtrips go through the registry) {
auto hdl = sys.spawn(dummy);
byte_buffer buf;
binary_serializer sink{sys, buf};
if (auto err = sink(hdl))
CAF_FAIL("serialization failed: " << sys.render(err));
actor hdl2;
binary_deserializer source{sys, buf};
if (auto err = source(hdl2))
CAF_FAIL("serialization failed: " << sys.render(err));
CAF_CHECK_EQUAL(hdl, hdl2);
anon_send_exit(hdl, exit_reason::user_shutdown);
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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. *
******************************************************************************/
#define CAF_SUITE binary_deserializer
#include "caf/binary_serializer.hpp"
#include "caf/test/dsl.hpp"
#include <cstring>
#include <vector>
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/byte.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/duration.hpp"
#include "caf/timestamp.hpp"
using namespace caf;
namespace {
byte operator"" _b(unsigned long long int x) {
return static_cast<byte>(x);
}
byte operator"" _b(char x) {
return static_cast<byte>(x);
}
enum class test_enum : int32_t {
a,
b,
c,
};
struct test_data {
int32_t i32_;
int64_t i64_;
float f32_;
double f64_;
caf::duration dur_;
caf::timestamp ts_;
test_enum te_;
std::string str_;
};
bool operator==(const test_data& data, const test_data& other) {
return (data.f64_ == other.f64_ && data.i32_ == other.i32_
&& data.i64_ == other.i64_ && data.str_ == other.str_
&& data.te_ == other.te_ && data.ts_ == other.ts_);
}
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, test_data& x) {
return f(caf::meta::type_name("test_data"), x.i32_, x.i64_, x.f32_, x.f64_,
x.dur_, x.ts_, x.te_, x.str_);
}
struct fixture {
template <class T>
auto load(const std::vector<byte>& buf) {
auto result = T{};
binary_deserializer source{nullptr, buf};
if (auto err = source(result))
CAF_FAIL("binary_deserializer failed to load: "
<< actor_system_config::render(err));
return result;
}
template <class... Ts>
void load(const std::vector<byte>& buf, Ts&... xs) {
binary_deserializer source{nullptr, buf};
if (auto err = source(xs...))
CAF_FAIL("binary_deserializer failed to load: "
<< actor_system_config::render(err));
}
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(binary_deserializer_tests, fixture)
#define SUBTEST(msg) \
CAF_MESSAGE(msg); \
for (int subtest_dummy = 0; subtest_dummy < 1; ++subtest_dummy)
#define CHECK_EQ(lhs, rhs) CAF_CHECK_EQUAL(lhs, rhs)
#define CHECK_LOAD(type, value, ...) \
CAF_CHECK_EQUAL(load<type>({__VA_ARGS__}), value)
CAF_TEST(binary deserializer handles all primitive types) {
SUBTEST("8-bit integers") {
CHECK_LOAD(int8_t, 60, 0b00111100_b);
CHECK_LOAD(int8_t, -61, 0b11000011_b);
CHECK_LOAD(uint8_t, 60u, 0b00111100_b);
CHECK_LOAD(uint8_t, 195u, 0b11000011_b);
}
SUBTEST("16-bit integers") {
CHECK_LOAD(int16_t, 85, 0b00000000_b, 0b01010101_b);
CHECK_LOAD(int16_t, -32683, 0b10000000_b, 0b01010101_b);
CHECK_LOAD(uint16_t, 85u, 0b00000000_b, 0b01010101_b);
CHECK_LOAD(uint16_t, 32853u, 0b10000000_b, 0b01010101_b);
}
SUBTEST("32-bit integers") {
CHECK_LOAD(int32_t, -345, 0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b);
CHECK_LOAD(uint32_t, 4294966951u, 0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b);
}
SUBTEST("64-bit integers") {
CHECK_LOAD(int64_t, -1234567890123456789ll, //
0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b);
CHECK_LOAD(uint64_t, 17212176183586094827llu, //
0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b);
}
SUBTEST("floating points use IEEE-754 conversion") {
CHECK_LOAD(float, 3.45f, 0x40_b, 0x5C_b, 0xCC_b, 0xCD_b);
}
SUBTEST("strings use a varbyte-encoded size prefix") {
CHECK_LOAD(std::string, "hello", 5_b, 'h'_b, 'e'_b, 'l'_b, 'l'_b, 'o'_b);
}
SUBTEST("enum types") {
CHECK_LOAD(test_enum, test_enum::a, 0_b, 0_b, 0_b, 0_b);
CHECK_LOAD(test_enum, test_enum::b, 0_b, 0_b, 0_b, 1_b);
CHECK_LOAD(test_enum, test_enum::c, 0_b, 0_b, 0_b, 2_b);
}
}
CAF_TEST(concatenation) {
SUBTEST("calling f(a, b) writes a and b into the buffer in order") {
int8_t x = 0;
int16_t y = 0;
load(byte_buffer({7_b, 0x80_b, 0x55_b}), x, y);
CAF_CHECK_EQUAL(x, 7);
CAF_CHECK_EQUAL(y, -32683);
load(byte_buffer({0x80_b, 0x55_b, 7_b}), y, x);
load(byte_buffer({7_b, 0x80_b, 0x55_b}), x, y);
}
SUBTEST("calling f(make_pair(a, b)) is equal to calling f(a, b)") {
using i8i16_pair = std::pair<int8_t, int16_t>;
using i16i8_pair = std::pair<int16_t, int8_t>;
CHECK_LOAD(i8i16_pair, std::make_pair(int8_t{7}, int16_t{-32683}), //
7_b, 0x80_b, 0x55_b);
CHECK_LOAD(i16i8_pair, std::make_pair(int16_t{-32683}, int8_t{7}), //
0x80_b, 0x55_b, 7_b);
}
SUBTEST("calling f(make_tuple(a, b)) is equivalent to f(make_pair(a, b))") {
using i8i16_tuple = std::tuple<int8_t, int16_t>;
using i16i8_tuple = std::tuple<int16_t, int8_t>;
CHECK_LOAD(i8i16_tuple, std::make_tuple(int8_t{7}, int16_t{-32683}), //
7_b, 0x80_b, 0x55_b);
CHECK_LOAD(i16i8_tuple, std::make_tuple(int16_t{-32683}, int8_t{7}), //
0x80_b, 0x55_b, 7_b);
}
SUBTEST("arrays behave like tuples") {
int8_t xs[] = {0, 0, 0};
load(byte_buffer({1_b, 2_b, 3_b}), xs);
CAF_CHECK_EQUAL(xs[0], 1);
CAF_CHECK_EQUAL(xs[1], 2);
CAF_CHECK_EQUAL(xs[2], 3);
}
}
CAF_TEST(container types) {
SUBTEST("STL vectors") {
CHECK_LOAD(std::vector<int8_t>, std::vector<int8_t>({1, 2, 4, 8}), //
4_b, 1_b, 2_b, 4_b, 8_b);
}
SUBTEST("STL sets") {
CHECK_LOAD(std::set<int8_t>, std::set<int8_t>({1, 2, 4, 8}), //
4_b, 1_b, 2_b, 4_b, 8_b);
}
}
CAF_TEST(binary serializer picks up inspect functions) {
SUBTEST("duration") {
CHECK_LOAD(duration, duration(time_unit::minutes, 3),
// Bytes 1-4 contain the time_unit.
0_b, 0_b, 0_b, 1_b,
// Bytes 5-12 contain the count.
0_b, 0_b, 0_b, 0_b, 0_b, 0_b, 0_b, 3_b);
}
SUBTEST("node ID") {
auto nid = make_node_id(123, "000102030405060708090A0B0C0D0E0F10111213");
CHECK_LOAD(node_id, unbox(nid),
// Implementation ID: atom("default").
0_b, 0_b, 0x3E_b, 0x9A_b, 0xAB_b, 0x9B_b, 0xAC_b, 0x79_b,
// Process ID.
0_b, 0_b, 0_b, 123_b,
// Host ID.
0_b, 1_b, 2_b, 3_b, 4_b, 5_b, 6_b, 7_b, 8_b, 9_b, //
10_b, 11_b, 12_b, 13_b, 14_b, 15_b, 16_b, 17_b, 18_b, 19_b);
}
SUBTEST("custom struct") {
test_data value{
-345,
-1234567890123456789ll,
3.45,
54.3,
caf::duration(caf::time_unit::seconds, 123),
caf::timestamp{caf::timestamp::duration{1478715821 * 1000000000ll}},
test_enum::b,
"Lorem ipsum dolor sit amet."};
CHECK_LOAD(test_data, value,
// 32-bit i32_ member: -345
0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b,
// 64-bit i64_ member: -1234567890123456789ll
0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b,
// 32-bit f32_ member: 3.45f
0x40_b, 0x5C_b, 0xCC_b, 0xCD_b,
// 64-bit f64_ member: 54.3
0x40_b, 0x4B_b, 0x26_b, 0x66_b, 0x66_b, 0x66_b, 0x66_b, 0x66_b,
// 32-bit dur_.unit member: time_unit::seconds
0x00_b, 0x00_b, 0x00_b, 0x02_b,
// 64-bit dur_.count member: 123
0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x7B_b,
// 64-bit ts_ member.
0x14_b, 0x85_b, 0x74_b, 0x34_b, 0x62_b, 0x74_b, 0x82_b, 0x00_b,
// 32-bit te_ member: test_enum::b
0x00_b, 0x00_b, 0x00_b, 0x01_b,
// str_ member:
0x1B_b, //
'L'_b, 'o'_b, 'r'_b, 'e'_b, 'm'_b, ' '_b, 'i'_b, 'p'_b, 's'_b,
'u'_b, 'm'_b, ' '_b, 'd'_b, 'o'_b, 'l'_b, 'o'_b, 'r'_b, ' '_b,
's'_b, 'i'_b, 't'_b, ' '_b, 'a'_b, 'm'_b, 'e'_b, 't'_b, '.'_b);
}
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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. *
******************************************************************************/
#define CAF_SUITE binary_serializer
#include "caf/binary_serializer.hpp"
#include "caf/test/dsl.hpp"
#include <cstring>
#include <vector>
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/byte.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/duration.hpp"
#include "caf/timestamp.hpp"
using namespace caf;
namespace {
byte operator"" _b(unsigned long long int x) {
return static_cast<byte>(x);
}
byte operator"" _b(char x) {
return static_cast<byte>(x);
}
enum class test_enum : int32_t {
a,
b,
c,
};
struct test_data {
int32_t i32_;
int64_t i64_;
float f32_;
double f64_;
caf::duration dur_;
caf::timestamp ts_;
test_enum te_;
std::string str_;
};
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, test_data& x) {
return f(caf::meta::type_name("test_data"), x.i32_, x.i64_, x.f32_, x.f64_,
x.dur_, x.ts_, x.te_, x.str_);
}
struct fixture {
template <class... Ts>
auto save(const Ts&... xs) {
byte_buffer result;
binary_serializer sink{nullptr, result};
if (auto err = sink(xs...))
CAF_FAIL("binary_serializer failed to save: "
<< actor_system_config::render(err));
return result;
}
template <class... Ts>
void save_to_buf(byte_buffer& data, const Ts&... xs) {
binary_serializer sink{nullptr, data};
if (auto err = sink(xs...))
CAF_FAIL("binary_serializer failed to save: "
<< actor_system_config::render(err));
}
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(binary_serializer_tests, fixture)
#define SUBTEST(msg) \
CAF_MESSAGE(msg); \
for (int subtest_dummy = 0; subtest_dummy < 1; ++subtest_dummy)
#define CHECK_SAVE(type, value, ...) \
CAF_CHECK_EQUAL(save(type{value}), byte_buffer({__VA_ARGS__}))
CAF_TEST(primitive types) {
SUBTEST("8-bit integers") {
CHECK_SAVE(int8_t, 60, 0b00111100_b);
CHECK_SAVE(int8_t, -61, 0b11000011_b);
CHECK_SAVE(uint8_t, 60u, 0b00111100_b);
CHECK_SAVE(uint8_t, 195u, 0b11000011_b);
}
SUBTEST("16-bit integers") {
CHECK_SAVE(int16_t, 85, 0b00000000_b, 0b01010101_b);
CHECK_SAVE(int16_t, -32683, 0b10000000_b, 0b01010101_b);
CHECK_SAVE(uint16_t, 85u, 0b00000000_b, 0b01010101_b);
CHECK_SAVE(uint16_t, 32853u, 0b10000000_b, 0b01010101_b);
}
SUBTEST("32-bit integers") {
CHECK_SAVE(int32_t, -345, 0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b);
CHECK_SAVE(uint32_t, 4294966951u, 0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b);
}
SUBTEST("64-bit integers") {
CHECK_SAVE(int64_t, -1234567890123456789ll, //
0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b);
CHECK_SAVE(uint64_t, 17212176183586094827llu, //
0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b);
}
SUBTEST("floating points use IEEE-754 conversion") {
CHECK_SAVE(float, 3.45f, 0x40_b, 0x5C_b, 0xCC_b, 0xCD_b);
}
SUBTEST("strings use a varbyte-encoded size prefix") {
CHECK_SAVE(std::string, "hello", 5_b, 'h'_b, 'e'_b, 'l'_b, 'l'_b, 'o'_b);
}
SUBTEST("enum types") {
CHECK_SAVE(test_enum, test_enum::a, 0_b, 0_b, 0_b, 0_b);
CHECK_SAVE(test_enum, test_enum::b, 0_b, 0_b, 0_b, 1_b);
CHECK_SAVE(test_enum, test_enum::c, 0_b, 0_b, 0_b, 2_b);
}
}
CAF_TEST(concatenation) {
SUBTEST("calling f(a, b) writes a and b into the buffer in order") {
CAF_CHECK_EQUAL(save(int8_t{7}, int16_t{-32683}),
byte_buffer({7_b, 0x80_b, 0x55_b}));
CAF_CHECK_EQUAL(save(int16_t{-32683}, int8_t{7}),
byte_buffer({0x80_b, 0x55_b, 7_b}));
}
SUBTEST("calling f(a) and then f(b) is equal to calling f(a, b)") {
byte_buffer data;
binary_serializer sink{nullptr, data};
save_to_buf(data, int8_t{7});
save_to_buf(data, int16_t{-32683});
CAF_CHECK_EQUAL(data, byte_buffer({7_b, 0x80_b, 0x55_b}));
}
SUBTEST("calling f(make_pair(a, b)) is equal to calling f(a, b)") {
using i8i16_pair = std::pair<int8_t, int16_t>;
using i16i8_pair = std::pair<int16_t, int8_t>;
CHECK_SAVE(i8i16_pair, std::make_pair(int8_t{7}, int16_t{-32683}), //
7_b, 0x80_b, 0x55_b);
CHECK_SAVE(i16i8_pair, std::make_pair(int16_t{-32683}, int8_t{7}), //
0x80_b, 0x55_b, 7_b);
}
SUBTEST("calling f(make_tuple(a, b)) is equivalent to f(make_pair(a, b))") {
using i8i16_tuple = std::tuple<int8_t, int16_t>;
using i16i8_tuple = std::tuple<int16_t, int8_t>;
CHECK_SAVE(i8i16_tuple, std::make_tuple(int8_t{7}, int16_t{-32683}), //
7_b, 0x80_b, 0x55_b);
CHECK_SAVE(i16i8_tuple, std::make_tuple(int16_t{-32683}, int8_t{7}), //
0x80_b, 0x55_b, 7_b);
}
SUBTEST("arrays behave like tuples") {
int8_t xs[] = {1, 2, 3};
CAF_CHECK_EQUAL(save(xs), byte_buffer({1_b, 2_b, 3_b}));
}
}
CAF_TEST(container types) {
SUBTEST("STL vectors") {
CHECK_SAVE(std::vector<int8_t>, std::vector<int8_t>({1, 2, 4, 8}), //
4_b, 1_b, 2_b, 4_b, 8_b);
}
SUBTEST("STL sets") {
CHECK_SAVE(std::set<int8_t>, std::set<int8_t>({1, 2, 4, 8}), //
4_b, 1_b, 2_b, 4_b, 8_b);
}
}
CAF_TEST(binary serializer picks up inspect functions) {
SUBTEST("duration") {
CHECK_SAVE(duration, duration(time_unit::minutes, 3),
// Bytes 1-4 contain the time_unit.
0_b, 0_b, 0_b, 1_b,
// Bytes 5-12 contain the count.
0_b, 0_b, 0_b, 0_b, 0_b, 0_b, 0_b, 3_b);
}
SUBTEST("node ID") {
auto nid = make_node_id(123, "000102030405060708090A0B0C0D0E0F10111213");
CHECK_SAVE(node_id, unbox(nid),
// Implementation ID: atom("default").
0_b, 0_b, 0x3E_b, 0x9A_b, 0xAB_b, 0x9B_b, 0xAC_b, 0x79_b,
// Process ID.
0_b, 0_b, 0_b, 123_b,
// Host ID.
0_b, 1_b, 2_b, 3_b, 4_b, 5_b, 6_b, 7_b, 8_b, 9_b, //
10_b, 11_b, 12_b, 13_b, 14_b, 15_b, 16_b, 17_b, 18_b, 19_b);
}
SUBTEST("custom struct") {
caf::timestamp ts{caf::timestamp::duration{1478715821 * 1000000000ll}};
test_data value{-345,
-1234567890123456789ll,
3.45,
54.3,
caf::duration(caf::time_unit::seconds, 123),
ts,
test_enum::b,
"Lorem ipsum dolor sit amet."};
CHECK_SAVE(test_data, value,
// 32-bit i32_ member: -345
0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b,
// 64-bit i64_ member: -1234567890123456789ll
0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b,
// 32-bit f32_ member: 3.45f
0x40_b, 0x5C_b, 0xCC_b, 0xCD_b,
// 64-bit f64_ member: 54.3
0x40_b, 0x4B_b, 0x26_b, 0x66_b, 0x66_b, 0x66_b, 0x66_b, 0x66_b,
// 32-bit dur_.unit member: time_unit::seconds
0x00_b, 0x00_b, 0x00_b, 0x02_b,
// 64-bit dur_.count member: 123
0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x7B_b,
// 64-bit ts_ member.
0x14_b, 0x85_b, 0x74_b, 0x34_b, 0x62_b, 0x74_b, 0x82_b, 0x00_b,
// 32-bit te_ member: test_enum::b
0x00_b, 0x00_b, 0x00_b, 0x01_b,
// str_ member:
0x1B_b, //
'L'_b, 'o'_b, 'r'_b, 'e'_b, 'm'_b, ' '_b, 'i'_b, 'p'_b, 's'_b,
'u'_b, 'm'_b, ' '_b, 'd'_b, 'o'_b, 'l'_b, 'o'_b, 'r'_b, ' '_b,
's'_b, 'i'_b, 't'_b, ' '_b, 'a'_b, 'm'_b, 'e'_b, 't'_b, '.'_b);
}
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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. *
******************************************************************************/
#define CAF_SUITE decorator.splitter
#include "caf/decorator/splitter.hpp"
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
#define ERROR_HANDLER [&](error& err) { CAF_FAIL(system.render(err)); }
using namespace caf;
namespace {
using first_stage = typed_actor<replies_to<double>::with<double, double>>;
using second_stage = typed_actor<replies_to<double, double>::with<double>,
replies_to<double>::with<double>>;
first_stage::behavior_type typed_first_stage() {
return {
[](double x) { return std::make_tuple(x * 2.0, x * 4.0); },
};
}
second_stage::behavior_type typed_second_stage() {
return {
[](double x, double y) { return x * y; },
[](double x) { return 23.0 * x; },
};
}
behavior untyped_first_stage() {
return typed_first_stage().unbox();
}
behavior untyped_second_stage() {
return typed_second_stage().unbox();
}
struct fixture {
actor_system_config cfg;
actor_system system;
scoped_actor self;
actor first;
actor second;
actor first_and_second;
fixture() : system(cfg), self(system, true) {
// nop
}
void init_untyped() {
using namespace std::placeholders;
first = system.spawn(untyped_first_stage);
second = system.spawn(untyped_second_stage);
first_and_second = splice(first, second);
}
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(sequencer_tests, fixture)
CAF_TEST(identity) {
init_untyped();
CAF_CHECK_NOT_EQUAL(first, second);
CAF_CHECK_NOT_EQUAL(first, first_and_second);
CAF_CHECK_NOT_EQUAL(second, first_and_second);
}
CAF_TEST(kill_first) {
init_untyped();
anon_send_exit(first, exit_reason::kill);
self->wait_for(first_and_second);
}
CAF_TEST(kill_second) {
init_untyped();
anon_send_exit(second, exit_reason::kill);
self->wait_for(first_and_second);
}
CAF_TEST(untyped_splicing) {
init_untyped();
self->request(first_and_second, infinite, 42.0)
.receive(
[](double x, double y, double z) {
CAF_CHECK_EQUAL(x, (42.0 * 2.0));
CAF_CHECK_EQUAL(y, (42.0 * 4.0));
CAF_CHECK_EQUAL(z, (23.0 * 42.0));
},
ERROR_HANDLER);
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -54,9 +54,7 @@ CAF_TEST(shutdown_with_delayed_send) {
auto f = [](event_based_actor* self) -> behavior {
self->delayed_send(self, std::chrono::nanoseconds(1), ok_atom::value);
return {
[=](ok_atom) {
self->quit();
}
[=](ok_atom) { self->quit(); },
};
};
sys.spawn<detached>(f);
......@@ -76,9 +74,7 @@ CAF_TEST(shutdown_with_after) {
"after()?");
auto f = [](event_based_actor* self) -> behavior {
return {
after(std::chrono::nanoseconds(1)) >> [=] {
self->quit();
}
after(std::chrono::nanoseconds(1)) >> [=] { self->quit(); },
};
};
sys.spawn<detached>(f);
......@@ -88,17 +84,16 @@ CAF_TEST(shutdown_delayed_send_loop) {
CAF_MESSAGE("does sys shut down after spawning a detached actor that used "
"a delayed send loop and was interrupted via exit message?");
auto f = [](event_based_actor* self) -> behavior {
self->send(self, std::chrono::milliseconds(1), ok_atom::value);
self->delayed_send(self, std::chrono::milliseconds(1), ok_atom::value);
return {
[=](ok_atom) {
self->send(self, std::chrono::milliseconds(1), ok_atom::value);
}
self->delayed_send(self, std::chrono::milliseconds(1), ok_atom::value);
},
};
};
auto a = sys.spawn<detached>(f);
auto g = detail::make_scope_guard([&] {
self->send_exit(a, exit_reason::user_shutdown);
});
auto g = detail::make_scope_guard(
[&] { self->send_exit(a, exit_reason::user_shutdown); });
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -16,66 +16,32 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE detail.serialized_size
#define CAF_SUITE error
#include "caf/detail/serialized_size.hpp"
#include "caf/error.hpp"
#include "caf/test/dsl.hpp"
#include <vector>
#include "caf/byte.hpp"
#include "caf/serializer_impl.hpp"
using namespace caf;
using caf::detail::serialized_size;
namespace {
struct fixture : test_coordinator_fixture<> {
using buffer_type = std::vector<byte>;
template <class... Ts>
size_t actual_size(const Ts&... xs) {
buffer_type buf;
serializer_impl<buffer_type> sink{sys, buf};
if (auto err = sink(xs...))
CAF_FAIL("failed to serialize data: " << sys.render(err));
return buf.size();
}
};
} // namespace
#define CHECK_SAME_SIZE(...) \
CAF_CHECK_EQUAL(serialized_size(sys, __VA_ARGS__), actual_size(__VA_ARGS__))
CAF_TEST_FIXTURE_SCOPE(serialized_size_tests, fixture)
CAF_TEST(numbers) {
CHECK_SAME_SIZE(int8_t{42});
CHECK_SAME_SIZE(int16_t{42});
CHECK_SAME_SIZE(int32_t{42});
CHECK_SAME_SIZE(int64_t{42});
CHECK_SAME_SIZE(uint8_t{42});
CHECK_SAME_SIZE(uint16_t{42});
CHECK_SAME_SIZE(uint32_t{42});
CHECK_SAME_SIZE(uint64_t{42});
CHECK_SAME_SIZE(4.2f);
CHECK_SAME_SIZE(4.2);
CAF_TEST(default constructed errors evaluate to false) {
error err;
CAF_CHECK(!err);
}
CAF_TEST(containers) {
CHECK_SAME_SIZE(std::string{"foobar"});
CHECK_SAME_SIZE(std::vector<char>({'a', 'b', 'c'}));
CHECK_SAME_SIZE(std::vector<std::string>({"hello", "world"}));
CAF_TEST(error code zero is not an error) {
CAF_CHECK(!error(0, atom("system")));
CAF_CHECK(!make_error(sec::none));
CAF_CHECK(!error{error_code<sec>(sec::none)});
}
CAF_TEST(messages) {
CHECK_SAME_SIZE(make_message(42));
CHECK_SAME_SIZE(make_message(1, 2, 3));
CHECK_SAME_SIZE(make_message("hello", "world"));
CAF_TEST(error codes that are not zero are errors) {
CAF_CHECK(error(1, atom("system")));
CAF_CHECK(make_error(sec::unexpected_message));
CAF_CHECK(error{error_code<sec>(sec::unexpected_message)});
}
CAF_TEST_FIXTURE_SCOPE_END()
CAF_TEST(errors convert enums to their integer value) {
CAF_CHECK_EQUAL(make_error(sec::unexpected_message).code(), 1u);
CAF_CHECK_EQUAL(error{error_code<sec>(sec::unexpected_message)}.code(), 1u);
}
......@@ -16,27 +16,26 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include <set>
#include <map>
#define CAF_SUITE inspector
#include "caf/test/unit_test.hpp"
#include <list>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include "caf/config.hpp"
#define CAF_SUITE inspector
#include "caf/test/unit_test.hpp"
#include <unordered_set>
#include <vector>
#include "caf/actor_system.hpp"
#include "caf/type_erased_value.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/make_type_erased_value.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/detail/stringification_inspector.hpp"
#include "caf/make_type_erased_value.hpp"
#include "caf/type_erased_value.hpp"
using namespace caf;
......@@ -239,8 +238,8 @@ struct binary_serialization_policy {
execution_unit& context;
template <class T>
std::vector<char> to_buf(const T& x) {
std::vector<char> result;
auto to_buf(const T& x) {
byte_buffer result;
binary_serializer sink{&context, result};
if (auto err = sink(x))
CAF_FAIL("failed to serialize " << x << ": " << err);
......
......@@ -29,10 +29,11 @@
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/detail/parse.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/serializer_impl.hpp"
#include "caf/span.hpp"
using namespace caf;
......@@ -52,9 +53,8 @@ struct fixture {
template <class T>
T roundtrip(T x) {
using container_type = std::vector<byte>;
container_type buf;
serializer_impl<container_type> sink(sys, buf);
byte_buffer buf;
binary_serializer sink(sys, buf);
if (auto err = sink(x))
CAF_FAIL("serialization failed: " << sys.render(err));
binary_deserializer source(sys, make_span(buf));
......
......@@ -28,11 +28,11 @@
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/ipv4_endpoint.hpp"
#include "caf/ipv6_address.hpp"
#include "caf/serializer_impl.hpp"
#include "caf/span.hpp"
using namespace caf;
......@@ -52,9 +52,8 @@ struct fixture {
template <class T>
T roundtrip(T x) {
using container_type = std::vector<byte>;
container_type buf;
serializer_impl<container_type> sink(sys, buf);
byte_buffer buf;
binary_serializer sink(sys, buf);
if (auto err = sink(x))
CAF_FAIL("serialization failed: " << sys.render(err));
binary_deserializer source(sys, make_span(buf));
......
......@@ -51,140 +51,11 @@ CAF_TEST(apply) {
m.apply(f2);
}
CAF_TEST(drop) {
auto m1 = make_message(1, 2, 3, 4, 5);
std::vector<message> messages{
m1,
make_message(2, 3, 4, 5),
make_message(3, 4, 5),
make_message(4, 5),
make_message(5),
message{}
};
for (size_t i = 0; i < messages.size(); ++i) {
CAF_CHECK_EQUAL(to_string(m1.drop(i)), to_string(messages[i]));
}
}
CAF_TEST(slice) {
auto m1 = make_message(1, 2, 3, 4, 5);
auto m2 = m1.slice(2, 2);
CAF_CHECK_EQUAL(to_string(m2), to_string(make_message(3, 4)));
}
CAF_TEST(extract1) {
auto m1 = make_message(1.0, 2.0, 3.0);
auto m2 = make_message(1, 2, 1.0, 2.0, 3.0);
auto m3 = make_message(1.0, 1, 2, 2.0, 3.0);
auto m4 = make_message(1.0, 2.0, 1, 2, 3.0);
auto m5 = make_message(1.0, 2.0, 3.0, 1, 2);
auto m6 = make_message(1, 2, 1.0, 2.0, 3.0, 1, 2);
auto m7 = make_message(1.0, 1, 2, 3, 4, 2.0, 3.0);
message_handler f{
[](int, int) { },
[](float, float) { }
};
auto m1s = to_string(m1);
CAF_CHECK_EQUAL(to_string(m2.extract(f)), m1s);
CAF_CHECK_EQUAL(to_string(m3.extract(f)), m1s);
CAF_CHECK_EQUAL(to_string(m4.extract(f)), m1s);
CAF_CHECK_EQUAL(to_string(m5.extract(f)), m1s);
CAF_CHECK_EQUAL(to_string(m6.extract(f)), m1s);
CAF_CHECK_EQUAL(to_string(m7.extract(f)), m1s);
}
CAF_TEST(extract2) {
auto m1 = make_message(1);
CAF_CHECK(m1.extract([](int) {}).empty());
auto m2 = make_message(1.0, 2, 3, 4.0);
auto m3 = m2.extract({
[](int, int) { },
[](double, double) { }
});
// check for false positives through collapsing
CAF_CHECK_EQUAL(to_string(m3), to_string(make_message(1.0, 4.0)));
}
CAF_TEST(extract_opts) {
auto f = [](std::vector<std::string> xs, std::vector<std::string> remainder) {
std::string filename;
size_t log_level = 0;
auto res = message_builder(xs.begin(), xs.end()).extract_opts({
{"version,v", "print version"},
{"log-level,l", "set the log level", log_level},
{"file,f", "set output file", filename},
{"whatever", "do whatever"}
});
CAF_CHECK_EQUAL(res.opts.count("file"), 1u);
CAF_CHECK(res.remainder.size() == remainder.size());
for (size_t i = 0; i < res.remainder.size() && i < remainder.size(); ++i) {
CAF_CHECK(remainder[i] == res.remainder.get_as<string>(i));
}
CAF_CHECK_EQUAL(filename, "hello.txt");
CAF_CHECK_EQUAL(log_level, 5u);
};
f({"--file=hello.txt", "-l", "5"}, {});
f({"-f", "hello.txt", "--log-level=5"}, {});
f({"-f", "hello.txt", "-l", "5"}, {});
f({"-f", "hello.txt", "-l5"}, {});
f({"-fhello.txt", "-l", "5"}, {});
f({"-l5", "-fhello.txt"}, {});
// on remainder
f({"--file=hello.txt", "-l", "5", "--", "a"}, {"a"});
f({"--file=hello.txt", "-l", "5", "--", "a", "b"}, {"a", "b"});
f({"--file=hello.txt", "-l", "5", "--", "aa", "bb"}, {"aa", "bb"});
f({"--file=hello.txt", "-l", "5", "--", "-a", "--bb"}, {"-a", "--bb"});
f({"--file=hello.txt", "-l", "5", "--", "-a1", "--bb=10"},
{"-a1", "--bb=10"});
f({"--file=hello.txt", "-l", "5", "--", "-a 1", "--b=10"},
{"-a 1", "--b=10"});
// multiple remainders
f({"--file=hello.txt", "-l", "5", "--", "a", "--"}, {"a", "--"});
f({"--file=hello.txt", "-l", "5", "--", "a", "--", "--"}, {"a", "--", "--"});
f({"--file=hello.txt", "-l", "5", "--", "a", "--", "b"}, {"a", "--", "b"});
f({"--file=hello.txt", "-l", "5", "--", "aa", "--", "bb"},
{"aa", "--", "bb"});
f({"--file=hello.txt", "-l", "5", "--", "aa", "--", "--", "bb"},
{"aa", "--", "--", "bb"});
f({"--file=hello.txt", "-l", "5", "--", "--", "--", "-a1", "--bb=10"},
{"--", "--", "-a1", "--bb=10"});
CAF_MESSAGE("ensure that failed parsing doesn't consume input");
auto msg = make_message("-f", "42", "-b", "1337");
auto foo = 0;
auto bar = 0;
auto r = msg.extract_opts({
{"foo,f", "foo desc", foo}
});
CAF_CHECK(r.opts.count("foo") > 0);
CAF_CHECK_EQUAL(foo, 42);
CAF_CHECK_EQUAL(bar, 0);
CAF_CHECK(!r.error.empty()); // -b is an unknown option
CAF_CHECK(!r.remainder.empty()
&& to_string(r.remainder) == to_string(make_message("-b", "1337")));
r = r.remainder.extract_opts({
{"bar,b", "bar desc", bar}
});
CAF_CHECK(r.opts.count("bar") > 0);
CAF_CHECK_EQUAL(bar, 1337);
CAF_CHECK(r.error.empty());
}
CAF_TEST(type_token) {
auto m1 = make_message(get_atom::value);
CAF_CHECK_EQUAL(m1.type_token(), make_type_token<get_atom>());
}
CAF_TEST(concat) {
auto m1 = make_message(get_atom::value);
auto m2 = make_message(uint32_t{1});
auto m3 = message::concat(m1, m2);
CAF_CHECK_EQUAL(to_string(m3), to_string(m1 + m2));
CAF_CHECK_EQUAL(to_string(m3), "('get', 1)");
auto m4 = make_message(get_atom::value, uint32_t{1},
get_atom::value, uint32_t{1});
CAF_CHECK_EQUAL(to_string(message::concat(m3, message{}, m1, m2)), to_string(m4));
}
namespace {
struct s1 {
......
......@@ -16,10 +16,9 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE serialization
#include "caf/test/unit_test.hpp"
#include "caf/test/dsl.hpp"
#include <algorithm>
#include <cassert>
......@@ -47,7 +46,13 @@
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/get_mac_addresses.hpp"
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/safe_equal.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/make_type_erased_tuple_view.hpp"
#include "caf/make_type_erased_view.hpp"
......@@ -57,17 +62,8 @@
#include "caf/proxy_registry.hpp"
#include "caf/ref_counted.hpp"
#include "caf/serializer.hpp"
#include "caf/stream_deserializer.hpp"
#include "caf/stream_serializer.hpp"
#include "caf/streambuf.hpp"
#include "caf/variant.hpp"
#include "caf/detail/get_mac_addresses.hpp"
#include "caf/detail/ieee_754.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/safe_equal.hpp"
#include "caf/detail/type_traits.hpp"
using namespace std;
using namespace caf;
using caf::detail::type_erased_value_impl;
......@@ -143,8 +139,7 @@ public:
}
};
template <class Serializer, class Deserializer>
struct fixture {
struct fixture : test_coordinator_fixture<config> {
int32_t i32 = -345;
int64_t i64 = -1234567890123456789ll;
float f32 = 3.45f;
......@@ -154,36 +149,31 @@ struct fixture {
test_enum te = test_enum::b;
string str = "Lorem ipsum dolor sit amet.";
raw_struct rs;
test_array ta {
{0, 1, 2, 3},
{
test_array ta{
{0, 1, 2, 3},
{4, 5, 6, 7}
},
{{0, 1, 2, 3}, {4, 5, 6, 7}},
};
int ra[3] = {1, 2, 3};
config cfg;
actor_system system;
message msg;
message recursive;
template <class T, class... Ts>
vector<char> serialize(T& x, Ts&... xs) {
vector<char> buf;
binary_serializer sink{system, buf};
if (auto err = sink(x, xs...))
template <class... Ts>
byte_buffer serialize(const Ts&... xs) {
byte_buffer buf;
binary_serializer sink{sys, buf};
if (auto err = sink(xs...))
CAF_FAIL("serialization failed: "
<< system.render(err) << ", data: "
<< deep_to_string(std::forward_as_tuple(x, xs...)));
<< sys.render(err)
<< ", data: " << deep_to_string(std::forward_as_tuple(xs...)));
return buf;
}
template <class T, class... Ts>
void deserialize(const vector<char>& buf, T& x, Ts&... xs) {
binary_deserializer source{system, buf};
if (auto err = source(x, xs...))
CAF_FAIL("deserialization failed: " << system.render(err));
template <class... Ts>
void deserialize(const byte_buffer& buf, Ts&... xs) {
binary_deserializer source{sys, buf};
if (auto err = source(xs...))
CAF_FAIL("deserialization failed: " << sys.render(err));
}
// serializes `x` and then deserializes and returns the serialized value
......@@ -206,7 +196,7 @@ struct fixture {
return result.get_as<T>(0);
}
fixture() : system(cfg) {
fixture() {
rs.str.assign(string(str.rbegin(), str.rend()));
msg = make_message(i32, i64, dur, ts, te, str, rs);
config_value::dictionary dict;
......@@ -230,11 +220,8 @@ struct is_message {
bool ok = false;
// work around for gcc 4.8.4 bug
auto tup = tie(v, vs...);
message_handler impl {
[&](T const& u, Ts const&... us) {
ok = tup == tie(u, us...);
}
};
message_handler impl{
[&](T const& u, Ts const&... us) { ok = tup == tie(u, us...); }};
impl(msg);
return ok;
}
......@@ -242,43 +229,13 @@ struct is_message {
} // namespace
#define SERIALIZATION_TEST(name) \
namespace { \
template <class Serializer, class Deserializer> \
struct name##_tpl : fixture<Serializer, Deserializer> { \
using super = fixture<Serializer, Deserializer>; \
using super::i32; \
using super::i64; \
using super::f32; \
using super::f64; \
using super::dur; \
using super::ts; \
using super::te; \
using super::str; \
using super::rs; \
using super::ta; \
using super::ra; \
using super::system; \
using super::msg; \
using super::recursive; \
using super::serialize; \
using super::deserialize; \
using super::roundtrip; \
using super::msg_roundtrip; \
void run_test_impl(); \
}; \
using name##_binary = name##_tpl<binary_serializer, binary_deserializer>; \
using name##_stream = name##_tpl<stream_serializer<vectorbuf>, \
stream_deserializer<charbuf>>; \
::caf::test::detail::adder<::caf::test::test_impl<name##_binary>> \
CAF_UNIQUE(a_binary){CAF_XSTR(CAF_SUITE), CAF_XSTR(name##_binary), false}; \
::caf::test::detail::adder<::caf::test::test_impl<name##_stream>> \
CAF_UNIQUE(a_stream){CAF_XSTR(CAF_SUITE), CAF_XSTR(name##_stream), false}; \
} \
template <class Serializer, class Deserializer> \
void name##_tpl<Serializer, Deserializer>::run_test_impl()
SERIALIZATION_TEST(ieee_754_conversion) {
#define CHECK_RT(val) CAF_CHECK_EQUAL(val, roundtrip(val))
#define CHECK_MSG_RT(val) CAF_CHECK_EQUAL(val, msg_roundtrip(val))
CAF_TEST_FIXTURE_SCOPE(serialization_tests, fixture)
CAF_TEST(ieee_754_conversion) {
// check conversion of float
float f1 = 3.1415925f; // float value
auto p1 = caf::detail::pack754(f1); // packet value
......@@ -293,79 +250,33 @@ SERIALIZATION_TEST(ieee_754_conversion) {
CAF_CHECK_EQUAL(f2, u2);
}
SERIALIZATION_TEST(i32_values) {
auto buf = serialize(i32);
int32_t x;
deserialize(buf, x);
CAF_CHECK_EQUAL(i32, x);
}
SERIALIZATION_TEST(i64_values) {
auto buf = serialize(i64);
int64_t x;
deserialize(buf, x);
CAF_CHECK_EQUAL(i64, x);
}
SERIALIZATION_TEST(float_values) {
auto buf = serialize(f32);
float x;
deserialize(buf, x);
CAF_CHECK_EQUAL(f32, x);
}
SERIALIZATION_TEST(double_values) {
auto buf = serialize(f64);
double x;
deserialize(buf, x);
CAF_CHECK_EQUAL(f64, x);
}
SERIALIZATION_TEST(duration_values) {
auto buf = serialize(dur);
duration x;
deserialize(buf, x);
CAF_CHECK_EQUAL(dur, x);
}
SERIALIZATION_TEST(timestamp_values) {
auto buf = serialize(ts);
timestamp x;
deserialize(buf, x);
CAF_CHECK_EQUAL(ts, x);
}
SERIALIZATION_TEST(enum_classes) {
auto buf = serialize(te);
test_enum x;
deserialize(buf, x);
CAF_CHECK_EQUAL(te, x);
}
SERIALIZATION_TEST(strings) {
auto buf = serialize(str);
string x;
deserialize(buf, x);
CAF_CHECK_EQUAL(str, x);
}
SERIALIZATION_TEST(custom_struct) {
auto buf = serialize(rs);
raw_struct x;
deserialize(buf, x);
CAF_CHECK_EQUAL(rs, x);
}
SERIALIZATION_TEST(atoms) {
auto foo = atom("foo");
CAF_CHECK_EQUAL(foo, roundtrip(foo));
CAF_CHECK_EQUAL(foo, msg_roundtrip(foo));
using bar_atom = atom_constant<atom("bar")>;
CAF_CHECK_EQUAL(bar_atom::value, roundtrip(atom("bar")));
CAF_CHECK_EQUAL(bar_atom::value, msg_roundtrip(atom("bar")));
}
SERIALIZATION_TEST(raw_arrays) {
CAF_TEST(serializing and then deserializing produces the same value) {
CHECK_RT(i32);
CHECK_RT(i64);
CHECK_RT(f32);
CHECK_RT(f64);
CHECK_RT(dur);
CHECK_RT(ts);
CHECK_RT(te);
CHECK_RT(str);
CHECK_RT(rs);
CHECK_RT(atom("foo"));
}
CAF_TEST(messages serialize and deserialize their content) {
CHECK_MSG_RT(i32);
CHECK_MSG_RT(i64);
CHECK_MSG_RT(f32);
CHECK_MSG_RT(f64);
CHECK_MSG_RT(dur);
CHECK_MSG_RT(ts);
CHECK_MSG_RT(te);
CHECK_MSG_RT(str);
CHECK_MSG_RT(rs);
CHECK_MSG_RT(atom("foo"));
}
CAF_TEST(raw_arrays) {
auto buf = serialize(ra);
int x[3];
deserialize(buf, x);
......@@ -373,7 +284,7 @@ SERIALIZATION_TEST(raw_arrays) {
CAF_CHECK_EQUAL(ra[i], x[i]);
}
SERIALIZATION_TEST(arrays) {
CAF_TEST(arrays) {
auto buf = serialize(ta);
test_array x;
deserialize(buf, x);
......@@ -384,7 +295,7 @@ SERIALIZATION_TEST(arrays) {
CAF_CHECK_EQUAL(ta.value2[i][j], x.value2[i][j]);
}
SERIALIZATION_TEST(empty_non_pods) {
CAF_TEST(empty_non_pods) {
test_empty_non_pod x;
auto buf = serialize(x);
CAF_REQUIRE(buf.empty());
......@@ -403,7 +314,7 @@ std::string hexstr(const std::vector<char>& buf) {
return oss.str();
}
SERIALIZATION_TEST(messages) {
CAF_TEST(messages) {
// serialize original message which uses tuple_vals internally and
// deserialize into a message which uses type_erased_value pointers
message x;
......@@ -421,7 +332,7 @@ SERIALIZATION_TEST(messages) {
CAF_CHECK_EQUAL(to_string(recursive), to_string(roundtrip(recursive)));
}
SERIALIZATION_TEST(multiple_messages) {
CAF_TEST(multiple_messages) {
auto m = make_message(rs, te);
auto buf = serialize(te, m, msg);
test_enum t;
......@@ -434,16 +345,15 @@ SERIALIZATION_TEST(multiple_messages) {
CAF_CHECK(is_message(m2).equal(i32, i64, dur, ts, te, str, rs));
}
SERIALIZATION_TEST(type_erased_value) {
CAF_TEST(type_erased_value) {
auto buf = serialize(str);
type_erased_value_ptr ptr{new type_erased_value_impl<std::string>};
binary_deserializer source{system, buf};
binary_deserializer source{sys, buf};
ptr->load(source);
CAF_CHECK_EQUAL(str, *reinterpret_cast<const std::string*>(ptr->get()));
}
SERIALIZATION_TEST(type_erased_view) {
CAF_TEST(type_erased_view) {
auto str_view = make_type_erased_view(str);
auto buf = serialize(str_view);
std::string res;
......@@ -451,7 +361,7 @@ SERIALIZATION_TEST(type_erased_view) {
CAF_CHECK_EQUAL(str, res);
}
SERIALIZATION_TEST(type_erased_tuple) {
CAF_TEST(type_erased_tuple) {
auto tview = make_type_erased_tuple_view(str, i32);
CAF_CHECK_EQUAL(to_string(tview), deep_to_string(std::make_tuple(str, i32)));
auto buf = serialize(tview);
......@@ -465,55 +375,8 @@ SERIALIZATION_TEST(type_erased_tuple) {
CAF_CHECK_EQUAL(to_string(tview), deep_to_string(std::make_tuple(str, i32)));
}
SERIALIZATION_TEST(streambuf_serialization) {
auto data = std::string{"The quick brown fox jumps over the lazy dog"};
std::vector<char> buf;
// First, we check the standard use case in CAF where stream serializers own
// their stream buffers.
stream_serializer<vectorbuf> bs{vectorbuf{buf}};
auto e = bs(data);
CAF_REQUIRE_EQUAL(e, none);
stream_deserializer<charbuf> bd{charbuf{buf}};
std::string target;
e = bd(target);
CAF_REQUIRE_EQUAL(e, none);
CAF_CHECK_EQUAL(data, target);
// Second, we test another use case where the serializers only keep
// references of the stream buffers.
buf.clear();
target.clear();
vectorbuf vb{buf};
stream_serializer<vectorbuf&> vs{vb};
e = vs(data);
CAF_REQUIRE_EQUAL(e, none);
charbuf cb{buf};
stream_deserializer<charbuf&> vd{cb};
e = vd(target);
CAF_REQUIRE_EQUAL(e, none);
CAF_CHECK(data == target);
}
SERIALIZATION_TEST(byte_sequence_optimization) {
std::vector<uint8_t> data(42);
std::fill(data.begin(), data.end(), 0x2a);
std::vector<uint8_t> buf;
using streambuf_type = containerbuf<std::vector<uint8_t>>;
streambuf_type cb{buf};
stream_serializer<streambuf_type&> bs{cb};
auto e = bs(data);
CAF_REQUIRE(!e);
data.clear();
streambuf_type cb2{buf};
stream_deserializer<streambuf_type&> bd{cb2};
e = bd(data);
CAF_REQUIRE(!e);
CAF_CHECK_EQUAL(data.size(), 42u);
CAF_CHECK(std::all_of(data.begin(), data.end(),
[](uint8_t c) { return c == 0x2a; }));
}
SERIALIZATION_TEST(long_sequences) {
std::vector<char> data;
CAF_TEST(long_sequences) {
byte_buffer data;
binary_serializer sink{nullptr, data};
size_t n = std::numeric_limits<uint32_t>::max();
sink.begin_sequence(n);
......@@ -525,7 +388,7 @@ SERIALIZATION_TEST(long_sequences) {
CAF_CHECK_EQUAL(n, m);
}
SERIALIZATION_TEST(non_empty_vector) {
CAF_TEST(non_empty_vector) {
CAF_MESSAGE("deserializing into a non-empty vector overrides any content");
std::vector<int> foo{1, 2, 3};
std::vector<int> bar{0};
......@@ -534,7 +397,7 @@ SERIALIZATION_TEST(non_empty_vector) {
CAF_CHECK_EQUAL(foo, bar);
}
SERIALIZATION_TEST(variant_with_tree_types) {
CAF_TEST(variant_with_tree_types) {
CAF_MESSAGE("deserializing into a non-empty vector overrides any content");
using test_variant = variant<int, double, std::string>;
test_variant x{42};
......@@ -548,21 +411,28 @@ SERIALIZATION_TEST(variant_with_tree_types) {
// -- our vector<bool> serialization packs into an uint64_t. Hence, the
// critical sizes to test are 0, 1, 63, 64, and 65.
SERIALIZATION_TEST(bool_vector_size_0) {
CAF_TEST(bool_vector_size_0) {
std::vector<bool> xs;
CAF_CHECK_EQUAL(deep_to_string(xs), "[]");
CAF_CHECK_EQUAL(xs, roundtrip(xs));
CAF_CHECK_EQUAL(xs, msg_roundtrip(xs));
}
SERIALIZATION_TEST(bool_vector_size_1) {
CAF_TEST(bool_vector_size_1) {
std::vector<bool> xs{true};
CAF_CHECK_EQUAL(deep_to_string(xs), "[true]");
CAF_CHECK_EQUAL(xs, roundtrip(xs));
CAF_CHECK_EQUAL(xs, msg_roundtrip(xs));
}
SERIALIZATION_TEST(bool_vector_size_63) {
CAF_TEST(bool_vector_size_2) {
std::vector<bool> xs{true, true};
CAF_CHECK_EQUAL(deep_to_string(xs), "[true, true]");
CAF_CHECK_EQUAL(xs, roundtrip(xs));
CAF_CHECK_EQUAL(xs, msg_roundtrip(xs));
}
CAF_TEST(bool_vector_size_63) {
std::vector<bool> xs;
for (int i = 0; i < 63; ++i)
xs.push_back(i % 3 == 0);
......@@ -578,7 +448,7 @@ SERIALIZATION_TEST(bool_vector_size_63) {
CAF_CHECK_EQUAL(xs, msg_roundtrip(xs));
}
SERIALIZATION_TEST(bool_vector_size_64) {
CAF_TEST(bool_vector_size_64) {
std::vector<bool> xs;
for (int i = 0; i < 64; ++i)
xs.push_back(i % 5 == 0);
......@@ -595,7 +465,7 @@ SERIALIZATION_TEST(bool_vector_size_64) {
CAF_CHECK_EQUAL(xs, msg_roundtrip(xs));
}
SERIALIZATION_TEST(bool_vector_size_65) {
CAF_TEST(bool_vector_size_65) {
std::vector<bool> xs;
for (int i = 0; i < 65; ++i)
xs.push_back(!(i % 7 == 0));
......@@ -610,3 +480,5 @@ SERIALIZATION_TEST(bool_vector_size_65) {
CAF_CHECK_EQUAL(xs, roundtrip(xs));
CAF_CHECK_EQUAL(xs, msg_roundtrip(xs));
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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. *
******************************************************************************/
#define CAF_SUITE serializer_impl
#include "caf/serializer_impl.hpp"
#include "caf/test/dsl.hpp"
#include <cstring>
#include <vector>
#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte.hpp"
#include "caf/duration.hpp"
#include "caf/timestamp.hpp"
using namespace caf;
namespace {
enum class test_enum {
a,
b,
c,
};
struct test_data {
test_data(int32_t i32, int64_t i64, float f32, double f64, caf::duration dur,
caf::timestamp ts, test_enum te, const std::string& str)
: i32_(i32),
i64_(i64),
f32_(f32),
f64_(f64),
dur_(dur),
ts_(ts),
te_(te),
str_(str) {
// nop
}
test_data()
: test_data(-345, -1234567890123456789ll, 3.45, 54.3,
caf::duration(caf::time_unit::seconds, 123),
caf::timestamp{
caf::timestamp::duration{1478715821 * 1000000000ll}},
test_enum::b, "Lorem ipsum dolor sit amet.") {
// nop
}
int32_t i32_;
int64_t i64_;
float f32_;
double f64_;
caf::duration dur_;
caf::timestamp ts_;
test_enum te_;
std::string str_;
friend bool operator==(const test_data& data, const test_data& other) {
return (data.f64_ == other.f64_ && data.i32_ == other.i32_
&& data.i64_ == other.i64_ && data.str_ == other.str_
&& data.te_ == other.te_ && data.ts_ == other.ts_);
}
};
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, test_data& x) {
return f(caf::meta::type_name("test_data"), x.i32_, x.i64_, x.f32_, x.f64_,
x.dur_, x.ts_, x.te_, x.str_);
}
struct serialization_fixture {
caf::actor_system_config cfg;
caf::actor_system sys{cfg};
test_data data_to_serialize;
test_data deserialized_data{0,
0,
0,
0,
caf::duration(caf::time_unit::seconds, 0),
caf::timestamp{caf::timestamp::duration{0}},
test_enum::a,
""};
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(serializer_impl_tests, serialization_fixture)
CAF_TEST(serialize and deserialize with std::vector<char>) {
using container_type = std::vector<char>;
std::vector<char> binary_serializer_buffer;
container_type serializer_impl_buffer;
binary_serializer sink1{sys, binary_serializer_buffer};
serializer_impl<container_type> sink2{sys, serializer_impl_buffer};
if (auto err = sink1(data_to_serialize))
CAF_FAIL("serialization failed: " << sys.render(err));
if (auto err = sink2(data_to_serialize))
CAF_FAIL("serialization failed: " << sys.render(err));
CAF_CHECK_EQUAL(memcmp(binary_serializer_buffer.data(),
serializer_impl_buffer.data(),
binary_serializer_buffer.size()),
0);
binary_deserializer source(sys, serializer_impl_buffer);
if (auto err = source(deserialized_data))
CAF_FAIL("deserialization failed: " << sys.render(err));
CAF_CHECK_EQUAL(data_to_serialize, deserialized_data);
}
CAF_TEST(serialize and deserialize with std::vector<byte>) {
using container_type = std::vector<byte>;
std::vector<char> binary_serializer_buffer;
container_type serializer_impl_buffer;
binary_serializer sink1{sys, binary_serializer_buffer};
serializer_impl<container_type> sink2{sys, serializer_impl_buffer};
if (auto err = sink1(data_to_serialize))
CAF_FAIL("serialization failed: " << sys.render(err));
if (auto err = sink2(data_to_serialize))
CAF_FAIL("serialization failed: " << sys.render(err));
CAF_CHECK_EQUAL(memcmp(binary_serializer_buffer.data(),
serializer_impl_buffer.data(),
binary_serializer_buffer.size()),
0);
binary_deserializer source(sys, serializer_impl_buffer);
if (auto err = source(deserialized_data))
CAF_FAIL("deserialization failed: " << sys.render(err));
CAF_CHECK_EQUAL(data_to_serialize, deserialized_data);
}
CAF_TEST(serialize and deserialize with std::vector<uint8_t>) {
using container_type = std::vector<uint8_t>;
std::vector<char> binary_serializer_buffer;
container_type serializer_impl_buffer;
binary_serializer sink1{sys, binary_serializer_buffer};
serializer_impl<container_type> sink2{sys, serializer_impl_buffer};
if (auto err = sink1(data_to_serialize))
CAF_FAIL("serialization failed: " << sys.render(err));
if (auto err = sink2(data_to_serialize))
CAF_FAIL("serialization failed: " << sys.render(err));
CAF_CHECK_EQUAL(memcmp(binary_serializer_buffer.data(),
serializer_impl_buffer.data(),
binary_serializer_buffer.size()),
0);
binary_deserializer source(sys, serializer_impl_buffer);
if (auto err = source(deserialized_data))
CAF_FAIL("deserialization failed: " << sys.render(err));
CAF_CHECK_EQUAL(data_to_serialize, deserialized_data);
}
CAF_TEST_FIXTURE_SCOPE_END()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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/config.hpp"
#define CAF_SUITE streambuf
#include "caf/test/unit_test.hpp"
#include "caf/streambuf.hpp"
using namespace caf;
CAF_TEST(signed_arraybuf) {
auto data = std::string{"The quick brown fox jumps over the lazy dog"};
arraybuf<char> ab{data};
// Let's read some.
CAF_CHECK_EQUAL(static_cast<size_t>(ab.in_avail()), data.size());
CAF_CHECK_EQUAL(ab.sgetc(), 'T');
std::string buf;
buf.resize(3);
auto got = ab.sgetn(&buf[0], 3);
CAF_CHECK_EQUAL(got, 3);
CAF_CHECK_EQUAL(buf, "The");
CAF_CHECK_EQUAL(ab.sgetc(), ' ');
// Exhaust the stream.
buf.resize(data.size());
got = ab.sgetn(&buf[0] + 3, static_cast<std::streamsize>(data.size() - 3));
CAF_CHECK_EQUAL(static_cast<size_t>(got), data.size() - 3);
CAF_CHECK_EQUAL(data, buf);
CAF_CHECK_EQUAL(ab.in_avail(), 0);
// No more.
auto c = ab.sgetc();
CAF_CHECK_EQUAL(c, charbuf::traits_type::eof());
// Reset the stream and write into it.
ab.pubsetbuf(&data[0], static_cast<std::streamsize>(data.size()));
CAF_CHECK_EQUAL(static_cast<size_t>(ab.in_avail()), data.size());
auto put = ab.sputn("One", 3);
CAF_CHECK_EQUAL(put, 3);
CAF_CHECK(data.compare(0, 3, "One") == 0);
}
CAF_TEST(unsigned_arraybuf) {
using buf_type = arraybuf<uint8_t>;
std::vector<uint8_t> data = {0x0a, 0x0b, 0x0c, 0x0d};
buf_type ab{data};
decltype(data) buf;
std::copy(std::istreambuf_iterator<uint8_t>{&ab},
std::istreambuf_iterator<uint8_t>{},
std::back_inserter(buf));
CAF_CHECK_EQUAL(data, buf);
// Relative positioning.
using pos_type = buf_type::pos_type;
using int_type = buf_type::int_type;
CAF_CHECK_EQUAL(ab.pubseekoff(2, std::ios::beg, std::ios::in), pos_type{2});
CAF_CHECK_EQUAL(ab.sbumpc(), int_type{0x0c});
CAF_CHECK_EQUAL(ab.sgetc(), int_type{0x0d});
CAF_CHECK_EQUAL(ab.pubseekoff(0, std::ios::cur, std::ios::in), pos_type{3});
CAF_CHECK_EQUAL(ab.pubseekoff(-2, std::ios::cur, std::ios::in), pos_type{1});
CAF_CHECK_EQUAL(ab.sgetc(), int_type{0x0b});
CAF_CHECK_EQUAL(ab.pubseekoff(-4, std::ios::end, std::ios::in), pos_type{0});
CAF_CHECK_EQUAL(ab.sgetc(), int_type{0x0a});
// Absolute positioning.
CAF_CHECK_EQUAL(ab.pubseekpos(1, std::ios::in), pos_type{1});
CAF_CHECK_EQUAL(ab.sgetc(), int_type{0x0b});
CAF_CHECK_EQUAL(ab.pubseekpos(3, std::ios::in), pos_type{3});
CAF_CHECK_EQUAL(ab.sbumpc(), int_type{0x0d});
CAF_CHECK_EQUAL(ab.in_avail(), std::streamsize{0});
}
CAF_TEST(containerbuf) {
std::string data{
"Habe nun, ach! Philosophie,\n"
"Juristerei und Medizin,\n"
"Und leider auch Theologie\n"
"Durchaus studiert, mit heißem Bemühn.\n"
"Da steh ich nun, ich armer Tor!\n"
"Und bin so klug als wie zuvor"
};
// Write some data.
std::vector<char> buf;
vectorbuf vb{buf};
auto put = vb.sputn(data.data(), static_cast<std::streamsize>(data.size()));
CAF_CHECK_EQUAL(static_cast<size_t>(put), data.size());
put = vb.sputn(";", 1);
CAF_CHECK_EQUAL(put, 1);
std::string target;
std::copy(buf.begin(), buf.end(), std::back_inserter(target));
CAF_CHECK_EQUAL(data + ';', target);
// Check "overflow" on a new stream.
buf.clear();
vectorbuf vb2{buf};
auto chr = vb2.sputc('x');
CAF_CHECK_EQUAL(chr, 'x');
// Let's read some data into a stream.
buf.clear();
containerbuf<std::string> scb{data};
std::copy(std::istreambuf_iterator<char>{&scb},
std::istreambuf_iterator<char>{},
std::back_inserter(buf));
CAF_CHECK_EQUAL(buf.size(), data.size());
CAF_CHECK(std::equal(buf.begin(), buf.end(), data.begin() /*, data.end() */));
// We're done, nothing to see here, please move along.
CAF_CHECK_EQUAL(scb.sgetc(), containerbuf<std::string>::traits_type::eof());
// Let's read again, but now in one big block.
buf.clear();
containerbuf<std::string> sib2{data};
buf.resize(data.size());
auto got = sib2.sgetn(&buf[0], static_cast<std::streamsize>(buf.size()));
CAF_CHECK_EQUAL(static_cast<size_t>(got), data.size());
CAF_CHECK_EQUAL(buf.size(), data.size());
CAF_CHECK(std::equal(buf.begin(), buf.end(), data.begin() /*, data.end() */));
}
CAF_TEST(containerbuf_reset_get_area) {
std::string str{"foobar"};
std::vector<char> buf;
vectorbuf vb{buf};
// We can always write to the underlying buffer; no put area needed.
auto n = vb.sputn(str.data(), static_cast<std::streamsize>(str.size()));
CAF_REQUIRE_EQUAL(n, 6);
// Readjust the get area.
CAF_REQUIRE_EQUAL(buf.size(), 6u);
vb.pubsetbuf(buf.data() + 3, static_cast<std::streamsize>(buf.size() - 3));
// Now read from a new get area into a buffer.
char bar[3];
n = vb.sgetn(bar, 3);
CAF_CHECK_EQUAL(n, 3);
CAF_CHECK_EQUAL(std::string(bar, 3), "bar");
// Synchronize the get area after having messed with the underlying buffer.
buf.resize(1);
CAF_CHECK_EQUAL(vb.pubsync(), 0);
CAF_CHECK_EQUAL(vb.sbumpc(), 'f');
CAF_CHECK_EQUAL(vb.in_avail(), 0);
}
......@@ -16,13 +16,14 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE uri
#include "caf/uri.hpp"
#include "caf/test/dsl.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/uri.hpp"
#include "caf/uri_builder.hpp"
using namespace caf;
......@@ -115,10 +116,6 @@ struct uri_str_builder {
};
struct fixture {
// -- member types -----------------------------------------------------------
using buffer = std::vector<char>;
// -- constructors, destructors, and assignment operators --------------------
fixture() {
......@@ -133,21 +130,19 @@ struct fixture {
// -- utility functions ------------------------------------------------------
buffer serialize(uri x) {
buffer buf;
binary_serializer dst{nullptr, buf};
auto err = inspect(dst, x);
if (err)
byte_buffer serialize(uri x) {
byte_buffer buf;
binary_serializer sink{nullptr, buf};
if (auto err = sink(x))
CAF_FAIL("unable to serialize " << x << ": " << to_string(err));
return buf;
}
uri deserialize(buffer buf) {
uri deserialize(byte_buffer buf) {
uri result;
binary_deserializer src{nullptr, buf};
auto err = inspect(src, result);
if (err)
CAF_FAIL("unable to deserialize from buffer: " << to_string(err));
binary_deserializer source{nullptr, buf};
if (auto err = source(result))
CAF_FAIL("unable to deserialize from byte_buffer: " << to_string(err));
return result;
}
};
......
......@@ -23,13 +23,14 @@
#include <string>
#include "caf/none.hpp"
#include "caf/variant.hpp"
#include "caf/actor_system.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/none.hpp"
#include "caf/variant.hpp"
using namespace std;
using namespace caf;
......@@ -99,13 +100,15 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10,
CAF_CHECK_EQUAL(x4, i##n{0}); \
CAF_CHECK_EQUAL(x3, i##n{0x##n}); \
{ \
std::vector<char> buf; \
binary_serializer bs{sys.dummy_execution_unit(), buf}; \
inspect(bs, x3); \
byte_buffer buf; \
binary_serializer sink{sys.dummy_execution_unit(), buf}; \
if (auto err = sink(x3)) \
CAF_FAIL("failed to serialize data: " << sys.render(err)); \
CAF_CHECK_EQUAL(x3, i##n{0x##n}); \
v20 tmp; \
binary_deserializer bd{sys.dummy_execution_unit(), buf}; \
inspect(bd, tmp); \
binary_deserializer source{sys.dummy_execution_unit(), buf}; \
if (auto err = source(tmp)) \
CAF_FAIL("failed to deserialize data: " << sys.render(err)); \
CAF_CHECK_EQUAL(tmp, i##n{0x##n}); \
CAF_CHECK_EQUAL(tmp, x3); \
}
......
......@@ -21,21 +21,20 @@
#include <vector>
#include <unordered_map>
#include "caf/scheduled_actor.hpp"
#include "caf/prohibit_top_level_spawn_marker.hpp"
#include "caf/io/fwd.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/io/accept_handle.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/io/datagram_handle.hpp"
#include "caf/io/system_messages.hpp"
#include "caf/io/connection_handle.hpp"
#include "caf/io/datagram_handle.hpp"
#include "caf/io/fwd.hpp"
#include "caf/io/network/acceptor_manager.hpp"
#include "caf/io/network/datagram_manager.hpp"
#include "caf/io/network/ip_endpoint.hpp"
#include "caf/io/network/native_socket.hpp"
#include "caf/io/network/stream_manager.hpp"
#include "caf/io/network/acceptor_manager.hpp"
#include "caf/io/network/datagram_manager.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/io/system_messages.hpp"
#include "caf/prohibit_top_level_spawn_marker.hpp"
#include "caf/scheduled_actor.hpp"
namespace caf::io {
......@@ -145,7 +144,7 @@ public:
void ack_writes(connection_handle hdl, bool enable);
/// Returns the write buffer for a given connection.
std::vector<char>& wr_buf(connection_handle hdl);
byte_buffer& wr_buf(connection_handle hdl);
/// Writes `data` into the buffer for a given connection.
void write(connection_handle hdl, size_t bs, const void* buf);
......@@ -157,10 +156,10 @@ public:
void ack_writes(datagram_handle hdl, bool enable);
/// Returns the write buffer for a given sink.
std::vector<char>& wr_buf(datagram_handle hdl);
byte_buffer& wr_buf(datagram_handle hdl);
/// Enqueue a buffer to be sent as a datagram via a given endpoint.
void enqueue_datagram(datagram_handle, std::vector<char>);
void enqueue_datagram(datagram_handle, byte_buffer);
/// Writes `data` into the buffer of a given sink.
void write(datagram_handle hdl, size_t data_size, const void* data);
......@@ -422,8 +421,7 @@ private:
scribe_map scribes_;
doorman_map doormen_;
datagram_servant_map datagram_servants_;
std::vector<char> dummy_wr_buf_;
byte_buffer dummy_wr_buf_;
};
} // namespace caf
......@@ -18,7 +18,6 @@
#pragma once
#include "caf/io/basp/buffer_type.hpp"
#include "caf/io/basp/connection_state.hpp"
#include "caf/io/basp/endpoint_context.hpp"
#include "caf/io/basp/header.hpp"
......
......@@ -21,11 +21,10 @@
#include <limits>
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/callback.hpp"
#include "caf/detail/worker_hub.hpp"
#include "caf/error.hpp"
#include "caf/io/basp/buffer_type.hpp"
#include "caf/io/basp/connection_state.hpp"
#include "caf/io/basp/header.hpp"
#include "caf/io/basp/message_queue.hpp"
......@@ -45,10 +44,6 @@ public:
/// Provides a callback-based interface for certain BASP events.
class callee {
public:
// -- member types ---------------------------------------------------------
using buffer_type = std::vector<char>;
// -- constructors, destructors, and assignment operators ------------------
explicit callee(actor_system& sys, proxy_registry::backend& backend);
......@@ -60,7 +55,8 @@ public:
/// Called if a server handshake was received and
/// the connection to `nid` is established.
virtual void finalize_handshake(const node_id& nid, actor_id aid,
std::set<std::string>& sigs) = 0;
std::set<std::string>& sigs)
= 0;
/// Called whenever a direct connection was closed or a
/// node became unreachable for other reasons *before*
......@@ -75,8 +71,9 @@ public:
/// Called whenever BASP learns the ID of a remote node
/// to which it does not have a direct connection.
virtual void learned_new_node_directly(const node_id& nid,
bool was_known_indirectly) = 0;
virtual void
learned_new_node_directly(const node_id& nid, bool was_known_indirectly)
= 0;
/// Called whenever BASP learns the ID of a remote node
/// to which it does not have a direct connection.
......@@ -94,7 +91,7 @@ public:
}
/// Returns a reference to the sent buffer.
virtual buffer_type& get_buffer(connection_handle hdl) = 0;
virtual byte_buffer& get_buffer(connection_handle hdl) = 0;
/// Flushes the underlying write buffer of `hdl`.
virtual void flush(connection_handle hdl) = 0;
......@@ -108,15 +105,16 @@ public:
/// Describes a function object responsible for writing
/// the payload for a BASP message.
using payload_writer = callback<serializer&>;
using payload_writer = callback<error_code<sec>(binary_serializer&)>;
/// Describes a callback function object for `remove_published_actor`.
using removed_published_actor = callback<const strong_actor_ptr&, uint16_t>;
using removed_published_actor
= callback<error_code<sec>(const strong_actor_ptr&, uint16_t)>;
instance(abstract_broker* parent, callee& lstnr);
/// Handles received data and returns a config for receiving the
/// next data or `none` if an error occurred.
/// next data or `none` if an error occured.
connection_state handle(execution_unit* ctx,
new_data_msg& dm, header& hdr, bool is_payload);
......@@ -131,17 +129,16 @@ public:
/// Sends a BASP message and implicitly flushes the output buffer of `r`.
/// This function will update `hdr.payload_len` if a payload was written.
void write(execution_unit* ctx, const routing_table::route& r,
header& hdr, payload_writer* writer = nullptr);
void write(execution_unit* ctx, const routing_table::route& r, header& hdr,
payload_writer* writer = nullptr);
/// Adds a new actor to the map of published actors.
void add_published_actor(uint16_t port,
strong_actor_ptr published_actor,
void add_published_actor(uint16_t port, strong_actor_ptr published_actor,
std::set<std::string> published_interface);
/// Removes the actor currently assigned to `port`.
size_t remove_published_actor(uint16_t port,
removed_published_actor* cb = nullptr);
size_t
remove_published_actor(uint16_t port, removed_published_actor* cb = nullptr);
/// Removes `whom` if it is still assigned to `port` or from all of its
/// current ports if `port == 0`.
......@@ -178,30 +175,30 @@ public:
}
/// Writes a header followed by its payload to `storage`.
static void write(execution_unit* ctx, buffer_type& buf, header& hdr,
static void write(execution_unit* ctx, byte_buffer& buf, header& hdr,
payload_writer* pw = nullptr);
/// Writes the server handshake containing the information of the
/// actor published at `port` to `buf`. If `port == none` or
/// if no actor is published at this port then a standard handshake is
/// written (e.g. used when establishing direct connections on-the-fly).
void write_server_handshake(execution_unit* ctx,
buffer_type& out_buf, optional<uint16_t> port);
void write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
optional<uint16_t> port);
/// Writes the client handshake to `buf`.
void write_client_handshake(execution_unit* ctx, buffer_type& buf);
void write_client_handshake(execution_unit* ctx, byte_buffer& buf);
/// Writes an `announce_proxy` to `buf`.
void write_monitor_message(execution_unit* ctx, buffer_type& buf,
void write_monitor_message(execution_unit* ctx, byte_buffer& buf,
const node_id& dest_node, actor_id aid);
/// Writes a `kill_proxy` to `buf`.
void write_down_message(execution_unit* ctx, buffer_type& buf,
const node_id& dest_node, actor_id aid,
const error& rsn);
void
write_down_message(execution_unit* ctx, byte_buffer& buf,
const node_id& dest_node, actor_id aid, const error& rsn);
/// Writes a `heartbeat` to `buf`.
void write_heartbeat(execution_unit* ctx, buffer_type& buf);
void write_heartbeat(execution_unit* ctx, byte_buffer& buf);
const node_id& this_node() const {
return this_node_;
......@@ -224,11 +221,11 @@ public:
}
bool handle(execution_unit* ctx, connection_handle hdl, header& hdr,
std::vector<char>* payload);
byte_buffer* payload);
private:
void forward(execution_unit* ctx, const node_id& dest_node, const header& hdr,
std::vector<char>& payload);
byte_buffer& payload);
routing_table tbl_;
published_actor_map published_actors_;
......@@ -240,4 +237,4 @@ private:
/// @}
} // namespace caf
} // namespace caf::io::basp
......@@ -24,7 +24,6 @@
#include <vector>
#include "caf/io/abstract_broker.hpp"
#include "caf/io/basp/buffer_type.hpp"
#include "caf/node_id.hpp"
namespace caf::io::basp {
......
......@@ -22,6 +22,7 @@
#include <cstdint>
#include <vector>
#include "caf/byte_buffer.hpp"
#include "caf/config.hpp"
#include "caf/detail/abstract_worker.hpp"
#include "caf/detail/worker_hub.hpp"
......@@ -48,8 +49,6 @@ public:
using scheduler_type = scheduler::abstract_coordinator;
using buffer_type = std::vector<char>;
using hub_type = detail::worker_hub<worker>;
// -- constructors, destructors, and assignment operators --------------------
......@@ -62,7 +61,7 @@ public:
// -- management -------------------------------------------------------------
void launch(const node_id& last_hop, const basp::header& hdr,
const buffer_type& payload);
const byte_buffer& payload);
// -- implementation of resumable --------------------------------------------
......@@ -108,7 +107,7 @@ private:
header hdr_;
/// Contains whatever this worker deserializes next.
buffer_type payload_;
byte_buffer payload_;
};
} // namespace caf
......@@ -27,15 +27,14 @@
#include <unordered_map>
#include <unordered_set>
#include "caf/stateful_actor.hpp"
#include "caf/proxy_registry.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/forwarding_actor_proxy.hpp"
#include "caf/io/basp/all.hpp"
#include "caf/io/broker.hpp"
#include "caf/io/typed_broker.hpp"
#include "caf/proxy_registry.hpp"
#include "caf/stateful_actor.hpp"
namespace caf::io {
......@@ -91,7 +90,7 @@ public:
void learned_new_node_indirectly(const node_id& nid) override;
buffer_type& get_buffer(connection_handle hdl) override;
byte_buffer& get_buffer(connection_handle hdl) override;
void flush(connection_handle hdl) override;
......
......@@ -20,14 +20,14 @@
#include <vector>
#include "caf/message.hpp"
#include "caf/io/datagram_handle.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/io/broker_servant.hpp"
#include "caf/io/system_messages.hpp"
#include "caf/io/network/ip_endpoint.hpp"
#include "caf/io/datagram_handle.hpp"
#include "caf/io/network/datagram_manager.hpp"
#include "caf/io/network/ip_endpoint.hpp"
#include "caf/io/network/receive_buffer.hpp"
#include "caf/io/system_messages.hpp"
#include "caf/message.hpp"
namespace caf::io {
......@@ -46,10 +46,10 @@ public:
virtual void ack_writes(bool enable) = 0;
/// Returns a new output buffer.
virtual std::vector<char>& wr_buf(datagram_handle) = 0;
virtual byte_buffer& wr_buf(datagram_handle) = 0;
/// Enqueue a buffer to be sent as a datagram.
virtual void enqueue_datagram(datagram_handle, std::vector<char>) = 0;
virtual void enqueue_datagram(datagram_handle, byte_buffer) = 0;
/// Returns the current input buffer.
virtual network::receive_buffer& rd_buf() = 0;
......@@ -75,7 +75,7 @@ public:
network::receive_buffer& buf) override;
void datagram_sent(execution_unit*, datagram_handle hdl, size_t,
std::vector<char> buffer) override;
byte_buffer buffer) override;
virtual void detach_handles() = 0;
......@@ -94,4 +94,3 @@ using datagram_servant_ptr = intrusive_ptr<datagram_servant>;
// Allows the `middleman_actor` to create an `datagram_servant` and then send it
// to the BASP broker.
CAF_ALLOW_UNSAFE_MESSAGE_TYPE(caf::io::datagram_servant_ptr)
......@@ -18,20 +18,19 @@
#pragma once
#include <vector>
#include <unordered_map>
#include <vector>
#include "caf/logger.hpp"
#include "caf/raise_error.hpp"
#include "caf/ref_counted.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/io/fwd.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/io/network/ip_endpoint.hpp"
#include "caf/io/network/datagram_manager.hpp"
#include "caf/io/network/event_handler.hpp"
#include "caf/io/network/ip_endpoint.hpp"
#include "caf/io/network/native_socket.hpp"
#include "caf/io/network/datagram_manager.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/logger.hpp"
#include "caf/raise_error.hpp"
#include "caf/ref_counted.hpp"
namespace caf::io::network {
......@@ -40,12 +39,10 @@ public:
/// A smart pointer to a datagram manager.
using manager_ptr = intrusive_ptr<datagram_manager>;
/// A buffer class providing a compatible interface to `std::vector`.
using write_buffer_type = std::vector<char>;
using read_buffer_type = network::receive_buffer;
/// A job for sending a datagram consisting of the sender and a buffer.
using job_type = std::pair<datagram_handle, write_buffer_type>;
using job_type = std::pair<datagram_handle, byte_buffer>;
datagram_handler(default_multiplexer& backend_ref, native_socket sockfd);
......@@ -62,7 +59,7 @@ public:
/// Returns the write buffer of this endpoint.
/// @warning Must not be modified outside the IO multiplexers event loop
/// once the stream has been started.
write_buffer_type& wr_buf(datagram_handle hdl) {
byte_buffer& wr_buf(datagram_handle hdl) {
wr_offline_buf_.emplace_back();
wr_offline_buf_.back().first = hdl;
return wr_offline_buf_.back().second;
......@@ -71,7 +68,7 @@ public:
/// Enqueues a buffer to be sent as a datagram.
/// @warning Must not be modified outside the IO multiplexers event loop
/// once the stream has been started.
void enqueue_datagram(datagram_handle hdl, std::vector<char> buf) {
void enqueue_datagram(datagram_handle hdl, byte_buffer buf) {
wr_offline_buf_.emplace_back(hdl, move(buf));
}
......@@ -133,7 +130,7 @@ protected:
CAF_RAISE_ERROR("got write event for undefined endpoint");
auto& id = itr->first;
auto& ep = itr->second;
std::vector<char> buf;
byte_buffer buf;
std::swap(buf, wr_buf_.second);
auto size_as_int = static_cast<int>(buf.size());
if (size_as_int > send_buffer_size_) {
......@@ -160,7 +157,7 @@ private:
bool handle_read_result(bool read_result);
void handle_write_result(bool write_result, datagram_handle id,
std::vector<char>& buf, size_t wb);
byte_buffer& buf, size_t wb);
void handle_error();
......@@ -182,4 +179,4 @@ private:
manager_ptr writer_;
};
} // namespace caf
} // namespace caf::io::network
......@@ -32,12 +32,14 @@ public:
/// Called by the underlying I/O device whenever it received data.
/// @returns `true` if the manager accepts further reads, otherwise `false`.
virtual bool consume(execution_unit*, datagram_handle hdl,
receive_buffer& buf) = 0;
virtual bool
consume(execution_unit*, datagram_handle hdl, receive_buffer& buf)
= 0;
/// Called by the underlying I/O device whenever it sent data.
virtual void datagram_sent(execution_unit*, datagram_handle hdl, size_t,
std::vector<char> buffer) = 0;
byte_buffer buffer)
= 0;
/// Called by the underlying I/O device to indicate that a new remote
/// endpoint has been detected, passing in the received datagram.
......@@ -52,5 +54,4 @@ public:
virtual std::string addr(datagram_handle) const = 0;
};
} // namespace caf
} // namespace caf::io::network
......@@ -40,9 +40,9 @@ public:
void ack_writes(bool enable) override;
std::vector<char>& wr_buf(datagram_handle hdl) override;
byte_buffer& wr_buf(datagram_handle hdl) override;
void enqueue_datagram(datagram_handle hdl, std::vector<char> buf) override;
void enqueue_datagram(datagram_handle hdl, byte_buffer buf) override;
network::receive_buffer& rd_buf() override;
......
......@@ -101,10 +101,10 @@ uint16_t port(const ip_endpoint& ep);
uint32_t family(const ip_endpoint& ep);
error load_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h,
error_code<sec> load_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h,
uint16_t& p, size_t& l);
error save_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h,
error_code<sec> save_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h,
uint16_t& p, size_t& l);
template <class Inspector>
......
......@@ -18,12 +18,12 @@
#pragma once
#include "caf/message.hpp"
#include "caf/ref_counted.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/io/fwd.hpp"
#include "caf/io/network/operation.hpp"
#include "caf/message.hpp"
#include "caf/ref_counted.hpp"
namespace caf::io::network {
......@@ -74,4 +74,3 @@ protected:
};
} // namespace caf
......@@ -37,9 +37,9 @@ public:
void ack_writes(bool enable) override;
std::vector<char>& wr_buf() override;
byte_buffer& wr_buf() override;
std::vector<char>& rd_buf() override;
byte_buffer& rd_buf() override;
void graceful_shutdown() override;
......
......@@ -20,16 +20,14 @@
#include <vector>
#include "caf/logger.hpp"
#include "caf/ref_counted.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/io/fwd.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/io/network/rw_state.hpp"
#include "caf/io/network/event_handler.hpp"
#include "caf/io/network/rw_state.hpp"
#include "caf/io/network/stream_manager.hpp"
#include "caf/io/network/event_handler.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/logger.hpp"
#include "caf/ref_counted.hpp"
namespace caf::io::network {
......@@ -40,10 +38,6 @@ public:
/// A smart pointer to a stream manager.
using manager_ptr = intrusive_ptr<stream_manager>;
/// A buffer class providing a compatible
/// interface to `std::vector`.
using buffer_type = std::vector<char>;
stream(default_multiplexer& backend_ref, native_socket sockfd);
/// Starts reading data from the socket, forwarding incoming data to `mgr`.
......@@ -64,14 +58,14 @@ public:
/// Returns the write buffer of this stream.
/// @warning Must not be modified outside the IO multiplexers event loop
/// once the stream has been started.
inline buffer_type& wr_buf() {
inline byte_buffer& wr_buf() {
return wr_offline_buf_;
}
/// Returns the read buffer of this stream.
/// @warning Must not be modified outside the IO multiplexers event loop
/// once the stream has been started.
inline buffer_type& rd_buf() {
inline byte_buffer& rd_buf() {
return rd_buf_;
}
......@@ -148,13 +142,13 @@ private:
size_t read_threshold_;
size_t collected_;
size_t max_;
buffer_type rd_buf_;
byte_buffer rd_buf_;
// State for writing.
manager_ptr writer_;
size_t written_;
buffer_type wr_buf_;
buffer_type wr_offline_buf_;
byte_buffer wr_buf_;
byte_buffer wr_offline_buf_;
};
} // namespace caf
......@@ -20,11 +20,11 @@
#include <thread>
#include "caf/io/receive_policy.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/io/abstract_broker.hpp"
#include "caf/io/network/multiplexer.hpp"
#include "caf/io/network/receive_buffer.hpp"
#include "caf/io/receive_policy.hpp"
namespace caf::io::network {
......@@ -102,32 +102,29 @@ public:
/// Generate an id for a new servant.
int64_t next_endpoint_id();
/// A buffer storing bytes used for TCP related components.
using buffer_type = std::vector<char>;
/// Buffers storing bytes for UDP related components.
using read_buffer_type = network::receive_buffer;
using write_buffer_type = buffer_type;
using read_job_type = std::pair<datagram_handle, read_buffer_type>;
using write_job_type = std::pair<datagram_handle, write_buffer_type>;
using read_byte_buffer = network::receive_buffer;
using write_byte_buffer = byte_buffer;
using read_job_type = std::pair<datagram_handle, read_byte_buffer>;
using write_job_type = std::pair<datagram_handle, write_byte_buffer>;
using write_job_queue_type = std::deque<write_job_type>;
using shared_buffer_type = std::shared_ptr<buffer_type>;
using shared_byte_buffer = std::shared_ptr<byte_buffer>;
using shared_job_queue_type = std::shared_ptr<write_job_queue_type>;
/// Models pending data on the network, i.e., the network
/// input buffer usually managed by the operating system.
buffer_type& virtual_network_buffer(connection_handle hdl);
byte_buffer& virtual_network_buffer(connection_handle hdl);
/// Models pending data on the network, i.e., the network
/// input buffer usually managed by the operating system.
write_job_queue_type& virtual_network_buffer(datagram_handle hdl);
/// Returns the output buffer of the scribe identified by `hdl`.
buffer_type& output_buffer(connection_handle hdl);
byte_buffer& output_buffer(connection_handle hdl);
/// Returns the input buffer of the scribe identified by `hdl`.
buffer_type& input_buffer(connection_handle hdl);
byte_buffer& input_buffer(connection_handle hdl);
/// Returns the output buffer of the dgram servant identified by `hdl`.
write_job_type& output_buffer(datagram_handle hdl);
......@@ -247,12 +244,12 @@ public:
/// Appends `buf` to the virtual network buffer of `hdl`
/// and calls `read_data(hdl)` afterwards.
void virtual_send(connection_handle hdl, const buffer_type& buf);
void virtual_send(connection_handle hdl, const byte_buffer& buf);
/// Appends `buf` to the virtual network buffer of `hdl`
/// and calls `read_data(hdl)` afterwards.
void virtual_send(datagram_handle src, datagram_handle ep,
const buffer_type&);
void
virtual_send(datagram_handle src, datagram_handle ep, const byte_buffer&);
/// Waits until a `runnable` is available and executes it.
void exec_runnable();
......@@ -296,11 +293,11 @@ private:
std::shared_ptr<datagram_data> data_for_hdl(datagram_handle hdl);
struct scribe_data {
shared_buffer_type vn_buf_ptr;
shared_buffer_type wr_buf_ptr;
buffer_type& vn_buf;
buffer_type rd_buf;
buffer_type& wr_buf;
shared_byte_buffer vn_buf_ptr;
shared_byte_buffer wr_buf_ptr;
byte_buffer& vn_buf;
byte_buffer rd_buf;
byte_buffer& wr_buf;
receive_policy::config recv_conf;
bool stopped_reading;
bool passive_mode;
......@@ -309,8 +306,8 @@ private:
// Allows creating an entangled scribes where the input of this scribe is
// the output of another scribe and vice versa.
scribe_data(shared_buffer_type input = std::make_shared<buffer_type>(),
shared_buffer_type output = std::make_shared<buffer_type>());
scribe_data(shared_byte_buffer input = std::make_shared<byte_buffer>(),
shared_byte_buffer output = std::make_shared<byte_buffer>());
};
struct doorman_data {
......
......@@ -18,14 +18,12 @@
#pragma once
#include <vector>
#include "caf/message.hpp"
#include "caf/allowed_unsafe_message_type.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/io/broker_servant.hpp"
#include "caf/io/network/stream_manager.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/io/system_messages.hpp"
#include "caf/io/network/stream_manager.hpp"
namespace caf::io {
......@@ -47,10 +45,10 @@ public:
virtual void ack_writes(bool enable) = 0;
/// Returns the current output buffer.
virtual std::vector<char>& wr_buf() = 0;
virtual byte_buffer& wr_buf() = 0;
/// Returns the current input buffer.
virtual std::vector<char>& rd_buf() = 0;
virtual byte_buffer& rd_buf() = 0;
/// Flushes the output buffer, i.e., sends the
/// content of the buffer via the network.
......@@ -71,4 +69,3 @@ using scribe_ptr = intrusive_ptr<scribe>;
// Allows the `middleman_actor` to create a `scribe` and then send it to the
// BASP broker.
CAF_ALLOW_UNSAFE_MESSAGE_TYPE(caf::io::scribe_ptr)
......@@ -23,14 +23,14 @@
#include <sstream>
#include <iomanip>
#include "caf/meta/type_name.hpp"
#include "caf/meta/hex_formatted.hpp"
#include "caf/io/handle.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/io/accept_handle.hpp"
#include "caf/io/datagram_handle.hpp"
#include "caf/io/connection_handle.hpp"
#include "caf/io/datagram_handle.hpp"
#include "caf/io/handle.hpp"
#include "caf/io/network/receive_buffer.hpp"
#include "caf/meta/hex_formatted.hpp"
#include "caf/meta/type_name.hpp"
namespace caf::io {
......@@ -52,7 +52,7 @@ struct new_data_msg {
/// Handle to the related connection.
connection_handle handle;
/// Buffer containing the received data.
std::vector<char> buf;
byte_buffer buf;
};
/// @relates new_data_msg
......@@ -148,7 +148,7 @@ struct datagram_sent_msg {
// Number of bytes written.
uint64_t written;
// Buffer of the sent datagram, for reuse.
std::vector<char> buf;
byte_buffer buf;
};
/// @relates datagram_sent_msg
......
......@@ -86,7 +86,7 @@ void abstract_broker::ack_writes(connection_handle hdl, bool enable) {
x->ack_writes(enable);
}
std::vector<char>& abstract_broker::wr_buf(connection_handle hdl) {
byte_buffer& abstract_broker::wr_buf(connection_handle hdl) {
CAF_ASSERT(hdl != invalid_connection_handle);
auto x = by_id(hdl);
if (!x) {
......@@ -99,7 +99,7 @@ std::vector<char>& abstract_broker::wr_buf(connection_handle hdl) {
void abstract_broker::write(connection_handle hdl, size_t bs, const void* buf) {
auto& out = wr_buf(hdl);
auto first = reinterpret_cast<const char*>(buf);
auto first = reinterpret_cast<const byte*>(buf);
auto last = first + bs;
out.insert(out.end(), first, last);
}
......@@ -117,7 +117,7 @@ void abstract_broker::ack_writes(datagram_handle hdl, bool enable) {
x->ack_writes(enable);
}
std::vector<char>& abstract_broker::wr_buf(datagram_handle hdl) {
byte_buffer& abstract_broker::wr_buf(datagram_handle hdl) {
auto x = by_id(hdl);
if (!x) {
CAF_LOG_ERROR("tried to access wr_buf() of an unknown"
......@@ -127,8 +127,7 @@ std::vector<char>& abstract_broker::wr_buf(datagram_handle hdl) {
return x->wr_buf(hdl);
}
void abstract_broker::enqueue_datagram(datagram_handle hdl,
std::vector<char> buf) {
void abstract_broker::enqueue_datagram(datagram_handle hdl, byte_buffer buf) {
auto x = by_id(hdl);
if (!x)
CAF_LOG_ERROR("tried to access datagram_buffer() of an unknown"
......@@ -138,7 +137,7 @@ void abstract_broker::enqueue_datagram(datagram_handle hdl,
void abstract_broker::write(datagram_handle hdl, size_t bs, const void* buf) {
auto& out = wr_buf(hdl);
auto first = reinterpret_cast<const char*>(buf);
auto first = reinterpret_cast<const byte*>(buf);
auto last = first + bs;
out.insert(out.end(), first, last);
}
......@@ -354,8 +353,8 @@ resumable::subtype_t abstract_broker::subtype() const {
return io_actor;
}
resumable::resume_result abstract_broker::resume(execution_unit* ctx,
size_t mt) {
resumable::resume_result
abstract_broker::resume(execution_unit* ctx, size_t mt) {
CAF_ASSERT(ctx != nullptr);
CAF_ASSERT(ctx == &backend());
return scheduled_actor::resume(ctx, mt);
......@@ -394,4 +393,4 @@ void abstract_broker::launch_servant(datagram_servant_ptr& ptr) {
ptr->launch();
}
} // namespace caf
} // namespace caf::io
......@@ -26,7 +26,6 @@
#include "caf/io/basp/version.hpp"
#include "caf/io/basp/worker.hpp"
#include "caf/settings.hpp"
#include "caf/streambuf.hpp"
namespace caf::io::basp {
......@@ -57,7 +56,7 @@ connection_state instance::handle(execution_unit* ctx, new_data_msg& dm,
callee_.purge_state(nid);
return close_connection;
};
std::vector<char>* payload = nullptr;
byte_buffer* payload = nullptr;
if (is_payload) {
payload = &dm.buf;
if (payload->size() != hdr.payload_len) {
......@@ -177,8 +176,9 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
mid.integer_value(),
sender ? sender->id() : invalid_actor_id,
dest_actor};
auto writer = make_callback(
[&](serializer& sink) -> error { return sink(forwarding_stack, msg); });
auto writer = make_callback([&](binary_serializer& sink) { //
return sink(forwarding_stack, msg);
});
write(ctx, callee_.get_buffer(path->hdl), hdr, &writer);
} else {
header hdr{message_type::routed_message,
......@@ -187,7 +187,7 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
mid.integer_value(),
sender ? sender->id() : invalid_actor_id,
dest_actor};
auto writer = make_callback([&](serializer& sink) -> error {
auto writer = make_callback([&](binary_serializer& sink) {
return sink(source_node, dest_node, forwarding_stack, msg);
});
write(ctx, callee_.get_buffer(path->hdl), hdr, &writer);
......@@ -196,7 +196,7 @@ bool instance::dispatch(execution_unit* ctx, const strong_actor_ptr& sender,
return true;
}
void instance::write(execution_unit* ctx, buffer_type& buf, header& hdr,
void instance::write(execution_unit* ctx, byte_buffer& buf, header& hdr,
payload_writer* pw) {
CAF_LOG_TRACE(CAF_ARG(hdr));
binary_serializer sink{ctx, buf};
......@@ -214,7 +214,7 @@ void instance::write(execution_unit* ctx, buffer_type& buf, header& hdr,
CAF_LOG_ERROR(CAF_ARG(err));
}
void instance::write_server_handshake(execution_unit* ctx, buffer_type& out_buf,
void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
optional<uint16_t> port) {
CAF_LOG_TRACE(CAF_ARG(port));
using namespace detail;
......@@ -225,7 +225,7 @@ void instance::write_server_handshake(execution_unit* ctx, buffer_type& out_buf,
pa = &i->second;
}
CAF_LOG_DEBUG_IF(!pa && port, "no actor published");
auto writer = make_callback([&](serializer& sink) -> error {
auto writer = make_callback([&](binary_serializer& sink) {
auto app_ids = get_or(config(), "middleman.app-identifiers",
defaults::middleman::app_identifiers);
auto aid = invalid_actor_id;
......@@ -245,9 +245,10 @@ void instance::write_server_handshake(execution_unit* ctx, buffer_type& out_buf,
write(ctx, out_buf, hdr, &writer);
}
void instance::write_client_handshake(execution_unit* ctx, buffer_type& buf) {
auto writer = make_callback(
[&](serializer& sink) -> error { return sink(this_node_); });
void instance::write_client_handshake(execution_unit* ctx, byte_buffer& buf) {
auto writer = make_callback([&](binary_serializer& sink) { //
return sink(this_node_);
});
header hdr{message_type::client_handshake,
0,
0,
......@@ -257,27 +258,28 @@ void instance::write_client_handshake(execution_unit* ctx, buffer_type& buf) {
write(ctx, buf, hdr, &writer);
}
void instance::write_monitor_message(execution_unit* ctx, buffer_type& buf,
void instance::write_monitor_message(execution_unit* ctx, byte_buffer& buf,
const node_id& dest_node, actor_id aid) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid));
auto writer = make_callback(
[&](serializer& sink) -> error { return sink(this_node_, dest_node); });
auto writer = make_callback([&](binary_serializer& sink) { //
return sink(this_node_, dest_node);
});
header hdr{message_type::monitor_message, 0, 0, 0, invalid_actor_id, aid};
write(ctx, buf, hdr, &writer);
}
void instance::write_down_message(execution_unit* ctx, buffer_type& buf,
void instance::write_down_message(execution_unit* ctx, byte_buffer& buf,
const node_id& dest_node, actor_id aid,
const error& rsn) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(aid) << CAF_ARG(rsn));
auto writer = make_callback([&](serializer& sink) -> error {
auto writer = make_callback([&](binary_serializer& sink) { //
return sink(this_node_, dest_node, rsn);
});
header hdr{message_type::down_message, 0, 0, 0, aid, invalid_actor_id};
write(ctx, buf, hdr, &writer);
}
void instance::write_heartbeat(execution_unit* ctx, buffer_type& buf) {
void instance::write_heartbeat(execution_unit* ctx, byte_buffer& buf) {
CAF_LOG_TRACE("");
header hdr{message_type::heartbeat, 0, 0, 0, invalid_actor_id,
invalid_actor_id};
......@@ -285,7 +287,7 @@ void instance::write_heartbeat(execution_unit* ctx, buffer_type& buf) {
}
bool instance::handle(execution_unit* ctx, connection_handle hdl, header& hdr,
std::vector<char>* payload) {
byte_buffer* payload) {
CAF_LOG_TRACE(CAF_ARG(hdl) << CAF_ARG(hdr));
// Check payload validity.
if (payload == nullptr) {
......@@ -409,7 +411,7 @@ bool instance::handle(execution_unit* ctx, connection_handle hdl, header& hdr,
struct handler : remote_message_handler<handler> {
handler(message_queue* queue, proxy_registry* proxies,
actor_system* system, node_id last_hop, basp::header& hdr,
buffer_type& payload)
byte_buffer& payload)
: queue_(queue),
proxies_(proxies),
system_(system),
......@@ -423,7 +425,7 @@ bool instance::handle(execution_unit* ctx, connection_handle hdl, header& hdr,
actor_system* system_;
node_id last_hop_;
basp::header& hdr_;
buffer_type& payload_;
byte_buffer& payload_;
uint64_t msg_id_;
};
handler f{&queue_, &proxies(), &system(), last_hop, hdr, *payload};
......@@ -486,7 +488,7 @@ bool instance::handle(execution_unit* ctx, connection_handle hdl, header& hdr,
}
void instance::forward(execution_unit* ctx, const node_id& dest_node,
const header& hdr, std::vector<char>& payload) {
const header& hdr, byte_buffer& payload) {
CAF_LOG_TRACE(CAF_ARG(dest_node) << CAF_ARG(hdr) << CAF_ARG(payload));
auto path = lookup(dest_node);
if (path) {
......@@ -495,10 +497,7 @@ void instance::forward(execution_unit* ctx, const node_id& dest_node,
CAF_LOG_ERROR("unable to serialize BASP header");
return;
}
if (auto err = bs.apply_raw(payload.size(), payload.data())) {
CAF_LOG_ERROR("unable to serialize raw payload");
return;
}
bs.apply(span<const byte>{payload.data(), payload.size()});
flush(*path);
} else {
CAF_LOG_WARNING("cannot forward message, no route to destination");
......
......@@ -39,7 +39,7 @@ worker::~worker() {
// -- management ---------------------------------------------------------------
void worker::launch(const node_id& last_hop, const basp::header& hdr,
const buffer_type& payload) {
const byte_buffer& payload) {
CAF_ASSERT(hdr.dest_actor != 0);
CAF_ASSERT(hdr.operation == basp::message_type::direct_message
|| hdr.operation == basp::message_type::routed_message);
......
......@@ -295,10 +295,9 @@ behavior basp_broker::make_behavior() {
},
[=](unpublish_atom, const actor_addr& whom, uint16_t port) -> result<void> {
CAF_LOG_TRACE(CAF_ARG(whom) << CAF_ARG(port));
auto cb = make_callback(
[&](const strong_actor_ptr&, uint16_t x) -> error {
auto cb = make_callback([&](const strong_actor_ptr&, uint16_t x) {
close(hdl_by_port(x));
return none;
return error_code<sec>{};
});
if (instance.remove_published_actor(whom, port, &cb) == 0)
return sec::no_actor_published_at_port;
......@@ -583,8 +582,7 @@ void basp_broker::connection_cleanup(connection_handle hdl) {
}
}
basp::instance::callee::buffer_type&
basp_broker::get_buffer(connection_handle hdl) {
byte_buffer& basp_broker::get_buffer(connection_handle hdl) {
return wr_buf(hdl);
}
......
......@@ -60,7 +60,7 @@ bool datagram_servant::consume(execution_unit* ctx, datagram_handle hdl,
}
void datagram_servant::datagram_sent(execution_unit* ctx, datagram_handle hdl,
size_t written, std::vector<char> buffer) {
size_t written, byte_buffer buffer) {
CAF_LOG_TRACE(CAF_ARG(written));
if (detached())
return;
......
......@@ -219,9 +219,13 @@ expected<group> middleman::remote_group(const std::string& group_identifier,
self->request(mm, infinite, connect_atom::value, host, port)
.then([=](const node_id&, strong_actor_ptr& ptr,
const std::set<std::string>&) mutable {
CAF_LOG_DEBUG("received handle to target node:" << CAF_ARG(ptr));
auto hdl = actor_cast<actor>(ptr);
self->request(hdl, infinite, get_atom::value, group_identifier)
.then([=](group& grp) mutable { rp.deliver(std::move(grp)); });
.then([=](group& grp) mutable {
CAF_LOG_DEBUG("received remote group handle:" << CAF_ARG(grp));
rp.deliver(std::move(grp));
});
});
},
};
......@@ -232,9 +236,19 @@ expected<group> middleman::remote_group(const std::string& group_identifier,
auto worker = self->spawn<lazy_init + monitored>(two_step_lookup,
actor_handle());
self->send(worker, get_atom::value);
self->receive([&](group& grp) { result = std::move(grp); },
[&](error& err) { result = std::move(err); },
[&](down_msg& dm) { result = std::move(dm.reason); });
self->receive(
[&](group& grp) {
CAF_LOG_DEBUG("received remote group handle:" << CAF_ARG(grp));
result = std::move(grp);
},
[&](error& err) {
CAF_LOG_DEBUG("received an error while waiting for the group:" << err);
result = std::move(err);
},
[&](down_msg& dm) {
CAF_LOG_DEBUG("lookup actor failed:" << CAF_ARG(dm));
result = std::move(dm.reason);
});
return result;
}
......@@ -352,6 +366,11 @@ void middleman::init(actor_system_config& cfg) {
return sec::no_such_group_module;
}
error_code<sec> load(binary_deserializer&, group&) override {
// never called, because we hand out group instances of the local module
return sec::no_such_group_module;
}
private:
middleman& parent_;
};
......
......@@ -70,10 +70,9 @@ void datagram_handler::write(datagram_handle hdl, const void* buf,
size_t num_bytes) {
wr_offline_buf_.emplace_back();
wr_offline_buf_.back().first = hdl;
auto cbuf = reinterpret_cast<const char*>(buf);
wr_offline_buf_.back().second.assign(cbuf,
cbuf
+ static_cast<ptrdiff_t>(num_bytes));
auto cbuf = reinterpret_cast<const byte*>(buf);
wr_offline_buf_.back().second.assign(
cbuf, cbuf + static_cast<ptrdiff_t>(num_bytes));
}
void datagram_handler::flush(const manager_ptr& mgr) {
......@@ -195,8 +194,8 @@ bool datagram_handler::handle_read_result(bool read_result) {
}
void datagram_handler::handle_write_result(bool write_result,
datagram_handle id,
std::vector<char>& buf, size_t wb) {
datagram_handle id, byte_buffer& buf,
size_t wb) {
if (!write_result) {
writer_->io_failure(&backend(), operation::write);
backend().del(operation::write, fd(), this);
......@@ -220,4 +219,4 @@ void datagram_handler::handle_error() {
// no need to call backend().del() here
}
} // namespace caf
} // namespace caf::io::network
......@@ -60,12 +60,12 @@ void datagram_servant_impl::ack_writes(bool enable) {
handler_.ack_writes(enable);
}
std::vector<char>& datagram_servant_impl::wr_buf(datagram_handle hdl) {
byte_buffer& datagram_servant_impl::wr_buf(datagram_handle hdl) {
return handler_.wr_buf(hdl);
}
void datagram_servant_impl::enqueue_datagram(datagram_handle hdl,
std::vector<char> buffer) {
byte_buffer buffer) {
handler_.enqueue_datagram(hdl, std::move(buffer));
}
......
......@@ -206,8 +206,8 @@ uint32_t family(const ip_endpoint& ep) {
return ep.caddress()->sa_family;
}
error load_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h, uint16_t& p,
size_t& l) {
error_code<sec> load_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h,
uint16_t& p, size_t& l) {
ep.clear();
if (l > 0) {
*ep.length() = l;
......@@ -233,8 +233,8 @@ error load_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h, uint16_t& p,
return none;
}
error save_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h, uint16_t& p,
size_t& l) {
error_code<sec> save_endpoint(ip_endpoint& ep, uint32_t& f, std::string& h,
uint16_t& p, size_t& l) {
if (*ep.length() > 0) {
f = family(ep);
h = host(ep);
......
......@@ -45,11 +45,11 @@ void scribe_impl::ack_writes(bool enable) {
stream_.ack_writes(enable);
}
std::vector<char>& scribe_impl::wr_buf() {
byte_buffer& scribe_impl::wr_buf() {
return stream_.wr_buf();
}
std::vector<char>& scribe_impl::rd_buf() {
byte_buffer& scribe_impl::rd_buf() {
return stream_.rd_buf();
}
......
......@@ -59,7 +59,7 @@ void stream::configure_read(receive_policy::config config) {
void stream::write(const void* buf, size_t num_bytes) {
CAF_LOG_TRACE(CAF_ARG(num_bytes));
auto first = reinterpret_cast<const char*>(buf);
auto first = reinterpret_cast<const byte*>(buf);
auto last = first + num_bytes;
wr_offline_buf_.insert(wr_offline_buf_.end(), first, last);
}
......
......@@ -32,8 +32,8 @@ constexpr size_t receive_buffer_size = std::numeric_limits<uint16_t>::max();
} // namespace
test_multiplexer::scribe_data::scribe_data(shared_buffer_type input,
shared_buffer_type output)
test_multiplexer::scribe_data::scribe_data(shared_byte_buffer input,
shared_byte_buffer output)
: vn_buf_ptr(std::move(input)),
wr_buf_ptr(std::move(output)),
vn_buf(*vn_buf_ptr),
......@@ -94,10 +94,10 @@ scribe_ptr test_multiplexer::new_scribe(connection_handle hdl) {
void ack_writes(bool enable) override {
mpx_->ack_writes(hdl()) = enable;
}
std::vector<char>& wr_buf() override {
byte_buffer& wr_buf() override {
return mpx_->output_buffer(hdl());
}
std::vector<char>& rd_buf() override {
byte_buffer& rd_buf() override {
return mpx_->input_buffer(hdl());
}
void graceful_shutdown() override {
......@@ -133,8 +133,8 @@ scribe_ptr test_multiplexer::new_scribe(connection_handle hdl) {
return sptr;
}
expected<scribe_ptr> test_multiplexer::new_tcp_scribe(const std::string& host,
uint16_t port) {
expected<scribe_ptr>
test_multiplexer::new_tcp_scribe(const std::string& host, uint16_t port) {
CAF_LOG_TRACE(CAF_ARG(host) << CAF_ARG(port));
connection_handle hdl;
{ // lifetime scope of guard
......@@ -212,8 +212,8 @@ doorman_ptr test_multiplexer::new_doorman(accept_handle hdl, uint16_t port) {
return dptr;
}
expected<doorman_ptr> test_multiplexer::new_tcp_doorman(uint16_t desired_port,
const char*, bool) {
expected<doorman_ptr>
test_multiplexer::new_tcp_doorman(uint16_t desired_port, const char*, bool) {
CAF_LOG_TRACE(CAF_ARG(desired_port));
accept_handle hdl;
uint16_t port = 0;
......@@ -318,8 +318,8 @@ test_multiplexer::new_local_udp_endpoint(uint16_t desired_port, const char*,
return new_datagram_servant(hdl, port);
}
datagram_servant_ptr test_multiplexer::new_datagram_servant(datagram_handle hdl,
uint16_t port) {
datagram_servant_ptr
test_multiplexer::new_datagram_servant(datagram_handle hdl, uint16_t port) {
CAF_LOG_TRACE(CAF_ARG(hdl));
class impl : public datagram_servant {
public:
......@@ -347,12 +347,12 @@ datagram_servant_ptr test_multiplexer::new_datagram_servant(datagram_handle hdl,
void ack_writes(bool enable) override {
mpx_->ack_writes(hdl()) = enable;
}
std::vector<char>& wr_buf(datagram_handle dh) override {
byte_buffer& wr_buf(datagram_handle dh) override {
auto& buf = mpx_->output_buffer(dh);
buf.first = dh;
return buf.second;
}
void enqueue_datagram(datagram_handle dh, std::vector<char> buf) override {
void enqueue_datagram(datagram_handle dh, byte_buffer buf) override {
auto& q = mpx_->output_queue(dh);
q.emplace_back(dh, std::move(buf));
}
......@@ -430,8 +430,8 @@ datagram_servant_ptr test_multiplexer::new_datagram_servant(datagram_handle hdl,
return dptr;
}
datagram_servant_ptr test_multiplexer::new_datagram_servant(datagram_handle,
const std::string&,
datagram_servant_ptr
test_multiplexer::new_datagram_servant(datagram_handle, const std::string&,
uint16_t) {
CAF_CRITICAL("This has no implementation in the test multiplexer");
}
......@@ -441,9 +441,8 @@ int64_t test_multiplexer::next_endpoint_id() {
}
bool test_multiplexer::is_known_port(uint16_t x) const {
auto pred1 = [&](const doorman_data_map::value_type& y) {
return x == y.second.port;
};
auto pred1
= [&](const doorman_data_map::value_type& y) { return x == y.second.port; };
auto pred2 = [&](const datagram_data_map::value_type& y) {
return x == y.second->port;
};
......@@ -453,9 +452,8 @@ bool test_multiplexer::is_known_port(uint16_t x) const {
}
bool test_multiplexer::is_known_handle(accept_handle x) const {
auto pred = [&](const pending_doorman_map::value_type& y) {
return x == y.second;
};
auto pred
= [&](const pending_doorman_map::value_type& y) { return x == y.second; };
return doorman_data_.count(x) > 0
|| std::any_of(doormen_.begin(), doormen_.end(), pred);
}
......@@ -526,8 +524,7 @@ void test_multiplexer::provide_datagram_servant(std::string host,
/// The external input buffer should be filled by
/// the test program.
test_multiplexer::buffer_type&
test_multiplexer::virtual_network_buffer(connection_handle hdl) {
byte_buffer& test_multiplexer::virtual_network_buffer(connection_handle hdl) {
CAF_ASSERT(std::this_thread::get_id() == tid_);
return scribe_data_[hdl].vn_buf;
}
......@@ -538,14 +535,12 @@ test_multiplexer::virtual_network_buffer(datagram_handle hdl) {
return data_for_hdl(hdl)->vn_buf;
}
test_multiplexer::buffer_type&
test_multiplexer::output_buffer(connection_handle hdl) {
byte_buffer& test_multiplexer::output_buffer(connection_handle hdl) {
CAF_ASSERT(std::this_thread::get_id() == tid_);
return scribe_data_[hdl].wr_buf;
}
test_multiplexer::buffer_type&
test_multiplexer::input_buffer(connection_handle hdl) {
byte_buffer& test_multiplexer::input_buffer(connection_handle hdl) {
CAF_ASSERT(std::this_thread::get_id() == tid_);
return scribe_data_[hdl].rd_buf;
}
......@@ -668,8 +663,8 @@ void test_multiplexer::prepare_connection(accept_handle src,
CAF_ASSERT(this != &peer);
CAF_LOG_TRACE(CAF_ARG(src) << CAF_ARG(hdl) << CAF_ARG(host) << CAF_ARG(port)
<< CAF_ARG(peer_hdl));
auto input = std::make_shared<buffer_type>();
auto output = std::make_shared<buffer_type>();
auto input = std::make_shared<byte_buffer>();
auto output = std::make_shared<byte_buffer>();
CAF_LOG_DEBUG("insert scribe data for" << CAF_ARG(hdl));
auto res1 = scribe_data_.emplace(hdl, scribe_data{input, output});
if (!res1.second)
......@@ -905,7 +900,8 @@ bool test_multiplexer::read_data(datagram_handle hdl) {
to.first = from.first;
CAF_ASSERT(to.second.capacity() > from.second.size());
to.second.resize(from.second.size());
std::copy(from.second.begin(), from.second.end(), to.second.begin());
std::transform(from.second.begin(), from.second.end(), to.second.begin(),
[](byte x) { return caf::to_integer<char>(x); });
data->vn_buf.pop_front();
auto sitr = datagram_data_.find(data->rd_buf.first);
if (sitr == datagram_data_.end()) {
......@@ -919,7 +915,7 @@ bool test_multiplexer::read_data(datagram_handle hdl) {
}
void test_multiplexer::virtual_send(connection_handle hdl,
const buffer_type& buf) {
const byte_buffer& buf) {
CAF_ASSERT(std::this_thread::get_id() == tid_);
CAF_LOG_TRACE(CAF_ARG(hdl));
auto& vb = virtual_network_buffer(hdl);
......@@ -928,7 +924,7 @@ void test_multiplexer::virtual_send(connection_handle hdl,
}
void test_multiplexer::virtual_send(datagram_handle dst, datagram_handle ep,
const buffer_type& buf) {
const byte_buffer& buf) {
CAF_ASSERT(std::this_thread::get_id() == tid_);
CAF_LOG_TRACE(CAF_ARG(dst) << CAF_ARG(ep));
auto& vb = virtual_network_buffer(dst);
......@@ -1035,4 +1031,4 @@ void test_multiplexer::exec(resumable_ptr& ptr) {
}
}
} // namespace caf
} // namespace caf::io::network
......@@ -31,10 +31,8 @@
#include <vector>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/io/all.hpp"
#include "caf/io/network/interfaces.hpp"
#include "caf/io/network/test_multiplexer.hpp"
......@@ -82,9 +80,7 @@ constexpr auto config_serv_atom = caf::atom("ConfigServ");
constexpr uint32_t num_remote_nodes = 2;
using buffer = std::vector<char>;
std::string hexstr(const buffer& buf) {
std::string hexstr(const byte_buffer& buf) {
return deep_to_string(meta::hex_formatted(), buf);
}
......@@ -162,10 +158,10 @@ public:
}
uint32_t serialized_size(const message& msg) {
buffer buf;
byte_buffer buf;
binary_serializer bs{mpx_, buf};
auto e = bs(const_cast<message&>(msg));
CAF_REQUIRE(!e);
if (auto err = bs(const_cast<message&>(msg)))
CAF_FAIL("returned to serialize message: " << sys.render(err));
return static_cast<uint32_t>(buf.size());
}
......@@ -220,38 +216,34 @@ public:
using payload_writer = basp::instance::payload_writer;
template <class... Ts>
void to_payload(binary_serializer& bs, const Ts&... xs) {
bs(const_cast<Ts&>(xs)...);
}
template <class... Ts>
void to_payload(buffer& buf, const Ts&... xs) {
binary_serializer bs{mpx_, buf};
to_payload(bs, xs...);
void to_payload(byte_buffer& buf, const Ts&... xs) {
binary_serializer sink{mpx_, buf};
if (auto err = sink(xs...))
CAF_FAIL("failed to serialize payload: " << sys.render(err));
}
void to_buf(buffer& buf, basp::header& hdr, payload_writer* writer) {
void to_buf(byte_buffer& buf, basp::header& hdr, payload_writer* writer) {
instance().write(mpx_, buf, hdr, writer);
}
template <class T, class... Ts>
void to_buf(buffer& buf, basp::header& hdr, payload_writer* writer,
void to_buf(byte_buffer& buf, basp::header& hdr, payload_writer* writer,
const T& x, const Ts&... xs) {
auto pw = make_callback([&](serializer& sink) -> error {
auto pw = make_callback([&](binary_serializer& sink) {
if (writer)
return error::eval([&] { return (*writer)(sink); },
[&] { return sink(const_cast<T&>(x)); });
if (auto err = (*writer)(sink))
return err;
return sink(const_cast<T&>(x));
});
to_buf(buf, hdr, &pw, xs...);
}
std::pair<basp::header, buffer> from_buf(const buffer& buf) {
std::pair<basp::header, byte_buffer> from_buf(const byte_buffer& buf) {
basp::header hdr;
binary_deserializer bd{mpx_, buf};
auto e = bd(hdr);
CAF_REQUIRE(!e);
buffer payload;
byte_buffer payload;
if (hdr.payload_len > 0) {
std::copy(buf.begin() + basp::header_size, buf.end(),
std::back_inserter(payload));
......@@ -295,7 +287,7 @@ public:
CAF_CHECK_EQUAL(path->next_hop, n.id);
}
std::pair<basp::header, buffer> read_from_out_buf(connection_handle hdl) {
auto read_from_out_buf(connection_handle hdl) {
CAF_MESSAGE("read from output buffer for connection " << hdl.id());
auto& buf = mpx_->output_buffer(hdl);
while (buf.size() < basp::header_size)
......@@ -308,7 +300,7 @@ public:
void dispatch_out_buf(connection_handle hdl) {
basp::header hdr;
buffer buf;
byte_buffer buf;
std::tie(hdr, buf) = read_from_out_buf(hdl);
CAF_MESSAGE("dispatch output buffer for connection " << hdl.id());
CAF_REQUIRE_EQUAL(hdr.operation, basp::message_type::direct_message);
......@@ -346,9 +338,9 @@ public:
maybe<actor_id> source_actor, maybe<actor_id> dest_actor,
const Ts&... xs) {
CAF_MESSAGE("expect #" << num);
buffer buf;
byte_buffer buf;
this_->to_payload(buf, xs...);
buffer& ob = this_->mpx()->output_buffer(hdl);
auto& ob = this_->mpx()->output_buffer(hdl);
while (this_->mpx()->try_exec_runnable()) {
// repeat
}
......@@ -356,10 +348,11 @@ public:
basp::header hdr;
{ // lifetime scope of source
binary_deserializer source{this_->mpx(), ob};
auto e = source(hdr);
CAF_REQUIRE_EQUAL(e, none);
if (auto err = source(hdr))
CAF_FAIL("unable to deserialize header: "
<< actor_system_config::render(err));
}
buffer payload;
byte_buffer payload;
if (hdr.payload_len > 0) {
CAF_REQUIRE(ob.size() >= (basp::header_size + hdr.payload_len));
auto first = ob.begin() + basp::header_size;
......@@ -390,7 +383,7 @@ public:
template <class... Ts>
mock_t mock(connection_handle hdl, basp::header hdr, const Ts&... xs) {
buffer buf;
byte_buffer buf;
to_buf(buf, hdr, nullptr, xs...);
CAF_MESSAGE("virtually send " << to_string(hdr.operation) << " with "
<< (buf.size() - basp::header_size)
......@@ -454,10 +447,10 @@ CAF_TEST_FIXTURE_SCOPE(basp_tests, fixture)
CAF_TEST(empty_server_handshake) {
// test whether basp instance correctly sends a
// server handshake when there's no actor published
buffer buf;
byte_buffer buf;
instance().write_server_handshake(mpx(), buf, none);
basp::header hdr;
buffer payload;
byte_buffer payload;
std::tie(hdr, payload) = from_buf(buf);
basp::header expected{basp::message_type::server_handshake,
0,
......@@ -473,12 +466,12 @@ CAF_TEST(empty_server_handshake) {
CAF_TEST(non_empty_server_handshake) {
// test whether basp instance correctly sends a
// server handshake with published actors
buffer buf;
byte_buffer buf;
instance().add_published_actor(4242, actor_cast<strong_actor_ptr>(self()),
{"caf::replies_to<@u16>::with<@u16>"});
instance().write_server_handshake(mpx(), buf, uint16_t{4242});
basp::header hdr;
buffer payload;
byte_buffer payload;
std::tie(hdr, payload) = from_buf(buf);
basp::header expected{basp::message_type::server_handshake,
0,
......@@ -489,10 +482,12 @@ CAF_TEST(non_empty_server_handshake) {
CAF_CHECK(basp::valid(hdr));
CAF_CHECK(basp::is_handshake(hdr));
CAF_CHECK_EQUAL(to_string(hdr), to_string(expected));
buffer expected_payload;
binary_serializer bd{nullptr, expected_payload};
bd(instance().this_node(), defaults::middleman::app_identifiers, self()->id(),
std::set<std::string>{"caf::replies_to<@u16>::with<@u16>"});
byte_buffer expected_payload;
std::set<std::string> ifs{"caf::replies_to<@u16>::with<@u16>"};
binary_serializer sink{nullptr, expected_payload};
if (auto err = sink(instance().this_node(),
defaults::middleman::app_identifiers, self()->id(), ifs))
CAF_FAIL("serializing handshake failed: " << sys.render(err));
CAF_CHECK_EQUAL(hexstr(payload), hexstr(expected_payload));
}
......
......@@ -91,7 +91,7 @@ behavior peer_fun(broker* self, connection_handle hdl, const actor& buddy) {
self->configure_read(hdl, receive_policy::exactly(sizeof(atom_value)));
auto write = [=](atom_value type) {
auto& buf = self->wr_buf(hdl);
auto first = reinterpret_cast<char*>(&type);
auto first = reinterpret_cast<byte*>(&type);
buf.insert(buf.end(), first, first + sizeof(atom_value));
self->flush(hdl);
};
......
......@@ -93,17 +93,20 @@ behavior http_worker(http_broker* self, connection_handle hdl) {
[=](const new_data_msg& msg) {
assert(!msg.buf.empty());
assert(msg.handle == hdl);
// interpret the received bytes as a string
string_view msg_buf{reinterpret_cast<const char*>(msg.buf.data()),
msg.buf.size()};
// extract lines from received buffer
auto& lines = self->state.lines;
auto i = msg.buf.begin();
auto e = msg.buf.end();
auto i = msg_buf.begin();
auto e = msg_buf.end();
// search position of first newline in data chunk
auto nl = std::search(i, e, std::begin(newline), std::end(newline));
// store whether we are continuing a previously started line
auto append_to_last_line = self->state.ps == receive_continued_line;
// check whether our last chunk ended between \r and \n
if (self->state.ps == receive_second_newline_half) {
if (msg.buf.front() == '\n') {
if (msg_buf.front() == '\n') {
// simply skip this character
++i;
}
......@@ -125,10 +128,10 @@ behavior http_worker(http_broker* self, connection_handle hdl) {
}
} while (nl != e);
// store current state of our parser
if (msg.buf.back() == '\r') {
if (msg_buf.back() == '\r') {
self->state.ps = receive_second_newline_half;
self->state.lines.pop_back(); // drop '\r' from our last read line
} else if (msg.buf.back() == '\n') {
} else if (msg_buf.back() == '\n') {
self->state.ps = receive_new_line; // we've got a clean cut
} else {
self->state.ps = receive_continued_line; // interrupted in the
......@@ -141,13 +144,17 @@ behavior http_worker(http_broker* self, connection_handle hdl) {
// end
if (lines.size() > 1 && lines.back().empty()) {
auto& out = self->wr_buf(hdl);
auto append = [&](string_view str) {
auto bytes = as_bytes(make_span(str));
out.insert(out.end(), bytes.begin(), bytes.end());
};
// we only look at the first line in our example and reply with
// our OK message if we receive exactly "GET / HTTP/1.1",
// otherwise we send a 404 HTTP response
if (lines.front() == http_valid_get)
out.insert(out.end(), std::begin(http_ok), std::end(http_ok));
append(http_ok);
else
out.insert(out.end(), std::begin(http_error), std::end(http_error));
append(http_error);
// write data and close connection
self->flush(hdl);
self->quit();
......@@ -202,11 +209,12 @@ public:
mock_t(const mock_t&) = default;
mock_t& expect(const std::string& x) {
auto bytes = as_bytes(make_span(x));
auto& buf = this_->mpx_->output_buffer(this_->connection_);
CAF_REQUIRE(buf.size() >= x.size());
CAF_REQUIRE(std::equal(buf.begin(),
buf.begin() + static_cast<ptrdiff_t>(x.size()),
x.begin()));
bytes.begin()));
buf.erase(buf.begin(), buf.begin() + static_cast<ptrdiff_t>(x.size()));
return *this;
}
......@@ -216,10 +224,9 @@ public:
// mocks some input for our AUT and allows to
// check the output for this operation
mock_t mock(const char* what) {
std::vector<char> buf;
for (char c = *what++; c != '\0'; c = *what++)
buf.push_back(c);
mock_t mock(string_view what) {
auto bytes = as_bytes(make_span(what));
byte_buffer buf{bytes.begin(), bytes.end()};
mpx_->virtual_send(connection_, std::move(buf));
return {this};
}
......
......@@ -20,7 +20,7 @@
#include "caf/io/network/ip_endpoint.hpp"
#include "caf/test/unit_test.hpp"
#include "caf/test/dsl.hpp"
#include <vector>
......@@ -44,34 +44,29 @@ public:
}
};
struct fixture {
struct fixture : test_coordinator_fixture<> {
template <class T, class... Ts>
std::vector<char> serialize(T& x, Ts&... xs) {
std::vector<char> buf;
binary_serializer bs{&context, buf};
bs(x, xs...);
auto serialize(T& x, Ts&... xs) {
byte_buffer buf;
binary_serializer sink{sys, buf};
if (auto err = sink(x, xs...))
CAF_FAIL("serialization failed: " << sys.render(err));
return buf;
}
template <class T, class... Ts>
void deserialize(const std::vector<char>& buf, T& x, Ts&... xs) {
binary_deserializer bd{&context, buf};
bd(x, xs...);
}
fixture() : cfg(), system(cfg), context(&system) {
template <class Buffer, class T, class... Ts>
void deserialize(const Buffer& buf, T& x, Ts&... xs) {
binary_deserializer source{sys, buf};
if (auto err = source(x, xs...))
CAF_FAIL("serialization failed: " << sys.render(err));
}
config cfg;
actor_system system;
scoped_execution_unit context;
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(ep_endpoint_tests, fixture)
CAF_TEST(ip_endpoint) {
CAF_TEST_DISABLED(ip_endpoint) {
// create an empty endpoint
network::ip_endpoint ep;
ep.clear();
......@@ -88,7 +83,7 @@ CAF_TEST(ip_endpoint) {
CAF_CHECK_EQUAL(12345, p);
CAF_CHECK(0 < l);
// serialize the endpoint and clear it
std::vector<char> buf = serialize(ep);
auto buf = serialize(ep);
auto save = ep;
ep.clear();
CAF_CHECK_EQUAL("", network::host(ep));
......
......@@ -87,7 +87,7 @@ CAF_TEST(connecting to remote group) {
CAF_MESSAGE("call remote_group on earth");
loop_after_next_enqueue(earth);
auto grp = unbox(earth.mm.remote_group(group_name, server, port));
CAF_CHECK(grp);
CAF_REQUIRE(grp != nullptr);
CAF_CHECK_EQUAL(grp->get()->identifier(), group_name);
}
......
......@@ -105,7 +105,7 @@ CAF_TEST(deliver serialized message) {
CAF_REQUIRE_NOT_EQUAL(hub.peek(), nullptr);
auto w = hub.pop();
CAF_MESSAGE("create a fake message + BASP header");
std::vector<char> payload;
byte_buffer payload;
std::vector<strong_actor_ptr> stages;
binary_serializer sink{sys, payload};
if (auto err = sink(stages, make_message(ok_atom::value)))
......
......@@ -125,11 +125,11 @@ public:
stream_.ack_writes(enable);
}
std::vector<char>& wr_buf() override {
byte_buffer& wr_buf() override {
return stream_.wr_buf();
}
std::vector<char>& rd_buf() override {
byte_buffer& rd_buf() override {
return stream_.rd_buf();
}
......
......@@ -21,6 +21,8 @@
#include <type_traits>
#include "caf/all.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/config.hpp"
#include "caf/detail/gcd.hpp"
#include "caf/meta/annotation.hpp"
......@@ -679,9 +681,6 @@ public:
/// A deterministic scheduler type.
using scheduler_type = caf::scheduler::test_coordinator;
/// A buffer for serializing or deserializing objects.
using byte_buffer = std::vector<char>;
// -- constructors, destructors, and assignment operators --------------------
static Config& init_config(Config& cfg) {
......@@ -850,8 +849,8 @@ public:
}
template <class... Ts>
byte_buffer serialize(const Ts&... xs) {
byte_buffer buf;
caf::byte_buffer serialize(const Ts&... xs) {
caf::byte_buffer buf;
caf::binary_serializer sink{sys, buf};
if (auto err = sink(xs...))
CAF_FAIL("serialization failed: " << sys.render(err));
......@@ -859,7 +858,7 @@ public:
}
template <class... Ts>
void deserialize(const byte_buffer& buf, Ts&... xs) {
void deserialize(const caf::byte_buffer& buf, Ts&... xs) {
caf::binary_deserializer source{sys, buf};
if (auto err = source(xs...))
CAF_FAIL("deserialization failed: " << sys.render(err));
......
......@@ -33,10 +33,11 @@
#include <unistd.h>
#endif
#include "caf/message_builder.hpp"
#include "caf/message_handler.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/config_option_set.hpp"
#include "caf/config_value.hpp"
#include "caf/settings.hpp"
#include "caf/string_algorithms.hpp"
#include "caf/test/unit_test.hpp"
namespace caf::test {
......@@ -508,34 +509,43 @@ int main(int argc, char** argv) {
// use all arguments after '--' for the test engine.
std::string delimiter = "--";
auto divider = argc;
auto cli_argv = argv + 1;
for (auto i = 1; i < argc; ++i) {
if (delimiter == argv[i]) {
divider = i;
break;
}
}
// our simple command line parser.
auto res = message_builder(cli_argv, cli_argv + divider - 1).extract_opts({
{"no-colors,n", "disable coloring (ignored on Windows)"},
{"log-file,l", "set output file", log_file},
{"console-verbosity,v", "set verbosity level of console (1-5)",
verbosity_console},
{"file-verbosity,V", "set verbosity level of file output (1-5)",
verbosity_file},
{"max-runtime,r", "set maximum runtime in seconds (0 = infinite)",
max_runtime},
{"suites,s",
"define what suites to run, either * or a comma-separated list", suites},
{"not-suites,S", "exclude suites", not_suites},
{"tests,t", "set tests", tests},
{"not-tests,T", "exclude tests", not_tests},
{"available-suites,a", "print available suites"},
{"available-tests,A", "print available tests for given suite", suite_query}
});
if (res.opts.count("help") > 0) {
std::cout << res.helptext << std::endl;
return 0;
caf::config_option_set options;
caf::config_option_adder{options, "global"}
.add<bool>("no-colors,n", "disable coloring (ignored on Windows)")
.add<bool>("help,h?", "print this help text")
.add<bool>("available-suites,a", "print available suites")
.add(log_file, "log-file,l", "set output file")
.add(verbosity_console, "console-verbosity,v",
"set verbosity level of console (1-5)")
.add(verbosity_file, "file-verbosity,V",
"set verbosity level of file output (1-5)")
.add(max_runtime, "max-runtime,r",
"set maximum runtime in seconds (0 = infinite)")
.add(suites, "suites,s",
"define what suites to run, either * or a comma-separated list")
.add(not_suites, "not-suites,S", "exclude suites")
.add(tests, "tests,t", "set tests")
.add(not_tests, "not-tests,T", "exclude tests")
.add(suite_query, "available-tests,A",
"print available tests for given suite");
caf::settings conf;
std::vector<std::string> args_cpy{argv + 1, argv + divider};
auto res = options.parse(conf, args_cpy);
if (res.first != caf::pec::success) {
std::cerr << "error while parsing argument \"" << *res.second
<< "\": " << to_string(res.first) << "\n\n";
std::cerr << options.help_text() << std::endl;
return EXIT_FAILURE;
}
if (caf::get_or(conf, "help", false)) {
std::cout << options.help_text() << std::endl;
return EXIT_SUCCESS;
}
if (!suite_query.empty()) {
std::cout << "available tests in suite " << suite_query << ":" << std::endl;
......@@ -543,18 +553,13 @@ int main(int argc, char** argv) {
std::cout << " - " << t << std::endl;
return 0;
}
if (res.opts.count("available-suites") > 0) {
if (caf::get_or(conf, "available-suites", false)) {
std::cout << "available suites:" << std::endl;
for (auto& s : engine::available_suites())
std::cout << " - " << s << std::endl;
return 0;
}
if (!res.remainder.empty()) {
std::cerr << "*** invalid command line options" << std::endl
<< res.helptext << std::endl;
return 1;
return EXIT_SUCCESS;
}
auto colorize = res.opts.count("no-colors") == 0;
auto colorize = !caf::get_or(conf, "no-colors", false);
std::vector<char*> args;
if (divider < argc) {
// make a new args vector that contains argv[0] and all remaining args
......
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