1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
10#define _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
11
12#include <__config>
13#include <__cstddef/size_t.h>
14#include <__iterator/iterator_traits.h>
15#include <__memory/addressof.h>
16#include <__memory/allocator_traits.h>
17#include <__type_traits/enable_if.h>
18#include <__type_traits/extent.h>
19#include <__type_traits/is_array.h>
20#include <__type_traits/is_same.h>
21#include <__type_traits/remove_extent.h>
22#include <__utility/exception_guard.h>
23
24#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
25# pragma GCC system_header
26#endif
27
28_LIBCPP_PUSH_MACROS
29#include <__undef_macros>
30
31#if _LIBCPP_STD_VER >= 17
32
33_LIBCPP_BEGIN_NAMESPACE_STD
34
35// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
36// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
37// destruction. If elements are themselves C-style arrays, they are recursively destroyed
38// in the same manner.
39//
40// This function assumes that destructors do not throw, and that the allocator is bound to
41// the correct type.
42template <class _Alloc,
43 class _BidirIter,
44 __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
45_LIBCPP_HIDE_FROM_ABI constexpr void
46__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
47 using _ValueType = typename iterator_traits<_BidirIter>::value_type;
48 static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
49 "The allocator should already be rebound to the correct type");
50
51 if (__first == __last)
52 return;
53
54 if constexpr (is_array_v<_ValueType>) {
55 static_assert(!__is_unbounded_array_v<_ValueType>,
56 "arrays of unbounded arrays don't exist, but if they did we would mess up here");
57
58 using _Element = remove_extent_t<_ValueType>;
59 __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
60 do {
61 --__last;
62 decltype(auto) __array = *__last;
63 std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
64 } while (__last != __first);
65 } else {
66 do {
67 --__last;
68 allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
69 } while (__last != __first);
70 }
71}
72
73// Constructs the object at the given location using the allocator's construct method.
74//
75// If the object being constructed is an array, each element of the array is allocator-constructed,
76// recursively. If an exception is thrown during the construction of an array, the initialized
77// elements are destroyed in reverse order of initialization using allocator destruction.
78//
79// This function assumes that the allocator is bound to the correct type.
80template <class _Alloc, class _Tp>
81_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
82 static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
83 "The allocator should already be rebound to the correct type");
84
85 if constexpr (is_array_v<_Tp>) {
86 using _Element = remove_extent_t<_Tp>;
87 __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
88 size_t __i = 0;
89 _Tp& __array = *__loc;
90
91 // If an exception is thrown, destroy what we have constructed so far in reverse order.
92 auto __guard = std::__make_exception_guard([&]() {
93 std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
94 });
95
96 for (; __i != extent_v<_Tp>; ++__i) {
97 std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
98 }
99 __guard.__complete();
100 } else {
101 allocator_traits<_Alloc>::construct(__alloc, __loc);
102 }
103}
104
105// Constructs the object at the given location using the allocator's construct method, passing along
106// the provided argument.
107//
108// If the object being constructed is an array, the argument is also assumed to be an array. Each
109// each element of the array being constructed is allocator-constructed from the corresponding
110// element of the argument array. If an exception is thrown during the construction of an array,
111// the initialized elements are destroyed in reverse order of initialization using allocator
112// destruction.
113//
114// This function assumes that the allocator is bound to the correct type.
115template <class _Alloc, class _Tp, class _Arg>
116_LIBCPP_HIDE_FROM_ABI constexpr void
117__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
118 static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
119 "The allocator should already be rebound to the correct type");
120
121 if constexpr (is_array_v<_Tp>) {
122 static_assert(is_array_v<_Arg>,
123 "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
124 "trying to construct an array.");
125
126 using _Element = remove_extent_t<_Tp>;
127 __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
128 size_t __i = 0;
129 _Tp& __array = *__loc;
130
131 // If an exception is thrown, destroy what we have constructed so far in reverse order.
132 auto __guard = std::__make_exception_guard([&]() {
133 std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
134 });
135 for (; __i != extent_v<_Tp>; ++__i) {
136 std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
137 }
138 __guard.__complete();
139 } else {
140 allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
141 }
142}
143
144// Given a range starting at it and containing n elements, initializes each element in the
145// range from left to right using the construct method of the allocator (rebound to the
146// correct type).
147//
148// If an exception is thrown, the initialized elements are destroyed in reverse order of
149// initialization using allocator_traits destruction. If the elements in the range are C-style
150// arrays, they are initialized element-wise using allocator construction, and recursively so.
151template <class _Alloc,
152 class _BidirIter,
153 class _Tp,
154 class _Size = typename iterator_traits<_BidirIter>::difference_type>
155_LIBCPP_HIDE_FROM_ABI constexpr void
156__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
157 using _ValueType = typename iterator_traits<_BidirIter>::value_type;
158 __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
159 _BidirIter __begin = __it;
160
161 // If an exception is thrown, destroy what we have constructed so far in reverse order.
162 auto __guard =
163 std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
164 for (; __n != 0; --__n, ++__it) {
165 std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
166 }
167 __guard.__complete();
168}
169
170// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
171// to the allocator's construct method, which results in value initialization.
172template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
173_LIBCPP_HIDE_FROM_ABI constexpr void
174__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
175 using _ValueType = typename iterator_traits<_BidirIter>::value_type;
176 __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
177 _BidirIter __begin = __it;
178
179 // If an exception is thrown, destroy what we have constructed so far in reverse order.
180 auto __guard =
181 std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
182 for (; __n != 0; --__n, ++__it) {
183 std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
184 }
185 __guard.__complete();
186}
187
188_LIBCPP_END_NAMESPACE_STD
189
190#endif // _LIBCPP_STD_VER >= 17
191
192_LIBCPP_POP_MACROS
193
194#endif // _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H