/* * 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, }; /// 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 FragmentRange = requires (T range) { typename T::fragment_type; requires std::is_same_v || std::is_same_v; { *range.begin() } -> std::convertible_to; { *range.end() } -> std::convertible_to; { range.size_bytes() } -> std::convertible_to; { range.empty() } -> std::same_as; // 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; /// A non-mutable view of a FragmentRange /// /// Provide a trivially copyable and movable, non-mutable view on a /// fragment range. This allows uniform ownership semantics across /// multi-fragment ranges and the single fragment and empty fragment /// adaptors below, i.e. it allows treating all fragment ranges /// uniformly as views. template requires FragmentRange class fragment_range_view { const T* _range; public: using fragment_type = typename T::fragment_type; using iterator = typename T::const_iterator; using const_iterator = typename T::const_iterator; public: explicit fragment_range_view(const T& range) : _range(&range) { } const_iterator begin() const { return _range->begin(); } const_iterator end() const { return _range->end(); } size_t size_bytes() const { return _range->size_bytes(); } bool empty() const { return _range->empty(); } }; /// 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; } }; static_assert(FragmentRange); static_assert(FragmentRange>); static_assert(FragmentRange>); template 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 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); }