git-subtree-dir: third_party/immer git-subtree-split: ad3e3556d38bb75966dd24c61a774970a7c7957e
932 lines
28 KiB
C++
932 lines
28 KiB
C++
//
|
|
// immer: immutable data structures for C++
|
|
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
|
|
//
|
|
// This software is distributed under the Boost Software License, Version 1.0.
|
|
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include <immer/detail/combine_standard_layout.hpp>
|
|
#include <immer/detail/rbts/bits.hpp>
|
|
#include <immer/detail/util.hpp>
|
|
#include <immer/heap/tags.hpp>
|
|
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
|
|
namespace immer {
|
|
namespace detail {
|
|
namespace rbts {
|
|
|
|
template <typename T, typename MemoryPolicy, bits_t B, bits_t BL>
|
|
struct node
|
|
{
|
|
static constexpr auto bits = B;
|
|
static constexpr auto bits_leaf = BL;
|
|
|
|
using node_t = node;
|
|
using memory = MemoryPolicy;
|
|
using heap_policy = typename memory::heap;
|
|
using transience = typename memory::transience_t;
|
|
using refs_t = typename memory::refcount;
|
|
using ownee_t = typename transience::ownee;
|
|
using edit_t = typename transience::edit;
|
|
using value_t = T;
|
|
|
|
static constexpr bool embed_relaxed = memory::prefer_fewer_bigger_objects;
|
|
|
|
enum class kind_t
|
|
{
|
|
leaf,
|
|
inner
|
|
};
|
|
|
|
struct relaxed_data_t
|
|
{
|
|
count_t count;
|
|
size_t sizes[branches<B>];
|
|
};
|
|
|
|
using relaxed_data_with_meta_t =
|
|
combine_standard_layout_t<relaxed_data_t, refs_t, ownee_t>;
|
|
|
|
using relaxed_data_no_meta_t = combine_standard_layout_t<relaxed_data_t>;
|
|
|
|
using relaxed_t = std::conditional_t<embed_relaxed,
|
|
relaxed_data_no_meta_t,
|
|
relaxed_data_with_meta_t>;
|
|
|
|
struct leaf_t
|
|
{
|
|
aligned_storage_for<T> buffer;
|
|
};
|
|
|
|
struct inner_t
|
|
{
|
|
relaxed_t* relaxed;
|
|
aligned_storage_for<node_t*> buffer;
|
|
};
|
|
|
|
union data_t
|
|
{
|
|
inner_t inner;
|
|
leaf_t leaf;
|
|
};
|
|
|
|
struct impl_data_t
|
|
{
|
|
#if IMMER_TAGGED_NODE
|
|
kind_t kind;
|
|
#endif
|
|
data_t data;
|
|
};
|
|
|
|
using impl_t = combine_standard_layout_t<impl_data_t, refs_t, ownee_t>;
|
|
|
|
impl_t impl;
|
|
|
|
// assume that we need to keep headroom space in the node when we
|
|
// are doing reference counting, since any node may become
|
|
// transient when it has only one reference
|
|
constexpr static bool keep_headroom = !std::is_empty<refs_t>{};
|
|
|
|
constexpr static std::size_t sizeof_packed_leaf_n(count_t count)
|
|
{
|
|
return immer_offsetof(impl_t, d.data.leaf.buffer) +
|
|
sizeof(leaf_t::buffer) * count;
|
|
}
|
|
|
|
constexpr static std::size_t sizeof_packed_inner_n(count_t count)
|
|
{
|
|
return immer_offsetof(impl_t, d.data.inner.buffer) +
|
|
sizeof(inner_t::buffer) * count;
|
|
}
|
|
|
|
constexpr static std::size_t sizeof_packed_relaxed_n(count_t count)
|
|
{
|
|
return immer_offsetof(relaxed_t, d.sizes) + sizeof(size_t) * count;
|
|
}
|
|
|
|
constexpr static std::size_t sizeof_packed_inner_r_n(count_t count)
|
|
{
|
|
return embed_relaxed ? sizeof_packed_inner_n(count) +
|
|
sizeof_packed_relaxed_n(count)
|
|
: sizeof_packed_inner_n(count);
|
|
}
|
|
|
|
constexpr static std::size_t max_sizeof_leaf =
|
|
sizeof_packed_leaf_n(branches<BL>);
|
|
|
|
constexpr static std::size_t max_sizeof_inner =
|
|
sizeof_packed_inner_n(branches<B>);
|
|
|
|
constexpr static std::size_t max_sizeof_relaxed =
|
|
sizeof_packed_relaxed_n(branches<B>);
|
|
|
|
constexpr static std::size_t max_sizeof_inner_r =
|
|
sizeof_packed_inner_r_n(branches<B>);
|
|
|
|
constexpr static std::size_t sizeof_inner_n(count_t n)
|
|
{
|
|
return keep_headroom ? max_sizeof_inner : sizeof_packed_inner_n(n);
|
|
}
|
|
|
|
constexpr static std::size_t sizeof_inner_r_n(count_t n)
|
|
{
|
|
return keep_headroom ? max_sizeof_inner_r : sizeof_packed_inner_r_n(n);
|
|
}
|
|
|
|
constexpr static std::size_t sizeof_relaxed_n(count_t n)
|
|
{
|
|
return keep_headroom ? max_sizeof_relaxed : sizeof_packed_relaxed_n(n);
|
|
}
|
|
|
|
constexpr static std::size_t sizeof_leaf_n(count_t n)
|
|
{
|
|
return keep_headroom ? max_sizeof_leaf : sizeof_packed_leaf_n(n);
|
|
}
|
|
|
|
using heap =
|
|
typename heap_policy::template optimized<max_sizeof_inner>::type;
|
|
|
|
#if IMMER_TAGGED_NODE
|
|
kind_t kind() const { return impl.d.kind; }
|
|
#endif
|
|
|
|
relaxed_t* relaxed()
|
|
{
|
|
IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
|
|
return impl.d.data.inner.relaxed;
|
|
}
|
|
|
|
const relaxed_t* relaxed() const
|
|
{
|
|
IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
|
|
return impl.d.data.inner.relaxed;
|
|
}
|
|
|
|
node_t** inner()
|
|
{
|
|
IMMER_ASSERT_TAGGED(kind() == kind_t::inner);
|
|
return reinterpret_cast<node_t**>(&impl.d.data.inner.buffer);
|
|
}
|
|
|
|
T* leaf()
|
|
{
|
|
IMMER_ASSERT_TAGGED(kind() == kind_t::leaf);
|
|
return reinterpret_cast<T*>(&impl.d.data.leaf.buffer);
|
|
}
|
|
|
|
static refs_t& refs(const relaxed_t* x)
|
|
{
|
|
return auto_const_cast(get<refs_t>(*x));
|
|
}
|
|
static const ownee_t& ownee(const relaxed_t* x) { return get<ownee_t>(*x); }
|
|
static ownee_t& ownee(relaxed_t* x) { return get<ownee_t>(*x); }
|
|
|
|
static refs_t& refs(const node_t* x)
|
|
{
|
|
return auto_const_cast(get<refs_t>(x->impl));
|
|
}
|
|
static const ownee_t& ownee(const node_t* x)
|
|
{
|
|
return get<ownee_t>(x->impl);
|
|
}
|
|
static ownee_t& ownee(node_t* x) { return get<ownee_t>(x->impl); }
|
|
|
|
static node_t* make_inner_n(count_t n)
|
|
{
|
|
assert(n <= branches<B>);
|
|
auto m = heap::allocate(sizeof_inner_n(n));
|
|
auto p = new (m) node_t;
|
|
p->impl.d.data.inner.relaxed = nullptr;
|
|
#if IMMER_TAGGED_NODE
|
|
p->impl.d.kind = node_t::kind_t::inner;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_e(edit_t e)
|
|
{
|
|
auto m = heap::allocate(max_sizeof_inner);
|
|
auto p = new (m) node_t;
|
|
ownee(p) = e;
|
|
p->impl.d.data.inner.relaxed = nullptr;
|
|
#if IMMER_TAGGED_NODE
|
|
p->impl.d.kind = node_t::kind_t::inner;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_r_n(count_t n)
|
|
{
|
|
assert(n <= branches<B>);
|
|
auto mp = heap::allocate(sizeof_inner_r_n(n));
|
|
auto mr = static_cast<void*>(nullptr);
|
|
if (embed_relaxed) {
|
|
mr = reinterpret_cast<unsigned char*>(mp) + sizeof_inner_n(n);
|
|
} else {
|
|
try {
|
|
mr = heap::allocate(sizeof_relaxed_n(n), norefs_tag{});
|
|
} catch (...) {
|
|
heap::deallocate(sizeof_inner_r_n(n), mp);
|
|
throw;
|
|
}
|
|
}
|
|
auto p = new (mp) node_t;
|
|
auto r = new (mr) relaxed_t;
|
|
r->d.count = 0;
|
|
p->impl.d.data.inner.relaxed = r;
|
|
#if IMMER_TAGGED_NODE
|
|
p->impl.d.kind = node_t::kind_t::inner;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_sr_n(count_t n, relaxed_t* r)
|
|
{
|
|
return static_if<embed_relaxed, node_t*>(
|
|
[&](auto) { return node_t::make_inner_r_n(n); },
|
|
[&](auto) {
|
|
auto p =
|
|
new (heap::allocate(node_t::sizeof_inner_r_n(n))) node_t;
|
|
assert(r->d.count >= n);
|
|
node_t::refs(r).inc();
|
|
p->impl.d.data.inner.relaxed = r;
|
|
#if IMMER_TAGGED_NODE
|
|
p->impl.d.kind = node_t::kind_t::inner;
|
|
#endif
|
|
return p;
|
|
});
|
|
}
|
|
|
|
static node_t* make_inner_r_e(edit_t e)
|
|
{
|
|
auto mp = heap::allocate(max_sizeof_inner_r);
|
|
auto mr = static_cast<void*>(nullptr);
|
|
if (embed_relaxed) {
|
|
mr = reinterpret_cast<unsigned char*>(mp) + max_sizeof_inner;
|
|
} else {
|
|
try {
|
|
mr = heap::allocate(max_sizeof_relaxed, norefs_tag{});
|
|
} catch (...) {
|
|
heap::deallocate(max_sizeof_inner_r, mp);
|
|
throw;
|
|
}
|
|
}
|
|
auto p = new (mp) node_t;
|
|
auto r = new (mr) relaxed_t;
|
|
ownee(p) = e;
|
|
static_if<!embed_relaxed>([&](auto) { node_t::ownee(r) = e; });
|
|
r->d.count = 0;
|
|
p->impl.d.data.inner.relaxed = r;
|
|
#if IMMER_TAGGED_NODE
|
|
p->impl.d.kind = node_t::kind_t::inner;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_sr_e(edit_t e, relaxed_t* r)
|
|
{
|
|
return static_if<embed_relaxed, node_t*>(
|
|
[&](auto) { return node_t::make_inner_r_e(e); },
|
|
[&](auto) {
|
|
auto p =
|
|
new (heap::allocate(node_t::max_sizeof_inner_r)) node_t;
|
|
node_t::refs(r).inc();
|
|
p->impl.d.data.inner.relaxed = r;
|
|
node_t::ownee(p) = e;
|
|
#if IMMER_TAGGED_NODE
|
|
p->impl.d.kind = node_t::kind_t::inner;
|
|
#endif
|
|
return p;
|
|
});
|
|
}
|
|
|
|
static node_t* make_leaf_n(count_t n)
|
|
{
|
|
assert(n <= branches<BL>);
|
|
auto p = new (heap::allocate(sizeof_leaf_n(n))) node_t;
|
|
#if IMMER_TAGGED_NODE
|
|
p->impl.d.kind = node_t::kind_t::leaf;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_leaf_e(edit_t e)
|
|
{
|
|
auto p = new (heap::allocate(max_sizeof_leaf)) node_t;
|
|
ownee(p) = e;
|
|
#if IMMER_TAGGED_NODE
|
|
p->impl.d.kind = node_t::kind_t::leaf;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_n(count_t n, node_t* x)
|
|
{
|
|
assert(n >= 1);
|
|
auto p = make_inner_n(n);
|
|
p->inner()[0] = x;
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_n(edit_t n, node_t* x)
|
|
{
|
|
assert(n >= 1);
|
|
auto p = make_inner_n(n);
|
|
p->inner()[0] = x;
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_n(count_t n, node_t* x, node_t* y)
|
|
{
|
|
assert(n >= 2);
|
|
auto p = make_inner_n(n);
|
|
p->inner()[0] = x;
|
|
p->inner()[1] = y;
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_r_n(count_t n, node_t* x)
|
|
{
|
|
assert(n >= 1);
|
|
auto p = make_inner_r_n(n);
|
|
auto r = p->relaxed();
|
|
p->inner()[0] = x;
|
|
r->d.count = 1;
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs)
|
|
{
|
|
assert(n >= 1);
|
|
auto p = make_inner_r_n(n);
|
|
auto r = p->relaxed();
|
|
p->inner()[0] = x;
|
|
r->d.sizes[0] = xs;
|
|
r->d.count = 1;
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_r_n(count_t n, node_t* x, node_t* y)
|
|
{
|
|
assert(n >= 2);
|
|
auto p = make_inner_r_n(n);
|
|
auto r = p->relaxed();
|
|
p->inner()[0] = x;
|
|
p->inner()[1] = y;
|
|
r->d.count = 2;
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs, node_t* y)
|
|
{
|
|
assert(n >= 2);
|
|
auto p = make_inner_r_n(n);
|
|
auto r = p->relaxed();
|
|
p->inner()[0] = x;
|
|
p->inner()[1] = y;
|
|
r->d.sizes[0] = xs;
|
|
r->d.count = 2;
|
|
return p;
|
|
}
|
|
|
|
static node_t*
|
|
make_inner_r_n(count_t n, node_t* x, size_t xs, node_t* y, size_t ys)
|
|
{
|
|
assert(n >= 2);
|
|
auto p = make_inner_r_n(n);
|
|
auto r = p->relaxed();
|
|
p->inner()[0] = x;
|
|
p->inner()[1] = y;
|
|
r->d.sizes[0] = xs;
|
|
r->d.sizes[1] = xs + ys;
|
|
r->d.count = 2;
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_inner_r_n(count_t n,
|
|
node_t* x,
|
|
size_t xs,
|
|
node_t* y,
|
|
size_t ys,
|
|
node_t* z,
|
|
size_t zs)
|
|
{
|
|
assert(n >= 3);
|
|
auto p = make_inner_r_n(n);
|
|
auto r = p->relaxed();
|
|
p->inner()[0] = x;
|
|
p->inner()[1] = y;
|
|
p->inner()[2] = z;
|
|
r->d.sizes[0] = xs;
|
|
r->d.sizes[1] = xs + ys;
|
|
r->d.sizes[2] = xs + ys + zs;
|
|
r->d.count = 3;
|
|
return p;
|
|
}
|
|
|
|
template <typename U>
|
|
static node_t* make_leaf_n(count_t n, U&& x)
|
|
{
|
|
assert(n >= 1);
|
|
auto p = make_leaf_n(n);
|
|
try {
|
|
new (p->leaf()) T{std::forward<U>(x)};
|
|
} catch (...) {
|
|
heap::deallocate(node_t::sizeof_leaf_n(n), p);
|
|
throw;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
template <typename U>
|
|
static node_t* make_leaf_e(edit_t e, U&& x)
|
|
{
|
|
auto p = make_leaf_e(e);
|
|
try {
|
|
new (p->leaf()) T{std::forward<U>(x)};
|
|
} catch (...) {
|
|
heap::deallocate(node_t::max_sizeof_leaf, p);
|
|
throw;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static node_t* make_path(shift_t shift, node_t* node)
|
|
{
|
|
IMMER_ASSERT_TAGGED(node->kind() == kind_t::leaf);
|
|
if (shift == endshift<B, BL>)
|
|
return node;
|
|
else {
|
|
auto n = node_t::make_inner_n(1);
|
|
try {
|
|
n->inner()[0] = make_path(shift - B, node);
|
|
} catch (...) {
|
|
heap::deallocate(node_t::sizeof_inner_n(1), n);
|
|
throw;
|
|
}
|
|
return n;
|
|
}
|
|
}
|
|
|
|
static node_t* make_path_e(edit_t e, shift_t shift, node_t* node)
|
|
{
|
|
IMMER_ASSERT_TAGGED(node->kind() == kind_t::leaf);
|
|
if (shift == endshift<B, BL>)
|
|
return node;
|
|
else {
|
|
auto n = node_t::make_inner_e(e);
|
|
try {
|
|
n->inner()[0] = make_path_e(e, shift - B, node);
|
|
} catch (...) {
|
|
heap::deallocate(node_t::max_sizeof_inner, n);
|
|
throw;
|
|
}
|
|
return n;
|
|
}
|
|
}
|
|
|
|
static node_t* copy_inner(node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto dst = make_inner_n(n);
|
|
inc_nodes(src->inner(), n);
|
|
std::uninitialized_copy(src->inner(), src->inner() + n, dst->inner());
|
|
return dst;
|
|
}
|
|
|
|
static node_t* copy_inner_n(count_t allocn, node_t* src, count_t n)
|
|
{
|
|
assert(allocn >= n);
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto dst = make_inner_n(allocn);
|
|
return do_copy_inner(dst, src, n);
|
|
}
|
|
|
|
static node_t* copy_inner_e(edit_t e, node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto dst = make_inner_e(e);
|
|
return do_copy_inner(dst, src, n);
|
|
}
|
|
|
|
static node_t* do_copy_inner(node_t* dst, node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner);
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto p = src->inner();
|
|
inc_nodes(p, n);
|
|
std::uninitialized_copy(p, p + n, dst->inner());
|
|
return dst;
|
|
}
|
|
|
|
static node_t* copy_inner_r(node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto dst = make_inner_r_n(n);
|
|
return do_copy_inner_r(dst, src, n);
|
|
}
|
|
|
|
static node_t* copy_inner_r_n(count_t allocn, node_t* src, count_t n)
|
|
{
|
|
assert(allocn >= n);
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto dst = make_inner_r_n(allocn);
|
|
return do_copy_inner_r(dst, src, n);
|
|
}
|
|
|
|
static node_t* copy_inner_r_e(edit_t e, node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto dst = make_inner_r_e(e);
|
|
return do_copy_inner_r(dst, src, n);
|
|
}
|
|
|
|
static node_t* copy_inner_sr_e(edit_t e, node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto dst = make_inner_sr_e(e, src->relaxed());
|
|
return do_copy_inner_sr(dst, src, n);
|
|
}
|
|
|
|
static node_t* do_copy_inner_r(node_t* dst, node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(dst->kind() == kind_t::inner);
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::inner);
|
|
auto src_r = src->relaxed();
|
|
auto dst_r = dst->relaxed();
|
|
inc_nodes(src->inner(), n);
|
|
std::copy(src->inner(), src->inner() + n, dst->inner());
|
|
std::copy(src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
|
|
dst_r->d.count = n;
|
|
return dst;
|
|
}
|
|
|
|
static node_t* do_copy_inner_sr(node_t* dst, node_t* src, count_t n)
|
|
{
|
|
if (embed_relaxed)
|
|
return do_copy_inner_r(dst, src, n);
|
|
else {
|
|
inc_nodes(src->inner(), n);
|
|
std::copy(src->inner(), src->inner() + n, dst->inner());
|
|
return dst;
|
|
}
|
|
}
|
|
|
|
static node_t* copy_leaf(node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
|
|
auto dst = make_leaf_n(n);
|
|
try {
|
|
std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
|
|
} catch (...) {
|
|
heap::deallocate(node_t::sizeof_leaf_n(n), dst);
|
|
throw;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static node_t* copy_leaf_e(edit_t e, node_t* src, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
|
|
auto dst = make_leaf_e(e);
|
|
try {
|
|
std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
|
|
} catch (...) {
|
|
heap::deallocate(node_t::max_sizeof_leaf, dst);
|
|
throw;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static node_t* copy_leaf_n(count_t allocn, node_t* src, count_t n)
|
|
{
|
|
assert(allocn >= n);
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
|
|
auto dst = make_leaf_n(allocn);
|
|
try {
|
|
std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf());
|
|
} catch (...) {
|
|
heap::deallocate(node_t::sizeof_leaf_n(allocn), dst);
|
|
throw;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static node_t* copy_leaf(node_t* src1, count_t n1, node_t* src2, count_t n2)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src1->kind() == kind_t::leaf);
|
|
IMMER_ASSERT_TAGGED(src2->kind() == kind_t::leaf);
|
|
auto dst = make_leaf_n(n1 + n2);
|
|
try {
|
|
std::uninitialized_copy(
|
|
src1->leaf(), src1->leaf() + n1, dst->leaf());
|
|
} catch (...) {
|
|
heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst);
|
|
throw;
|
|
}
|
|
try {
|
|
std::uninitialized_copy(
|
|
src2->leaf(), src2->leaf() + n2, dst->leaf() + n1);
|
|
} catch (...) {
|
|
destroy_n(dst->leaf(), n1);
|
|
heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst);
|
|
throw;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static node_t*
|
|
copy_leaf_e(edit_t e, node_t* src1, count_t n1, node_t* src2, count_t n2)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src1->kind() == kind_t::leaf);
|
|
IMMER_ASSERT_TAGGED(src2->kind() == kind_t::leaf);
|
|
auto dst = make_leaf_e(e);
|
|
try {
|
|
std::uninitialized_copy(
|
|
src1->leaf(), src1->leaf() + n1, dst->leaf());
|
|
} catch (...) {
|
|
heap::deallocate(max_sizeof_leaf, dst);
|
|
throw;
|
|
}
|
|
try {
|
|
std::uninitialized_copy(
|
|
src2->leaf(), src2->leaf() + n2, dst->leaf() + n1);
|
|
} catch (...) {
|
|
destroy_n(dst->leaf(), n1);
|
|
heap::deallocate(max_sizeof_leaf, dst);
|
|
throw;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static node_t* copy_leaf_e(edit_t e, node_t* src, count_t idx, count_t last)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
|
|
auto dst = make_leaf_e(e);
|
|
try {
|
|
std::uninitialized_copy(
|
|
src->leaf() + idx, src->leaf() + last, dst->leaf());
|
|
} catch (...) {
|
|
heap::deallocate(max_sizeof_leaf, dst);
|
|
throw;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static node_t* copy_leaf(node_t* src, count_t idx, count_t last)
|
|
{
|
|
IMMER_ASSERT_TAGGED(src->kind() == kind_t::leaf);
|
|
auto dst = make_leaf_n(last - idx);
|
|
try {
|
|
std::uninitialized_copy(
|
|
src->leaf() + idx, src->leaf() + last, dst->leaf());
|
|
} catch (...) {
|
|
heap::deallocate(node_t::sizeof_leaf_n(last - idx), dst);
|
|
throw;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
template <typename U>
|
|
static node_t* copy_leaf_emplace(node_t* src, count_t n, U&& x)
|
|
{
|
|
auto dst = copy_leaf_n(n + 1, src, n);
|
|
try {
|
|
new (dst->leaf() + n) T{std::forward<U>(x)};
|
|
} catch (...) {
|
|
destroy_n(dst->leaf(), n);
|
|
heap::deallocate(node_t::sizeof_leaf_n(n + 1), dst);
|
|
throw;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static void delete_inner(node_t* p, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
|
|
assert(!p->relaxed());
|
|
heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_inner
|
|
: node_t::sizeof_inner_n(n),
|
|
p);
|
|
}
|
|
|
|
static void delete_inner_e(node_t* p)
|
|
{
|
|
IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
|
|
assert(!p->relaxed());
|
|
heap::deallocate(node_t::max_sizeof_inner, p);
|
|
}
|
|
|
|
static void delete_inner_any(node_t* p, count_t n)
|
|
{
|
|
if (p->relaxed())
|
|
delete_inner_r(p, n);
|
|
else
|
|
delete_inner(p, n);
|
|
}
|
|
|
|
static void delete_inner_r(node_t* p, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
|
|
auto r = p->relaxed();
|
|
assert(r);
|
|
static_if<!embed_relaxed>([&](auto) {
|
|
if (node_t::refs(r).dec())
|
|
heap::deallocate(node_t::ownee(r).owned()
|
|
? node_t::max_sizeof_relaxed
|
|
: node_t::sizeof_relaxed_n(n),
|
|
r);
|
|
});
|
|
heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_inner_r
|
|
: node_t::sizeof_inner_r_n(n),
|
|
p);
|
|
}
|
|
|
|
static void delete_inner_r_e(node_t* p)
|
|
{
|
|
IMMER_ASSERT_TAGGED(p->kind() == kind_t::inner);
|
|
auto r = p->relaxed();
|
|
assert(r);
|
|
static_if<!embed_relaxed>([&](auto) {
|
|
if (node_t::refs(r).dec())
|
|
heap::deallocate(node_t::max_sizeof_relaxed, r);
|
|
});
|
|
heap::deallocate(node_t::max_sizeof_inner_r, p);
|
|
}
|
|
|
|
static void delete_leaf(node_t* p, count_t n)
|
|
{
|
|
IMMER_ASSERT_TAGGED(p->kind() == kind_t::leaf);
|
|
destroy_n(p->leaf(), n);
|
|
heap::deallocate(ownee(p).owned() ? node_t::max_sizeof_leaf
|
|
: node_t::sizeof_leaf_n(n),
|
|
p);
|
|
}
|
|
|
|
bool can_mutate(edit_t e) const
|
|
{
|
|
return refs(this).unique() || ownee(this).can_mutate(e);
|
|
}
|
|
|
|
bool can_relax() const { return !embed_relaxed || relaxed(); }
|
|
|
|
relaxed_t* ensure_mutable_relaxed(edit_t e)
|
|
{
|
|
auto src_r = relaxed();
|
|
return static_if<embed_relaxed, relaxed_t*>(
|
|
[&](auto) { return src_r; },
|
|
[&](auto) {
|
|
if (node_t::refs(src_r).unique() ||
|
|
node_t::ownee(src_r).can_mutate(e))
|
|
return src_r;
|
|
else {
|
|
if (src_r)
|
|
node_t::refs(src_r).dec_unsafe();
|
|
auto dst_r = impl.d.data.inner.relaxed =
|
|
new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
|
|
node_t::ownee(dst_r) = e;
|
|
return dst_r;
|
|
}
|
|
});
|
|
}
|
|
|
|
relaxed_t* ensure_mutable_relaxed_e(edit_t e, edit_t ec)
|
|
{
|
|
auto src_r = relaxed();
|
|
return static_if<embed_relaxed, relaxed_t*>(
|
|
[&](auto) { return src_r; },
|
|
[&](auto) {
|
|
if (src_r && (node_t::refs(src_r).unique() ||
|
|
node_t::ownee(src_r).can_mutate(e))) {
|
|
node_t::ownee(src_r) = ec;
|
|
return src_r;
|
|
} else {
|
|
if (src_r)
|
|
node_t::refs(src_r).dec_unsafe();
|
|
auto dst_r = impl.d.data.inner.relaxed =
|
|
new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
|
|
node_t::ownee(dst_r) = ec;
|
|
return dst_r;
|
|
}
|
|
});
|
|
}
|
|
|
|
relaxed_t* ensure_mutable_relaxed_n(edit_t e, count_t n)
|
|
{
|
|
auto src_r = relaxed();
|
|
return static_if<embed_relaxed, relaxed_t*>(
|
|
[&](auto) { return src_r; },
|
|
[&](auto) {
|
|
if (node_t::refs(src_r).unique() ||
|
|
node_t::ownee(src_r).can_mutate(e))
|
|
return src_r;
|
|
else {
|
|
if (src_r)
|
|
node_t::refs(src_r).dec_unsafe();
|
|
auto dst_r =
|
|
new (heap::allocate(max_sizeof_relaxed)) relaxed_t;
|
|
std::copy(
|
|
src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes);
|
|
node_t::ownee(dst_r) = e;
|
|
return impl.d.data.inner.relaxed = dst_r;
|
|
}
|
|
});
|
|
}
|
|
|
|
node_t* inc()
|
|
{
|
|
refs(this).inc();
|
|
return this;
|
|
}
|
|
|
|
const node_t* inc() const
|
|
{
|
|
refs(this).inc();
|
|
return this;
|
|
}
|
|
|
|
bool dec() const { return refs(this).dec(); }
|
|
void dec_unsafe() const { refs(this).dec_unsafe(); }
|
|
|
|
static void inc_nodes(node_t** p, count_t n)
|
|
{
|
|
for (auto i = p, e = i + n; i != e; ++i)
|
|
refs(*i).inc();
|
|
}
|
|
|
|
#if IMMER_TAGGED_NODE
|
|
shift_t compute_shift()
|
|
{
|
|
if (kind() == kind_t::leaf)
|
|
return endshift<B, BL>;
|
|
else
|
|
return B + inner()[0]->compute_shift();
|
|
}
|
|
#endif
|
|
|
|
bool check(shift_t shift, size_t size)
|
|
{
|
|
#if IMMER_DEBUG_DEEP_CHECK
|
|
assert(size > 0);
|
|
if (shift == endshift<B, BL>) {
|
|
IMMER_ASSERT_TAGGED(kind() == kind_t::leaf);
|
|
assert(size <= branches<BL>);
|
|
} else if (auto r = relaxed()) {
|
|
auto count = r->d.count;
|
|
assert(count > 0);
|
|
assert(count <= branches<B>);
|
|
if (r->d.sizes[count - 1] != size) {
|
|
IMMER_TRACE_F("check");
|
|
IMMER_TRACE_E(r->d.sizes[count - 1]);
|
|
IMMER_TRACE_E(size);
|
|
}
|
|
assert(r->d.sizes[count - 1] == size);
|
|
for (auto i = 1; i < count; ++i)
|
|
assert(r->d.sizes[i - 1] < r->d.sizes[i]);
|
|
auto last_size = size_t{};
|
|
for (auto i = 0; i < count; ++i) {
|
|
assert(inner()[i]->check(shift - B, r->d.sizes[i] - last_size));
|
|
last_size = r->d.sizes[i];
|
|
}
|
|
} else {
|
|
assert(size <= branches<B> << shift);
|
|
auto count =
|
|
(size >> shift) + (size - ((size >> shift) << shift) > 0);
|
|
assert(count <= branches<B>);
|
|
if (count) {
|
|
for (auto i = 1; i < count - 1; ++i)
|
|
assert(inner()[i]->check(shift - B, 1 << shift));
|
|
assert(inner()[count - 1]->check(
|
|
shift - B, size - ((count - 1) << shift)));
|
|
}
|
|
}
|
|
#endif // IMMER_DEBUG_DEEP_CHECK
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename T, typename MP, bits_t B>
|
|
constexpr bits_t derive_bits_leaf_aux()
|
|
{
|
|
using node_t = node<T, MP, B, B>;
|
|
constexpr auto sizeof_elem = sizeof(T);
|
|
constexpr auto space =
|
|
node_t::max_sizeof_inner - node_t::sizeof_packed_leaf_n(0);
|
|
constexpr auto full_elems = space / sizeof_elem;
|
|
constexpr auto BL = log2(full_elems);
|
|
return BL;
|
|
}
|
|
|
|
template <typename T, typename MP, bits_t B>
|
|
constexpr bits_t derive_bits_leaf = derive_bits_leaf_aux<T, MP, B>();
|
|
|
|
} // namespace rbts
|
|
} // namespace detail
|
|
} // namespace immer
|