30#if not defined(SPARROW_BUFFER_GROWTH_FACTOR)
31# define SPARROW_BUFFER_GROWTH_FACTOR 2
50 using pointer =
typename alloc_traits::pointer;
114 std::same_as<std::remove_cvref_t<T>, T>,
115 "buffer must have a non-const, non-volatile, non-reference value_type"
124 using pointer =
typename alloc_traits::pointer;
136 requires(not std::same_as<A, buffer<T>> and
allocator<A>)
142 template <allocator A = allocator_type>
143 constexpr explicit buffer(size_type n,
const A& a = A());
145 template <allocator A = allocator_type>
146 constexpr buffer(size_type n,
const value_type& v,
const A& a = A());
148 template <allocator A = allocator_type>
149 constexpr buffer(pointer p, size_type n,
const A& a = A());
151 template <allocator A = allocator_type>
152 constexpr buffer(std::initializer_list<value_type> init,
const A& a = A());
154 template <
class It, allocator A = allocator_type>
155 constexpr buffer(It first, It last,
const A& a = A());
157 template <std::ranges::input_range Range, allocator A = allocator_type>
158 requires std::same_as<std::ranges::range_value_t<Range>, T>
159 constexpr buffer(
const Range& range,
const A& a = A());
165 template <allocator A>
170 template <allocator A>
189 template <
class U = T>
190 [[nodiscard]]
constexpr U*
data() noexcept;
193 template <class U = T>
194 [[nodiscard]] constexpr const U*
data() const noexcept;
218 [[nodiscard]] constexpr
bool
219 empty() const noexcept;
228 constexpr
void clear();
233 template <
mpl::iterator_of_type<T> InputIt>
235 template <
std::ranges::input_range R>
236 requires
std::same_as<
std::ranges::range_value_t<R>, T>
240 template <class... Args>
246 constexpr
void push_back(const T& value);
257 using base_type::get_allocator;
258 using base_type::get_data;
261 constexpr
void resize_impl(
size_type new_size, F&& initializer);
264 constexpr
void assign_range_impl(It first, It last,
std::forward_iterator_tag);
266 constexpr
void erase_at_end(
pointer p);
271 constexpr
void reserve_with_growth_factor(
size_type new_cap);
285 [[nodiscard]] static constexpr
pointer
298 constexpr
bool operator==(const
buffer<T>& lhs, const
buffer<T>& rhs) noexcept;
310 rhs.p_begin =
nullptr;
312 rhs.p_storage_end =
nullptr;
318 std::swap(
p_begin, rhs.p_begin);
319 std::swap(
p_end, rhs.p_end);
325 template <allocator A>
332 template <allocator A>
340 template <allocator A>
354 template <allocator A>
357 , m_data(
std::move(rhs.m_data))
388 return alloc_traits::allocate(m_alloc, n);
394 alloc_traits::deallocate(m_alloc, p, n);
401 m_data.p_end = m_data.p_begin + n;
402 m_data.p_storage_end = m_data.p_begin + n;
410 m_data.p_end = p + n;
411 m_data.p_storage_end = p + cap;
419 template <allocator A>
421 : base_type(check_init_length(n, a), a)
423 get_data().p_end = default_initialize(get_data().p_begin, n, get_allocator());
427 template <allocator A>
429 : base_type(check_init_length(n, a), a)
431 get_data().p_end = fill_initialize(get_data().p_begin, n, v, get_allocator());
435 template <allocator A>
437 : base_type(p, check_init_length(n, a), a)
442 template <allocator A>
444 : base_type(check_init_length(init.
size(), a), a)
446 get_data().p_end = copy_initialize(init.begin(), init.end(), get_data().p_begin, get_allocator());
450 template <
class It, allocator A>
452 : base_type(check_init_length(static_cast<
size_type>(
std::distance(first, last)), a), a)
454 get_data().p_end = copy_initialize(first, last, get_data().p_begin, get_allocator());
458 template <std::ranges::input_range Range, allocator A>
459 requires std::same_as<std::ranges::range_value_t<Range>, T>
461 : base_type(check_init_length(static_cast<
size_type>(
std::ranges::
size(range)), a), a)
463 get_data().p_end = copy_initialize(
464 std::ranges::begin(range),
465 std::ranges::end(range),
474 destroy(get_data().p_begin, get_data().p_end, get_allocator());
479 : base_type(rhs.
size(), rhs.get_allocator())
481 get_data().p_end = copy_initialize(rhs.
begin(), rhs.
end(), get_data().p_begin, get_allocator());
485 template <allocator A>
487 : base_type(rhs.
size(), a)
489 get_data().p_end = copy_initialize(rhs.
begin(), rhs.
end(), get_data().p_begin, get_allocator());
493 template <allocator A>
497 if (rhs.get_allocator() == get_allocator())
499 get_data() = std::move(rhs.m_data);
501 else if (!rhs.empty())
503 this->create_storage(rhs.size());
504 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
512 if (std::addressof(rhs) !=
this)
515 assign_range_impl(rhs.get_data().
p_begin, rhs.get_data().
p_end, std::random_access_iterator_tag());
523 if (get_allocator() == rhs.get_allocator())
525 get_data() = std::move(rhs.get_data());
530 std::make_move_iterator(rhs.begin()),
531 std::make_move_iterator(rhs.end()),
532 std::random_access_iterator_tag()
543 std::make_move_iterator(init.begin()),
544 std::make_move_iterator(init.end()),
545 std::random_access_iterator_tag()
554 return get_data().p_begin[i];
561 return get_data().p_begin[i];
568 return *(get_data().p_begin);
575 return *(get_data().p_begin);
582 return *(get_data().p_end - 1);
589 return *(get_data().p_end - 1);
597# pragma GCC diagnostic push
598# pragma GCC diagnostic ignored "-Wcast-align"
600 return reinterpret_cast<U*
>(get_data().p_begin);
602# pragma GCC diagnostic pop
611# pragma GCC diagnostic push
612# pragma GCC diagnostic ignored "-Wcast-align"
614 return reinterpret_cast<U*
>(get_data().p_begin);
616# pragma GCC diagnostic pop
695 return get_data().p_begin == get_data().p_end;
701 return static_cast<size_type>(get_data().p_storage_end - get_data().p_begin);
707 return static_cast<size_type>(get_data().p_end - get_data().p_begin);
713 return max_size_impl(get_allocator());
721 throw std::length_error(
"buffer::reserve called with new_cap > max_size()");
726 pointer tmp = allocate_and_copy(
728 std::make_move_iterator(get_data().p_begin),
729 std::make_move_iterator(get_data().p_end)
731 destroy(get_data().p_begin, get_data().p_end, get_allocator());
734 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
741 constexpr void buffer<T>::reserve_with_growth_factor(size_type new_cap)
743 if (new_cap > capacity())
754 buffer(std::make_move_iterator(
begin()), std::make_move_iterator(
end()), get_allocator()).swap(*
this);
761 erase_at_end(get_data().p_begin);
777 return emplace(pos, std::move(value));
789 reserve_with_growth_factor(
size() + count);
792 std::fill_n(it, count, value);
793 get_data().p_end += count;
795 return std::next(
begin(), offset);
798 template <
typename T>
803 template <
typename Iterator>
808 template <
typename T>
812 template <mpl::iterator_of_type<T> InputIt>
820 reserve_with_growth_factor(new_size);
824 std::move_backward(new_pos, end_it,
end());
827 std::uninitialized_move(first, last, new_pos);
831 std::uninitialized_copy(first, last, new_pos);
837 template <std::ranges::input_range R>
838 requires std::same_as<std::ranges::range_value_t<R>, T>
843 return insert(pos, std::ranges::begin(range), std::ranges::end(range));
851 return insert(pos, ilist.begin(), ilist.end());
855 template <
class... Args>
861 reserve_with_growth_factor(
size() + 1);
862 pointer p = get_data().p_begin + offset;
863 if (p != get_data().p_end)
865 alloc_traits::construct(get_allocator(), get_data().p_end, std::move(*(get_data().p_end - 1)));
866 std::move_backward(p, get_data().p_end - 1, get_data().p_end);
867 alloc_traits::construct(get_allocator(), p, std::forward<Args>(args)...);
871 alloc_traits::construct(get_allocator(), get_data().p_end, std::forward<Args>(args)...);
882 return erase(pos, pos + 1);
893 pointer p = get_data().p_begin + offset;
894 erase_at_end(std::move(p + len, get_data().p_end, p));
914 destroy(get_allocator(), get_data().p_end - 1);
925 get_data().p_end = default_initialize(get_data().p_end, nb_init, get_allocator());
937 get_data().p_end = fill_initialize(get_data().p_end, nb_init, value, get_allocator());
945 std::swap(this->get_data(), rhs.get_data());
950 constexpr void buffer<T>::resize_impl(size_type new_size, F&& initializer)
952 if (new_size > size())
954 const std::size_t nb_init = new_size - size();
955 if (new_size <= capacity())
957 initializer(nb_init);
962 initializer(nb_init);
965 else if (new_size <
size())
967 erase_at_end(get_data().p_begin + new_size);
973 constexpr void buffer<T>::assign_range_impl(It first, It last, std::forward_iterator_tag)
975 const size_type sz =
size();
976 const size_type len =
static_cast<size_type
>(std::distance(first, last));
977 if (len > capacity())
979 check_init_length(len, get_allocator());
980 pointer p = allocate_and_copy(len, first, last);
981 destroy(get_data().p_begin, get_data().p_end, get_allocator());
982 this->deallocate(get_data().p_begin, capacity());
983 this->assign_storage(p, len, len);
987 pointer p = std::copy(first, last, get_data().p_begin);
993 std::advance(mid, sz);
994 std::copy(first, mid, get_data().p_begin);
995 get_data().p_end = copy_initialize(mid, last, get_data().p_end, get_allocator());
1000 constexpr void buffer<T>::erase_at_end(pointer p)
1002 destroy(p, get_data().p_end, get_allocator());
1003 get_data().p_end = p;
1013 copy_initialize(first, last, p, get_allocator());
1024 constexpr auto buffer<T>::check_init_length(size_type n,
const allocator_type& a) -> size_type
1026 if (n > max_size_impl(a))
1028 throw std::length_error(
"cannot create buffer larger than max_size()");
1034 constexpr auto buffer<T>::max_size_impl(
const allocator_type& a)
noexcept -> size_type
1036 const size_type diff_max =
static_cast<size_type
>(std::numeric_limits<difference_type>::max());
1037 const size_type alloc_max = std::allocator_traits<allocator_type>::max_size(a);
1038 return (std::min)(diff_max, alloc_max);
1042 constexpr auto buffer<T>::default_initialize(pointer begin, size_type n, allocator_type& a) -> pointer
1044 pointer current = begin;
1045 for (; n > 0; --n, ++current)
1047 alloc_traits::construct(a, current);
1054 buffer<T>::fill_initialize(pointer begin, size_type n,
const value_type& v, allocator_type& a) -> pointer
1056 pointer current = begin;
1057 for (; n > 0; --n, ++current)
1059 alloc_traits::construct(a, current, v);
1069 for (; first != last; ++first, ++current)
1071 alloc_traits::construct(a, current, *first);
1077 constexpr void buffer<T>::destroy(pointer first, pointer last, allocator_type& a)
1079 for (; first != last; ++first)
1081 alloc_traits::destroy(a, first);
1088 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
typename alloc_traits::size_type size_type
constexpr void resize(size_type new_size)
constexpr size_type max_size() const noexcept
typename alloc_traits::const_pointer const_pointer
constexpr iterator insert(const_iterator pos, const 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
typename alloc_traits::pointer pointer
constexpr buffer(const A &a)
pointer_iterator< pointer > iterator
constexpr const_iterator cend() const noexcept
constexpr iterator begin() noexcept
constexpr iterator end() noexcept
constexpr reverse_iterator rend() noexcept
constexpr size_type size() const noexcept
constexpr reference operator[](size_type i)
constexpr void reserve(size_type new_cap)
buffer(buffer &&rhs) noexcept=default
constexpr void pop_back()
constexpr buffer & operator=(const buffer &rhs)
constexpr void swap(buffer &rhs) noexcept
constexpr void push_back(const T &value)
constexpr const_reverse_iterator crbegin() const noexcept
constexpr iterator emplace(const_iterator pos, Args &&... args)
constexpr reference front()
constexpr reverse_iterator rbegin() noexcept
constexpr size_type capacity() const noexcept
constexpr bool empty() const noexcept
#define SPARROW_ASSERT_TRUE(expr__)
#define SPARROW_ASSERT_FALSE(expr__)
constexpr std::size_t size(typelist< T... >={})
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
constexpr buffer_data & operator=(buffer_data &&) noexcept