30#if not defined(SPARROW_BUFFER_GROWTH_FACTOR)
31# define SPARROW_BUFFER_GROWTH_FACTOR 2
50 using pointer =
typename alloc_traits::pointer;
84 [[nodiscard]] constexpr buffer_data&
get_data() noexcept;
85 [[nodiscard]] constexpr const buffer_data&
get_data() const noexcept;
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>
145 template <allocator A = allocator_type>
148 template <allocator A = allocator_type>
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
233 template <
mpl::iterator_of_type<T> InputIt>
240 template <class... Args>
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>
355 template <allocator A>
358 , m_data(
std::move(rhs.m_data))
389 return alloc_traits::allocate(m_alloc, n);
395 alloc_traits::deallocate(m_alloc, p, n);
402 m_data.p_end = m_data.p_begin + n;
403 m_data.p_storage_end = m_data.p_end;
411 m_data.p_end = p + n;
412 m_data.p_storage_end = p + cap;
420 template <allocator A>
422 : base_type(check_init_length(n, a), a)
424 get_data().p_end = default_initialize(get_data().p_begin, n, get_allocator());
428 template <allocator A>
430 : base_type(check_init_length(n, a), a)
432 get_data().p_end = fill_initialize(get_data().p_begin, n, v, get_allocator());
436 template <allocator A>
438 : base_type(p, check_init_length(n, a), a)
443 template <allocator A>
445 : base_type(check_init_length(init.
size(), a), a)
447 get_data().p_end = copy_initialize(init.begin(), init.end(), get_data().p_begin, get_allocator());
451 template <
class It, allocator A>
453 : base_type(check_init_length(static_cast<
size_type>(
std::distance(first, last)), a), a)
455 get_data().p_end = copy_initialize(first, last, get_data().p_begin, get_allocator());
459 template <std::ranges::input_range Range, allocator A>
460 requires std::same_as<std::ranges::range_value_t<Range>, T>
464 get_data().p_end = copy_initialize(
465 std::ranges::begin(range),
466 std::ranges::end(range),
475 destroy(get_data().p_begin, get_data().p_end, get_allocator());
480 : base_type(rhs.get_allocator())
482 if (rhs.get_data().
p_begin !=
nullptr)
484 this->create_storage(rhs.size());
485 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
490 template <allocator A>
494 if (rhs.get_data().
p_begin !=
nullptr)
496 this->create_storage(rhs.size());
497 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
502 template <allocator A>
506 if (rhs.get_allocator() == get_allocator())
508 get_data() = std::move(rhs.m_data);
510 else if (!rhs.empty())
512 if (rhs.get_data().p_begin != nullptr)
514 this->create_storage(rhs.size());
515 get_data().p_end = copy_initialize(rhs.begin(), rhs.end(), get_data().p_begin, get_allocator());
524 if (std::addressof(rhs) !=
this)
526 if (rhs.get_data().
p_begin !=
nullptr)
529 assign_range_impl(rhs.get_data().
p_begin, rhs.get_data().
p_end, std::random_access_iterator_tag());
536 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
547 if (get_allocator() == rhs.get_allocator())
549 get_data() = std::move(rhs.get_data());
553 if (rhs.get_data().p_begin !=
nullptr)
556 std::make_move_iterator(rhs.begin()),
557 std::make_move_iterator(rhs.end()),
558 std::random_access_iterator_tag()
575 std::make_move_iterator(init.begin()),
576 std::make_move_iterator(init.end()),
577 std::random_access_iterator_tag()
586 return get_data().p_begin[i];
593 return get_data().p_begin[i];
600 return *(get_data().p_begin);
607 return *(get_data().p_begin);
614 return *(get_data().p_end - 1);
621 return *(get_data().p_end - 1);
629# pragma GCC diagnostic push
630# pragma GCC diagnostic ignored "-Wcast-align"
632 return reinterpret_cast<U*
>(get_data().p_begin);
634# pragma GCC diagnostic pop
643# pragma GCC diagnostic push
644# pragma GCC diagnostic ignored "-Wcast-align"
646 return reinterpret_cast<U*
>(get_data().p_begin);
648# pragma GCC diagnostic pop
727 return get_data().p_begin == get_data().p_end;
733 return static_cast<size_type>(get_data().p_storage_end - get_data().p_begin);
739 return static_cast<size_type>(get_data().p_end - get_data().p_begin);
745 return max_size_impl(get_allocator());
753 throw std::length_error(
"buffer::reserve called with new_cap > max_size()");
757 if (
data() ==
nullptr)
760 get_data().p_end = get_data().p_begin;
765 pointer tmp = allocate_and_copy(
767 std::make_move_iterator(get_data().p_begin),
768 std::make_move_iterator(get_data().p_end)
770 destroy(get_data().p_begin, get_data().p_end, get_allocator());
773 static_cast<size_type>(get_data().p_storage_end - get_data().p_begin)
781 constexpr void buffer<T>::reserve_with_growth_factor(size_type new_cap)
783 if (new_cap > capacity())
794 buffer(std::make_move_iterator(
begin()), std::make_move_iterator(
end()), get_allocator()).swap(*
this);
801 if (get_data().p_begin !=
nullptr)
803 erase_at_end(get_data().p_begin);
820 return emplace(pos, std::move(value));
832 reserve_with_growth_factor(
size() + count);
835 std::fill_n(it, count, value);
836 get_data().p_end += count;
838 return std::next(
begin(), offset);
841 template <
typename T>
846 template <
typename Iterator>
851 template <
typename T>
855 template <mpl::iterator_of_type<T> InputIt>
863 reserve_with_growth_factor(new_size);
867 std::move_backward(new_pos, end_it,
end());
870 std::uninitialized_move(first, last, new_pos);
874 std::uninitialized_copy(first, last, new_pos);
880 template <std::ranges::input_range R>
881 requires std::same_as<std::ranges::range_value_t<R>, T>
886 return insert(pos, std::ranges::begin(range), std::ranges::end(range));
894 return insert(pos, ilist.begin(), ilist.end());
898 template <
class... Args>
904 reserve_with_growth_factor(
size() + 1);
905 pointer p = get_data().p_begin + offset;
906 if (p != get_data().p_end)
908 alloc_traits::construct(get_allocator(), get_data().p_end, std::move(*(get_data().p_end - 1)));
909 std::move_backward(p, get_data().p_end - 1, get_data().p_end);
910 alloc_traits::construct(get_allocator(), p, std::forward<Args>(args)...);
914 alloc_traits::construct(get_allocator(), get_data().p_end, std::forward<Args>(args)...);
925 return erase(pos, pos + 1);
936 pointer p = get_data().p_begin + offset;
937 erase_at_end(std::move(p + len, get_data().p_end, p));
957 destroy(get_allocator(), get_data().p_end - 1);
968 get_data().p_end = default_initialize(get_data().p_end, nb_init, get_allocator());
980 get_data().p_end = fill_initialize(get_data().p_end, nb_init, value, get_allocator());
988 std::swap(this->get_data(), rhs.get_data());
993 constexpr void buffer<T>::resize_impl(size_type new_size, F&& initializer)
995 if (new_size > size())
997 const std::size_t nb_init = new_size - size();
998 if (new_size <= capacity())
1000 initializer(nb_init);
1005 initializer(nb_init);
1008 else if (new_size <
size())
1010 erase_at_end(get_data().p_begin + new_size);
1016 constexpr void buffer<T>::assign_range_impl(It first, It last, std::forward_iterator_tag)
1018 const size_type sz =
size();
1019 const size_type len =
static_cast<size_type
>(std::distance(first, last));
1020 if (len > capacity())
1022 check_init_length(len, get_allocator());
1023 pointer p = allocate_and_copy(len, first, last);
1024 destroy(get_data().p_begin, get_data().p_end, get_allocator());
1025 this->deallocate(get_data().p_begin, capacity());
1026 this->assign_storage(p, len, len);
1030 pointer p = std::copy(first, last, get_data().p_begin);
1036 std::advance(mid, sz);
1037 std::copy(first, mid, get_data().p_begin);
1038 get_data().p_end = copy_initialize(mid, last, get_data().p_end, get_allocator());
1043 constexpr void buffer<T>::erase_at_end(pointer p)
1045 destroy(p, get_data().p_end, get_allocator());
1046 get_data().p_end = p;
1067 constexpr auto buffer<T>::check_init_length(size_type n,
const allocator_type& a) -> size_type
1069 if (n > max_size_impl(a))
1071 throw std::length_error(
"cannot create buffer larger than max_size()");
1077 constexpr auto buffer<T>::max_size_impl(
const allocator_type& a)
noexcept -> size_type
1079 const size_type diff_max =
static_cast<size_type
>(std::numeric_limits<difference_type>::max());
1080 const size_type alloc_max = std::allocator_traits<allocator_type>::max_size(a);
1081 return (std::min) (diff_max, alloc_max);
1085 constexpr auto buffer<T>::default_initialize(pointer begin, size_type n, allocator_type& a) -> pointer
1087 pointer current = begin;
1088 for (; n > 0; --n, ++current)
1090 alloc_traits::construct(a, current);
1097 buffer<T>::fill_initialize(pointer begin, size_type n,
const value_type& v, allocator_type& a) -> pointer
1099 pointer current = begin;
1100 for (; n > 0; --n, ++current)
1102 alloc_traits::construct(a, current, v);
1112 for (; first != last; ++first, ++current)
1114 alloc_traits::construct(a, current, *first);
1120 constexpr void buffer<T>::destroy(pointer first, pointer last, allocator_type& a)
1123 for (; first != last; ++first)
1125 alloc_traits::destroy(a, first);
1132 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
constexpr buffer(size_type n, const A &a=A())
typename alloc_traits::difference_type difference_type
typename alloc_traits::size_type size_type
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 auto allocate_and_copy(size_type n, It first, It last) -> 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
buffer(const buffer &rhs, const A &a)
constexpr auto copy_initialize(It first, It last, pointer begin, allocator_type &a) -> pointer
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)
buffer(const buffer &rhs)
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, const value_type &v, const A &a=A())
constexpr buffer(pointer p, size_type n, const A &a=A())
constexpr const_iterator cend() const noexcept
constexpr iterator begin() noexcept
constexpr iterator end() noexcept
constexpr buffer(It first, It last, const A &a=A())
constexpr auto insert(const_iterator pos, InputIt first, InputIt last) -> iterator
constexpr reverse_iterator rend() noexcept
buffer(buffer &&rhs, const A &a)
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 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 buffer(std::initializer_list< value_type > init, const A &a=A())
constexpr reference front()
constexpr reverse_iterator rbegin() noexcept
constexpr size_type capacity() const noexcept
constexpr buffer(const Range &range, const A &a=A())
constexpr buffer & operator=(buffer &&rhs)
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