/*
* 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 "imr/core.hh"
#include "imr/alloc.hh"
#include "imr/concepts.hh"
namespace imr {
namespace utils {
class basic_object {
public:
struct tags {
class back_pointer { };
class object { };
};
protected:
uint8_t* _data = nullptr;
friend struct methods::mover>>;
protected:
explicit basic_object(uint8_t* ptr) noexcept : _data(ptr) { }
void set_data(uint8_t* ptr) noexcept { _data = ptr; }
public:
basic_object() = default;
basic_object(basic_object&& other) noexcept : _data(std::exchange(other._data, nullptr)) { }
basic_object(const basic_object&) = delete;
};
template
class object_context {
std::tuple _state;
private:
template
Context create(const uint8_t* ptr, std::index_sequence) const noexcept {
return Context(ptr, std::get(_state)...);
}
public:
object_context(const uint8_t*, State... state) : _state { state... } { }
template
auto context_for(const uint8_t* ptr, Args&&... args) const noexcept {
if constexpr (std::is_same_v) {
return no_context_t();
} else {
return create(ptr, std::index_sequence_for());
}
}
};
}
namespace methods {
template<>
struct mover>> {
static void run(uint8_t* ptr, ...) {
auto bptr = imr::tagged_type>::make_view(ptr).load();
bptr->_data = ptr;
}
};
}
namespace utils {
/// Unique pointer to an IMR object
///
/// This is an LSA-aware unique-owner pointer to an IMR object.
template
class object : public basic_object {
public:
using structure = imr::structure<
imr::member>>,
imr::member
>;
static constexpr size_t size_overhead = sizeof(basic_object*);
private:
explicit object(uint8_t* ptr) noexcept
: basic_object(ptr)
{
structure::template get_member(_data).store(this);
}
public:
object() = default;
object(object&& other) noexcept : basic_object(std::move(other)) {
if (_data) {
structure::template get_member(_data).store(this);
}
}
object& operator=(object&& other) noexcept {
swap(other);
return *this;
}
~object() {
if (_data) {
imr::methods::destroy(_data);
current_allocator().free(_data);
}
}
void swap(object& other) noexcept {
std::swap(_data, other._data);
if (_data) {
structure::template get_member(_data).store(this);
}
if (other._data) {
structure::template get_member(other._data).store(&other);
}
}
explicit operator bool() const noexcept { return bool(_data); }
uint8_t* get() noexcept { return _data ? _data + structure::template offset_of(_data) : nullptr; }
const uint8_t* get() const noexcept { return _data ? _data + structure::template offset_of(_data) : nullptr; }
/// Creates an IMR object from a raw writer
///
/// This low-level function creates an IMR object owned by `object` using
/// a raw writer (i.e. does not necessarily follow the standard IMR
/// serialisation process). This is useful for fast copying of trivial
/// IMR objects.
///
/// \note This function could be deprecated once the IMR starts supporting
/// copying IMR objects.
template
GCC6_CONCEPT(requires requires (RawWriter wr, uint8_t* ptr) {
{ wr(ptr) } noexcept;
})
static object make_raw(size_t len, RawWriter&& wr, allocation_strategy::migrate_fn migrate = &imr::alloc::default_lsa_migrate_fn::migrate_fn) {
object obj;
auto ptr = static_cast(current_allocator().alloc(migrate, sizeof(void*) + len, 1));
wr(ptr + sizeof(void*));
auto view = structure::make_view(ptr);
view.template get().store(&obj);
obj.set_data(ptr);
return obj;
}
/// Create an IMR objects
template
GCC6_CONCEPT(requires WriterAllocator)
static object make(Writer&& object_writer,
MigrateFn* migrate = &imr::alloc::default_lsa_migrate_fn::migrate_fn) {
static_assert(std::is_same_v);
return do_make(std::forward(object_writer), migrate);
}
private:
template
GCC6_CONCEPT(requires WriterAllocator)
static object do_make(Writer&& object_writer, allocation_strategy::migrate_fn migrate) {
struct alloc_deleter {
size_t _size;
void operator()(uint8_t* ptr) {
current_allocator().free(ptr, _size);
}
};
using alloc_unique_ptr = std::unique_ptr;
auto writer = [&object_writer] (auto&& ser, auto&& alloc) {
return object_writer(ser.serialize(nullptr).serialize_nested(), alloc).done();
};
auto& alloc = current_allocator();
alloc::object_allocator allocator(alloc);
auto obj_size = structure::size_when_serialized(writer, allocator.get_sizer());
auto ptr = alloc_unique_ptr(static_cast(alloc.alloc(migrate, obj_size, 1)), alloc_deleter { obj_size });
allocator.allocate_all();
structure::serialize(ptr.get(), writer, allocator.get_serializer());
return object(ptr.release());
}
};
}
}