Unverified Commit 9a37d249 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #875

Templatize binary serializer
parents 4d3e2683 da145b07
......@@ -32,7 +32,6 @@ set(LIBCAF_CORE_SRCS
src/behavior_impl.cpp
src/behavior_stack.cpp
src/binary_deserializer.cpp
src/binary_serializer.cpp
src/blocking_actor.cpp
src/blocking_behavior.cpp
src/chars.cpp
......
......@@ -22,7 +22,9 @@
#include <cstdint>
#include <vector>
#include "caf/byte.hpp"
#include "caf/deserializer.hpp"
#include "caf/span.hpp"
namespace caf {
......@@ -33,17 +35,27 @@ public:
using super = deserializer;
using buffer = std::vector<char>;
// -- constructors, destructors, and assignment operators --------------------
binary_deserializer(actor_system& sys, const char* buf, size_t buf_size);
binary_deserializer(actor_system& sys, span<const byte> bytes);
binary_deserializer(execution_unit* ctx, const char* buf, size_t buf_size);
binary_deserializer(execution_unit* ctx, span<const byte> bytes);
template <class T>
binary_deserializer(actor_system& sys, const std::vector<T>& buf)
: binary_deserializer(sys, as_bytes(make_span(buf))) {
// nop
}
template <class T>
binary_deserializer(execution_unit* ctx, const std::vector<T>& buf)
: binary_deserializer(ctx, as_bytes(make_span(buf))) {
// nop
}
binary_deserializer(actor_system& sys, const buffer& buf);
binary_deserializer(actor_system& sys, const char* buf, size_t buf_size);
binary_deserializer(execution_unit* ctx, const buffer& buf);
binary_deserializer(execution_unit* ctx, const char* buf, size_t buf_size);
// -- overridden member functions --------------------------------------------
......@@ -60,24 +72,24 @@ public:
// -- properties -------------------------------------------------------------
/// Returns the current read position.
const char* current() const {
return current_;
}
const char* current() const CAF_DEPRECATED_MSG("use remaining() instead");
/// Returns the past-the-end iterator.
const char* end() const {
return end_;
}
const char* end() const CAF_DEPRECATED_MSG("use remaining() instead");
/// Returns how many bytes are still available to read.
size_t remaining() const;
size_t remaining() const noexcept {
return static_cast<size_t>(end_ - current_);
}
/// Jumps `num_bytes` forward.
/// @pre `num_bytes <= remaining()`
void skip(size_t num_bytes) {
current_ += num_bytes;
/// Returns the remaining bytes.
span<const byte> remainder() const noexcept {
return make_span(current_, end_);
}
/// Jumps `num_bytes` forward.
/// @pre `num_bytes <= remaining()`
void skip(size_t num_bytes);
protected:
error apply_impl(int8_t&) override;
......@@ -113,8 +125,8 @@ private:
return current_ + read_size <= end_;
}
const char* current_;
const char* end_;
const byte* current_;
const byte* end_;
};
} // namespace caf
......@@ -18,97 +18,12 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <vector>
#include "caf/serializer.hpp"
#include "caf/serializer_impl.hpp"
namespace caf {
/// Implements the serializer interface with a binary serialization protocol.
class binary_serializer final : public serializer {
public:
// -- member types -----------------------------------------------------------
using super = serializer;
using buffer = std::vector<char>;
// -- constructors, destructors, and assignment operators --------------------
binary_serializer(actor_system& sys, buffer& buf);
binary_serializer(execution_unit* ctx, buffer& buf);
// -- position management ----------------------------------------------------
/// Sets the write position to given offset.
/// @pre `offset <= buf.size()`
void seek(size_t offset);
/// Jumps `num_bytes` forward. Resizes the buffer (filling it with zeros)
/// when skipping past the end.
void skip(size_t num_bytes);
// -- 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 -------------------------------------------------------------
buffer& buf() {
return buf_;
}
const buffer& buf() const {
return buf_;
}
size_t write_pos() const noexcept {
return write_pos_;
}
protected:
error apply_impl(int8_t&) override;
error apply_impl(uint8_t&) override;
error apply_impl(int16_t&) override;
error apply_impl(uint16_t&) override;
error apply_impl(int32_t&) override;
error apply_impl(uint32_t&) override;
error apply_impl(int64_t&) override;
error apply_impl(uint64_t&) override;
error apply_impl(float&) override;
error apply_impl(double&) override;
error apply_impl(long double&) override;
error apply_impl(std::string&) override;
error apply_impl(std::u16string&) override;
error apply_impl(std::u32string&) override;
private:
buffer& buf_;
size_t write_pos_;
};
using binary_serializer = serializer_impl<std::vector<char>>;
} // namespace caf
......@@ -82,6 +82,9 @@ using enable_if_t = typename std::enable_if<V, T>::type;
template <class Trait, class T = void>
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 {
......
......@@ -22,6 +22,7 @@
#include <map>
#include <memory>
#include <tuple>
#include <vector>
#include "caf/detail/is_one_of.hpp"
#include "caf/detail/is_primitive_config_value.hpp"
......@@ -29,6 +30,8 @@
namespace caf {
// clang-format off
// -- 1 param templates --------------------------------------------------------
template <class> class behavior_type_of;
......@@ -39,6 +42,7 @@ 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 stream;
template <class> class stream_sink;
template <class> class stream_source;
......@@ -72,6 +76,8 @@ template <class...> class typed_response_promise;
//
template <class, class...> class output_stream;
// clang-format on
// -- classes ------------------------------------------------------------------
class abstract_actor;
......@@ -89,7 +95,6 @@ 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;
......@@ -173,11 +178,11 @@ enum class stream_priority;
// -- aliases ------------------------------------------------------------------
using actor_id = uint64_t;
using binary_serializer = serializer_impl<std::vector<char>>;
using ip_address = ipv6_address;
using ip_subnet = ipv6_subnet;
using stream_slot = uint16_t;
using settings = dictionary<config_value>;
using stream_slot = uint16_t;
// -- functions ----------------------------------------------------------------
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
......@@ -211,7 +211,7 @@ span<byte> as_writable_bytes(span<T> xs) {
/// Convenience function to make using `caf::span` more convenient without the
/// deduction guides.
template <class T>
auto make_span(T& xs) -> span<detail::decay_t<decltype(xs[0])>> {
auto make_span(T& xs) -> span<detail::remove_reference_t<decltype(xs[0])>> {
return {xs.data(), xs.size()};
}
......
......@@ -26,7 +26,6 @@
#include "caf/detail/network_order.hpp"
#include "caf/sec.hpp"
namespace caf {
namespace {
......@@ -49,27 +48,31 @@ error apply_float(binary_deserializer& bs, T& x) {
return none;
}
} // namespace <anonmyous>
} // namespace
binary_deserializer::binary_deserializer(actor_system& sys, const char* buf,
size_t buf_size)
: super(sys), current_(buf), end_(buf + buf_size) {
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, const char* buf,
size_t buf_size)
: super(ctx), current_(buf), end_(buf + buf_size) {
binary_deserializer::binary_deserializer(execution_unit* ctx,
span<const byte> bytes)
: super(ctx), current_(bytes.data()), end_(bytes.data() + bytes.size()) {
// nop
}
binary_deserializer::binary_deserializer(actor_system& sys, const buffer& buf)
: binary_deserializer(sys, buf.data(), buf.size()) {
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
}
binary_deserializer::binary_deserializer(execution_unit* ctx, const buffer& buf)
: binary_deserializer(ctx, buf.data(), buf.size()) {
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)) {
// nop
}
......@@ -112,8 +115,17 @@ error binary_deserializer::apply_raw(size_t num_bytes, void* storage) {
return none;
}
size_t binary_deserializer::remaining() const {
return static_cast<size_t>(end_ - current_);
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::skip(size_t num_bytes) {
CAF_ASSERT(num_bytes <= remaining());
current_ += num_bytes;
}
error binary_deserializer::apply_impl(int8_t& x) {
......@@ -172,7 +184,7 @@ error binary_deserializer::apply_impl(std::string& x) {
return err;
if (!range_check(str_size))
return sec::end_of_stream;
x.assign(current_, current_ + str_size);
x.assign(reinterpret_cast<const char*>(current_), str_size);
current_ += str_size;
return end_sequence();
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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();
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