Files
scylladb/imr/compound.hh
2018-05-31 10:09:01 +01:00

593 lines
24 KiB
C++

/*
* Copyright (C) 2018 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <array>
#include <type_traits>
#include <seastar/util/gcc6-concepts.hh>
#include "utils/meta.hh"
#include "imr/core.hh"
namespace imr {
/// Optionally present object
///
/// Represents a value that may be not present. Information whether or not
/// the optional is engaged is not stored and must be provided by external
/// context.
template<typename Tag, typename Type>
struct optional {
using underlying = Type;
public:
template<::mutable_view is_mutable>
class basic_view {
using pointer_type = std::conditional_t<is_mutable == ::mutable_view::no,
const uint8_t*, uint8_t*>;
pointer_type _ptr;
public:
explicit basic_view(pointer_type ptr) noexcept : _ptr(ptr) { }
operator basic_view<::mutable_view::no>() const noexcept {
return basic_view<::mutable_view::no>(_ptr);
}
template<typename Context = no_context_t>
GCC6_CONCEPT(requires requires(const Context& ctx) {
{ ctx.template context_for<Tag>() } noexcept;
})
auto get(const Context& ctx = no_context) noexcept {
return Type::make_view(_ptr, ctx.template context_for<Tag>(_ptr));
}
};
using view = basic_view<::mutable_view::no>;
using mutable_view = basic_view<::mutable_view::yes>;
public:
template<typename Context = no_context_t>
static auto make_view(const uint8_t* in, const Context& ctx = no_context) noexcept {
return view(in);
}
template<typename Context = no_context_t>
static auto make_view(uint8_t* in, const Context& ctx = no_context) noexcept {
return mutable_view(in);
}
public:
template<typename Context>
GCC6_CONCEPT(requires requires(const Context& ctx) {
{ ctx.template is_present<Tag>() } noexcept -> bool;
})
static size_t serialized_object_size(const uint8_t* in, const Context& context) noexcept {
return context.template is_present<Tag>()
? Type::serialized_object_size(in, context)
: 0;
}
template<typename... Args>
static size_t size_when_serialized(Args&&... args) noexcept {
return Type::size_when_serialized(std::forward<Args>(args)...);
}
template<typename... Args>
static size_t serialize(uint8_t* out, Args&&... args) noexcept {
return Type::serialize(out, std::forward<Args>(args)...);
}
template<typename Continuation = no_op_continuation>
static auto get_sizer(Continuation cont = no_op_continuation()) {
return Type::get_sizer(std::move(cont));
}
template<typename Continuation = no_op_continuation>
static auto get_serializer(uint8_t* out, Continuation cont = no_op_continuation()) {
return Type::get_serializer(out, std::move(cont));
}
};
template<typename Tag, typename Type>
struct member {
using tag = Tag;
using type = Type;
};
namespace internal {
template<typename Tag>
struct do_find_member {
template<typename Member>
using type = std::is_same<Tag, typename Member::tag>;
};
template<typename Tag, typename... Members>
static constexpr auto get_member_index = meta::find_if<do_find_member<Tag>::template type, Members...>;
template<typename Tag, typename... Members>
using get_member = meta::get<get_member_index<Tag, Members...>, Members...>;
template<size_t Offset, size_t N, template<size_t> typename Function>
struct do_generate_branch_tree {
template<typename... Args>
static decltype(auto) run(size_t n, Args&&... args) {
if constexpr (N == 1) {
return Function<Offset>::run(std::forward<Args>(args)...);
} else if (N >= 2) {
if (n < Offset + N / 2) {
return do_generate_branch_tree<Offset, N / 2, Function>::run(n, std::forward<Args>(args)...);
} else {
return do_generate_branch_tree<Offset + N / 2, N - N / 2, Function>::run(n, std::forward<Args>(args)...);
}
}
}
};
template<size_t N, template<size_t> typename Function>
using generate_branch_tree = do_generate_branch_tree<0, N, Function>;
}
template<typename Tag, typename... Alternatives>
struct variant {
class alternative_index {
size_t _index;
private:
constexpr explicit alternative_index(size_t idx) noexcept
: _index(idx) { }
friend class variant;
public:
constexpr size_t index() const noexcept { return _index; }
};
template<typename AlternativeTag>
constexpr static alternative_index index_for() noexcept {
return alternative_index(internal::get_member_index<AlternativeTag, Alternatives...>);
}
private:
template<size_t N>
struct alternative_visitor {
template<typename Visitor>
static decltype(auto) run(Visitor&& visitor) {
using member = typename meta::get<N, Alternatives...>;
return visitor(static_cast<member*>(nullptr));
}
};
template<typename Visitor>
static decltype(auto) choose_alternative(alternative_index index, Visitor&& visitor) {
// For large sizeof...(Alternatives) a jump table may be the better option.
return internal::generate_branch_tree<sizeof...(Alternatives), alternative_visitor>::run(index.index(), std::forward<Visitor>(visitor));
}
public:
template<::mutable_view is_mutable>
class basic_view {
using pointer_type = std::conditional_t<is_mutable == ::mutable_view::no,
const uint8_t*, uint8_t*>;
pointer_type _ptr;
public:
explicit basic_view(pointer_type ptr) noexcept
: _ptr(ptr)
{ }
pointer_type raw_pointer() const noexcept { return _ptr; }
operator basic_view<::mutable_view::no>() const noexcept {
return basic_view<::mutable_view::no>(_ptr);
}
template<typename AlternativeTag, typename Context = no_context_t>
auto as(const Context& context = no_context) noexcept {
using member = internal::get_member<AlternativeTag, Alternatives...>;
return member::type::make_view(_ptr, context.template context_for<AlternativeTag>(_ptr));
}
template<typename Visitor, typename Context>
decltype(auto) visit(Visitor&& visitor, const Context& context) {
auto alt_idx = context.template active_alternative_of<Tag>();
return choose_alternative(alt_idx, [&] (auto object) {
using type = std::remove_pointer_t<decltype(object)>;
return visitor(type::type::make_view(_ptr, context.template context_for<typename type::tag>(_ptr)));
});
}
template<typename Visitor, typename Context>
decltype(auto) visit_type(Visitor&& visitor, const Context& context) {
auto alt_idx = context.template active_alternative_of<Tag>();
return choose_alternative(alt_idx, [&] (auto object) {
using type = std::remove_pointer_t<decltype(object)>;
return visitor(static_cast<type*>(nullptr));
});
}
};
using view = basic_view<::mutable_view::no>;
using mutable_view = basic_view<::mutable_view::yes>;
public:
template<typename Context>
static view make_view(const uint8_t* in, const Context& context) noexcept {
return view(in);
}
template<typename Context>
static mutable_view make_view(uint8_t* in, const Context& context) noexcept {
return mutable_view(in);
}
public:
template<typename Context>
GCC6_CONCEPT(requires requires(const Context& ctx) {
{ ctx.template active_alternative_of<Tag>() } noexcept -> alternative_index;
})
static size_t serialized_object_size(const uint8_t* in, const Context& context) noexcept {
return choose_alternative(context.template active_alternative_of<Tag>(), [&] (auto object) noexcept {
using alternative = std::remove_pointer_t<decltype(object)>;
return alternative::type::serialized_object_size(in, context.template context_for<typename alternative::tag>(in));
});
}
template<typename AlternativeTag, typename... Args>
static size_t size_when_serialized(Args&&... args) noexcept {
using member = internal::get_member<AlternativeTag, Alternatives...>;
return member::type::size_when_serialized(std::forward<Args>(args)...);
}
template<typename AlternativeTag, typename... Args>
static size_t serialize(uint8_t* out, Args&&... args) noexcept {
using member = internal::get_member<AlternativeTag, Alternatives...>;
return member::type::serialize(out, std::forward<Args>(args)...);
}
template<typename AlternativeTag, typename Continuation = no_op_continuation>
static auto get_sizer(Continuation cont = no_op_continuation()) {
using member = internal::get_member<AlternativeTag, Alternatives...>;
return member::type::get_sizer(std::move(cont));
}
template<typename AlternativeTag, typename Continuation = no_op_continuation>
static auto get_serializer(uint8_t* out, Continuation cont = no_op_continuation()) {
using member = internal::get_member<AlternativeTag, Alternatives...>;
return member::type::get_serializer(out, std::move(cont));
}
};
template<typename Tag, typename Type>
using optional_member = member<Tag, optional<Tag, Type>>;
template<typename Tag, typename... Types>
using variant_member = member<Tag, variant<Tag, Types...>>;
namespace internal {
template<typename Continuation, typename... Members>
class structure_sizer : Continuation {
size_t _size;
public:
explicit structure_sizer(size_t size, Continuation&& cont) noexcept
: Continuation(std::move(cont)), _size(size) {}
uint8_t* position() const noexcept {
// We are in the sizing phase and there is no object to point to yet.
// The serializer will return a real position in the destination buffer,
// but since sizer and serializer need to expose the same interface we
// need to return something even though the value will be ignored.
return nullptr;
}
auto done() noexcept { return Continuation::run(_size); }
};
template<typename NestedContinuation, typename... Members>
class structure_sizer_continuation : NestedContinuation {
size_t _size;
public:
explicit structure_sizer_continuation(size_t size, NestedContinuation&& cont) noexcept
: NestedContinuation(std::move(cont)), _size(size) {}
structure_sizer<NestedContinuation, Members...> run(size_t size) noexcept {
return structure_sizer<NestedContinuation, Members...>(size + _size,
std::move(*static_cast<NestedContinuation*>(this)));
}
};
template<typename Continuation, typename Member, typename... Members>
class basic_structure_sizer : protected Continuation {
protected:
size_t _size;
using continuation = structure_sizer_continuation<Continuation, Members...>;
public:
explicit basic_structure_sizer(size_t size, Continuation&& cont) noexcept
: Continuation(std::move(cont)), _size(size) {}
uint8_t* position() const noexcept { return nullptr; }
template<typename... Args>
structure_sizer<Continuation, Members...> serialize(Args&& ... args) noexcept {
auto size = Member::type::size_when_serialized(std::forward<Args>(args)...);
return structure_sizer<Continuation, Members...>(size + _size, std::move(*static_cast<Continuation*>(this)));
}
template<typename... Args>
auto serialize_nested(Args&& ... args) noexcept {
return Member::type::get_sizer(continuation(_size, std::move(*static_cast<Continuation*>(this))),
std::forward<Args>(args)...);
}
};
template<typename Continuation, typename Member, typename... Members>
struct structure_sizer<Continuation, Member, Members...>
: basic_structure_sizer<Continuation, Member, Members...> {
using basic_structure_sizer<Continuation, Member, Members...>::basic_structure_sizer;
};
template<typename Continuation, typename Tag, typename Type, typename... Members>
struct structure_sizer<Continuation, optional_member<Tag, Type>, Members...>
: basic_structure_sizer<Continuation, optional_member<Tag, Type>, Members...> {
using basic_structure_sizer<Continuation, optional_member<Tag, Type>, Members...>::basic_structure_sizer;
structure_sizer<Continuation, Members...> skip() noexcept {
return structure_sizer<Continuation, Members...>(this->_size, std::move(*static_cast<Continuation*>(this)));
}
};
template<typename Continuation, typename Tag, typename... Types, typename... Members>
struct structure_sizer<Continuation, variant_member<Tag, Types...>, Members...>
: basic_structure_sizer<Continuation, variant_member<Tag, Types...>, Members...> {
using basic_structure_sizer<Continuation, variant_member<Tag, Types...>, Members...>::basic_structure_sizer;
template<typename... Args>
structure_sizer<Continuation, Members...> serialize(Args&& ... args) noexcept = delete;
template<typename... Args>
auto serialize_nested(Args&& ... args) noexcept = delete;
template<typename AlternativeTag, typename... Args>
structure_sizer<Continuation, Members...> serialize_as(Args&& ... args) noexcept {
using type = variant<Tag, Types...>;
auto size = type::template size_when_serialized<AlternativeTag>(std::forward<Args>(args)...);
return structure_sizer<Continuation, Members...>(size + this->_size, std::move(*static_cast<Continuation*>(this)));
}
template<typename AlternativeTag, typename... Args>
auto serialize_as_nested(Args&& ... args) noexcept {
using type = variant<Tag, Types...>;
using cont_type = typename basic_structure_sizer<Continuation, variant_member<Tag, Types...>, Members...>::continuation;
auto cont = cont_type(this->_size, std::move(*static_cast<Continuation*>(this)));
return type::template get_sizer<AlternativeTag>(std::move(cont),
std::forward<Args>(args)...);
}
};
template<typename Continuation, typename... Members>
class structure_serializer : Continuation {
uint8_t* _out;
public:
explicit structure_serializer(uint8_t* out, Continuation&& cont) noexcept
: Continuation(std::move(cont)), _out(out) {}
uint8_t* position() const noexcept { return _out; }
auto done() noexcept { return Continuation::run(_out); }
};
template<typename NestedContinuation, typename... Members>
struct structure_serializer_continuation : private NestedContinuation {
explicit structure_serializer_continuation(NestedContinuation&& cont) noexcept
: NestedContinuation(std::move(cont)) {}
structure_serializer<NestedContinuation, Members...> run(uint8_t* out) noexcept {
return structure_serializer<NestedContinuation, Members...>(out,
std::move(*static_cast<NestedContinuation*>(this)));
}
};
template<typename Continuation, typename Member, typename... Members>
class basic_structure_serializer : protected Continuation {
protected:
uint8_t* _out;
using continuation = structure_serializer_continuation<Continuation, Members...>;
public:
explicit basic_structure_serializer(uint8_t* out, Continuation&& cont) noexcept
: Continuation(std::move(cont)), _out(out) {}
uint8_t* position() const noexcept { return _out; }
template<typename... Args>
structure_serializer<Continuation, Members...> serialize(Args&& ... args) noexcept {
auto size = Member::type::serialize(_out, std::forward<Args>(args)...);
return structure_serializer<Continuation, Members...>(_out + size, std::move(*static_cast<Continuation*>(this)));
}
template<typename... Args>
auto serialize_nested(Args&& ... args) noexcept {
return Member::type::get_serializer(_out,
continuation(std::move(*static_cast<Continuation*>(this))),
std::forward<Args>(args)...);
}
};
template<typename Continuation, typename Member, typename... Members>
struct structure_serializer<Continuation, Member, Members...>
: basic_structure_serializer<Continuation, Member, Members...> {
using basic_structure_serializer<Continuation, Member, Members...>::basic_structure_serializer;
};
template<typename Continuation, typename Tag, typename Type, typename... Members>
struct structure_serializer<Continuation, optional_member<Tag, Type>, Members...>
: basic_structure_serializer<Continuation, optional_member<Tag, Type>, Members...> {
using basic_structure_serializer<Continuation, optional_member<Tag, Type>, Members...>::basic_structure_serializer;
structure_serializer<Continuation, Members...> skip() noexcept {
return structure_serializer<Continuation, Members...>(this->_out,
std::move(*static_cast<Continuation*>(this)));
}
};
template<typename Continuation, typename Tag, typename... Types, typename... Members>
struct structure_serializer<Continuation, variant_member<Tag, Types...>, Members...>
: basic_structure_serializer<Continuation, variant_member<Tag, Types...>, Members...> {
using basic_structure_serializer<Continuation, variant_member<Tag, Types...>, Members...>::basic_structure_serializer;
template<typename... Args>
structure_serializer<Continuation, Members...> serialize(Args&& ... args) noexcept = delete;
template<typename... Args>
auto serialize_nested(Args&& ... args) noexcept = delete;
template<typename AlternativeTag, typename... Args>
structure_serializer<Continuation, Members...> serialize_as(Args&& ... args) noexcept {
using type = variant<Tag, Types...>;
auto size = type::template serialize<AlternativeTag>(this->_out, std::forward<Args>(args)...);
return structure_serializer<Continuation, Members...>(this->_out + size,
std::move(*static_cast<Continuation*>(this)));
}
template<typename AlternativeTag, typename... Args>
auto serialize_as_nested(Args&& ... args) noexcept {
using type = variant<Tag, Types...>;
using cont_type = typename basic_structure_serializer<Continuation, variant_member<Tag, Types...>, Members...>::continuation;
auto cont = cont_type(std::move(*static_cast<Continuation*>(this)));
return type::template get_serializer<AlternativeTag>(this->_out,
std::move(cont),
std::forward<Args>(args)...);
}
};
}
// Represents a compound type.
template<typename... Members>
struct structure {
template<::mutable_view is_mutable>
class basic_view {
using pointer_type = std::conditional_t<is_mutable == ::mutable_view::no,
const uint8_t*, uint8_t*>;
pointer_type _ptr;
public:
template<typename Context>
explicit basic_view(pointer_type ptr, const Context& context) noexcept : _ptr(ptr) { }
pointer_type raw_pointer() const noexcept { return _ptr; }
operator basic_view<::mutable_view::no>() const noexcept {
return basic_view<::mutable_view::no>(_ptr, no_context);
}
template<typename Tag, typename Context = no_context_t>
auto offset_of(const Context& context = no_context) const noexcept {
static constexpr auto idx = internal::get_member_index<Tag, Members...>;
size_t total_size = 0;
meta::for_each<meta::take<idx, Members...>>([&] (auto ptr) {
using member = std::remove_pointer_t<decltype(ptr)>;
auto offset = _ptr + total_size;
auto this_size = member::type::serialized_object_size(offset, context.template context_for<typename member::tag>(offset));
total_size += this_size;
});
return total_size;
}
template<typename Tag, typename Context = no_context_t>
auto get(const Context& context = no_context) const noexcept {
using member = internal::get_member<Tag, Members...>;
auto offset = _ptr + offset_of<Tag, Context>(context);
return member::type::make_view(offset, context.template context_for<Tag>(offset));
}
};
using view = basic_view<::mutable_view::no>;
using mutable_view = basic_view<::mutable_view::yes>;
public:
template<typename Context = no_context_t>
static view make_view(const uint8_t* in, const Context& context = no_context) noexcept {
return view(in, context);
}
template<typename Context = no_context_t>
static mutable_view make_view(uint8_t* in, const Context& context = no_context) noexcept {
return mutable_view(in, context);
}
public:
template<typename Context = no_context_t>
static size_t serialized_object_size(const uint8_t* in, const Context& context = no_context) noexcept {
size_t total_size = 0;
meta::for_each<Members...>([&] (auto ptr) noexcept {
using member = std::remove_pointer_t<decltype(ptr)>;
auto offset = in + total_size;
auto this_size = member::type::serialized_object_size(offset, context.template context_for<typename member::tag>(offset));
total_size += this_size;
});
return total_size;
}
template<typename Continuation = no_op_continuation>
static internal::structure_sizer<Continuation, Members...> get_sizer(Continuation cont = no_op_continuation()) {
return internal::structure_sizer<Continuation, Members...>(0, std::move(cont));
}
template<typename Continuation = no_op_continuation>
static internal::structure_serializer<Continuation, Members...> get_serializer(uint8_t* out, Continuation cont = no_op_continuation()) {
return internal::structure_serializer<Continuation, Members...>(out, std::move(cont));
}
template<typename Writer, typename... Args>
static size_t size_when_serialized(Writer&& writer, Args&&... args) noexcept {
return std::forward<Writer>(writer)(get_sizer(), std::forward<Args>(args)...);
}
template<typename Writer, typename... Args>
static size_t serialize(uint8_t* out, Writer&& writer, Args&&... args) noexcept {
auto ptr = std::forward<Writer>(writer)(get_serializer(out), std::forward<Args>(args)...);
return ptr - out;
}
template<typename Tag, typename Context = no_context_t>
static size_t offset_of(const uint8_t* in, const Context& context = no_context) noexcept {
static constexpr auto idx = internal::get_member_index<Tag, Members...>;
size_t total_size = 0;
meta::for_each<meta::take<idx, Members...>>([&] (auto ptr) noexcept {
using member = std::remove_pointer_t<decltype(ptr)>;
auto offset = in + total_size;
auto this_size = member::type::serialized_object_size(offset, context.template context_for<typename member::tag>(offset));
total_size += this_size;
});
return total_size;
}
template<typename Tag, typename Context = no_context_t>
static auto get_member(const uint8_t* in, const Context& context = no_context) noexcept {
auto off = offset_of<Tag>(in, context);
using member = internal::get_member<Tag, Members...>;
return member::type::make_view(in + off, context.template context_for<typename member::tag>(in + off));
}
template<typename Tag, typename Context = decltype(no_context)>
static auto get_member(uint8_t* in, const Context& context = no_context) noexcept {
auto off = offset_of<Tag>(in, context);
using member = internal::get_member<Tag, Members...>;
return member::type::make_view(in + off, context.template context_for<typename member::tag>(in + off));
}
};
template<typename Tag, typename T>
struct tagged_type : T { };
}