32#if not defined(SPARROW_BUFFER_GROWTH_FACTOR)
33# define SPARROW_BUFFER_GROWTH_FACTOR 2
55 using pointer =
typename alloc_traits::pointer;
89 [[nodiscard]] constexpr buffer_data&
get_data() noexcept;
90 [[nodiscard]] constexpr const buffer_data&
get_data() const noexcept;
119 std::same_as<std::remove_cvref_t<T>, T>,
120 "buffer must have a non-const, non-volatile, non-reference value_type"
130 using pointer =
typename alloc_traits::pointer;
142 requires(not std::same_as<A, buffer<T>> and
allocator<A>)
148 template <allocator A>
151 template <allocator A>
154 template <allocator A>
157 template <allocator A>
158 constexpr buffer(std::initializer_list<value_type> init,
const A& a);
160 template <
class It, allocator A>
161 constexpr buffer(It first, It last,
const A& a);
163 template <std::ranges::input_range Range, allocator A>
165 constexpr buffer(
const Range& range,
const A& a);
171 template <allocator A>
176 template <allocator A>
195 template <
class U = T>
196 [[nodiscard]]
constexpr U*
data() noexcept;
199 template <class U = T>
200 [[nodiscard]] constexpr const U*
data() const noexcept;
224 [[nodiscard]] constexpr
bool empty() const noexcept;
238 template <
mpl::iterator_of_type<T> InputIt>
245 template <class... Args>
264 using base_type::get_data;
267 constexpr
void resize_impl(
size_type new_size, F&& initializer);
270 constexpr
void assign_range_impl(It first, It last,
std::forward_iterator_tag);
272 constexpr
void erase_at_end(
pointer p);
277 constexpr
void reserve_with_growth_factor(
size_type new_cap);
291 [[nodiscard]] static constexpr
pointer
304 constexpr
bool operator==(const
buffer<T>& lhs, const
buffer<T>& rhs) noexcept;
316 rhs.p_begin =
nullptr;
318 rhs.p_storage_end =
nullptr;
324 std::swap(
p_begin, rhs.p_begin);
325 std::swap(
p_end, rhs.p_end);
331 template <allocator A>
338 template <allocator A>
346 template <allocator A>
361 template <allocator A>
364 , m_data(
std::move(rhs.m_data))
395 return alloc_traits::allocate(m_alloc, n);
401 alloc_traits::deallocate(m_alloc, p, n);
408 m_data.p_end = m_data.p_begin + n;
409 m_data.p_storage_end = m_data.p_end;
417 m_data.p_end = p + n;
418 m_data.p_storage_end = p + cap;
426 template <allocator A>
428 : base_type(check_init_length(n, a), a)
430 get_data().p_end = default_initialize(get_data().p_begin, n,
get_allocator());
434 template <allocator A>
436 : base_type(check_init_length(n, a), a)
438 get_data().p_end = fill_initialize(get_data().p_begin, n, v,
get_allocator());
442 template <allocator A>
444 : base_type(p, check_init_length(n, a), a)
449 template <allocator A>
451 : base_type(check_init_length(init.
size(), a), a)
453 get_data().p_end = copy_initialize(init.begin(), init.end(), get_data().p_begin,
get_allocator());
457 template <
class It, allocator A>
459 : base_type(check_init_length(static_cast<
size_type>(
std::distance(first, last)), a), a)
461 get_data().p_end = copy_initialize(first, last, get_data().p_begin,
get_allocator());
465 template <std::ranges::input_range Range, allocator A>
468 : base_type(check_init_length(
static_cast<size_type>(std::ranges::size(range)), a), a)
470 get_data().p_end = copy_initialize(
471 std::ranges::begin(range),
472 std::ranges::end(range),
481 destroy(get_data().p_begin, get_data().p_end,
get_allocator());
488 if (rhs.get_data().
p_begin !=
nullptr)
490 this->create_storage(rhs.size());
491 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
496 template <allocator A>
500 if (rhs.get_data().
p_begin !=
nullptr)
502 this->create_storage(rhs.size());
503 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
508 template <allocator A>
514 get_data() = std::move(rhs.m_data);
516 else if (!rhs.empty())
518 if (rhs.get_data().p_begin != nullptr)
520 this->create_storage(rhs.size());
521 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
530 if (std::addressof(rhs) !=
this)
532 if (rhs.get_data().
p_begin !=
nullptr)
535 assign_range_impl(rhs.get_data().
p_begin, rhs.get_data().
p_end, std::random_access_iterator_tag());
542 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
555 get_data() = std::move(rhs.get_data());
559 if (rhs.get_data().p_begin !=
nullptr)
562 std::make_move_iterator(rhs.begin()),
563 std::make_move_iterator(rhs.end()),
564 std::random_access_iterator_tag()
581 std::make_move_iterator(init.begin()),
582 std::make_move_iterator(init.end()),
583 std::random_access_iterator_tag()
592 return get_data().p_begin[i];
599 return get_data().p_begin[i];
606 return *(get_data().p_begin);
613 return *(get_data().p_begin);
620 return *(get_data().p_end - 1);
627 return *(get_data().p_end - 1);
635# pragma GCC diagnostic push
636# pragma GCC diagnostic ignored "-Wcast-align"
638 return reinterpret_cast<U*
>(get_data().p_begin);
640# pragma GCC diagnostic pop
649# pragma GCC diagnostic push
650# pragma GCC diagnostic ignored "-Wcast-align"
652 return reinterpret_cast<U*
>(get_data().p_begin);
654# pragma GCC diagnostic pop
733 return get_data().p_begin == get_data().p_end;
739 return static_cast<size_type>(get_data().p_storage_end - get_data().p_begin);
745 return static_cast<size_type>(get_data().p_end - get_data().p_begin);
759 throw std::length_error(
"buffer::reserve called with new_cap > max_size()");
763 if (
data() ==
nullptr)
766 get_data().p_end = get_data().p_begin;
771 pointer tmp = allocate_and_copy(
773 std::make_move_iterator(get_data().p_begin),
774 std::make_move_iterator(get_data().p_end)
776 destroy(get_data().p_begin, get_data().p_end,
get_allocator());
779 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
787 constexpr void buffer<T>::reserve_with_growth_factor(size_type new_cap)
789 if (new_cap > capacity())
807 if (get_data().p_begin !=
nullptr)
809 erase_at_end(get_data().p_begin);
826 return emplace(pos, std::move(value));
838 reserve_with_growth_factor(
size() + count);
841 std::fill_n(it, count, value);
842 get_data().p_end += count;
844 return std::next(
begin(), offset);
847 template <
typename T>
852 template <
typename Iterator>
857 template <
typename T>
861 template <mpl::iterator_of_type<T> InputIt>
869 reserve_with_growth_factor(new_size);
873 std::move_backward(new_pos, end_it,
end());
876 std::uninitialized_move(first, last, new_pos);
880 std::uninitialized_copy(first, last, new_pos);
886 template <std::ranges::input_range R>
887 requires std::same_as<std::ranges::range_value_t<R>, T>
892 return insert(pos, std::ranges::begin(range), std::ranges::end(range));
900 return insert(pos, ilist.begin(), ilist.end());
904 template <
class... Args>
910 reserve_with_growth_factor(
size() + 1);
911 pointer p = get_data().p_begin + offset;
912 if (p != get_data().p_end)
914 alloc_traits::construct(
get_allocator(), get_data().p_end, std::move(*(get_data().p_end - 1)));
915 std::move_backward(p, get_data().p_end - 1, get_data().p_end);
916 alloc_traits::construct(
get_allocator(), p, std::forward<Args>(args)...);
920 alloc_traits::construct(
get_allocator(), get_data().p_end, std::forward<Args>(args)...);
931 return erase(pos, pos + 1);
942 pointer p = get_data().p_begin + offset;
943 erase_at_end(std::move(p + len, get_data().p_end, p));
963 alloc_traits::destroy(
get_allocator(), get_data().p_end - 1);
974 get_data().p_end = default_initialize(get_data().p_end, nb_init,
get_allocator());
986 get_data().p_end = fill_initialize(get_data().p_end, nb_init, value,
get_allocator());
994 std::swap(this->get_data(), rhs.get_data());
999 constexpr void buffer<T>::resize_impl(size_type new_size, F&& initializer)
1001 if (new_size > size())
1003 const std::size_t nb_init = new_size - size();
1004 if (new_size <= capacity())
1006 initializer(nb_init);
1011 initializer(nb_init);
1014 else if (new_size <
size())
1016 erase_at_end(get_data().p_begin + new_size);
1022 constexpr void buffer<T>::assign_range_impl(It first, It last, std::forward_iterator_tag)
1024 const size_type sz =
size();
1025 const size_type len =
static_cast<size_type
>(std::distance(first, last));
1026 if (len > capacity())
1028 check_init_length(len, get_allocator());
1029 pointer p = allocate_and_copy(len, first, last);
1030 destroy(get_data().p_begin, get_data().p_end, get_allocator());
1031 this->deallocate(get_data().p_begin, capacity());
1032 this->assign_storage(p, len, len);
1036 pointer p = std::copy(first, last, get_data().p_begin);
1042 std::advance(mid, sz);
1043 std::copy(first, mid, get_data().p_begin);
1044 get_data().p_end = copy_initialize(mid, last, get_data().p_end, get_allocator());
1049 constexpr void buffer<T>::erase_at_end(pointer p)
1051 destroy(p, get_data().p_end, get_allocator());
1052 get_data().p_end = p;
1073 constexpr auto buffer<T>::check_init_length(size_type n,
const allocator_type& a) -> size_type
1075 if (n > max_size_impl(a))
1077 throw std::length_error(
"cannot create buffer larger than max_size()");
1083 constexpr auto buffer<T>::max_size_impl(
const allocator_type& a)
noexcept -> size_type
1085 const size_type diff_max =
static_cast<size_type
>(std::numeric_limits<difference_type>::max());
1086 const size_type alloc_max = std::allocator_traits<allocator_type>::max_size(a);
1087 return (std::min) (diff_max, alloc_max);
1091 constexpr auto buffer<T>::default_initialize(pointer begin, size_type n, allocator_type& a) -> pointer
1093 pointer current = begin;
1094 for (; n > 0; --n, ++current)
1096 alloc_traits::construct(a, current);
1103 buffer<T>::fill_initialize(pointer begin, size_type n,
const value_type& v, allocator_type& a) -> pointer
1105 pointer current = begin;
1106 for (; n > 0; --n, ++current)
1108 alloc_traits::construct(a, current, v);
1118 for (; first != last; ++first, ++current)
1120 alloc_traits::construct(a, current, *first);
1126 constexpr void buffer<T>::destroy(pointer first, pointer last, allocator_type& a)
1129 for (; first != last; ++first)
1131 alloc_traits::destroy(a, first);
1138 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 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__)
constexpr std::size_t size(typelist< T... >={})
Gets the count of types contained in a typelist.
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