/* * 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 "data/cell.hh" namespace data { template class value_writer { FragmentRange _value; typename FragmentRange::const_iterator _value_it; typename FragmentRange::const_iterator _value_end; bytes_view _value_current; size_t _value_size; bool _force_internal; private: // Distinguishes between cell::make_live_uninitialized() and other // cell::make_live*() variants. static constexpr bool initialize_value() { return !std::is_same_v; } auto write_all_to_destination() { if constexpr (initialize_value()) { return [this] (uint8_t* out) noexcept { auto dst = reinterpret_cast(out); while (_value_it != _value_end) { _value_current = *_value_it++; dst = std::copy_n(_value_current.data(), _value_current.size(), dst); } }; } else { return [] (uint8_t*) noexcept { }; } } auto write_to_destination(size_t n) { if constexpr (initialize_value()) { return [this, n] (uint8_t* out) mutable noexcept { auto dst = reinterpret_cast(out); while (n) { auto this_size = std::min(_value_current.size(), n); dst = std::copy_n(_value_current.data(), this_size, dst); _value_current.remove_prefix(this_size); if (_value_current.empty()) { ++_value_it; _value_current = *_value_it; } n -= this_size; } }; } else { return [] (uint8_t*) noexcept { }; } } public: value_writer(FragmentRange value, size_t value_size, bool force_internal) : _value(std::move(value)) , _value_it(_value.begin()) , _value_end(_value.end()) , _value_current(_value.empty() ? bytes_view() : *_value_it) , _value_size(value_size) , _force_internal(force_internal) { } template GCC6_CONCEPT( requires (imr::is_sizer_for_v && std::is_same_v) || (imr::is_serializer_for_v && std::is_same_v) ) auto operator()(Serializer serializer, Allocator allocations) { auto after_size = serializer.serialize(_value_size); if (_force_internal || _value_size <= cell::maximum_internal_storage_length) { return after_size .template serialize_as(_value_size, write_all_to_destination()) .done(); } imr::placeholder> next_pointer_phldr; auto next_pointer_position = after_size.position(); auto cell_ser = after_size.template serialize_as(next_pointer_phldr); auto offset = 0; auto migrate_fn_ptr = &cell::lsa_chunk_migrate_fn; while (_value_size - offset > cell::maximum_external_chunk_length) { imr::placeholder> phldr; auto chunk_ser = allocations.template allocate_nested(migrate_fn_ptr) .serialize(next_pointer_position); next_pointer_position = chunk_ser.position(); next_pointer_phldr.serialize( chunk_ser.serialize(phldr) .serialize(cell::maximum_external_chunk_length, write_to_destination(cell::maximum_external_chunk_length)) .done() ); next_pointer_phldr = phldr; offset += cell::maximum_external_chunk_length; } size_t left = _value_size - offset; auto ptr = allocations.template allocate_nested(&cell::lsa_last_chunk_migrate_fn) .serialize(next_pointer_position) .serialize(left) .serialize(left, write_to_destination(left)) .done(); next_pointer_phldr.serialize(ptr); return cell_ser.done(); } }; inline value_writer cell::variable_value::write(size_t value_size, bool force_internal) noexcept { GCC6_CONCEPT(static_assert(imr::WriterAllocator, structure>)); return value_writer(empty_fragment_range(), value_size, force_internal); } template inline value_writer> cell::variable_value::write(FragmentRange&& value, bool force_internal) noexcept { GCC6_CONCEPT(static_assert(imr::WriterAllocator>, structure>)); return value_writer>(std::forward(value), value.size_bytes(), force_internal); } inline auto cell::variable_value::write(bytes_view value, bool force_internal) noexcept { return write(single_fragment_range(value), force_internal); } template inline basic_value_view cell::variable_value::do_make_view(structure::basic_view view, bool external_storage) { auto size = view.template get().load(); context ctx(external_storage, size); return view.template get().visit(make_visitor( [&] (imr::pod::view ptr) { auto ex_ptr = static_cast(ptr.load()); if (size > maximum_external_chunk_length) { auto ex_ctx = chunk_context(ex_ptr); auto ex_view = external_chunk::make_view(ex_ptr, ex_ctx); auto next = static_cast(ex_view.get().load()); return basic_value_view(ex_view.get(ex_ctx), size - maximum_external_chunk_length, next); } else { auto ex_ctx = last_chunk_context(ex_ptr); auto ex_view = external_last_chunk::make_view(ex_ptr, ex_ctx); assert(ex_view.get(ex_ctx).size() == size); return basic_value_view(ex_view.get(ex_ctx), 0, nullptr); } }, [] (imr::buffer::basic_view data) { return basic_value_view(data, 0, nullptr); } ), ctx); } }