Skip to content

Commit

Permalink
insert/emplace implementation improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
KRM7 committed Mar 15, 2024
1 parent 036f9b7 commit fcc26e4
Showing 1 changed file with 122 additions and 56 deletions.
178 changes: 122 additions & 56 deletions src/small_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,7 @@ namespace detail
constexpr const T& operator*() const noexcept { return data_; }

private:
union
{
unsigned char dummy_ = {};
T data_;
};
union { T data_; };
Allocator& alloc_;
};

Expand Down Expand Up @@ -713,6 +709,7 @@ class small_vector
constexpr bool empty() const noexcept { return first_ == last_; }

constexpr size_type size() const noexcept { return size_type(last_ - first_); }
constexpr difference_type ssize() const noexcept { return last_ - first_; }
constexpr size_type capacity() const noexcept { return size_type(last_alloc_ - first_); }
constexpr size_type max_size() const noexcept { return std::allocator_traits<A>::max_size(alloc_); }

Expand Down Expand Up @@ -818,64 +815,73 @@ class small_vector
template<typename... Args>
constexpr iterator emplace(const_iterator pos, Args&&... args)
{
if (pos == cend()) return std::addressof(emplace_back(std::forward<Args>(args)...));
if (size() != capacity())
{
if (pos == cend()) return std::addressof(emplace_back_unchecked(std::forward<Args>(args)...));

detail::allocator_managed<T, A> new_elem(alloc_, std::forward<Args>(args)...);
detail::allocator_managed<T, A> new_elem(alloc_, std::forward<Args>(args)...);

const auto offset = std::distance(cbegin(), pos);
const difference_type offset = std::distance(cbegin(), pos);

if (size() == capacity()) reallocate_n(next_capacity());
detail::construct(alloc_, last_, std::move(back()));
std::shift_right(first_ + offset, last_++, 1);
*(first_ + offset) = std::move(*new_elem);
return first_ + offset;
detail::construct(alloc_, last_, std::move(back()));
std::shift_right(first_ + offset, last_++, 1);
*(first_ + offset) = std::move(*new_elem);
return first_ + offset;
}

return reallocate_emplace(next_capacity(), pos, std::forward<Args>(args)...);
}

constexpr iterator insert(const_iterator pos, size_type count, const T& value)
{
const auto offset = std::distance(cbegin(), pos);
const auto src_size = static_cast<difference_type>(count);
const auto new_size = size() + count;

reserve(new_size);

const auto middle = std::max(last_ - src_size, first_ + offset);
const auto moved_size = last_ - middle;
const auto new_last = last_ + src_size;
const auto new_middle = last_ + src_size - moved_size;
const auto old_last = last_;

detail::construct_range(alloc_, last_, new_middle, value);
last_ = new_middle;
detail::relocate_range_weak(alloc_, middle, old_last, new_middle);
last_ = new_last;
detail::assign_range(middle, old_last, value);
if (capacity() - size() >= count)
{
const difference_type offset = std::distance(cbegin(), pos);
const difference_type src_size = difference_type(count);

const auto middle = first_ + std::max(ssize() - src_size, offset);
const auto moved_size = last_ - middle;
const auto old_last = last_;
const auto new_last = last_ + src_size;
const auto new_middle = middle + src_size;

detail::construct_range(alloc_, last_, new_middle, value);
last_ = new_middle;
detail::relocate_range_weak(alloc_, middle, old_last, new_middle);
last_ = new_last;
detail::assign_range(middle, old_last, value);

return first_ + offset;
}

return first_ + offset;
return reallocate_insert(next_capacity(count), pos, count, value);
}

template<std::forward_iterator Iter>
constexpr iterator insert(const_iterator pos, Iter src_first, Iter src_last)
{
const auto offset = std::distance(cbegin(), pos);
const auto src_size = std::distance(src_first, src_last);
const auto new_size = size() + src_size;
const difference_type src_size = std::distance(src_first, src_last);

reserve(new_size);
if (capacity() - size() >= size_type(src_size))
{
const difference_type offset = std::distance(cbegin(), pos);

const auto middle = std::max(last_ - src_size, first_ + offset);
const auto moved_size = last_ - middle;
const auto new_last = last_ + src_size;
const auto new_middle = last_ + src_size - moved_size;
const auto old_last = last_;
const auto middle = first_ + std::max(ssize() - src_size, offset);
const auto moved_size = last_ - middle;
const auto old_last = last_;
const auto new_last = last_ + src_size;
const auto new_middle = middle + src_size;

detail::construct_range(alloc_, last_, new_middle, src_first + moved_size);
last_ = new_middle;
detail::relocate_range_weak(alloc_, middle, old_last, new_middle);
last_ = new_last;
detail::assign_range(middle, old_last, src_first);
detail::construct_range(alloc_, last_, new_middle, std::next(src_first, moved_size));
last_ = new_middle;
detail::relocate_range_weak(alloc_, middle, old_last, new_middle);
last_ = new_last;
detail::assign_range(middle, old_last, src_first);

return first_ + offset;
return first_ + offset;
}

return reallocate_insert(next_capacity(size_type(src_size)), pos, src_first, src_last);
}

