/* * 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 #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>); ) template GCC6_CONCEPT(requires FragmentRange) bytes linearized(const FragmentedBuffer& buffer) { bytes b(bytes::initialized_later(), buffer.size_bytes()); auto dst = b.begin(); using boost::range::for_each; for_each(buffer, [&] (bytes_view fragment) { dst = boost::copy(fragment, dst); }); return b; } template GCC6_CONCEPT(requires FragmentRange && requires (Function fn, bytes_view bv) { fn(bv); }) decltype(auto) with_linearized(const FragmentedBuffer& buffer, Function&& fn) { bytes b; bytes_view bv; if (__builtin_expect(!buffer.empty() && std::next(buffer.begin()) == buffer.end(), true)) { bv = *buffer.begin(); } else if (!buffer.empty()) { b = linearized(buffer); bv = b; } return fn(bv); }