1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-23 06:44:38 +01:00
Files
OpenRCT2/src/thirdparty/sfl/segmented_devector.hpp
2025-03-22 17:23:26 +02:00

2757 lines
74 KiB
C++

//
// Copyright (c) 2022 Slaven Falandys
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef SFL_SEGMENTED_DEVECTOR_HPP_INCLUDED
#define SFL_SEGMENTED_DEVECTOR_HPP_INCLUDED
#include <sfl/detail/container_compatible_range.hpp>
#include <sfl/detail/cpp.hpp>
#include <sfl/detail/exceptions.hpp>
#include <sfl/detail/initialized_memory_algorithms.hpp>
#include <sfl/detail/segmented_iterator.hpp>
#include <sfl/detail/tags.hpp>
#include <sfl/detail/type_traits.hpp>
#include <sfl/detail/uninitialized_memory_algorithms.hpp>
#include <algorithm> // copy, move, swap, swap_ranges
#include <cstddef> // size_t
#include <initializer_list> // initializer_list
#include <iterator> // distance, next, reverse_iterator
#include <limits> // numeric_limits
#include <memory> // allocator, allocator_traits, pointer_traits
#include <type_traits> // is_same, is_nothrow_xxxxx
#include <utility> // forward, move, pair
namespace sfl
{
template < typename T,
std::size_t N,
typename Allocator = std::allocator<T> >
class segmented_devector
{
static_assert(N > 0, "N must be greater than zero.");
public:
using allocator_type = Allocator;
using allocator_traits = std::allocator_traits<Allocator>;
using value_type = T;
using size_type = typename allocator_traits::size_type;
using difference_type = typename allocator_traits::difference_type;
using reference = T&;
using const_reference = const T&;
using pointer = typename allocator_traits::pointer;
using const_pointer = typename allocator_traits::const_pointer;
private:
using segment_allocator = typename std::allocator_traits<allocator_type>::template rebind_alloc<pointer>;
using segment_pointer = typename std::allocator_traits<segment_allocator>::pointer;
public:
using iterator = sfl::dtl::segmented_iterator<segment_pointer, pointer, N, segmented_devector>;
using const_iterator = sfl::dtl::segmented_iterator<segment_pointer, const_pointer, N, segmented_devector>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static_assert
(
std::is_same<typename Allocator::value_type, value_type>::value,
"Allocator::value_type must be same as sfl::segmented_devector::value_type."
);
public:
static constexpr size_type segment_capacity = N;
private:
class data_base
{
public:
// ---- TABLE (OF SEGMENTS) ----
segment_pointer table_bos_; // Begin of storage
segment_pointer table_eos_; // End of storage
segment_pointer table_first_; // First element in table
segment_pointer table_last_; // One-past-last element in table
// ---- ELEMENTS IN VECTOR ----
iterator bos_; // Begin of storage
iterator eos_; // End of storage
iterator first_; // First element in vector
iterator last_; // One-past-last element in vector
};
class data : public data_base, public allocator_type
{
public:
data() noexcept(std::is_nothrow_default_constructible<allocator_type>::value)
: allocator_type()
{}
data(const allocator_type& alloc) noexcept(std::is_nothrow_copy_constructible<allocator_type>::value)
: allocator_type(alloc)
{}
data(allocator_type&& other) noexcept(std::is_nothrow_move_constructible<allocator_type>::value)
: allocator_type(std::move(other))
{}
allocator_type& ref_to_alloc() noexcept
{
return *this;
}
const allocator_type& ref_to_alloc() const noexcept
{
return *this;
}
};
data data_;
public:
//
// ---- CONSTRUCTION AND DESTRUCTION --------------------------------------
//
segmented_devector()
: data_()
{
initialize_empty();
}
explicit segmented_devector(const Allocator& alloc)
: data_(alloc)
{
initialize_empty();
}
segmented_devector(size_type n)
: data_()
{
initialize_default_n(n);
}
explicit segmented_devector(size_type n, const Allocator& alloc)
: data_(alloc)
{
initialize_default_n(n);
}
segmented_devector(size_type n, const T& value)
: data_()
{
initialize_fill_n(n, value);
}
segmented_devector(size_type n, const T& value, const Allocator& alloc)
: data_(alloc)
{
initialize_fill_n(n, value);
}
template <typename InputIt,
sfl::dtl::enable_if_t<sfl::dtl::is_input_iterator<InputIt>::value>* = nullptr>
segmented_devector(InputIt first, InputIt last)
: data_()
{
initialize_range(first, last);
}
template <typename InputIt,
sfl::dtl::enable_if_t<sfl::dtl::is_input_iterator<InputIt>::value>* = nullptr>
segmented_devector(InputIt first, InputIt last, const Allocator& alloc)
: data_(alloc)
{
initialize_range(first, last);
}
segmented_devector(std::initializer_list<T> ilist)
: segmented_devector(ilist.begin(), ilist.end())
{}
segmented_devector(std::initializer_list<T> ilist, const Allocator& alloc)
: segmented_devector(ilist.begin(), ilist.end(), alloc)
{}
segmented_devector(const segmented_devector& other)
: data_
(
allocator_traits::select_on_container_copy_construction
(
other.data_.ref_to_alloc()
)
)
{
initialize_copy(other);
}
segmented_devector(const segmented_devector& other, const Allocator& alloc)
: data_(alloc)
{
initialize_copy(other);
}
segmented_devector(segmented_devector&& other)
: data_(std::move(other.data_.ref_to_alloc()))
{
initialize_move(other);
}
segmented_devector(segmented_devector&& other, const Allocator& alloc)
: data_(alloc)
{
initialize_move(other);
}
#if SFL_CPP_VERSION >= SFL_CPP_20
template <sfl::dtl::container_compatible_range<value_type> Range>
segmented_devector(sfl::from_range_t, Range&& range)
: data_()
{
initialize_range(std::forward<Range>(range));
}
template <sfl::dtl::container_compatible_range<value_type> Range>
segmented_devector(sfl::from_range_t, Range&& range, const Allocator& alloc)
: data_(alloc)
{
initialize_range(std::forward<Range>(range));
}
#else // before C++20
template <typename Range>
segmented_devector(sfl::from_range_t, Range&& range)
: data_()
{
initialize_range(std::forward<Range>(range));
}
template <typename Range>
segmented_devector(sfl::from_range_t, Range&& range, const Allocator& alloc)
: data_(alloc)
{
initialize_range(std::forward<Range>(range));
}
#endif // before C++20
~segmented_devector()
{
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
data_.first_,
data_.last_
);
deallocate_storage();
}
//
// ---- ASSIGNMENT --------------------------------------------------------
//
void assign(size_type n, const T& value)
{
assign_fill_n(n, value);
}
template <typename InputIt,
sfl::dtl::enable_if_t<sfl::dtl::is_input_iterator<InputIt>::value>* = nullptr>
void assign(InputIt first, InputIt last)
{
assign_range(first, last);
}
void assign(std::initializer_list<T> ilist)
{
assign_range(ilist.begin(), ilist.end());
}
#if SFL_CPP_VERSION >= SFL_CPP_20
template <sfl::dtl::container_compatible_range<value_type> Range>
void assign_range(Range&& range)
{
if constexpr (std::ranges::forward_range<Range>)
{
assign_range(std::ranges::begin(range), std::ranges::end(range), std::forward_iterator_tag());
}
else
{
assign_range(std::ranges::begin(range), std::ranges::end(range), std::input_iterator_tag());
}
}
#else // before C++20
template <typename Range>
void assign_range(Range&& range)
{
using std::begin;
using std::end;
assign_range(begin(range), end(range));
}
#endif // before C++20
segmented_devector& operator=(const segmented_devector& other)
{
assign_copy(other);
return *this;
}
segmented_devector& operator=(segmented_devector&& other)
{
assign_move(other);
return *this;
}
segmented_devector& operator=(std::initializer_list<T> ilist)
{
assign_range(ilist.begin(), ilist.end());
return *this;
}
//
// ---- ALLOCATOR ---------------------------------------------------------
//
SFL_NODISCARD
allocator_type get_allocator() const noexcept
{
return data_.ref_to_alloc();
}
//
// ---- ITERATORS ---------------------------------------------------------
//
SFL_NODISCARD
iterator begin() noexcept
{
return data_.first_;
}
SFL_NODISCARD
const_iterator begin() const noexcept
{
return data_.first_;
}
SFL_NODISCARD
const_iterator cbegin() const noexcept
{
return data_.first_;
}
SFL_NODISCARD
iterator end() noexcept
{
return data_.last_;
}
SFL_NODISCARD
const_iterator end() const noexcept
{
return data_.last_;
}
SFL_NODISCARD
const_iterator cend() const noexcept
{
return data_.last_;
}
SFL_NODISCARD
reverse_iterator rbegin() noexcept
{
return reverse_iterator(end());
}
SFL_NODISCARD
const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
SFL_NODISCARD
const_reverse_iterator crbegin() const noexcept
{
return const_reverse_iterator(end());
}
SFL_NODISCARD
reverse_iterator rend() noexcept
{
return reverse_iterator(begin());
}
SFL_NODISCARD
const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
SFL_NODISCARD
const_reverse_iterator crend() const noexcept
{
return const_reverse_iterator(begin());
}
SFL_NODISCARD
iterator nth(size_type pos) noexcept
{
SFL_ASSERT(pos <= size());
return begin() + pos;
}
SFL_NODISCARD
const_iterator nth(size_type pos) const noexcept
{
SFL_ASSERT(pos <= size());
return cbegin() + pos;
}
SFL_NODISCARD
size_type index_of(const_iterator pos) const noexcept
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
return std::distance(cbegin(), pos);
}
//
// ---- SIZE AND CAPACITY -------------------------------------------------
//
SFL_NODISCARD
bool empty() const noexcept
{
return data_.last_ == data_.first_;
}
SFL_NODISCARD
size_type size() const noexcept
{
return std::distance(data_.first_, data_.last_);
}
SFL_NODISCARD
size_type max_size() const noexcept
{
return std::min<size_type>
(
allocator_traits::max_size(data_.ref_to_alloc()),
std::numeric_limits<difference_type>::max() / sizeof(value_type)
);
}
SFL_NODISCARD
size_type capacity() const noexcept
{
return std::distance(data_.bos_, data_.eos_);
}
SFL_NODISCARD
size_type available_front() const noexcept
{
return std::distance(data_.bos_, data_.first_);
}
SFL_NODISCARD
size_type available_back() const noexcept
{
return std::distance(data_.last_, data_.eos_);
}
void reserve_front(size_type new_capacity)
{
const size_type size = this->size();
const size_type available_front = this->available_front();
if (new_capacity > size + available_front)
{
grow_storage_front(new_capacity - (size + available_front));
}
}
void reserve_back(size_type new_capacity)
{
const size_type size = this->size();
const size_type available_back = this->available_back();
if (new_capacity > size + available_back)
{
grow_storage_back(new_capacity - (size + available_back));
}
}
void shrink_to_fit()
{
shrink_storage();
}
//
// ---- ELEMENT ACCESS ----------------------------------------------------
//
SFL_NODISCARD
reference at(size_type pos)
{
if (pos >= size())
{
sfl::dtl::throw_out_of_range("sfl::segmented_devector::at");
}
return *(begin() + pos);
}
SFL_NODISCARD
const_reference at(size_type pos) const
{
if (pos >= size())
{
sfl::dtl::throw_out_of_range("sfl::segmented_devector::at");
}
return *(cbegin() + pos);
}
SFL_NODISCARD
reference operator[](size_type pos) noexcept
{
SFL_ASSERT(pos < size());
return *(begin() + pos);
}
SFL_NODISCARD
const_reference operator[](size_type pos) const noexcept
{
SFL_ASSERT(pos < size());
return *(cbegin() + pos);
}
SFL_NODISCARD
reference front() noexcept
{
SFL_ASSERT(!empty());
return *data_.first_;
}
SFL_NODISCARD
const_reference front() const noexcept
{
SFL_ASSERT(!empty());
return *data_.first_;
}
SFL_NODISCARD
reference back() noexcept
{
SFL_ASSERT(!empty());
return *(--iterator(data_.last_));
}
SFL_NODISCARD
const_reference back() const noexcept
{
SFL_ASSERT(!empty());
return *(--iterator(data_.last_));
}
//
// ---- MODIFIERS ---------------------------------------------------------
//
void clear() noexcept
{
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
data_.first_,
data_.last_
);
data_.last_ = data_.first_;
}
template <typename... Args>
iterator emplace(const_iterator pos, Args&&... args)
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
iterator p1 = begin() + std::distance(cbegin(), pos);
const size_type dist_to_begin = std::distance(cbegin(), pos);
const size_type dist_to_end = std::distance(pos, cend());
if (dist_to_begin < dist_to_end)
{
if (data_.first_ == data_.bos_)
{
grow_storage_front(1);
p1 = nth(dist_to_begin);
}
const iterator p2 = --iterator(p1);
if (p1 == data_.first_)
{
sfl::dtl::construct_at_a
(
data_.ref_to_alloc(),
std::addressof(*p2),
std::forward<Args>(args)...
);
data_.first_ = p2;
}
else
{
const iterator p3 = ++iterator(data_.first_);
const iterator p4 = --iterator(data_.first_);
const iterator old_first = data_.first_;
// The order of operations is critical. First we will construct
// temporary value because arguments `args...` can contain
// reference to element in this container and after that
// we will move elements and insert new element.
value_type tmp(std::forward<Args>(args)...);
sfl::dtl::construct_at_a
(
data_.ref_to_alloc(),
std::addressof(*p4),
std::move(*data_.first_)
);
data_.first_ = p4;
sfl::dtl::move
(
p3,
p1,
old_first
);
*p2 = std::move(tmp);
}
return p2;
}
else
{
if (data_.last_ == data_.eos_)
{
grow_storage_back(1);
p1 = nth(dist_to_begin);
}
if (p1 == data_.last_)
{
sfl::dtl::construct_at_a
(
data_.ref_to_alloc(),
std::addressof(*data_.last_),
std::forward<Args>(args)...
);
++data_.last_;
}
else
{
const iterator p2 = --iterator(data_.last_);
const iterator old_last = data_.last_;
// The order of operations is critical. First we will construct
// temporary value because arguments `args...` can contain
// reference to element in this container and after that
// we will move elements and insert new element.
value_type tmp(std::forward<Args>(args)...);
sfl::dtl::construct_at_a
(
data_.ref_to_alloc(),
std::addressof(*data_.last_),
std::move(*p2)
);
++data_.last_;
sfl::dtl::move_backward
(
p1,
p2,
old_last
);
*p1 = std::move(tmp);
}
return p1;
}
}
iterator insert(const_iterator pos, const T& value)
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
return emplace(pos, value);
}
iterator insert(const_iterator pos, T&& value)
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
return emplace(pos, std::move(value));
}
iterator insert(const_iterator pos, size_type n, const T& value)
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
return insert_fill_n(pos, n, value);
}
template <typename InputIt,
sfl::dtl::enable_if_t<sfl::dtl::is_input_iterator<InputIt>::value>* = nullptr>
iterator insert(const_iterator pos, InputIt first, InputIt last)
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
return insert_range(pos, first, last);
}
iterator insert(const_iterator pos, std::initializer_list<T> ilist)
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
return insert_range(pos, ilist.begin(), ilist.end());
}
#if SFL_CPP_VERSION >= SFL_CPP_20
template <sfl::dtl::container_compatible_range<value_type> Range>
iterator insert_range(const_iterator pos, Range&& range)
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
if constexpr (std::ranges::forward_range<Range>)
{
return insert_range(pos, std::ranges::begin(range), std::ranges::end(range), std::forward_iterator_tag());
}
else
{
return insert_range(pos, std::ranges::begin(range), std::ranges::end(range), std::input_iterator_tag());
}
}
#else // before C++20
template <typename Range>
iterator insert_range(const_iterator pos, Range&& range)
{
SFL_ASSERT(cbegin() <= pos && pos <= cend());
using std::begin;
using std::end;
return insert_range(pos, begin(range), end(range));
}
#endif // before C++20
template <typename... Args>
reference emplace_front(Args&&... args)
{
if (data_.first_ == data_.bos_)
{
grow_storage_front(1);
}
const iterator new_first = --iterator(data_.first_);
sfl::dtl::construct_at_a
(
data_.ref_to_alloc(),
std::addressof(*new_first),
std::forward<Args>(args)...
);
data_.first_ = new_first;
return *new_first;
}
template <typename... Args>
reference emplace_back(Args&&... args)
{
if (data_.last_ == data_.eos_)
{
grow_storage_back(1);
}
const iterator old_last = data_.last_;
sfl::dtl::construct_at_a
(
data_.ref_to_alloc(),
std::addressof(*data_.last_),
std::forward<Args>(args)...
);
++data_.last_;
return *old_last;
}
void push_front(const T& value)
{
emplace_front(value);
}
void push_front(T&& value)
{
emplace_front(std::move(value));
}
void push_back(const T& value)
{
emplace_back(value);
}
void push_back(T&& value)
{
emplace_back(std::move(value));
}
#if SFL_CPP_VERSION >= SFL_CPP_20
template <sfl::dtl::container_compatible_range<value_type> Range>
void prepend_range(Range&& range)
{
insert_range(begin(), std::forward<Range>(range));
}
template <sfl::dtl::container_compatible_range<value_type> Range>
void append_range(Range&& range)
{
insert_range(end(), std::forward<Range>(range));
}
#else // before C++20
template <typename Range>
void prepend_range(Range&& range)
{
insert_range(begin(), std::forward<Range>(range));
}
template <typename Range>
void append_range(Range&& range)
{
insert_range(end(), std::forward<Range>(range));
}
#endif // before C++20
void pop_front()
{
SFL_ASSERT(!empty());
sfl::dtl::destroy_at_a(data_.ref_to_alloc(), std::addressof(*data_.first_));
++data_.first_;
}
void pop_back()
{
SFL_ASSERT(!empty());
--data_.last_;
sfl::dtl::destroy_at_a(data_.ref_to_alloc(), std::addressof(*data_.last_));
}
iterator erase(const_iterator pos)
{
SFL_ASSERT(cbegin() <= pos && pos < cend());
const size_type dist_to_begin = std::distance(cbegin(), pos);
const size_type dist_to_end = std::distance(pos, cend());
const iterator p1 = begin() + std::distance(cbegin(), pos);
const iterator p2 = ++iterator(p1);
if (dist_to_begin < dist_to_end)
{
const iterator old_first = data_.first_;
data_.first_ = sfl::dtl::move_backward(data_.first_, p1, p2);
sfl::dtl::destroy_at_a(data_.ref_to_alloc(), std::addressof(*old_first));
return p2;
}
else
{
data_.last_ = sfl::dtl::move(p2, data_.last_, p1);
sfl::dtl::destroy_at_a(data_.ref_to_alloc(), std::addressof(*data_.last_));
return p1;
}
}
iterator erase(const_iterator first, const_iterator last)
{
SFL_ASSERT(cbegin() <= first && first <= last && last <= cend());
if (first == last)
{
return begin() + std::distance(cbegin(), first);
}
const size_type dist_to_begin = std::distance(cbegin(), first);
const size_type dist_to_end = std::distance(last, cend());
if (dist_to_begin < dist_to_end)
{
const iterator p1 = begin() + std::distance(cbegin(), first);
const iterator p2 = begin() + std::distance(cbegin(), last);
const iterator new_first = sfl::dtl::move_backward(data_.first_, p1, p2);
sfl::dtl::destroy_a(data_.ref_to_alloc(), data_.first_, new_first);
data_.first_ = new_first;
return p2;
}
else
{
const iterator p1 = begin() + std::distance(cbegin(), first);
const iterator p2 = begin() + std::distance(cbegin(), last);
const iterator new_last = sfl::dtl::move(p2, data_.last_, p1);
sfl::dtl::destroy_a(data_.ref_to_alloc(), new_last, data_.last_);
data_.last_ = new_last;
return p1;
}
}
void resize(size_type n)
{
const size_type size = this->size();
if (n <= size)
{
const iterator new_last = nth(n);
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
new_last,
data_.last_
);
data_.last_ = new_last;
}
else
{
const size_type available_back = this->available_back();
if (n <= size + available_back)
{
data_.last_ = sfl::dtl::uninitialized_default_construct_n_a
(
data_.ref_to_alloc(),
data_.last_,
n - size
);
}
else
{
const size_type available_front = this->available_front();
if (n <= available_front + size + available_back)
{
sfl::dtl::uninitialized_default_construct_a
(
data_.ref_to_alloc(),
data_.last_,
data_.eos_
);
data_.last_ = data_.eos_;
const iterator new_first = data_.first_ - (n - (size + available_back));
sfl::dtl::uninitialized_default_construct_a
(
data_.ref_to_alloc(),
new_first,
data_.first_
);
data_.first_ = new_first;
}
else
{
grow_storage_back(n - (available_front + size + available_back));
data_.last_ = sfl::dtl::uninitialized_default_construct_n_a
(
data_.ref_to_alloc(),
data_.last_,
n - (size + available_front)
);
sfl::dtl::uninitialized_default_construct_a
(
data_.ref_to_alloc(),
data_.bos_,
data_.first_
);
data_.first_ = data_.bos_;
}
}
}
}
void resize(size_type n, const T& value)
{
const size_type size = this->size();
if (n <= size)
{
const iterator new_last = nth(n);
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
new_last,
data_.last_
);
data_.last_ = new_last;
}
else
{
const size_type available_back = this->available_back();
if (n <= size + available_back)
{
data_.last_ = sfl::dtl::uninitialized_fill_n_a
(
data_.ref_to_alloc(),
data_.last_,
n - size,
value
);
}
else
{
const size_type available_front = this->available_front();
if (n <= available_front + size + available_back)
{
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
data_.last_,
data_.eos_,
value
);
data_.last_ = data_.eos_;
const iterator new_first = data_.first_ - (n - (size + available_back));
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
new_first,
data_.first_,
value
);
data_.first_ = new_first;
}
else
{
grow_storage_back(n - (available_front + size + available_back));
data_.last_ = sfl::dtl::uninitialized_fill_n_a
(
data_.ref_to_alloc(),
data_.last_,
n - (size + available_front),
value
);
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
data_.bos_,
data_.first_,
value
);
data_.first_ = data_.bos_;
}
}
}
}
void resize_front(size_type n)
{
const size_type size = this->size();
if (n <= size)
{
const iterator new_first = nth(size - n);
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
data_.first_,
new_first
);
data_.first_ = new_first;
}
else
{
const size_type available_front = this->available_front();
if (n > size + available_front)
{
grow_storage_front(n - (size + available_front));
}
const iterator new_first = data_.first_ - (n - size);
sfl::dtl::uninitialized_default_construct_a
(
data_.ref_to_alloc(),
new_first,
data_.first_
);
data_.first_ = new_first;
}
}
void resize_front(size_type n, const T& value)
{
const size_type size = this->size();
if (n <= size)
{
const iterator new_first = nth(size - n);
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
data_.first_,
new_first
);
data_.first_ = new_first;
}
else
{
const size_type available_front = this->available_front();
if (n > size + available_front)
{
grow_storage_front(n - (size + available_front));
}
const iterator new_first = data_.first_ - (n - size);
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
new_first,
data_.first_,
value
);
data_.first_ = new_first;
}
}
void resize_back(size_type n)
{
const size_type size = this->size();
if (n <= size)
{
const iterator new_last = nth(n);
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
new_last,
data_.last_
);
data_.last_ = new_last;
}
else
{
const size_type available_back = this->available_back();
if (n > size + available_back)
{
grow_storage_back(n - (size + available_back));
}
data_.last_ = sfl::dtl::uninitialized_default_construct_n_a
(
data_.ref_to_alloc(),
data_.last_,
n - size
);
}
}
void resize_back(size_type n, const T& value)
{
const size_type size = this->size();
if (n <= size)
{
const iterator new_last = nth(n);
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
new_last,
data_.last_
);
data_.last_ = new_last;
}
else
{
const size_type available_back = this->available_back();
if (n > size + available_back)
{
grow_storage_back(n - (size + available_back));
}
data_.last_ = sfl::dtl::uninitialized_fill_n_a
(
data_.ref_to_alloc(),
data_.last_,
n - size,
value
);
}
}
void swap(segmented_devector& other) noexcept
{
SFL_ASSERT
(
allocator_traits::propagate_on_container_swap::value ||
this->data_.ref_to_alloc() == other.data_.ref_to_alloc()
);
if (this == &other)
{
return;
}
using std::swap;
if (allocator_traits::propagate_on_container_swap::value)
{
swap(this->data_.ref_to_alloc(), other.data_.ref_to_alloc());
}
swap(this->data_, other.data_);
}
private:
// Allocates table for given number of elements (segments).
// It does not construct any element (segment).
// It only allocates memory for table.
//
segment_pointer allocate_table(size_type n)
{
segment_allocator seg_alloc(data_.ref_to_alloc());
return sfl::dtl::allocate(seg_alloc, n);
}
// Deallocates table.
// It does not destroy_a any element (segment).
// It only deallocates memory used by table.
//
void deallocate_table(segment_pointer p, size_type n) noexcept
{
segment_allocator seg_alloc(data_.ref_to_alloc());
sfl::dtl::deallocate(seg_alloc, p, n);
}
// Allocates memory for single segment.
// It does not construct any element.
// It only allocates memory for segment.
//
pointer allocate_segment()
{
return sfl::dtl::allocate(data_.ref_to_alloc(), N);
}
// Deallocates memory used by single segment.
// It does not destroy_a any element.
// It only deallocates memory used by segment.
//
void deallocate_segment(pointer p) noexcept
{
sfl::dtl::deallocate(data_.ref_to_alloc(), p, N);
}
// Allocates memory for multiple segments.
// It does not construct any element.
// It only allocates memory for segments.
//
void allocate_segments(segment_pointer first, segment_pointer last)
{
segment_pointer curr = first;
SFL_TRY
{
while (curr != last)
{
*curr = allocate_segment();
++curr;
}
}
SFL_CATCH (...)
{
deallocate_segments(first, curr);
SFL_RETHROW;
}
}
// Deallocates memory used by multiple segments.
// It does not destroy_a any element.
// It only deallocates memory used by segments.
//
void deallocate_segments(segment_pointer first, segment_pointer last) noexcept
{
while (first != last)
{
deallocate_segment(*first);
++first;
}
}
static constexpr size_type min_table_capacity() noexcept
{
return 8;
}
// Allocates storage big enough to keep given number of elements.
// It does not construct any element.
// It only allocates memory.
//
void allocate_storage(size_type num_elements)
{
if (num_elements > max_size())
{
sfl::dtl::throw_length_error("sfl::segmented_devector::allocate_storage");
}
// Required capacity of table
const size_type table_required =
num_elements / N + 1;
const size_type table_capacity =
std::max(table_required, min_table_capacity());
data_.table_bos_ = allocate_table(table_capacity);
data_.table_eos_ = data_.table_bos_ + table_capacity;
data_.table_first_ = data_.table_bos_;
data_.table_last_ = data_.table_bos_ + table_required;
SFL_TRY
{
allocate_segments(data_.table_first_, data_.table_last_);
data_.bos_.segment_ = data_.table_first_;
data_.bos_.local_ = *data_.table_first_;
data_.eos_.segment_ = (data_.table_last_ - 1);
data_.eos_.local_ = *(data_.table_last_ - 1) + (N - 1);
data_.first_ = data_.bos_;
data_.last_ = data_.first_;
}
SFL_CATCH (...)
{
deallocate_table(data_.table_bos_, table_capacity);
SFL_RETHROW;
}
}
// Deallocates storage.
// It does not destroy_a any element.
// It only deallocates memory.
//
void deallocate_storage() noexcept
{
deallocate_segments
(
data_.table_first_,
data_.table_last_
);
deallocate_table
(
data_.table_bos_,
std::distance(data_.table_bos_, data_.table_eos_)
);
}
// Increases capacity at the front for given number of elements.
// It does not construct any element.
// It only allocates memory.
//
void grow_storage_front(size_type num_additional_elements)
{
if (max_size() - capacity() < num_additional_elements)
{
sfl::dtl::throw_length_error("sfl::segmented_devector::grow_storage_front");
}
// Required capacity at the front of table
const size_type table_required_front =
num_additional_elements / N + (num_additional_elements % N == 0 ? 0 : 1);
// Available capacity at the front of table
const size_type table_available_front =
std::distance(data_.table_bos_, data_.table_first_);
// Increase table capacity at back if neccessary
if (table_required_front > table_available_front)
{
const size_type table_capacity =
std::distance(data_.table_bos_, data_.table_eos_);
const size_type new_table_capacity = std::max
(
table_capacity + table_capacity / 2,
table_capacity - table_available_front + table_required_front
);
// Distance (in segments) from BEGIN of storage to FIRST element.
const size_type dist1 =
std::distance(data_.bos_.segment_, data_.first_.segment_);
// Distance (in segments) from BEGIN of storage to LAST element.
const size_type dist2 =
std::distance(data_.bos_.segment_, data_.last_.segment_);
// Allocate new table. No effects if allocation fails.
const segment_pointer new_table_bos =
allocate_table(new_table_capacity);
const segment_pointer new_table_eos =
new_table_bos + new_table_capacity;
// Initialize LAST element in new table (noexecept).
const segment_pointer new_table_last =
new_table_eos - std::distance(data_.table_last_, data_.table_eos_);
// Initialize FIRST element in new table (noexecept).
const segment_pointer new_table_first = sfl::dtl::copy_backward
(
data_.table_first_,
data_.table_last_,
new_table_last
);
// Deallocate old table (noexecept).
deallocate_table(data_.table_bos_, table_capacity);
// Update table (noexcept).
data_.table_bos_ = new_table_bos;
data_.table_eos_ = new_table_eos;
data_.table_first_ = new_table_first;
data_.table_last_ = new_table_last;
// Update iterators (noexcept).
data_.bos_.segment_ = data_.table_first_;
data_.eos_.segment_ = data_.table_last_ - 1;
data_.first_.segment_ = data_.table_first_ + dist1;
data_.last_.segment_ = data_.table_first_ + dist2;
}
const segment_pointer new_table_first =
data_.table_first_ - table_required_front;
// Allocate additional segments. No effects if allocation fails.
allocate_segments(new_table_first, data_.table_first_);
// Update table (noexcept).
data_.table_first_ = new_table_first;
// Update iterators (noexcept).
data_.bos_.segment_ = data_.table_first_;
data_.bos_.local_ = *data_.table_first_;
}
// Increases capacity at the back for given number of elements.
// It does not construct any element.
// It only allocates memory.
//
void grow_storage_back(size_type num_additional_elements)
{
if (max_size() - capacity() < num_additional_elements)
{
sfl::dtl::throw_length_error("sfl::segmented_devector::grow_storage_back");
}
// Required capacity at the back of table
const size_type table_required_back =
num_additional_elements / N + 1;
// Available capacity at the back of table
const size_type table_available_back =
std::distance(data_.table_last_, data_.table_eos_);
// Increase table capacity at back if neccessary
if (table_required_back > table_available_back)
{
const size_type table_capacity =
std::distance(data_.table_bos_, data_.table_eos_);
const size_type new_table_capacity = std::max
(
table_capacity + table_capacity / 2,
table_capacity - table_available_back + table_required_back
);
// Distance (in segments) from BEGIN of storage to FIRST element.
const size_type dist1 =
std::distance(data_.bos_.segment_, data_.first_.segment_);
// Distance (in segments) from BEGIN of storage to LAST element.
const size_type dist2 =
std::distance(data_.bos_.segment_, data_.last_.segment_);
// Allocate new table. No effects if allocation fails.
const segment_pointer new_table_bos =
allocate_table(new_table_capacity);
const segment_pointer new_table_eos =
new_table_bos + new_table_capacity;
// Initialize FIRST element in new table (noexcept).
const segment_pointer new_table_first =
new_table_bos +
std::distance(data_.table_bos_, data_.table_first_);
// Initialize LAST element in new table (noexecept).
const segment_pointer new_table_last = sfl::dtl::copy
(
data_.table_first_,
data_.table_last_,
new_table_first
);
// Deallocate old table (noexecept).
deallocate_table(data_.table_bos_, table_capacity);
// Update table (noexcept).
data_.table_bos_ = new_table_bos;
data_.table_eos_ = new_table_eos;
data_.table_first_ = new_table_first;
data_.table_last_ = new_table_last;
// Update iterators (noexcept).
data_.bos_.segment_ = data_.table_first_;
data_.eos_.segment_ = data_.table_last_ - 1;
data_.first_.segment_ = data_.table_first_ + dist1;
data_.last_.segment_ = data_.table_first_ + dist2;
}
const segment_pointer new_table_last =
data_.table_last_ + table_required_back;
// Allocate additional segments. No effects if allocation fails.
allocate_segments(data_.table_last_, new_table_last);
// Update table (noexcept).
data_.table_last_ = new_table_last;
// Update iterators (noexcept).
data_.eos_.segment_ = data_.table_last_ - 1;
data_.eos_.local_ = *(data_.table_last_ - 1) + (N - 1);
}
// Removes unused capacity.
// It does not destroy_a any element.
// It only deallocates memory.
//
void shrink_storage()
{
// Destroy empty segments at front.
{
const segment_pointer new_table_first = data_.first_.segment_;
deallocate_segments(data_.table_first_, new_table_first);
data_.table_first_ = new_table_first;
data_.bos_.segment_ = data_.table_first_;
data_.bos_.local_ = *data_.table_first_;
}
// Destroy empty segments at back.
{
const segment_pointer new_table_last = data_.last_.segment_ + 1;
deallocate_segments(new_table_last, data_.table_last_);
data_.table_last_ = new_table_last;
data_.eos_.segment_ = (data_.table_last_ - 1);
data_.eos_.local_ = *(data_.table_last_ - 1) + (N - 1);
}
// Shrink table.
{
const size_type table_capacity =
std::distance(data_.table_bos_, data_.table_eos_);
const size_type table_size =
std::distance(data_.table_first_, data_.table_last_);
const size_type new_table_capacity =
std::max(table_size, min_table_capacity());
// Distance (in segments) from FIRST element to LAST element.
const size_type dist =
std::distance(data_.first_.segment_, data_.last_.segment_);
// Allocate new table. No effects if allocation fails.
const segment_pointer new_table_bos =
allocate_table(new_table_capacity);
const segment_pointer new_table_eos =
new_table_bos + new_table_capacity;
// Initialize FIRST element in new table (noexcept).
const segment_pointer new_table_first = new_table_bos;
// Initialize LAST element in new table (noexecept).
const segment_pointer new_table_last = sfl::dtl::copy
(
data_.table_first_,
data_.table_last_,
new_table_first
);
// Deallocate old table (noexecept).
deallocate_table(data_.table_bos_, table_capacity);
// Update table (noexcept).
data_.table_bos_ = new_table_bos;
data_.table_eos_ = new_table_eos;
data_.table_first_ = new_table_first;
data_.table_last_ = new_table_last;
// Update iterators (noexcept).
data_.bos_.segment_ = data_.table_first_;
data_.eos_.segment_ = data_.table_last_ - 1;
data_.first_.segment_ = data_.table_first_;
data_.last_.segment_ = data_.table_first_ + dist;
}
}
void initialize_empty()
{
allocate_storage(0);
}
void initialize_default_n(size_type n)
{
allocate_storage(n);
SFL_TRY
{
data_.last_ = sfl::dtl::uninitialized_default_construct_n_a
(
data_.ref_to_alloc(),
data_.first_,
n
);
}
SFL_CATCH (...)
{
deallocate_storage();
SFL_RETHROW;
}
}
void initialize_fill_n(size_type n, const T& value)
{
allocate_storage(n);
SFL_TRY
{
data_.last_ = sfl::dtl::uninitialized_fill_n_a
(
data_.ref_to_alloc(),
data_.first_,
n,
value
);
}
SFL_CATCH (...)
{
deallocate_storage();
SFL_RETHROW;
}
}
template <typename InputIt>
void initialize_range(InputIt first, InputIt last)
{
initialize_range(first, last, typename std::iterator_traits<InputIt>::iterator_category());
}
template <typename InputIt, typename Sentinel>
void initialize_range(InputIt first, Sentinel last, std::input_iterator_tag)
{
allocate_storage(0);
SFL_TRY
{
while (first != last)
{
emplace_back(*first);
++first;
}
}
SFL_CATCH (...)
{
clear();
deallocate_storage();
SFL_RETHROW;
}
}
template <typename ForwardIt, typename Sentinel>
void initialize_range(ForwardIt first, Sentinel last, std::forward_iterator_tag)
{
allocate_storage(std::distance(first, last));
SFL_TRY
{
data_.last_ = sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
first,
last,
data_.first_
);
}
SFL_CATCH (...)
{
deallocate_storage();
SFL_RETHROW;
}
}
#if SFL_CPP_VERSION >= SFL_CPP_20
template <sfl::dtl::container_compatible_range<value_type> Range>
void initialize_range(Range&& range)
{
if constexpr (std::ranges::forward_range<Range>)
{
initialize_range(std::ranges::begin(range), std::ranges::end(range), std::forward_iterator_tag());
}
else
{
initialize_range(std::ranges::begin(range), std::ranges::end(range), std::input_iterator_tag());
}
}
#else // before C++20
template <typename Range>
void initialize_range(Range&& range)
{
using std::begin;
using std::end;
initialize_range(begin(range), end(range));
}
#endif // before C++20
void initialize_copy(const segmented_devector& other)
{
allocate_storage(other.size());
SFL_TRY
{
data_.last_ = sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
other.data_.first_,
other.data_.last_,
data_.first_
);
}
SFL_CATCH (...)
{
deallocate_storage();
SFL_RETHROW;
}
}
void initialize_move(segmented_devector& other)
{
if (data_.ref_to_alloc() == other.data_.ref_to_alloc())
{
allocate_storage(0);
using std::swap;
swap(data_, other.data_);
}
else
{
allocate_storage(other.size());
SFL_TRY
{
data_.last_ = sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
other.data_.first_,
other.data_.last_,
data_.first_
);
}
SFL_CATCH (...)
{
deallocate_storage();
SFL_RETHROW;
}
}
}
void assign_fill_n(size_type n, const T& value)
{
const size_type size = this->size();
if (n <= size)
{
const iterator new_last = data_.first_ + n;
sfl::dtl::fill
(
data_.first_,
new_last,
value
);
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
new_last,
data_.last_
);
data_.last_ = new_last;
}
else
{
const size_type available_back = this->available_back();
if (n <= size + available_back)
{
sfl::dtl::fill
(
data_.first_,
data_.last_,
value
);
data_.last_ = sfl::dtl::uninitialized_fill_n_a
(
data_.ref_to_alloc(),
data_.last_,
n - size,
value
);
}
else
{
const size_type available_front = this->available_front();
if (n <= available_front + size + available_back)
{
sfl::dtl::fill
(
data_.first_,
data_.last_,
value
);
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
data_.last_,
data_.eos_,
value
);
data_.last_ = data_.eos_;
const iterator new_first = data_.first_ - (n - (size + available_back));
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
new_first,
data_.first_,
value
);
data_.first_ = new_first;
}
else
{
grow_storage_back(n - (available_front + size + available_back));
sfl::dtl::fill
(
data_.first_,
data_.last_,
value
);
data_.last_ = sfl::dtl::uninitialized_fill_n_a
(
data_.ref_to_alloc(),
data_.last_,
n - (size + available_front),
value
);
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
data_.bos_,
data_.first_,
value
);
data_.first_ = data_.bos_;
}
}
}
}
template <typename InputIt>
void assign_range(InputIt first, InputIt last)
{
assign_range(first, last, typename std::iterator_traits<InputIt>::iterator_category());
}
template <typename InputIt, typename Sentinel>
void assign_range(InputIt first, Sentinel last, std::input_iterator_tag)
{
iterator curr = data_.first_;
while (first != last && curr != data_.last_)
{
*curr = *first;
++curr;
++first;
}
if (first != last)
{
do
{
emplace_back(*first);
++first;
}
while (first != last);
}
else if (curr < data_.last_)
{
sfl::dtl::destroy_a(data_.ref_to_alloc(), curr, data_.last_);
data_.last_ = curr;
}
}
template <typename ForwardIt, typename Sentinel>
void assign_range(ForwardIt first, Sentinel last, std::forward_iterator_tag)
{
const size_type n = std::distance(first, last);
const size_type size = this->size();
if (n <= size)
{
const iterator new_last = sfl::dtl::copy
(
first,
last,
data_.first_
);
sfl::dtl::destroy_a
(
data_.ref_to_alloc(),
new_last,
data_.last_
);
data_.last_ = new_last;
}
else
{
const size_type available_back = this->available_back();
if (n <= size + available_back)
{
const ForwardIt mid = std::next(first, size);
sfl::dtl::copy
(
first,
mid,
data_.first_
);
data_.last_ = sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
mid,
last,
data_.last_
);
}
else
{
const size_type available_front = this->available_front();
if (n <= available_front + size + available_back)
{
const ForwardIt mid1 = std::next(first, n - (size + available_back));
const ForwardIt mid2 = std::next(mid1, size);
sfl::dtl::copy
(
mid1,
mid2,
data_.first_
);
data_.last_ = sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
mid2,
last,
data_.last_
);
const iterator new_first = data_.first_ - (n - (size + available_back));
sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
first,
mid1,
new_first
);
data_.first_ = new_first;
}
else
{
grow_storage_back(n - (available_front + size + available_back));
const ForwardIt mid1 = std::next(first, available_front);
const ForwardIt mid2 = std::next(mid1, size);
sfl::dtl::copy
(
mid1,
mid2,
data_.first_
);
data_.last_ = sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
mid2,
last,
data_.last_
);
sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
first,
mid1,
data_.bos_
);
data_.first_ = data_.bos_;
}
}
}
}
void assign_copy(const segmented_devector& other)
{
if (this != &other)
{
if (allocator_traits::propagate_on_container_copy_assignment::value)
{
if (data_.ref_to_alloc() != other.data_.ref_to_alloc())
{
// Create temporary vector using other allocator.
// There are no effects if allocation fails.
segmented_devector temp(other.data_.ref_to_alloc());
// Clear and destroy_a current storage (noexcept).
clear();
deallocate_storage();
// Set pointers to null (noexcept).
data_.table_bos_ = nullptr;
data_.table_eos_ = nullptr;
data_.table_first_ = nullptr;
data_.table_last_ = nullptr;
// Swap storage of this and temporary vector (noexcept).
using std::swap;
swap(data_, temp.data_);
// Temporary vector has no allocated storage (pointers
// are set to null) so destructor of temporary vector
// has nothing to do.
}
// Propagate allocator (noexcept).
data_.ref_to_alloc() = other.data_.ref_to_alloc();
}
assign_range
(
other.data_.first_,
other.data_.last_
);
}
}
void assign_move(segmented_devector& other)
{
using std::swap;
if (data_.ref_to_alloc() == other.data_.ref_to_alloc())
{
// Create temporary container using allocator of "this".
// There are no effects if allocation fails.
// NOTE: We could also use allocator of "other". There are no
// difference since both allocators compare equal.
segmented_devector temp(data_.ref_to_alloc());
// Clear storage of "this" (noexcept).
clear();
// Destroy storage of "this" (noexcept).
// NOTE: This does not set pointers to null.
deallocate_storage();
// Set pointers of "this" to null pointers (noexcept).
data_.table_bos_ = nullptr;
data_.table_eos_ = nullptr;
data_.table_first_ = nullptr;
data_.table_last_ = nullptr;
// Current state:
// - "this" has no allocated storage (pointers are null).
// Swap storage of "this" and "other" (noexcept)
swap(data_, other.data_);
// After swap:
// - "this" owns storage previously owned by "other".
// - "other" has no allocated storage (pointers are null).
// Swap storage of "other" and "temp" (noexcept)
swap(other.data_, temp.data_);
// After swap:
// - "other" owns storage previously owned by "temp".
// - "temp" has no allocated storage (pointers are null).
// This is OK in this case. Destructor of "temp" has
// nothing to do.
if (allocator_traits::propagate_on_container_move_assignment::value)
{
// Propagate allocator (noexcept).
data_.ref_to_alloc() = std::move(other.data_.ref_to_alloc());
}
}
else if (allocator_traits::propagate_on_container_move_assignment::value)
{
// Create temporary container using allocator of "other".
// There are no effects if allocation fails.
segmented_devector temp(other.data_.ref_to_alloc());
// Clear storage of "this" (noexcept).
clear();
// Destroy storage of "this" (noexcept).
// NOTE: This does not set pointers to null.
deallocate_storage();
// Set pointers of "this" to null pointers (noexcept).
data_.table_bos_ = nullptr;
data_.table_eos_ = nullptr;
data_.table_first_ = nullptr;
data_.table_last_ = nullptr;
// Current state:
// - "this" has no allocated storage (pointers are null).
// Swap storage of "this" and "temp" (noexcept)
swap(data_, temp.data_);
// After swap:
// - "this" owns storage previously owned by "temp".
// That storage was allocated by allocator of "temp"
// (i.e. copy of allocator of "other").
// "this" cannot deallocate that storage because allocators
// of "this" and "other" does not compare equal.
// - "temp" has no allocated storage (pointers are null).
// This is OK in this case. Destructor of "temp" has
// nothing to do.
// Propagate allocator (noexcept).
data_.ref_to_alloc() = std::move(other.data_.ref_to_alloc());
// After propagation:
// - "this" owns storage previously owned by "temp", but now
// "this" can deallocate that storage.
// Move elements one-by-one from "other" to "this" (can throw)
assign_range
(
std::make_move_iterator(other.data_.first_),
std::make_move_iterator(other.data_.last_)
);
}
else
{
// Move elements one-by-one from "other" to "this" (can throw)
assign_range
(
std::make_move_iterator(other.data_.first_),
std::make_move_iterator(other.data_.last_)
);
}
}
iterator insert_fill_n(const_iterator pos, size_type n, const T& value)
{
if (n == 0)
{
return begin() + std::distance(cbegin(), pos);
}
const value_type tmp(value);
const size_type dist_to_begin = std::distance(cbegin(), pos);
const size_type dist_to_end = std::distance(pos, cend());
if (dist_to_begin < dist_to_end)
{
const size_type available_front = this->available_front();
if (available_front < n)
{
grow_storage_front(n - available_front);
}
const iterator p1 = data_.first_ + dist_to_begin;
const iterator p2 = p1 - n;
if (dist_to_begin > n)
{
const iterator p3 = data_.first_ + n;
const iterator p4 = data_.first_ - n;
const iterator old_first = data_.first_;
sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
data_.first_,
p3,
p4
);
data_.first_ = p4;
sfl::dtl::move
(
p3,
p1,
old_first
);
sfl::dtl::fill
(
p2,
p1,
tmp
);
}
else
{
const iterator p3 = data_.first_ - n;
const iterator old_first = data_.first_;
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
p2,
data_.first_,
tmp
);
data_.first_ = p2;
sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
old_first,
p1,
p3
);
data_.first_ = p3;
sfl::dtl::fill
(
old_first,
p1,
tmp
);
}
return p2;
}
else
{
const size_type available_back = this->available_back();
if (available_back < n)
{
grow_storage_back(n - available_back);
}
const iterator p1 = data_.first_ + dist_to_begin;
const iterator p2 = p1 + n;
if (dist_to_end > n)
{
const iterator p3 = data_.last_ - n;
const iterator old_last = data_.last_;
data_.last_ = sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
p3,
data_.last_,
data_.last_
);
sfl::dtl::move_backward
(
p1,
p3,
old_last
);
sfl::dtl::fill
(
p1,
p2,
tmp
);
}
else
{
const iterator old_last = data_.last_;
sfl::dtl::uninitialized_fill_a
(
data_.ref_to_alloc(),
data_.last_,
p2,
tmp
);
data_.last_ = p2;
data_.last_ = sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
p1,
old_last,
data_.last_
);
sfl::dtl::fill
(
p1,
old_last,
tmp
);
}
return p1;
}
}
template <typename InputIt>
iterator insert_range(const_iterator pos, InputIt first, InputIt last)
{
return insert_range(pos, first, last, typename std::iterator_traits<InputIt>::iterator_category());
}
template <typename InputIt, typename Sentinel>
iterator insert_range(const_iterator pos, InputIt first, Sentinel last, std::input_iterator_tag)
{
const size_type offset = std::distance(cbegin(), pos);
while (first != last)
{
pos = insert(pos, *first);
++pos;
++first;
}
return nth(offset);
}
template <typename ForwardIt, typename Sentinel>
iterator insert_range(const_iterator pos, ForwardIt first, Sentinel last, std::forward_iterator_tag)
{
if (first == last)
{
return begin() + std::distance(cbegin(), pos);
}
const size_type n = std::distance(first, last);
const size_type dist_to_begin = std::distance(cbegin(), pos);
const size_type dist_to_end = std::distance(pos, cend());
if (dist_to_begin < dist_to_end)
{
const size_type available_front = this->available_front();
if (available_front < n)
{
grow_storage_front(n - available_front);
}
const iterator p1 = data_.first_ + dist_to_begin;
const iterator p2 = p1 - n;
if (dist_to_begin > n)
{
const iterator p3 = data_.first_ + n;
const iterator p4 = data_.first_ - n;
const iterator old_first = data_.first_;
sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
data_.first_,
p3,
p4
);
data_.first_ = p4;
sfl::dtl::move
(
p3,
p1,
old_first
);
sfl::dtl::copy
(
first,
last,
p2
);
}
else
{
const iterator p3 = data_.first_ - n;
const iterator old_first = data_.first_;
const ForwardIt mid = std::next(first, n - dist_to_begin);
sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
first,
mid,
p2
);
data_.first_ = p2;
sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
old_first,
p1,
p3
);
data_.first_ = p3;
sfl::dtl::copy
(
mid,
last,
old_first
);
}
return p2;
}
else
{
const size_type available_back = this->available_back();
if (available_back < n)
{
grow_storage_back(n - available_back);
}
const iterator p1 = data_.first_ + dist_to_begin;
if (dist_to_end > n)
{
const iterator p3 = data_.last_ - n;
const iterator old_last = data_.last_;
data_.last_ = sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
p3,
data_.last_,
data_.last_
);
sfl::dtl::move_backward
(
p1,
p3,
old_last
);
sfl::dtl::copy
(
first,
last,
p1
);
}
else
{
const iterator old_last = data_.last_;
const ForwardIt mid = std::next(first, dist_to_end);
data_.last_ = sfl::dtl::uninitialized_copy_a
(
data_.ref_to_alloc(),
mid,
last,
data_.last_
);
data_.last_ = sfl::dtl::uninitialized_move_a
(
data_.ref_to_alloc(),
p1,
old_last,
data_.last_
);
sfl::dtl::copy
(
first,
mid,
p1
);
}
return p1;
}
}
};
//
// ---- NON-MEMBER FUNCTIONS --------------------------------------------------
//
template <typename T, std::size_t N, typename A>
SFL_NODISCARD
bool operator==
(
const segmented_devector<T, N, A>& x,
const segmented_devector<T, N, A>& y
)
{
return x.size() == y.size() && std::equal(x.begin(), x.end(), y.begin());
}
template <typename T, std::size_t N, typename A>
SFL_NODISCARD
bool operator!=
(
const segmented_devector<T, N, A>& x,
const segmented_devector<T, N, A>& y
)
{
return !(x == y);
}
template <typename T, std::size_t N, typename A>
SFL_NODISCARD
bool operator<
(
const segmented_devector<T, N, A>& x,
const segmented_devector<T, N, A>& y
)
{
return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());
}
template <typename T, std::size_t N, typename A>
SFL_NODISCARD
bool operator>
(
const segmented_devector<T, N, A>& x,
const segmented_devector<T, N, A>& y
)
{
return y < x;
}
template <typename T, std::size_t N, typename A>
SFL_NODISCARD
bool operator<=
(
const segmented_devector<T, N, A>& x,
const segmented_devector<T, N, A>& y
)
{
return !(y < x);
}
template <typename T, std::size_t N, typename A>
SFL_NODISCARD
bool operator>=
(
const segmented_devector<T, N, A>& x,
const segmented_devector<T, N, A>& y
)
{
return !(x < y);
}
template <typename T, std::size_t N, typename A>
void swap
(
segmented_devector<T, N, A>& x,
segmented_devector<T, N, A>& y
)
{
x.swap(y);
}
template <typename T, std::size_t N, typename A, typename U>
typename segmented_devector<T, N, A>::size_type
erase(segmented_devector<T, N, A>& c, const U& value)
{
auto it = std::remove(c.begin(), c.end(), value);
auto r = std::distance(it, c.end());
c.erase(it, c.end());
return r;
}
template <typename T, std::size_t N, typename A, typename Predicate>
typename segmented_devector<T, N, A>::size_type
erase_if(segmented_devector<T, N, A>& c, Predicate pred)
{
auto it = std::remove_if(c.begin(), c.end(), pred);
auto r = std::distance(it, c.end());
c.erase(it, c.end());
return r;
}
} // namespace sfl
#endif // SFL_SEGMENTED_DEVECTOR_HPP_INCLUDED