Line data Source code
1 : #ifndef AMREX_PODVECTOR_H_
2 : #define AMREX_PODVECTOR_H_
3 : #include <AMReX_Config.H>
4 :
5 : #include <AMReX.H>
6 : #include <AMReX_Arena.H>
7 : #include <AMReX_GpuLaunch.H>
8 : #include <AMReX_GpuAllocators.H>
9 : #include <AMReX_GpuDevice.H>
10 : #include <AMReX_MemPool.H>
11 : #include <AMReX_TypeTraits.H>
12 :
13 : #include <iterator>
14 : #include <type_traits>
15 : #include <utility>
16 : #include <memory>
17 : #include <cstring>
18 :
19 : namespace amrex
20 : {
21 : namespace detail
22 : {
23 : template <typename T, typename Size, template<class> class Allocator>
24 : FatPtr<T> allocate_in_place ([[maybe_unused]] T* p, [[maybe_unused]] Size nmin, Size nmax,
25 : Allocator<T>& allocator)
26 : {
27 : if constexpr (IsArenaAllocator<Allocator<T>>::value) {
28 : return allocator.allocate_in_place(p, nmin, nmax);
29 : } else {
30 : T* pnew = allocator.allocate(nmax);
31 : return {pnew, nmax};
32 : }
33 : }
34 :
35 : template <typename T, typename Size, template<class> class Allocator>
36 : T* shrink_in_place ([[maybe_unused]] T* p, Size n, Allocator<T>& allocator)
37 : {
38 : if constexpr (IsArenaAllocator<Allocator<T>>::value) {
39 : return allocator.shrink_in_place(p, n);
40 : } else {
41 : return allocator.allocate(n);
42 : }
43 : }
44 :
45 : template <typename T, typename Size, template<class> class Allocator>
46 : void uninitializedFillNImpl (T* data, Size count, const T& value,
47 : [[maybe_unused]] Allocator<T> const& allocator)
48 : {
49 : #ifdef AMREX_USE_GPU
50 : if constexpr (RunOnGpu<Allocator<T>>::value)
51 : {
52 : amrex::ParallelFor(count, [=] AMREX_GPU_DEVICE (Size i) noexcept {
53 : data[i] = value;
54 : });
55 : Gpu::streamSynchronize();
56 : return;
57 : }
58 : else if constexpr (IsPolymorphicArenaAllocator<Allocator<T>>::value)
59 : {
60 : if (allocator.arena()->isManaged() ||
61 : allocator.arena()->isDevice())
62 : {
63 : amrex::ParallelFor(count, [=] AMREX_GPU_DEVICE (Size i) noexcept
64 : {
65 : data[i] = value;
66 : });
67 : Gpu::streamSynchronize();
68 : return;
69 : }
70 : }
71 : #endif
72 : std::uninitialized_fill_n(data, count, value);
73 : }
74 :
75 : template <typename T, template<class> class Allocator>
76 : void initFromListImpl (T* data, std::initializer_list<T> const& list,
77 : [[maybe_unused]] Allocator<T> const & allocator)
78 : {
79 : auto count = list.size() * sizeof(T);
80 : #ifdef AMREX_USE_GPU
81 : if constexpr (RunOnGpu<Allocator<T>>::value)
82 : {
83 : Gpu::htod_memcpy_async(data, std::data(list), count);
84 : Gpu::streamSynchronize();
85 : return;
86 : }
87 : else if constexpr (IsPolymorphicArenaAllocator<Allocator<T>>::value)
88 : {
89 : if (allocator.arena()->isManaged() ||
90 : allocator.arena()->isDevice())
91 : {
92 : Gpu::htod_memcpy_async(data, std::data(list), count);
93 : Gpu::streamSynchronize();
94 : return;
95 : }
96 : }
97 : #endif
98 : std::memcpy(data, std::data(list), count);
99 : }
100 :
101 : template <typename T, typename Size, template<class> class Allocator>
102 : void fillValuesImpl (T* dst, T const* src, Size count,
103 : [[maybe_unused]] Allocator<T> const& allocator)
104 : {
105 : #ifdef AMREX_USE_GPU
106 : if constexpr (RunOnGpu<Allocator<T>>::value)
107 : {
108 : amrex::ParallelFor(count, [=] AMREX_GPU_DEVICE (Size i) noexcept {
109 : dst[i] = src[i];
110 : });
111 : Gpu::Device::streamSynchronize();
112 : return;
113 : }
114 : else if constexpr (IsPolymorphicArenaAllocator<Allocator<T>>::value)
115 : {
116 : if (allocator.arena()->isManaged() ||
117 : allocator.arena()->isDevice())
118 : {
119 : amrex::ParallelFor(count, [=] AMREX_GPU_DEVICE (Size i) noexcept
120 : {
121 : dst[i] = src[i];
122 : });
123 : Gpu::streamSynchronize();
124 : return;
125 : }
126 : }
127 : #else
128 : static_assert(RunOnGpu<Allocator<T>>::value == false);
129 : #endif
130 : if constexpr (! RunOnGpu<Allocator<T>>::value) {
131 : for (Size i = 0; i < count; ++i) { dst[i] = src[i]; }
132 : }
133 : }
134 :
135 : template <typename Allocator>
136 : void memCopyImpl (void* dst, const void* src, std::size_t count,
137 : [[maybe_unused]] Allocator const& dst_allocator,
138 : [[maybe_unused]] Allocator const& src_allocator,
139 : [[maybe_unused]] bool sync = true)
140 : {
141 : #ifdef AMREX_USE_GPU
142 : if constexpr (RunOnGpu<Allocator>::value)
143 : {
144 : Gpu::dtod_memcpy_async(dst, src, count);
145 : if (sync) { Gpu::streamSynchronize(); }
146 : return;
147 : }
148 : else if constexpr (IsPolymorphicArenaAllocator<Allocator>::value)
149 : {
150 : bool dst_on_device = dst_allocator.arena()->isManaged() ||
151 : dst_allocator.arena()->isDevice();
152 : bool src_on_device = src_allocator.arena()->isManaged() ||
153 : src_allocator.arena()->isDevice();
154 : if (dst_on_device || src_on_device)
155 : {
156 : if (dst_on_device && src_on_device) {
157 : Gpu::dtod_memcpy_async(dst, src, count);
158 : } else if (dst_on_device) {
159 : Gpu::htod_memcpy_async(dst, src, count);
160 : } else {
161 : Gpu::dtoh_memcpy_async(dst, src, count);
162 : }
163 : if (sync) { Gpu::streamSynchronize(); }
164 : return;
165 : }
166 : }
167 : #endif
168 : std::memcpy(dst, src, count);
169 : }
170 :
171 : template <typename Allocator>
172 : void memMoveImpl (void* dst, const void* src, std::size_t count,
173 : [[maybe_unused]] Allocator const& allocator)
174 : {
175 : #ifdef AMREX_USE_GPU
176 : if constexpr (RunOnGpu<Allocator>::value)
177 : {
178 : auto* tmp = The_Arena()->alloc(count);
179 : Gpu::dtod_memcpy_async(tmp, src, count);
180 : Gpu::dtod_memcpy_async(dst, tmp, count);
181 : Gpu::streamSynchronize();
182 : The_Arena()->free(tmp);
183 : return;
184 : }
185 : else if constexpr (IsPolymorphicArenaAllocator<Allocator>::value)
186 : {
187 : if (allocator.arena()->isManaged() ||
188 : allocator.arena()->isDevice())
189 : {
190 : auto* tmp = The_Arena()->alloc(count);
191 : Gpu::dtod_memcpy_async(tmp, src, count);
192 : Gpu::dtod_memcpy_async(dst, tmp, count);
193 : Gpu::streamSynchronize();
194 : The_Arena()->free(tmp);
195 : return;
196 : }
197 : }
198 : #endif
199 : std::memmove(dst, src, count);
200 : }
201 :
202 : template <typename T, typename Size, template<class> class Allocator>
203 : void maybe_init_snan (T* data, Size count, Allocator<T> const& allocator)
204 : {
205 : amrex::ignore_unused(data, count, allocator);
206 : if constexpr (std::is_same_v<float, std::remove_cv_t<T>> ||
207 : std::is_same_v<double, std::remove_cv_t<T>>) {
208 : if (amrex::InitSNaN()) {
209 : #ifdef AMREX_USE_GPU
210 : if constexpr (RunOnGpu<Allocator<T>>::value) {
211 : amrex::fill_snan<RunOn::Device>(data, count);
212 : Gpu::streamSynchronize();
213 : return;
214 : } else if constexpr (IsPolymorphicArenaAllocator<Allocator<T>>::value) {
215 : if (allocator.arena()->isManaged() ||
216 : allocator.arena()->isDevice())
217 : {
218 : amrex::fill_snan<RunOn::Device>(data, count);
219 : Gpu::streamSynchronize();
220 : return;
221 : }
222 : }
223 : #endif
224 : amrex::fill_snan<RunOn::Host>(data, count);
225 : }
226 : }
227 : }
228 : }
229 :
230 : namespace VectorGrowthStrategy
231 : {
232 : extern AMREX_EXPORT Real growth_factor;
233 : inline Real GetGrowthFactor () { return growth_factor; }
234 : inline void SetGrowthFactor (Real a_factor);
235 :
236 : namespace detail
237 : {
238 : void ValidateUserInput ();
239 : }
240 :
241 : void Initialize ();
242 : }
243 :
244 : template <class T, class Allocator = std::allocator<T> >
245 : class PODVector : public Allocator
246 : {
247 : // static_assert(std::is_standard_layout<T>(), "PODVector can only hold standard layout types");
248 : static_assert(std::is_trivially_copyable<T>(), "PODVector can only hold trivially copyable types");
249 : // static_assert(std::is_trivially_default_constructible<T>(), "PODVector can only hold trivial dc types");
250 :
251 : using Allocator::allocate;
252 : using Allocator::deallocate;
253 :
254 : public:
255 : using value_type = T;
256 : using allocator_type = Allocator;
257 : using size_type = std::size_t;
258 : using difference_type = std::ptrdiff_t;
259 :
260 : using reference = T&;
261 : using pointer = T*;
262 : using iterator = T*;
263 : using reverse_iterator = std::reverse_iterator<iterator>;
264 :
265 : using const_reference = const T&;
266 : using const_pointer = const T*;
267 : using const_iterator = const T*;
268 : using const_reverse_iterator = std::reverse_iterator<const_iterator>;
269 :
270 : private:
271 : pointer m_data = nullptr;
272 : size_type m_size{0}, m_capacity{0};
273 :
274 : public:
275 : constexpr PODVector () noexcept = default;
276 :
277 : constexpr explicit PODVector (const allocator_type& a_allocator) noexcept
278 : : Allocator(a_allocator)
279 : {}
280 :
281 : explicit PODVector (size_type a_size)
282 : : m_size(a_size), m_capacity(a_size)
283 : {
284 : if (a_size != 0) {
285 : m_data = allocate(m_size);
286 : detail::maybe_init_snan(m_data, m_size, (Allocator const&)(*this));
287 : }
288 : }
289 :
290 : PODVector (size_type a_size, const value_type& a_value,
291 : const allocator_type& a_allocator = Allocator())
292 : : Allocator(a_allocator), m_size(a_size), m_capacity(a_size)
293 : {
294 : if (a_size != 0) {
295 : m_data = allocate(m_size);
296 : detail::uninitializedFillNImpl(m_data, a_size, a_value,
297 : (Allocator const&)(*this));
298 : }
299 : }
300 :
301 : PODVector (std::initializer_list<T> a_initializer_list,
302 : const allocator_type& a_allocator = Allocator())
303 : : Allocator(a_allocator),
304 : m_size (a_initializer_list.size()),
305 : m_capacity(a_initializer_list.size())
306 : {
307 : if (a_initializer_list.size() != 0) {
308 : m_data = allocate(m_size);
309 : detail::initFromListImpl(m_data, a_initializer_list,
310 : (Allocator const&)(*this));
311 : }
312 : }
313 :
314 : PODVector (const PODVector<T, Allocator>& a_vector)
315 : : Allocator(a_vector),
316 : m_size (a_vector.size()),
317 : m_capacity(a_vector.size())
318 : {
319 : if (a_vector.size() != 0) {
320 : m_data = allocate(m_size);
321 : detail::memCopyImpl(m_data, a_vector.m_data, a_vector.nBytes(),
322 : (Allocator const&)(*this),
323 : (Allocator const&)a_vector);
324 : }
325 : }
326 :
327 : PODVector (PODVector<T, Allocator>&& a_vector) noexcept
328 : : Allocator(static_cast<Allocator&&>(a_vector)),
329 : m_data(a_vector.m_data),
330 : m_size(a_vector.m_size),
331 : m_capacity(a_vector.m_capacity)
332 : {
333 : a_vector.m_data = nullptr;
334 : a_vector.m_size = 0;
335 : a_vector.m_capacity = 0;
336 : }
337 :
338 0 : ~PODVector ()
339 : {
340 : // let's not worry about other allocators
341 : static_assert(std::is_same<Allocator,std::allocator<T>>::value ||
342 : IsArenaAllocator<Allocator>::value);
343 0 : if (m_data != nullptr) {
344 0 : deallocate(m_data, capacity());
345 : }
346 0 : }
347 :
348 : PODVector& operator= (const PODVector<T, Allocator>& a_vector)
349 : {
350 : if (this == &a_vector) { return *this; }
351 :
352 : if ((Allocator const&)(*this) != (Allocator const&)a_vector) {
353 : if (m_data != nullptr) {
354 : deallocate(m_data, m_capacity);
355 : m_data = nullptr;
356 : m_size = 0;
357 : m_capacity = 0;
358 : }
359 : (Allocator&)(*this) = (Allocator const&)a_vector;
360 : }
361 :
362 : const auto other_size = a_vector.size();
363 : if ( other_size > m_capacity ) {
364 : clear();
365 : reserve(other_size);
366 : }
367 :
368 : m_size = other_size;
369 : if (m_size > 0) {
370 : detail::memCopyImpl(m_data, a_vector.m_data, nBytes(),
371 : (Allocator const&)(*this),
372 : (Allocator const&)a_vector);
373 : }
374 : return *this;
375 : }
376 :
377 : PODVector& operator= (PODVector<T, Allocator>&& a_vector) noexcept
378 : {
379 : if (this == &a_vector) { return *this; }
380 :
381 : if (static_cast<Allocator const&>(a_vector) ==
382 : static_cast<Allocator const&>(*this))
383 : {
384 : if (m_data != nullptr) {
385 : deallocate(m_data, m_capacity);
386 : }
387 :
388 : m_data = a_vector.m_data;
389 : m_size = a_vector.m_size;
390 : m_capacity = a_vector.m_capacity;
391 :
392 : a_vector.m_data = nullptr;
393 : a_vector.m_size = 0;
394 : a_vector.m_capacity = 0;
395 : }
396 : else
397 : {
398 : // if the allocators are not the same we give up and copy
399 : *this = a_vector; // must copy instead of move
400 : }
401 :
402 : return *this;
403 : }
404 :
405 : iterator erase (const_iterator a_pos)
406 : {
407 : auto* pos = const_cast<iterator>(a_pos);
408 : --m_size;
409 : detail::memMoveImpl(pos, a_pos+1, (end() - pos)*sizeof(T),
410 : (Allocator const&)(*this));
411 : return pos;
412 : }
413 :
414 : iterator erase (const_iterator a_first, const_iterator a_last)
415 : {
416 : size_type num_to_erase = a_last - a_first;
417 : auto* first = const_cast<iterator>(a_first);
418 : if (num_to_erase > 0) {
419 : m_size -= num_to_erase;
420 : detail::memMoveImpl(first, a_last, (end() - first)*sizeof(T),
421 : (Allocator const&)(*this));
422 : }
423 : return first;
424 : }
425 :
426 : iterator insert (const_iterator a_pos, const T& a_item)
427 : {
428 : return insert(a_pos, 1, a_item);
429 : }
430 :
431 : iterator insert (const_iterator a_pos, size_type a_count, const T& a_value)
432 : {
433 : auto* pos = const_cast<iterator>(a_pos);
434 : if (a_count > 0) {
435 : if (m_capacity < m_size + a_count)
436 : {
437 : std::size_t insert_index = std::distance(m_data, pos);
438 : AllocateBufferForInsert(insert_index, a_count);
439 : pos = m_data + insert_index;
440 : }
441 : else
442 : {
443 : detail::memMoveImpl(pos+a_count, a_pos, (end() - pos) * sizeof(T),
444 : (Allocator const&)(*this));
445 : m_size += a_count;
446 : }
447 : detail::uninitializedFillNImpl(pos, a_count, a_value,
448 : (Allocator const&)(*this));
449 : }
450 : return pos;
451 : }
452 :
453 : iterator insert (const_iterator a_pos, T&& a_item)
454 : {
455 : // This is *POD* vector after all
456 : return insert(a_pos, 1, std::move(a_item));
457 : }
458 :
459 : iterator insert (const_iterator a_pos,
460 : std::initializer_list<T> a_initializer_list)
461 : {
462 : auto* pos = const_cast<iterator>(a_pos);
463 : size_type count = a_initializer_list.size();
464 : if (count > 0) {
465 : if (m_capacity < m_size + count)
466 : {
467 : std::size_t insert_index = std::distance(m_data, pos);
468 : AllocateBufferForInsert(insert_index, count);
469 : pos = m_data + insert_index;
470 : }
471 : else
472 : {
473 : detail::memMoveImpl(pos+count, a_pos, (end() - pos) * sizeof(T),
474 : (Allocator const&)(*this));
475 : m_size += count;
476 : }
477 : detail::initFromListImpl(pos, a_initializer_list,
478 : (Allocator const&)(*this));
479 : }
480 : return pos;
481 : }
482 :
483 : template <class InputIt, class bar = typename std::iterator_traits<InputIt>::difference_type>
484 : iterator insert (const_iterator a_pos, InputIt a_first, InputIt a_last)
485 : {
486 : auto* pos = const_cast<iterator>(a_pos);
487 : size_type count = std::distance(a_first, a_last);
488 : if (count > 0) {
489 : if (m_capacity < m_size + count)
490 : {
491 : std::size_t insert_index = std::distance(m_data, pos);
492 : AllocateBufferForInsert(insert_index, count);
493 : pos = m_data + insert_index;
494 : }
495 : else
496 : {
497 : detail::memMoveImpl(pos+count, a_pos, (end() - pos) * sizeof(T),
498 : (Allocator const&)(*this));
499 : m_size += count;
500 : }
501 : // Unfortunately we don't know whether InputIt points
502 : // GPU or CPU memory. We will assume it's the same as
503 : // the vector.
504 : detail::fillValuesImpl(pos, a_first, count,
505 : (Allocator const&)(*this));
506 : }
507 : return pos;
508 : }
509 :
510 : void assign (size_type a_count, const T& a_value)
511 : {
512 : if ( a_count > m_capacity ) {
513 : clear();
514 : reserve(a_count);
515 : }
516 : m_size = a_count;
517 : detail::uninitializedFillNImpl(m_data, a_count, a_value,
518 : (Allocator const&)(*this));
519 : }
520 :
521 : void assign (std::initializer_list<T> a_initializer_list)
522 : {
523 : if (a_initializer_list.size() > m_capacity) {
524 : clear();
525 : reserve(a_initializer_list.size());
526 : }
527 : m_size = a_initializer_list.size();
528 : detail::initFromListImpl(m_data, a_initializer_list,
529 : (Allocator const&)(*this));
530 : }
531 :
532 : template <class InputIt, class bar = typename std::iterator_traits<InputIt>::difference_type>
533 : void assign (InputIt a_first, InputIt a_last)
534 : {
535 : std::size_t count = std::distance(a_first, a_last);
536 : if (count > m_capacity) {
537 : clear();
538 : reserve(count);
539 : }
540 : m_size = count;
541 : detail::fillValuesImpl(m_data, a_first, count,
542 : (Allocator const&)(*this));
543 : }
544 :
545 : /** Set the same value to every element of the vector
546 : *
547 : * @param a_value the value to assign
548 : */
549 : void assign (const T& a_value)
550 : {
551 : assign(m_size, a_value);
552 : }
553 :
554 : [[nodiscard]] allocator_type get_allocator () const noexcept { return *this; }
555 :
556 : void push_back (const T& a_value)
557 : {
558 : if (m_size == m_capacity) {
559 : auto new_capacity = GetNewCapacityForPush();
560 : AllocateBufferForPush(new_capacity);
561 : }
562 : detail::uninitializedFillNImpl(m_data+m_size, 1, a_value,
563 : (Allocator const&)(*this));
564 : ++m_size;
565 : }
566 :
567 : // Because T is trivial, there is no need for push_back(T&&)
568 :
569 : // Don't have the emplace methods, but not sure how often we use those.
570 :
571 : void pop_back () noexcept { --m_size; }
572 :
573 : void clear () noexcept { m_size = 0; }
574 :
575 : [[nodiscard]] size_type size () const noexcept { return m_size; }
576 :
577 0 : [[nodiscard]] size_type capacity () const noexcept { return m_capacity; }
578 :
579 : [[nodiscard]] bool empty () const noexcept { return m_size == 0; }
580 :
581 : [[nodiscard]] T& operator[] (size_type a_index) noexcept { return m_data[a_index]; }
582 :
583 : [[nodiscard]] const T& operator[] (size_type a_index) const noexcept { return m_data[a_index]; }
584 :
585 : [[nodiscard]] T& front () noexcept { return *m_data; }
586 :
587 : [[nodiscard]] const T& front () const noexcept { return *m_data; }
588 :
589 : [[nodiscard]] T& back () noexcept { return *(m_data + m_size - 1); }
590 :
591 : [[nodiscard]] const T& back () const noexcept { return *(m_data + m_size - 1); }
592 :
593 : [[nodiscard]] T* data () noexcept { return m_data; }
594 :
595 : [[nodiscard]] const T* data () const noexcept { return m_data; }
596 :
597 : [[nodiscard]] T* dataPtr () noexcept { return m_data; }
598 :
599 : [[nodiscard]] const T* dataPtr () const noexcept { return m_data; }
600 :
601 : [[nodiscard]] iterator begin () noexcept { return m_data; }
602 :
603 : [[nodiscard]] const_iterator begin () const noexcept { return m_data; }
604 :
605 : [[nodiscard]] iterator end () noexcept { return m_data + m_size; }
606 :
607 : [[nodiscard]] const_iterator end () const noexcept { return m_data + m_size; }
608 :
609 : [[nodiscard]] reverse_iterator rbegin () noexcept { return reverse_iterator(end()); }
610 :
611 : [[nodiscard]] const_reverse_iterator rbegin () const noexcept { return const_reverse_iterator(end()); }
612 :
613 : [[nodiscard]] reverse_iterator rend () noexcept { return reverse_iterator(begin()); }
614 :
615 : [[nodiscard]] const_reverse_iterator rend () const noexcept { return const_reverse_iterator(begin()); }
616 :
617 : [[nodiscard]] const_iterator cbegin () const noexcept { return m_data; }
618 :
619 : [[nodiscard]] const_iterator cend () const noexcept { return m_data + m_size; }
620 :
621 : [[nodiscard]] const_reverse_iterator crbegin () const noexcept { return const_reverse_iterator(end()); }
622 :
623 : [[nodiscard]] const_reverse_iterator crend () const noexcept { return const_reverse_iterator(begin()); }
624 :
625 : void resize (size_type a_new_size)
626 : {
627 : auto old_size = m_size;
628 : resize_without_init_snan(a_new_size);
629 : if (old_size < a_new_size) {
630 : detail::maybe_init_snan(m_data + old_size,
631 : m_size - old_size, (Allocator const&)(*this));
632 : }
633 : }
634 :
635 : void resize (size_type a_new_size, const T& a_val)
636 : {
637 : size_type old_size = m_size;
638 : resize_without_init_snan(a_new_size);
639 : if (old_size < a_new_size)
640 : {
641 : detail::uninitializedFillNImpl(m_data + old_size,
642 : m_size - old_size, a_val,
643 : (Allocator const&)(*this));
644 : }
645 : }
646 :
647 : void reserve (size_type a_capacity)
648 : {
649 : if (m_capacity < a_capacity) {
650 : auto fp = detail::allocate_in_place(m_data, a_capacity, a_capacity,
651 : (Allocator&)(*this));
652 : UpdateDataPtr(fp);
653 : }
654 : }
655 :
656 : void shrink_to_fit ()
657 : {
658 : if (m_data != nullptr) {
659 : if (m_size == 0) {
660 : deallocate(m_data, m_capacity);
661 : m_data = nullptr;
662 : m_capacity = 0;
663 : } else if (m_size < m_capacity) {
664 : auto* new_data = detail::shrink_in_place(m_data, m_size,
665 : (Allocator&)(*this));
666 : if (new_data != m_data) {
667 : detail::memCopyImpl(new_data, m_data, nBytes(),
668 : (Allocator const&)(*this),
669 : (Allocator const&)(*this));
670 : deallocate(m_data, m_capacity);
671 : }
672 : m_capacity = m_size;
673 : }
674 : }
675 : }
676 :
677 : void swap (PODVector<T, Allocator>& a_vector) noexcept
678 : {
679 : std::swap(m_data, a_vector.m_data);
680 : std::swap(m_size, a_vector.m_size);
681 : std::swap(m_capacity, a_vector.m_capacity);
682 : std::swap(static_cast<Allocator&>(a_vector), static_cast<Allocator&>(*this));
683 : }
684 :
685 : private:
686 :
687 : [[nodiscard]] size_type nBytes () const noexcept
688 : {
689 : return m_size*sizeof(T);
690 : }
691 :
692 : // this is where we would change the growth strategy for push_back
693 : [[nodiscard]] size_type GetNewCapacityForPush () const noexcept
694 : {
695 : if (m_capacity == 0) {
696 : return std::max(64/sizeof(T), size_type(1));
697 : } else {
698 : Real const gf = VectorGrowthStrategy::GetGrowthFactor();
699 : if (amrex::almostEqual(gf, Real(1.5))) {
700 : return (m_capacity*3+1)/2;
701 : } else {
702 : return size_type(gf*Real(m_capacity+1));
703 : }
704 : }
705 : }
706 :
707 : void UpdateDataPtr (FatPtr<T> const& fp)
708 : {
709 : auto* new_data = fp.ptr();
710 : auto new_capacity = fp.size();
711 : if (m_data != nullptr && m_data != new_data) {
712 : if (m_size > 0) {
713 : detail::memCopyImpl(new_data, m_data, nBytes(),
714 : (Allocator const&)(*this),
715 : (Allocator const&)(*this));
716 : }
717 : deallocate(m_data, capacity());
718 : }
719 : m_data = new_data;
720 : m_capacity = new_capacity;
721 : }
722 :
723 : // This is where we play games with the allocator. This function
724 : // updates m_data and m_capacity, but not m_size.
725 : void AllocateBufferForPush (size_type target_capacity)
726 : {
727 : auto fp = detail::allocate_in_place(m_data, m_size+1, target_capacity,
728 : (Allocator&)(*this));
729 : UpdateDataPtr(fp);
730 : }
731 :
732 : // This is where we play games with the allocator and the growth
733 : // strategy for insert. This function updates m_data, m_size and
734 : // m_capacity.
735 : void AllocateBufferForInsert (size_type a_index, size_type a_count)
736 : {
737 : size_type new_size = m_size + a_count;
738 : size_type new_capacity = std::max(new_size, GetNewCapacityForPush());
739 : auto fp = detail::allocate_in_place(m_data, new_size, new_capacity,
740 : (Allocator&)(*this));
741 : auto* new_data = fp.ptr();
742 : new_capacity = fp.size();
743 :
744 : if (m_data != nullptr) {
745 : if (m_data == new_data) {
746 : if (m_size > a_index) {
747 : detail::memMoveImpl(m_data+a_index+a_count, m_data+a_index,
748 : (m_size-a_index)*sizeof(T),
749 : (Allocator const&)(*this));
750 : }
751 : } else {
752 : if (m_size > 0) {
753 : if (a_index > 0) {
754 : detail::memCopyImpl(new_data, m_data, a_index*sizeof(T),
755 : (Allocator const&)(*this),
756 : (Allocator const&)(*this), false);
757 : }
758 : if (m_size > a_index) {
759 : detail::memCopyImpl(new_data+a_index+a_count, m_data+a_index,
760 : (m_size-a_index)*sizeof(T),
761 : (Allocator const&)(*this),
762 : (Allocator const&)(*this), false);
763 : }
764 : Gpu::streamSynchronize();
765 : }
766 : deallocate(m_data, m_capacity);
767 : }
768 : }
769 : m_data = new_data;
770 : m_size = new_size;
771 : m_capacity = new_capacity;
772 : }
773 :
774 : void resize_without_init_snan (size_type a_new_size)
775 : {
776 : if (m_capacity < a_new_size) {
777 : reserve(a_new_size);
778 : }
779 : m_size = a_new_size;
780 : }
781 : };
782 : }
783 :
784 : #endif
|