template<std::input_iterator Iter>
Expand Down Expand Up @@ -929,16 +935,12 @@ class small_vector

constexpr void allocate_n(size_type count)
{
if (count <= buffer_.size())
{
set_buffer_storage(0);
}
else
{
detail::alloc_result_t<A> alloc_result = detail::allocate<T>(alloc_, count);
first_ = alloc_result.data;
last_alloc_ = alloc_result.data + alloc_result.size;
}
if (count <= inline_capacity()) return set_buffer_storage(0);

detail::alloc_result_t<A> alloc_result = detail::allocate<T>(alloc_, count);
first_ = alloc_result.data;
last_ = alloc_result.data;
last_alloc_ = alloc_result.data + alloc_result.size;
}

constexpr void reallocate_n(size_type new_capacity)
Expand Down Expand Up @@ -970,6 +972,70 @@ class small_vector
set_storage(alloc_result.data, old_size + 1, alloc_result.size);
}

template<typename... Args>
constexpr iterator reallocate_emplace(size_t new_capacity, const_iterator pos, Args&&... args)
{
const size_type old_size = size();
const difference_type offset = std::distance(cbegin(), pos);

detail::alloc_result_t<A> alloc_result = detail::allocate<T>(alloc_, new_capacity);
detail::scope_exit guard1{ [&] { detail::deallocate(alloc_, alloc_result.data, alloc_result.size); } };
detail::construct(alloc_, alloc_result.data + offset, std::forward<Args>(args)...);
detail::scope_exit guard2{ [&] { detail::destroy(alloc_, alloc_result.data + offset); } };
detail::relocate_range_strong(alloc_, first_, first_ + offset, alloc_result.data);
detail::scope_exit guard3{ [&] { detail::destroy_range(alloc_, alloc_result.data, alloc_result.data + offset); } };
detail::relocate_range_strong(alloc_, first_ + offset, last_, alloc_result.data + offset + 1);
{ guard1.release(); guard2.release(); guard3.release(); }
detail::destroy_range(alloc_, first_, last_);
deallocate();
set_storage(alloc_result.data, old_size + 1, alloc_result.size);

return alloc_result.data + offset;
}

constexpr iterator reallocate_insert(size_t new_capacity, const_iterator pos, size_type count, const T& value)
{
const size_type old_size = size();
const difference_type src_size = difference_type(count);
const difference_type offset = std::distance(cbegin(), pos);

detail::alloc_result_t<A> alloc_result = detail::allocate<T>(alloc_, new_capacity);
detail::scope_exit guard1{ [&] { detail::deallocate(alloc_, alloc_result.data, alloc_result.size); } };
detail::relocate_range_weak(alloc_, first_, first_ + offset, alloc_result.data);
detail::scope_exit guard2{ [&] { detail::destroy_range(alloc_, alloc_result.data, alloc_result.data + offset); } };
detail::construct_range(alloc_, alloc_result.data + offset, alloc_result.data + offset + src_size, value);
detail::scope_exit guard3{ [&] { detail::destroy_range(alloc_, alloc_result.data + offset, alloc_result.data + offset + src_size); } };
detail::relocate_range_weak(alloc_, first_ + offset, last_, alloc_result.data + offset + src_size);
{ guard1.release(); guard2.release(); guard3.release(); }
detail::destroy_range(alloc_, first_, last_);
deallocate();
set_storage(alloc_result.data, old_size + count, alloc_result.size);

return alloc_result.data + offset;
}

template<std::forward_iterator Iter>
constexpr iterator reallocate_insert(size_t new_capacity, const_iterator pos, Iter src_first, Iter src_last)
{
const size_type old_size = size();
const difference_type src_size = std::distance(src_first, src_last);
const difference_type offset = std::distance(cbegin(), pos);

detail::alloc_result_t<A> alloc_result = detail::allocate<T>(alloc_, new_capacity);
detail::scope_exit guard1{ [&] { detail::deallocate(alloc_, alloc_result.data, alloc_result.size); } };
detail::relocate_range_weak(alloc_, first_, first_ + offset, alloc_result.data);
detail::scope_exit guard2{ [&] { detail::destroy_range(alloc_, alloc_result.data, alloc_result.data + offset); } };
detail::construct_range(alloc_, alloc_result.data + offset, alloc_result.data + offset + src_size, src_first);
detail::scope_exit guard3{ [&] { detail::destroy_range(alloc_, alloc_result.data + offset, alloc_result.data + offset + src_size); } };
detail::relocate_range_weak(alloc_, first_ + offset, last_, alloc_result.data + offset + src_size);
{ guard1.release(); guard2.release(); guard3.release(); }
detail::destroy_range(alloc_, first_, last_);
deallocate();
set_storage(alloc_result.data, old_size + size_type(src_size), alloc_result.size);

return alloc_result.data + offset;
}

constexpr void deallocate() noexcept
{
if (!is_small() && data()) detail::deallocate(alloc_, first_, capacity());
Expand Down

0 comments on commit fcc26e4

Please sign in to comment.