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()) +
">";
72 using pointer =
typename alloc_traits::pointer;
106 [[nodiscard]] constexpr buffer_data&
get_data() noexcept;
107 [[nodiscard]] constexpr const buffer_data&
get_data() const noexcept;
137 std::same_as<std::remove_cvref_t<T>, T>,
138 "buffer must have a non-const, non-volatile, non-reference value_type"
148 using pointer =
typename alloc_traits::pointer;
160 requires(not std::same_as<A, buffer<T>> and
allocator<A>)
166 template <allocator A>
169 template <allocator A>
172 template <allocator A>
175 template <allocator A>
176 constexpr buffer(std::initializer_list<value_type> init,
const A& a);
178 template <
class It, allocator A>
179 constexpr buffer(It first, It last,
const A& a);
181 template <std::ranges::input_range Range, allocator A>
183 constexpr buffer(
const Range& range,
const A& a);
189 template <allocator A>
194 template <allocator A>
213 template <
class U = T>
214 [[nodiscard]]
constexpr U*
data() noexcept;
217 template <class U = T>
218 [[nodiscard]] constexpr const U*
data() const noexcept;
242 [[nodiscard]] constexpr
bool empty() const noexcept;
256 template <
mpl::iterator_of_type<T> InputIt>
263 template <class... Args>
282 using base_type::get_data;
285 constexpr
void resize_impl(
size_type new_size, F&& initializer);
288 constexpr
void assign_range_impl(It first, It last,
std::forward_iterator_tag);
290 constexpr
void erase_at_end(
pointer p);
295 constexpr
void reserve_with_growth_factor(
size_type new_cap);
309 [[nodiscard]] static constexpr
pointer
322 constexpr
bool operator==(const
buffer<T>& lhs, const
buffer<T>& rhs) noexcept;
334 rhs.p_begin =
nullptr;
336 rhs.p_storage_end =
nullptr;
342 std::swap(
p_begin, rhs.p_begin);
343 std::swap(
p_end, rhs.p_end);
349 template <allocator A>
356 template <allocator A>
364 template <allocator A>
379 template <allocator A>
382 , m_data(
std::move(rhs.m_data))
413 return alloc_traits::allocate(m_alloc, n);
419 alloc_traits::deallocate(m_alloc, p, n);
426 m_data.p_end = m_data.p_begin + n;
427 m_data.p_storage_end = m_data.p_end;
435 m_data.p_end = p + n;
436 m_data.p_storage_end = p + cap;
444 template <allocator A>
446 : base_type(check_init_length(n, a), a)
448 get_data().p_end = default_initialize(get_data().p_begin, n,
get_allocator());
452 template <allocator A>
454 : base_type(check_init_length(n, a), a)
456 get_data().p_end = fill_initialize(get_data().p_begin, n, v,
get_allocator());
460 template <allocator A>
462 : base_type(p, check_init_length(n, a), a)
467 template <allocator A>
469 : base_type(check_init_length(init.
size(), a), a)
471 get_data().p_end = copy_initialize(init.begin(), init.end(), get_data().p_begin,
get_allocator());
475 template <
class It, allocator A>
477 : base_type(check_init_length(static_cast<
size_type>(
std::distance(first, last)), a), a)
479 get_data().p_end = copy_initialize(first, last, get_data().p_begin,
get_allocator());
483 template <std::ranges::input_range Range, allocator A>
486 : base_type(check_init_length(
static_cast<size_type>(std::ranges::size(range)), a), a)
488 get_data().p_end = copy_initialize(
489 std::ranges::begin(range),
490 std::ranges::end(range),
499 destroy(get_data().p_begin, get_data().p_end,
get_allocator());
506 if (rhs.get_data().
p_begin !=
nullptr)
508 this->create_storage(rhs.size());
509 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
515 template <allocator A>
519 if (rhs.get_data().
p_begin !=
nullptr)
521 this->create_storage(rhs.size());
522 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
528 template <allocator A>
534 get_data() = std::move(rhs.m_data);
536 else if (!rhs.empty())
538 if (rhs.get_data().p_begin != nullptr)
540 this->create_storage(rhs.size());
541 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
550 if (std::addressof(rhs) !=
this)
552 if (rhs.get_data().
p_begin !=
nullptr)
555 assign_range_impl(rhs.get_data().
p_begin, rhs.get_data().
p_end, std::random_access_iterator_tag());
562 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
576 get_data() = std::move(rhs.get_data());
580 if (rhs.get_data().p_begin !=
nullptr)
583 std::make_move_iterator(rhs.begin()),
584 std::make_move_iterator(rhs.end()),
585 std::random_access_iterator_tag()
602 std::make_move_iterator(init.begin()),
603 std::make_move_iterator(init.end()),
604 std::random_access_iterator_tag()
613 return get_data().p_begin[i];
620 return get_data().p_begin[i];
627 return *(get_data().p_begin);
634 return *(get_data().p_begin);
641 return *(get_data().p_end - 1);
648 return *(get_data().p_end - 1);
656# pragma GCC diagnostic push
657# pragma GCC diagnostic ignored "-Wcast-align"
659 return reinterpret_cast<U*
>(get_data().p_begin);
661# pragma GCC diagnostic pop
670# pragma GCC diagnostic push
671# pragma GCC diagnostic ignored "-Wcast-align"
673 return reinterpret_cast<U*
>(get_data().p_begin);
675# pragma GCC diagnostic pop
754 const auto&
data = get_data();
761 const auto&
data = get_data();
762 if (
data.p_begin ==
nullptr ||
data.p_storage_end ==
nullptr)
772 const auto&
data = get_data();
773 if (
data.p_begin ==
nullptr ||
data.p_end ==
nullptr)
791 throw std::length_error(
"buffer::reserve called with new_cap > max_size()");
795 if (
data() ==
nullptr)
798 get_data().p_end = get_data().p_begin;
803 pointer tmp = allocate_and_copy(
805 std::make_move_iterator(get_data().p_begin),
806 std::make_move_iterator(get_data().p_end)
808 destroy(get_data().p_begin, get_data().p_end,
get_allocator());
811 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
819 constexpr void buffer<T>::reserve_with_growth_factor(size_type new_cap)
821 if (new_cap > capacity())
839 if (get_data().p_begin !=
nullptr)
841 erase_at_end(get_data().p_begin);
858 return emplace(pos, std::move(value));
870 reserve_with_growth_factor(
size() + count);
873 std::fill_n(it, count, value);
874 get_data().p_end += count;
876 return std::next(
begin(), offset);
879 template <
typename T>
884 template <
typename Iterator>
889 template <
typename T>
893 template <mpl::iterator_of_type<T> InputIt>
901 reserve_with_growth_factor(new_size);
905 std::move_backward(new_pos, end_it,
end());
908 std::uninitialized_move(first, last, new_pos);
912 std::uninitialized_copy(first, last, new_pos);
918 template <std::ranges::input_range R>
919 requires std::same_as<std::ranges::range_value_t<R>, T>
924 return insert(pos, std::ranges::begin(range), std::ranges::end(range));
932 return insert(pos, ilist.begin(), ilist.end());
936 template <
class... Args>
942 reserve_with_growth_factor(
size() + 1);
943 pointer p = get_data().p_begin + offset;
944 if (p != get_data().p_end)
946 alloc_traits::construct(
get_allocator(), get_data().p_end, std::move(*(get_data().p_end - 1)));
947 std::move_backward(p, get_data().p_end - 1, get_data().p_end);
948 alloc_traits::construct(
get_allocator(), p, std::forward<Args>(args)...);
952 alloc_traits::construct(
get_allocator(), get_data().p_end, std::forward<Args>(args)...);
963 return erase(pos, pos + 1);
974 pointer p = get_data().p_begin + offset;
975 erase_at_end(std::move(p + len, get_data().p_end, p));
995 alloc_traits::destroy(
get_allocator(), get_data().p_end - 1);
1006 get_data().p_end = default_initialize(get_data().p_end, nb_init,
get_allocator());
1018 get_data().p_end = fill_initialize(get_data().p_end, nb_init, value,
get_allocator());
1026 std::swap(this->get_data(), rhs.get_data());
1031 constexpr void buffer<T>::resize_impl(size_type new_size, F&& initializer)
1033 if (new_size > size())
1035 const std::size_t nb_init = new_size - size();
1036 if (new_size <= capacity())
1038 initializer(nb_init);
1043 initializer(nb_init);
1046 else if (new_size <
size())
1048 erase_at_end(get_data().p_begin + new_size);
1054 constexpr void buffer<T>::assign_range_impl(It first, It last, std::forward_iterator_tag)
1056 const size_type sz =
size();
1057 const size_type len =
static_cast<size_type
>(std::distance(first, last));
1058 if (len > capacity())
1060 check_init_length(len, get_allocator());
1061 pointer p = allocate_and_copy(len, first, last);
1062 destroy(get_data().p_begin, get_data().p_end, get_allocator());
1063 this->deallocate(get_data().p_begin, capacity());
1064 this->assign_storage(p, len, len);
1068 pointer p = std::copy(first, last, get_data().p_begin);
1074 std::advance(mid, sz);
1075 std::copy(first, mid, get_data().p_begin);
1076 get_data().p_end = copy_initialize(mid, last, get_data().p_end, get_allocator());
1081 constexpr void buffer<T>::erase_at_end(pointer p)
1083 destroy(p, get_data().p_end, get_allocator());
1084 get_data().p_end = p;
1105 constexpr auto buffer<T>::check_init_length(size_type n,
const allocator_type& a) -> size_type
1107 if (n > max_size_impl(a))
1109 throw std::length_error(
"cannot create buffer larger than max_size()");
1115 constexpr auto buffer<T>::max_size_impl(
const allocator_type& a)
noexcept -> size_type
1117 const size_type diff_max =
static_cast<size_type
>(std::numeric_limits<difference_type>::max());
1118 const size_type alloc_max = std::allocator_traits<allocator_type>::max_size(a);
1119 return (std::min) (diff_max, alloc_max);
1123 constexpr auto buffer<T>::default_initialize(pointer begin, size_type n, allocator_type& a) -> pointer
1125 pointer current = begin;
1126 for (; n > 0; --n, ++current)
1128 alloc_traits::construct(a, current);
1135 buffer<T>::fill_initialize(pointer begin, size_type n,
const value_type& v, allocator_type& a) -> pointer
1137 pointer current = begin;
1138 for (; n > 0; --n, ++current)
1140 alloc_traits::construct(a, current, v);
1150 for (; first != last; ++first, ++current)
1152 alloc_traits::construct(a, current, *first);
1158 constexpr void buffer<T>::destroy(pointer first, pointer last, allocator_type& a)
1161 for (; first != last; ++first)
1163 alloc_traits::destroy(a, first);
1170 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__)
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