/*
* 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);
}
}