33#if not defined(SPARROW_BUFFER_GROWTH_FACTOR)
34# define SPARROW_BUFFER_GROWTH_FACTOR 2
45 namespace copy_tracker
51 return "buffer<" + std::string(
typeid(T).name()) +
">";
83 using pointer =
typename alloc_traits::pointer;
117 [[nodiscard]] constexpr buffer_data&
get_data() noexcept;
118 [[nodiscard]] constexpr const buffer_data&
get_data() const noexcept;
148 std::same_as<std::remove_cvref_t<T>, T>,
149 "buffer must have a non-const, non-volatile, non-reference value_type"
159 using pointer =
typename alloc_traits::pointer;
171 requires(not std::same_as<A, buffer<T>> and
allocator<A>)
177 template <allocator A>
185 template <allocator A>
187 std::is_trivially_default_constructible_v<T>
188 && std::is_trivially_destructible_v<T>
192 template <allocator A>
195 template <allocator A>
198 template <allocator A>
199 constexpr buffer(std::initializer_list<value_type> init,
const A& a);
201 template <
class It, allocator A>
202 constexpr buffer(It first, It last,
const A& a);
204 template <std::ranges::input_range Range, allocator A>
206 constexpr buffer(
const Range& range,
const A& a);
212 template <allocator A>
217 template <allocator A>
236 template <
class U = T>
237 [[nodiscard]]
constexpr U*
data() noexcept;
240 template <class U = T>
241 [[nodiscard]] constexpr const U*
data() const noexcept;
265 [[nodiscard]] constexpr
bool empty() const noexcept;
279 template <
mpl::iterator_of_type<T> InputIt>
286 template <class... Args>
305 using base_type::get_data;
308 constexpr
void resize_impl(
size_type new_size, F&& initializer);
311 constexpr
void assign_range_impl(It first, It last,
std::forward_iterator_tag);
313 constexpr
void erase_at_end(
pointer p);
318 constexpr
void reserve_with_growth_factor(
size_type new_cap);
332 [[nodiscard]] static constexpr
pointer
345 constexpr
bool operator==(const
buffer<T>& lhs, const
buffer<T>& rhs) noexcept;
357 rhs.p_begin =
nullptr;
359 rhs.p_storage_end =
nullptr;
365 std::swap(
p_begin, rhs.p_begin);
366 std::swap(
p_end, rhs.p_end);
372 template <allocator A>
379 template <allocator A>
390 template <allocator A>
405 template <allocator A>
408 , m_data(
std::move(rhs.m_data))
439 return alloc_traits::allocate(m_alloc, n);
445 alloc_traits::deallocate(m_alloc, p, n);
452 m_data.p_end = m_data.p_begin + n;
453 m_data.p_storage_end = m_data.p_end;
461 m_data.p_end = p + n;
462 m_data.p_storage_end = p + cap;
470 template <allocator A>
472 : base_type(check_init_length(n, a), a)
474 get_data().p_end = default_initialize(get_data().p_begin, n,
get_allocator());
478 template <allocator A>
480 std::is_trivially_default_constructible_v<T>
481 && std::is_trivially_destructible_v<T>
484 : base_type(check_init_length(n, a), a)
489 template <allocator A>
491 : base_type(check_init_length(n, a), a)
493 get_data().p_end = fill_initialize(get_data().p_begin, n, v,
get_allocator());
497 template <allocator A>
499 : base_type(p, check_init_length(n, a), a)
504 template <allocator A>
506 : base_type(check_init_length(init.
size(), a), a)
508 get_data().p_end = copy_initialize(init.begin(), init.end(), get_data().p_begin,
get_allocator());
512 template <
class It, allocator A>
514 : base_type(check_init_length(static_cast<
size_type>(
std::distance(first, last)), a), a)
516 get_data().p_end = copy_initialize(first, last, get_data().p_begin,
get_allocator());
520 template <std::ranges::input_range Range, allocator A>
523 : base_type(check_init_length(
static_cast<size_type>(std::ranges::size(range)), a), a)
525 get_data().p_end = copy_initialize(
526 std::ranges::begin(range),
527 std::ranges::end(range),
536 destroy(get_data().p_begin, get_data().p_end,
get_allocator());
543 if (rhs.get_data().
p_begin !=
nullptr)
545 this->create_storage(rhs.size());
546 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
552 template <allocator A>
556 if (rhs.get_data().
p_begin !=
nullptr)
558 this->create_storage(rhs.size());
559 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
565 template <allocator A>
571 get_data() = std::move(rhs.m_data);
573 else if (!rhs.empty())
575 if (rhs.get_data().p_begin != nullptr)
577 this->create_storage(rhs.size());
578 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
587 if (std::addressof(rhs) !=
this)
589 if (rhs.get_data().
p_begin !=
nullptr)
592 assign_range_impl(rhs.get_data().
p_begin, rhs.get_data().
p_end, std::random_access_iterator_tag());
599 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
613 get_data() = std::move(rhs.get_data());
617 if (rhs.get_data().p_begin !=
nullptr)
620 std::make_move_iterator(rhs.begin()),
621 std::make_move_iterator(rhs.end()),
622 std::random_access_iterator_tag()
639 std::make_move_iterator(init.begin()),
640 std::make_move_iterator(init.end()),
641 std::random_access_iterator_tag()
650 return get_data().p_begin[i];
657 return get_data().p_begin[i];
664 return *(get_data().p_begin);
671 return *(get_data().p_begin);
678 return *(get_data().p_end - 1);
685 return *(get_data().p_end - 1);
693# pragma GCC diagnostic push
694# pragma GCC diagnostic ignored "-Wcast-align"
696 return reinterpret_cast<U*
>(get_data().p_begin);
698# pragma GCC diagnostic pop
707# pragma GCC diagnostic push
708# pragma GCC diagnostic ignored "-Wcast-align"
710 return reinterpret_cast<U*
>(get_data().p_begin);
712# pragma GCC diagnostic pop
791 const auto&
data = get_data();
798 const auto&
data = get_data();
799 if (
data.p_begin ==
nullptr ||
data.p_storage_end ==
nullptr)
809 const auto&
data = get_data();
810 if (
data.p_begin ==
nullptr ||
data.p_end ==
nullptr)
828 throw std::length_error(
"buffer::reserve called with new_cap > max_size()");
832 if (
data() ==
nullptr)
835 get_data().p_end = get_data().p_begin;
840 pointer tmp = allocate_and_copy(
842 std::make_move_iterator(get_data().p_begin),
843 std::make_move_iterator(get_data().p_end)
845 destroy(get_data().p_begin, get_data().p_end,
get_allocator());
848 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
856 constexpr void buffer<T>::reserve_with_growth_factor(size_type new_cap)
858 if (new_cap > capacity())
876 if (get_data().p_begin !=
nullptr)
878 erase_at_end(get_data().p_begin);
895 return emplace(pos, std::move(value));
907 reserve_with_growth_factor(
size() + count);
910 std::fill_n(it, count, value);
911 get_data().p_end += count;
913 return std::next(
begin(), offset);
916 template <
typename T>
921 template <
typename Iterator>
926 template <
typename T>
930 template <mpl::iterator_of_type<T> InputIt>
938 reserve_with_growth_factor(new_size);
942 std::move_backward(new_pos, end_it,
end());
945 std::uninitialized_move(first, last, new_pos);
949 std::uninitialized_copy(first, last, new_pos);
955 template <std::ranges::input_range R>
956 requires std::same_as<std::ranges::range_value_t<R>, T>
961 return insert(pos, std::ranges::begin(range), std::ranges::end(range));
969 return insert(pos, ilist.begin(), ilist.end());
973 template <
class... Args>
979 reserve_with_growth_factor(
size() + 1);
980 pointer p = get_data().p_begin + offset;
981 if (p != get_data().p_end)
983 alloc_traits::construct(
get_allocator(), get_data().p_end, std::move(*(get_data().p_end - 1)));
984 std::move_backward(p, get_data().p_end - 1, get_data().p_end);
985 alloc_traits::construct(
get_allocator(), p, std::forward<Args>(args)...);
989 alloc_traits::construct(
get_allocator(), get_data().p_end, std::forward<Args>(args)...);
1000 return erase(pos, pos + 1);
1011 pointer p = get_data().p_begin + offset;
1012 erase_at_end(std::move(p + len, get_data().p_end, p));
1032 alloc_traits::destroy(
get_allocator(), get_data().p_end - 1);
1043 get_data().p_end = default_initialize(get_data().p_end, nb_init,
get_allocator());
1055 get_data().p_end = fill_initialize(get_data().p_end, nb_init, value,
get_allocator());
1063 std::swap(this->get_data(), rhs.get_data());
1068 constexpr void buffer<T>::resize_impl(size_type new_size, F&& initializer)
1070 if (new_size > size())
1072 const std::size_t nb_init = new_size - size();
1073 if (new_size <= capacity())
1075 initializer(nb_init);
1080 initializer(nb_init);
1083 else if (new_size <
size())
1085 erase_at_end(get_data().p_begin + new_size);
1091 constexpr void buffer<T>::assign_range_impl(It first, It last, std::forward_iterator_tag)
1093 const size_type sz =
size();
1094 const size_type len =
static_cast<size_type
>(std::distance(first, last));
1095 if (len > capacity())
1097 check_init_length(len, get_allocator());
1098 pointer p = allocate_and_copy(len, first, last);
1099 destroy(get_data().p_begin, get_data().p_end, get_allocator());
1100 this->deallocate(get_data().p_begin, capacity());
1101 this->assign_storage(p, len, len);
1105 pointer p = std::copy(first, last, get_data().p_begin);
1111 std::advance(mid, sz);
1112 std::copy(first, mid, get_data().p_begin);
1113 get_data().p_end = copy_initialize(mid, last, get_data().p_end, get_allocator());
1118 constexpr void buffer<T>::erase_at_end(pointer p)
1120 destroy(p, get_data().p_end, get_allocator());
1121 get_data().p_end = p;
1142 constexpr auto buffer<T>::check_init_length(size_type n,
const allocator_type& a) -> size_type
1144 if (n > max_size_impl(a))
1146 throw std::length_error(
"cannot create buffer larger than max_size()");
1152 constexpr auto buffer<T>::max_size_impl(
const allocator_type& a)
noexcept -> size_type
1154 const size_type diff_max =
static_cast<size_type
>(std::numeric_limits<difference_type>::max());
1155 const size_type alloc_max = std::allocator_traits<allocator_type>::max_size(a);
1156 return (std::min) (diff_max, alloc_max);
1160 constexpr auto buffer<T>::default_initialize(pointer begin, size_type n, allocator_type& a) -> pointer
1162 pointer current = begin;
1163 for (; n > 0; --n, ++current)
1165 alloc_traits::construct(a, current);
1172 buffer<T>::fill_initialize(pointer begin, size_type n,
const value_type& v, allocator_type& a) -> pointer
1174 pointer current = begin;
1175 for (; n > 0; --n, ++current)
1177 alloc_traits::construct(a, current, v);
1187 for (; first != last; ++first, ++current)
1189 alloc_traits::construct(a, current, *first);
1195 constexpr void buffer<T>::destroy(pointer first, pointer last, allocator_type& a)
1198 for (; first != last; ++first)
1200 alloc_traits::destroy(a, first);
1207 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
#define SPARROW_BUFFER_GROWTH_FACTOR
constexpr pointer allocate(size_type n)
typename alloc_traits::pointer pointer
any_allocator< T > allocator_type
constexpr void create_storage(size_type n)
constexpr void deallocate(pointer p, size_type n)
constexpr buffer_data & get_data() noexcept
constexpr void assign_storage(pointer p, size_type n, size_type cap)
std::allocator_traits< allocator_type > alloc_traits
typename alloc_traits::size_type size_type
constexpr allocator_type & get_allocator() noexcept
Object that owns a piece of contiguous memory.
std::reverse_iterator< iterator > reverse_iterator
std::reverse_iterator< const_iterator > const_reverse_iterator
typename alloc_traits::difference_type difference_type
constexpr buffer(size_type n, const value_type &v, const A &a)
typename alloc_traits::size_type size_type
xsimd::aligned_allocator< T > default_allocator
constexpr void resize(size_type new_size)
constexpr const_reference back() const
constexpr size_type max_size() const noexcept
typename alloc_traits::const_pointer const_pointer
constexpr buffer(const Range &range, const A &a)
constexpr iterator insert(const_iterator pos, const std::uint8_t &value)
constexpr void shrink_to_fit()
constexpr const_reverse_iterator crend() const noexcept
constexpr U * data() noexcept
pointer_iterator< const_pointer > const_iterator
constexpr iterator erase(const_iterator pos)
constexpr reference back()
typename base_type::allocator_type allocator_type
constexpr const_iterator cbegin() const noexcept
const value_type & const_reference
constexpr buffer & operator=(std::initializer_list< value_type > init)
typename alloc_traits::pointer pointer
constexpr buffer(const A &a)
constexpr const_reference operator[](size_type i) const
pointer_iterator< pointer > iterator
constexpr buffer(size_type n, uninitialized_t, const A &a)
Constructs a buffer whose elements are not initialized.
constexpr const_iterator cend() const noexcept
constexpr iterator begin() noexcept
constexpr iterator end() noexcept
constexpr buffer(const buffer &rhs)
constexpr buffer(buffer &&rhs) noexcept=default
constexpr buffer(buffer &&rhs, const A &a)
constexpr buffer(pointer p, size_type n, const A &a)
constexpr reverse_iterator rend() noexcept
constexpr buffer(std::initializer_list< value_type > init, const A &a)
constexpr buffer(const buffer &rhs, const A &a)
constexpr size_type size() const noexcept
constexpr buffer(It first, It last, const A &a)
constexpr reference operator[](size_type i)
constexpr buffer(size_type n, const A &a)
constexpr void reserve(size_type new_cap)
constexpr void pop_back()
constexpr buffer & operator=(const buffer &rhs)
constexpr void swap(buffer &rhs) noexcept
constexpr allocator_type & get_allocator() noexcept
constexpr void push_back(const std::uint8_t &value)
constexpr const_reverse_iterator crbegin() const noexcept
constexpr iterator emplace(const_iterator pos, Args &&... args)
constexpr const_reference front() const
constexpr reference front()
constexpr reverse_iterator rbegin() noexcept
constexpr size_type capacity() const noexcept
constexpr buffer & operator=(buffer &&rhs)
constexpr bool empty() const noexcept
Allocator for aligned memory.
#define SPARROW_ASSERT_TRUE(expr__)
#define SPARROW_ASSERT_FALSE(expr__)
SPARROW_API void increase(const std::string &key)
constexpr std::size_t size(typelist< T... >={})
Gets the count of types contained in a typelist.
constexpr bool is_type_instance_of_v
Variable template for convenient access to is_type_instance_of.
constexpr pointer_iterator< T * > make_pointer_iterator(T *t)
SPARROW_API bool operator==(const array &lhs, const array &rhs)
Compares the content of two arrays.
constexpr bool is_move_iterator_v
Extensions to the C++ standard library.
constexpr buffer_data() noexcept=default
constexpr buffer_data & operator=(buffer_data &&) noexcept
Tag selecting allocation without value-initializing the elements.
constexpr uninitialized_t()=default