From c41b9fc7ecf1008ba72a727f01220d6075b61145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Dziepak?= Date: Wed, 11 Apr 2018 18:42:20 +0100 Subject: [PATCH 01/37] utils: add fragment range This patch introduces a FragmentRange concept which is the minimal interface all classes representing a fragmented buffer should satisfy. --- utils/fragment_range.hh | 105 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 utils/fragment_range.hh diff --git a/utils/fragment_range.hh b/utils/fragment_range.hh new file mode 100644 index 0000000000..2cc6c2e5d1 --- /dev/null +++ b/utils/fragment_range.hh @@ -0,0 +1,105 @@ +/* + * 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 . + */ + +#pragma once + +#include + +#include "bytes.hh" + +enum class mutable_view { no, yes, }; + +GCC6_CONCEPT( + +/// Fragmented buffer +/// +/// Concept `FragmentedBuffer` is satisfied by any class that is a range of +/// fragments and provides a method `size_bytes()` which returns the total +/// size of the buffer. The interfaces accepting `FragmentedBuffer` will attempt +/// to avoid unnecessary linearisation. +template +concept bool FragmentRange = requires (T range) { + typename T::fragment_type; + requires std::is_same_v + || std::is_same_v; + { *range.begin() } -> typename T::fragment_type; + { *range.end() } -> typename T::fragment_type; + { range.size_bytes() } -> size_t; + { range.empty() } -> bool; // returns true iff size_bytes() == 0. +}; + +) + +template +struct is_fragment_range : std::false_type { }; + +template +struct is_fragment_range> : std::true_type { }; + +template +static constexpr bool is_fragment_range_v = is_fragment_range::value; + +/// Single-element fragment range +/// +/// This is a helper that allows converting a bytes_view into a FragmentRange. +template +class single_fragment_range { +public: + using fragment_type = std::conditional_t; +private: + fragment_type _view; +public: + using iterator = const fragment_type*; + using const_iterator = const fragment_type*; + + explicit single_fragment_range(fragment_type f) : _view { f } { } + + const_iterator begin() const { return &_view; } + const_iterator end() const { return &_view + 1; } + + size_t size_bytes() const { return _view.size(); } + bool empty() const { return _view.empty(); } +}; + +single_fragment_range(bytes_view) -> single_fragment_range; +single_fragment_range(bytes_mutable_view) -> single_fragment_range; + +/// Empty fragment range. +struct empty_fragment_range { + using fragment_type = bytes_view; + using iterator = bytes_view*; + using const_iterator = bytes_view*; + + iterator begin() const { return nullptr; } + iterator end() const { return nullptr; } + + size_t size_bytes() const { return 0; } + bool empty() const { return true; } +}; + +GCC6_CONCEPT( + +static_assert(FragmentRange); +static_assert(FragmentRange>); +static_assert(FragmentRange>); + +) From 5845d52632d2c35648fbcb31c0d212289980c4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Dziepak?= Date: Thu, 22 Mar 2018 17:18:58 +0000 Subject: [PATCH 02/37] idl: allow fragmented bytes_view in serialisation This patch adds new way of serialising bytes and sstring objects in the IDL. Using write_fragmented_() the caller can pass a range of fragments that would be serialised without linearising the buffer. --- idl-compiler.py | 18 +++++++++++++++++- serializer.hh | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/idl-compiler.py b/idl-compiler.py index 7cdb0e6d46..d5cb12ba49 100755 --- a/idl-compiler.py +++ b/idl-compiler.py @@ -418,12 +418,14 @@ def add_param_writer_basic_type(name, base_state, typ, var_type = "", var_index set_command = ("_state.f.end(_out);" if not root_node else "state.f.end(_out);") if var_type is not "" else "" return_command = "{ _out, std::move(_state._parent) }" if var_type is not "" and not root_node else "{ _out, std::move(_state) }" + allow_fragmented = False if typ in ['bytes', 'sstring']: typ += '_view' + allow_fragmented = True else: typ = 'const ' + typ + '&' - return Template(reindent(4, """ + writer = Template(reindent(4, """ after_${base_state}__$name write_$name$var_type($typ t) && { $create_variant_state $set_varient_index @@ -431,6 +433,20 @@ def add_param_writer_basic_type(name, base_state, typ, var_type = "", var_index $set_command return $return_command; }""")).substitute(locals()) + if allow_fragmented: + writer += Template(reindent(4, """ + template + GCC6_CONCEPT(requires FragmentRange) + after_${base_state}__$name write_fragmented_$name$var_type(FragmentedBuffer&& fragments) && { + $set_varient_index + using boost::range::for_each; + for_each(fragments, [&] ($typ t) { + serialize(_out, t); + }); + $set_command + return $return_command; + }""")).substitute(locals()) + return writer def add_param_writer_object(name, base_state, typ, var_type = "", var_index = None, root_node = False): var_type1 = "_" + var_type if var_type != "" else "" diff --git a/serializer.hh b/serializer.hh index 0af62e31d2..5f9aa0a201 100644 --- a/serializer.hh +++ b/serializer.hh @@ -32,6 +32,7 @@ #include "boost/variant/variant.hpp" #include "bytes_ostream.hh" #include "utils/input_stream.hh" +#include "utils/fragment_range.hh" namespace ser { using size_type = uint32_t; From ba5e64383aeb9f418fad11693ea22a1d9e2e3889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Dziepak?= Date: Fri, 25 Aug 2017 11:00:56 +0100 Subject: [PATCH 03/37] utils: add metaprogramming helper functions --- utils/meta.hh | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 utils/meta.hh diff --git a/utils/meta.hh b/utils/meta.hh new file mode 100644 index 0000000000..fc5f5ebc1a --- /dev/null +++ b/utils/meta.hh @@ -0,0 +1,170 @@ +/* + * 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 . + */ + +#pragma once + +namespace meta { + +// Wrappers that allows returning a list of types. All helpers defined in this +// file accept both unpacked and packed lists of types. +template +struct list { }; + +namespace internal { + +template +constexpr ssize_t do_find_if_unpacked() { + ssize_t i = -1; + ssize_t j = 0; + (..., ((Vs && i == -1) ? i = j : j++)); + return i; +} + +template +struct negative_to_empty : std::integral_constant { }; + +template<> +struct negative_to_empty<-1> { }; + +template +struct is_same_as { + template + using type = std::is_same; +}; + +template typename Predicate, typename... Ts> +struct do_find_if : internal::negative_to_empty::value...>()> { }; + +template typename Predicate, typename... Ts> +struct do_find_if> : internal::negative_to_empty::value...>()> { }; + +} + +// Returns the index of the first type in the list of types list of types Ts for +// which Predicate typename Predicate, typename... Ts> +constexpr size_t find_if = internal::do_find_if::value; + +// Returns the index of the first occurrence of type T in the list of types Ts. +template +constexpr size_t find = find_if::template type, Ts...>; + +namespace internal { + +template +struct do_get_unpacked { }; + +template +struct do_get_unpacked : do_get_unpacked { }; + +template +struct do_get_unpacked<0, T, Ts...> { + using type = T; +}; + +template +struct do_get : do_get_unpacked { }; + +template +struct do_get> : do_get_unpacked { }; + +} + +// Returns the Nth type in the provided list of types. +template +using get = typename internal::do_get::type; + +namespace internal { + +template +struct do_take_unpacked { }; + +template +struct do_take_unpacked<0, list> { + using type = list; +}; + +template +struct do_take_unpacked<0, list, U, Us...> { + using type = list; +}; + +template +struct do_take_unpacked, U, Us...> { + using type = typename do_take_unpacked, Us...>::type; +}; + +template +struct do_take : do_take_unpacked { }; + + +template +struct do_take> : do_take_unpacked { }; + +} + +// Returns a list containing N first elements of the provided list of types. +template +using take = typename internal::do_take, Ts...>::type; + +namespace internal { + +template +struct do_for_each_unpacked { + template + static constexpr void run(Function&& fn) { + (..., fn(static_cast(nullptr))); + } +}; + +template +struct do_for_each : do_for_each_unpacked { }; + +template +struct do_for_each> : do_for_each_unpacked { }; + +} + +// Executes the provided function for each element in the provided list of +// types. For each type T the Function is called with an argument of type T*. +template +constexpr void for_each(Function&& fn) { + internal::do_for_each::run(std::forward(fn)); +}; + +namespace internal { + +template +struct get_size : std::integral_constant { }; + +template +struct get_size> : std::integral_constant { }; + +} + +// Returns the size of a list of types. +template +constexpr size_t size = internal::get_size::value; + +template