1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef API_BASE_CONTAINERS_VECTOR_H
17 #define API_BASE_CONTAINERS_VECTOR_H
18
19 /*
20 IMPLEMENTATION NOTES:
21 Iterators are not compliant with c++17. They are just minimal effort implementations to make everything work.
22 Not all std::vector methods have been implemented yet.
23
24 Not handling alignment properly. (ie. expecting that malloc alignment is enough, and object granularity is enough. ie.
25 sizeof(value_type) is enough to align to next object) Exceptions are not handled (In engine exceptions are disabled
26 anyway) use of operator& (which could cause issues.. see std::address_of and operator& overloading)
27
28 some noexcepts are missing, and also constexprs...
29 allocators are partially done.
30 - can't specify allocator in all constructors
31 - allocator propagation rules are in a flux.
32 (there are cases where we want the allocator to be copied/moved along with data,
33 and then there are cases where we DONT want it)
34 */
35 // Allow support for initializer lists
36 #define BASE_VECTOR_HAS_INITIALIZE_LIST
37
38 #if !defined(BASE_HAS_ENGINE)
39 // yes, use the c headers. don't care about the c++ types and defines here.
40 #include <cstdint> // uint8_t
41 #endif
42
43 #ifdef STANDALONE_BASE_VECTOR
44 // STANDALONE has no dependencies to other engine parts.
45 // Use the compiler/system defined initializer_list and new headers. (which do pollute the namespaces alot)
46 #define USE_NEW_AND_INITIALIZE_LIST_HEADERS
47
48 #ifdef BASE_VECTOR_HAS_INITIALIZE_LIST
49 #ifdef USE_NEW_AND_INITIALIZE_LIST_HEADERS
50 // Due to a mistake in c++ standards (in my opinion), we need to include these headers to get placement new and
51 // initializer_list support.
52 #include <initializer_list> // std::initializer list
53 #include <new> // placement new
54 #else
55 // Fully declaring the placement new and std::intializer_list ourselves works, and pulls no extra headers.
new(size_t size,void * ptr)56 void* operator new(size_t size, void* ptr) noexcept
57 {
58 return ptr;
59 }
60 void* operator new[](size_t size, void* ptr) noexcept
61 {
62 return ptr;
63 }
64 namespace std {
65 template<class E>
66 class initializer_list {
67 public:
68 using value_type = E;
69 using reference = const E&;
70 using const_reference = const E&;
71 using size_type = size_t;
72 using iterator = const E*;
73 using const_iterator = const E*;
initializer_list()74 constexpr initializer_list() noexcept : _First(nullptr), _Last(nullptr) {}
75
initializer_list(const E * _First_arg,const E * _Last_arg)76 constexpr initializer_list(const E* _First_arg, const E* _Last_arg) noexcept : _First(_First_arg), _Last(_Last_arg)
77 {}
78
79 // number of elements
size()80 constexpr size_t size() const noexcept
81 {
82 return (static_cast<size_t>(_Last - _First));
83 }
84
85 // first element
begin()86 constexpr const E* begin() const noexcept
87 {
88 return _First;
89 }
90
91 // one past the last element
end()92 constexpr const E* end() const noexcept
93 {
94 return _Last;
95 }
96
97 private:
98 const E *_First, *_Last;
99 };
100 // initializer list range access
101 template<class E>
begin(initializer_list<E> il)102 constexpr const E* begin(initializer_list<E> il) noexcept
103 {
104 return (il.begin());
105 }
106
107 template<class E>
end(initializer_list<E> il)108 constexpr const E* end(initializer_list<E> il) noexcept
109 {
110 return (il.end());
111 }
112 } // namespace std
113 #endif
114 #endif
115 #endif
116
117 #include <base/containers/allocator.h>
118 #include <base/containers/iterator.h>
119 #include <base/containers/type_traits.h>
120 #include <base/namespace.h>
121 #include <base/util/log.h>
122
BASE_BEGIN_NAMESPACE()123 BASE_BEGIN_NAMESPACE()
124 namespace {
125 // C++ Standard library exception guarantees have been relaxed (due to not allowing exceptions)
126 // conditionally partially implemented (one case only)
127 // SO NO OPERATION SHOULD EVER THROW AN EXCEPTION!
128 // If EXCEPTION_GUARANTEE == 1 use copy constructor instead of move if move is not noexcept..
129 static constexpr bool EXCEPTION_GUARANTEE = false;
130
131 // Enable/Disable memory poisoning. (could be useful for debug purposes)
132 static constexpr bool BASE_CONTAINERS_ENABLE_POISON = false;
133 static constexpr const uint8_t POISON = 0xCD;
134 } // namespace
135
136 template<typename T>
137 class vector {
138 public:
139 using value_type = T;
140 using difference_type = ptrdiff_t;
141 using pointer = value_type*;
142 using reference = value_type&;
143
144 using size_type = allocator::size_type;
145 using const_reference = const value_type&;
146 using const_pointer = const value_type*;
147
148 using iterator = iterator<vector<T>>;
149 using const_iterator = const_iterator<vector<T>>;
150 using reverse_iterator = reverse_iterator<iterator>;
151 using const_reverse_iterator = BASE_NS::reverse_iterator<const_iterator>;
152
setAllocator(allocator & alloc)153 void setAllocator(allocator& alloc)
154 {
155 // Check if the allocators are different.
156 if (&alloc != &allocator_.get()) {
157 // check if vector has data allocated.
158 if ((size_ == 0) && (capacity_ == 0) && (data_ == 0)) {
159 // No data so just change the allocator
160 allocator_ = alloc;
161 } else {
162 // Vector contains data, so create a copy with new allocator
163 vector<T> temp(*this, alloc);
164 // And swap the vectors.
165 swap(temp);
166 }
167 }
168 }
169
getAllocator()170 allocator& getAllocator()
171 {
172 return allocator_.get();
173 }
174
getAllocator()175 const allocator& getAllocator() const
176 {
177 return allocator_.get();
178 }
179
vector(allocator & alloc)180 explicit vector(allocator& alloc) noexcept : allocator_(alloc) {}
181
vector()182 vector() noexcept : allocator_(default_allocator()) {}
183
vector(size_t size)184 vector(size_t size) : allocator_(default_allocator())
185 {
186 resize(size);
187 }
188
vector(size_t size,const_reference value)189 vector(size_t size, const_reference value) : allocator_(default_allocator())
190 {
191 resize(size, value);
192 }
193
vector(const vector & other)194 vector(const vector& other) : allocator_(other.allocator_)
195 {
196 if (!other.empty()) {
197 reserve(other.size());
198 init_copy(data_, other.data_, other.size());
199 size_ = other.size_;
200 }
201 }
202
vector(const vector & other,allocator & alloc)203 vector(const vector& other, allocator& alloc) : allocator_(alloc)
204 {
205 if (!other.empty()) {
206 reserve(other.size());
207 init_copy(data_, other.data_, other.size());
208 size_ = other.size_;
209 }
210 }
211
vector(vector && other)212 vector(vector&& other) noexcept
213 : size_(other.size_), capacity_(other.capacity_), data_(other.data_), allocator_(other.allocator_)
214 {
215 /*
216 NOTE: keep the allocator even during moves..
217 Check this at a later point "other.allocator_ = { nullptr,nullptr,nullptr };"
218 */
219 other.size_ = 0;
220 other.capacity_ = 0;
221 other.data_ = nullptr;
222 }
223
224 template<class InputIt,
225 enable_if_t<is_same<remove_const_t<typename InputIt::value_type>, value_type>::value, int> = 0>
vector(InputIt first,InputIt last)226 vector(InputIt first, InputIt last) : allocator_(default_allocator())
227 {
228 const difference_type size = last - first;
229 reserve(size);
230 pointer dst = data_;
231 for (; first != last; first++, dst++) {
232 const value_type& v = *first;
233 init_copy(dst, &v, 1);
234 }
235 size_ = size;
236 }
237
vector(const_pointer first,const_pointer last)238 vector(const_pointer first, const_pointer last) : allocator_(default_allocator())
239 {
240 const difference_type size = last - first;
241 reserve(size);
242 init_copy(data_, first, size);
243 size_ = size;
244 }
245
246 #ifdef BASE_VECTOR_HAS_INITIALIZE_LIST
247 // The only way to get initializer_lists is to use std::initializer_list.
248 // Also initializer_lists are bad since they cause copies. please avoid them.
vector(std::initializer_list<T> init)249 vector(std::initializer_list<T> init) : allocator_(default_allocator())
250 {
251 const size_t size = init.size();
252 reserve(size);
253 init_copy(data_, init.begin(), size);
254 size_ = size;
255 }
256
257 vector& operator=(std::initializer_list<T> ilist)
258 {
259 const size_t size = ilist.size();
260 allocator_ = default_allocator();
261 clear();
262 reserve(size);
263 init_copy(data_, ilist.begin(), size);
264 size_ = size;
265 return *this;
266 }
267 #endif
268
~vector()269 ~vector()
270 {
271 destroy(begin(), end());
272 allocator_.free(data_);
273 }
274
275 vector& operator=(const vector& other)
276 {
277 if (&other != this) {
278 /* NOTE: check how STD handles the allocator propagation (there IS a flag
279 "std::allocator_traits<allocator_type>::propagate_on_container_swap::value") and see if it's usable for us?
280 or do we want different semantics.. Check this at a later point "other.allocator_ = {
281 nullptr,nullptr,nullptr };" DO NOT USE THE ALLOCATOR FROM other!!!
282 */
283 if (other.size_ > capacity_) {
284 // need to create new storage.
285 clear();
286 reserve(other.size_);
287 init_copy(data_, other.data_, other.size_);
288 } else {
289 // use existing storage
290 size_t tocopy = size_; // can copy to this many slots.
291 size_t tocreate = other.size_;
292 size_t todestroy = 0;
293 if (size_ > other.size_) {
294 todestroy = size_ - other.size_;
295 }
296 if (tocopy > tocreate) {
297 tocopy = tocreate;
298 }
299 tocreate = tocreate - tocopy;
300 const_pointer src = other.data_;
301 pointer dst = data_;
302 if (tocopy > 0) {
303 // first copy to the initialized objects..
304 copy(dst, src, src + tocopy);
305 dst += tocopy;
306 src += tocopy;
307 }
308 if (tocreate > 0) {
309 // then create the new ones..
310 init_copy(dst, src, src + tocreate);
311 dst += tocreate;
312 }
313 if (todestroy > 0) {
314 // destroy the remaining objects.
315 destroy(iterator(dst), iterator(dst + todestroy));
316 }
317 }
318 size_ = other.size_;
319 }
320 return *this;
321 }
322
323 vector& operator=(vector&& other) noexcept
324 {
325 if (&other != this) {
326 if (data_) {
327 // destroy all old objects.
328 destroy(begin(), end());
329 // Free old memory..
330 allocator_.free(data_);
331 data_ = nullptr;
332 size_ = 0;
333 capacity_ = 0;
334 }
335 allocator_ = other.allocator_;
336 size_ = other.size_;
337 capacity_ = other.capacity_;
338 data_ = other.data_;
339 /* NOTE: keep the allocator even during moves..
340 Check this at a later point "other.allocator_ = { nullptr,nullptr,nullptr };"
341 */
342 other.size_ = 0;
343 other.capacity_ = 0;
344 other.data_ = nullptr;
345 }
346 return *this;
347 }
348
349 // Iterators
begin()350 iterator begin()
351 {
352 return iterator(data_);
353 }
354
end()355 iterator end()
356 {
357 return iterator(data_ + size_);
358 }
359
begin()360 const_iterator begin() const
361 {
362 return const_iterator(data_);
363 }
364
end()365 const_iterator end() const
366 {
367 return const_iterator(data_ + size_);
368 }
369
cbegin()370 const_iterator cbegin() const noexcept
371 {
372 return begin();
373 }
374
cend()375 const_iterator cend() const noexcept
376 {
377 return end();
378 }
379
rbegin()380 reverse_iterator rbegin() noexcept
381 {
382 return reverse_iterator(end());
383 }
384
rend()385 reverse_iterator rend() noexcept
386 {
387 return reverse_iterator(begin());
388 }
389
rbegin()390 const_reverse_iterator rbegin() const noexcept
391 {
392 return const_reverse_iterator(cend());
393 }
394
rend()395 const_reverse_iterator rend() const noexcept
396 {
397 return const_reverse_iterator(cbegin());
398 }
399
crbegin()400 const_reverse_iterator crbegin() const noexcept
401 {
402 return rbegin();
403 }
404
crend()405 const_reverse_iterator crend() const noexcept
406 {
407 return rend();
408 }
409
410 // Capacity
size()411 size_type size() const
412 {
413 return size_;
414 }
415
max_size()416 size_type max_size() const noexcept
417 {
418 return size_type(-1);
419 }
420
capacity()421 size_type capacity() const noexcept
422 {
423 return capacity_;
424 }
425
empty()426 bool empty() const noexcept
427 {
428 return size_ == 0;
429 }
430
reserve(size_type count)431 void reserve(size_type count)
432 {
433 finalize(setup_storage(count), size_);
434 }
435
resize(size_type count)436 void resize(size_type count)
437 {
438 // destroy the objects that we don't need anymore
439 if (size_ > count) {
440 destroy(begin() + count, end());
441 size_ = count;
442 }
443 pointer tmp = setup_storage(count);
444 BASE_ASSERT(capacity_ >= count);
445 // default initialize any new objects.
446 if (count > size_) {
447 init_value(tmp + size_, count - size_);
448 }
449 finalize(tmp, size_);
450 size_ = count;
451 }
452
resize(size_t count,const_reference value)453 void resize(size_t count, const_reference value)
454 {
455 // destroy the objects that we don't need anymore
456 if (size_ > count) {
457 destroy(begin() + count, end());
458 size_ = count;
459 }
460 pointer tmp = setup_storage(count);
461 BASE_ASSERT(capacity_ >= count);
462 // copy initialize any new objects.
463 if (count > size_) {
464 init_fill(tmp + size_, count - size_, value);
465 }
466 finalize(tmp, size_);
467 size_ = count;
468 }
469
shrink_to_fit()470 void shrink_to_fit()
471 {
472 if (size_ != capacity_) {
473 pointer tmp = (pointer)allocator_.alloc(size_ * sizeof(value_type));
474 finalize(tmp, size_);
475 capacity_ = size_;
476 }
477 }
478
479 // Element access
at(size_type index)480 reference at(size_type index)
481 {
482 // If index out-of-range, undefined behaviour
483 return data_[index];
484 }
485
at(size_type index)486 const_reference at(size_type index) const
487 {
488 // If index out-of-range, undefined behaviour
489 return data_[index];
490 }
491
492 reference operator[](size_type index)
493 {
494 // If index out-of-range, undefined behaviour
495 return data_[index];
496 }
497
498 const_reference operator[](size_type index) const
499 {
500 // If index out-of-range, undefined behaviour
501 return data_[index];
502 }
503
front()504 reference front()
505 {
506 // If container is empty, undefined behaviour
507 return *begin();
508 }
509
front()510 const_reference front() const
511 {
512 // If container is empty, undefined behaviour
513 return *begin();
514 }
515
back()516 reference back()
517 {
518 // If container is empty, undefined behaviour
519 return *(--end());
520 }
521
back()522 const_reference back() const
523 {
524 // If container is empty, undefined behaviour
525 return *(--end());
526 }
527
data()528 const_pointer data() const noexcept
529 {
530 return data_;
531 }
532
data()533 pointer data() noexcept
534 {
535 return data_;
536 }
537
538 // Modifiers
swap(vector & other)539 void swap(vector& other) noexcept
540 {
541 // NOTE: check how STD handles the allocator propagation (there IS a flag
542 // "std::allocator_traits<allocator_type>::propagate_on_container_swap::value") and see if it's usable for us?
543 // or do we want different semantics..
544 // swap allocators
545 const auto ta = allocator_;
546 allocator_ = other.allocator_;
547 other.allocator_ = ta;
548
549 const auto ts = size_;
550 size_ = other.size_;
551 other.size_ = ts;
552
553 const auto tc = capacity_;
554 capacity_ = other.capacity_;
555 other.capacity_ = tc;
556
557 const auto td = data_;
558 data_ = other.data_;
559 other.data_ = td;
560 }
561
insert(const_iterator pos,T && value)562 iterator insert(const_iterator pos, T&& value)
563 {
564 difference_type p = pos - cbegin();
565 pointer tmp = allocate_if_needed(size_ + 1U);
566 pointer res = nullptr;
567 if (data_ != tmp) {
568 pointer begin = data_;
569 pointer insrt = begin + p;
570 pointer end = begin + size_;
571 res = init_move(tmp, begin, p); // move old objects from before pos
572 pointer next = init_move(res, &value, 1);
573 init_move(next, insrt, size_ - p); // move old objects from after pos
574 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
575 // free old storage
576 allocator_.free(data_);
577 data_ = tmp;
578 } else {
579 res = ((pointer)pos.ptr());
580 if (cend() == pos) {
581 init_move(res, &value, 1);
582 } else {
583 reverse_move(end().ptr() - 1, res, end().ptr());
584 *res = BASE_NS::move(value); // move in the new item.
585 }
586 }
587 size_++;
588 return iterator(res);
589 }
590
insert(const_iterator pos,const T & value)591 iterator insert(const_iterator pos, const T& value)
592 {
593 difference_type p = pos - cbegin();
594 pointer tmp = allocate_if_needed(size_ + 1U);
595 pointer res = nullptr;
596 if (tmp != data_) {
597 pointer begin = data_;
598 pointer insrt = begin + p;
599 pointer end = begin + size_;
600 // Use new storage.
601 res = init_move(tmp, begin, p); // move old objects from before pos
602 pointer next = init_copy(res, &value, 1);
603 init_move(next, insrt, size_ - p); // move old objects from after pos
604 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
605 // free old storage
606 allocator_.free(data_);
607 data_ = tmp;
608 } else {
609 res = ((pointer)pos.ptr());
610 if (cend() == pos) {
611 init_copy(res, &value, 1);
612 } else {
613 reverse_move(end().ptr() - 1, res, end().ptr());
614 *res = value; // copy in the new item. (could inplace initialize?)
615 }
616 }
617 size_++;
618 return iterator(res);
619 }
620
insert(const_iterator pos,size_type count,const T & value)621 iterator insert(const_iterator pos, size_type count, const T& value)
622 {
623 // NOTE: do this properly
624 if (count == 0) {
625 return iterator((pointer)pos.ptr());
626 }
627 difference_type p = pos - cbegin();
628 pointer tmp = allocate_if_needed(size_ + count);
629 pointer res = nullptr;
630 if (tmp != data_) {
631 pointer begin = data_;
632 pointer insrt = begin + p;
633 pointer end = begin + size_;
634 // Use new storage.
635 res = init_move(tmp, begin, p); // move old objects from before pos
636 pointer next = init_fill(res, count, value);
637 init_move(next, insrt, size_ - p); // move old objects from after pos
638 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
639 // free old storage
640 allocator_.free(data_);
641 data_ = tmp;
642 } else {
643 res = ((pointer)pos.ptr());
644 if (cend() != pos) {
645 reverse_move(end().ptr() - 1, res, end().ptr() + count - 1);
646 }
647 init_fill(res, count, value);
648 }
649 size_ += count;
650 return iterator(res);
651 }
652
insert(const_iterator pos,const_iterator first,const_iterator last)653 iterator insert(const_iterator pos, const_iterator first, const_iterator last)
654 {
655 return insert_impl(pos, first, last);
656 }
657
insert(const_iterator pos,iterator first,iterator last)658 iterator insert(const_iterator pos, iterator first, iterator last)
659 {
660 return insert_impl(pos, first, last);
661 }
662
663 template<class InputIt>
insert(const_iterator pos,InputIt first,InputIt last)664 iterator insert(const_iterator pos, InputIt first, InputIt last)
665 {
666 return insert_impl(pos, first, last);
667 }
668
erase(const_iterator pos)669 iterator erase(const_iterator pos)
670 {
671 BASE_ASSERT(pos >= cbegin());
672 BASE_ASSERT(pos <= cend());
673 if (pos == cend()) {
674 return end();
675 }
676 return erase(pos, pos + 1);
677 }
678
erase(const_iterator first,const_iterator last)679 iterator erase(const_iterator first, const_iterator last)
680 {
681 BASE_ASSERT(first >= cbegin());
682 BASE_ASSERT(first <= cend());
683 BASE_ASSERT(last >= cbegin());
684 BASE_ASSERT(last <= cend());
685 if (first == last) {
686 // handle the zero range erase..
687 if (last == cend()) {
688 return end();
689 }
690 } else {
691 // move from [last -> end] to first
692 if (last < cend()) {
693 move((pointer)last.ptr(), end().ptr(), (pointer)first.ptr());
694 }
695
696 // destroy the leftovers.
697 const size_t newSize = size_ - (last - first);
698 destroy(begin() + newSize, end());
699 size_ = newSize;
700 }
701 return iterator((pointer)first.ptr());
702 }
703
704 template<class... Args>
emplace(iterator pos,Args &&...args)705 iterator emplace(iterator pos, Args&&... args)
706 {
707 const difference_type p = pos - begin();
708 pointer tmp = allocate_if_needed(size_ + 1U);
709 pointer res = nullptr;
710 if (tmp != data_) {
711 pointer bgin = begin().ptr();
712 pointer insrt = pos.ptr();
713 pointer ed = end().ptr();
714 // Use new storage.
715 res = init_move(tmp, bgin, p); // move old objects from before pos
716 pointer next = res + 1;
717 ::new (res) T(forward<Args>(args)...);
718 init_move(next, insrt, size_ - p); // move old objects from after pos
719 destroy(iterator(bgin), iterator(ed)); // Destroy the moved from items..
720 // free old storage
721 allocator_.free(data_);
722 data_ = tmp;
723 } else {
724 // move objects after pos..
725 res = pos.ptr();
726 if (pos != end()) {
727 reverse_move(end().ptr() - 1, res, end().ptr());
728 destroy_at(pos);
729 }
730 ::new (res) T(forward<Args>(args)...);
731 }
732 size_++;
733 return iterator(res);
734 }
735
736 template<class... Args>
emplace_back(Args &&...args)737 reference emplace_back(Args&&... args)
738 {
739 pointer tmp = allocate_if_needed(size_ + 1U);
740 pointer p = tmp + size_;
741 ::new (p) T(forward<Args>(args)...);
742 finalize(tmp, size_);
743 size_++;
744 return *p;
745 }
746
push_back(const value_type & val)747 void push_back(const value_type& val)
748 {
749 pointer tmp = allocate_if_needed(size_ + 1U);
750 init_copy(tmp + size_, &val, 1);
751 finalize(tmp, size_);
752 size_++;
753 }
754
push_back(value_type && val)755 void push_back(value_type&& val)
756 {
757 pointer tmp = allocate_if_needed(size_ + 1U);
758 init_move(tmp + size_, &val, 1);
759 finalize(tmp, size_);
760 size_++;
761 }
762
pop_back()763 void pop_back()
764 {
765 if (size_ > 0) {
766 destroy_at(end() - 1);
767 size_--;
768 }
769 }
770
clear()771 void clear()
772 {
773 destroy(begin(), end()); // destroy old objects
774 size_ = 0;
775 }
776
size_in_bytes()777 size_type size_in_bytes() const
778 {
779 return size_ * sizeof(value_type);
780 }
781
capacity_in_bytes()782 size_type capacity_in_bytes() const
783 {
784 return capacity_ * sizeof(value_type);
785 }
786
787 protected:
788 // Helpers for uninitialized memory.
uninitialized_default_construct(pointer first,pointer last)789 void uninitialized_default_construct(pointer first, pointer last)
790 {
791 if (first == last) {
792 return;
793 }
794 BASE_ASSERT(last > first);
795 for (; first < last; ++first) {
796 ::new (reinterpret_cast<void*>(first)) T;
797 }
798 }
799
uninitialized_value_construct(pointer first,pointer last)800 void uninitialized_value_construct(pointer first, pointer last)
801 {
802 if (first == last) {
803 return;
804 }
805 BASE_ASSERT(last > first);
806 for (; first < last; ++first) {
807 ::new (reinterpret_cast<void*>(first)) T();
808 }
809 }
810
uninitialized_copy(const_pointer first,const_pointer last,pointer d_first)811 void uninitialized_copy(const_pointer first, const_pointer last, pointer d_first)
812 {
813 if (first == last) {
814 return;
815 }
816 BASE_ASSERT(last > first);
817 // use copy constructor.
818 for (; first < last; ++first, ++d_first) {
819 ::new (reinterpret_cast<void*>(d_first)) T(*first);
820 }
821 }
822
uninitialized_fill(pointer first,const_pointer last,const_reference value)823 void uninitialized_fill(pointer first, const_pointer last, const_reference value)
824 {
825 if (first == last) {
826 return;
827 }
828 BASE_ASSERT(last > first);
829 // use copy constructor.
830 for (; first < last; ++first) {
831 ::new (first) T(value);
832 }
833 }
834
uninitialized_move(pointer first,const_pointer last,pointer d_first)835 void uninitialized_move(pointer first, const_pointer last, pointer d_first)
836 {
837 if (first == last) {
838 return;
839 }
840 BASE_ASSERT(last > first);
841 // use move constructor...
842 for (; first < last; ++first, ++d_first) {
843 ::new (d_first) T(BASE_NS::move(*first));
844 }
845 }
846
847 // helpers for initialized memory
848 // helper for range insert which may or may not need to convert values
849 template<class InputIt>
copy(pointer pos,InputIt first,InputIt last)850 void copy(pointer pos, InputIt first, InputIt last)
851 {
852 if (first == last) {
853 return;
854 }
855 BASE_ASSERT(last > first);
856 using TypeToInsert = BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>;
857 constexpr auto is_trivially_copy_constructable =
858 __is_trivially_constructible(value_type, add_lvalue_reference_t<const TypeToInsert>);
859 constexpr auto matching_type = BASE_NS::is_same_v<TypeToInsert, value_type>;
860 constexpr auto is_random_access =
861 has_iterator_category<InputIt>::value &&
862 BASE_NS::is_same_v<typename has_iterator_category<InputIt>::category, BASE_NS::random_access_iterator_tag>;
863 if constexpr (BASE_NS::is_pointer_v<InputIt> && matching_type && is_trivially_copy_constructable) {
864 const difference_type count = last - first;
865 CloneData(pos, count * sizeof(value_type), first, count * sizeof(TypeToInsert));
866 } else if constexpr (is_random_access && has_ptr_method<InputIt>::value && matching_type &&
867 is_trivially_copy_constructable) {
868 const difference_type count = last.ptr() - first.ptr();
869 CloneData(pos, count * sizeof(value_type), first.ptr(), count * sizeof(TypeToInsert));
870 } else {
871 constexpr auto convertible_type =
872 BASE_NS::is_convertible_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>,
873 value_type>;
874 static_assert(matching_type || convertible_type, "Invalid input type");
875 for (; first < last; ++first, ++pos) {
876 if constexpr (matching_type) {
877 // Same type so, no need to convert first.
878 *pos = *first;
879 } else if constexpr (convertible_type) {
880 *pos = value_type(*first);
881 }
882 }
883 }
884 }
885
move(pointer first,const_pointer last,pointer d_first)886 void move(pointer first, const_pointer last, pointer d_first) // last>first
887 {
888 if (first == last) {
889 return;
890 }
891 BASE_ASSERT(last > first);
892 for (; first < last; ++first, ++d_first) {
893 *d_first = BASE_NS::move(*first);
894 }
895 }
896
897 // same as move but goes backwards. (first>=last) NOTE: range is inclusive so not the same as others.. fix later
reverse_move(pointer first,pointer last,pointer dst)898 void reverse_move(pointer first, pointer last, pointer dst)
899 {
900 BASE_ASSERT(first >= last);
901 const auto endPtr = end().ptr();
902 for (; first >= last; first--, dst--) {
903 if (dst >= endPtr) {
904 // uninitialized storage, use move initialize.
905 init_move(dst, first, 1);
906 } else {
907 // Hit initialized storage, continue with move.
908 break;
909 }
910 }
911 // now move assign the rest..
912 for (; first >= last; first--, dst--) {
913 *dst = BASE_NS::move(*first);
914 }
915 }
916
destroy_at(iterator pos)917 void destroy_at(iterator pos)
918 {
919 pointer c = pos.ptr();
920 if constexpr (!__is_trivially_destructible(value_type)) {
921 c->~T();
922 }
923 if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
924 // Poison the memory.
925 ClearToValue(c, sizeof(value_type), POISON, sizeof(value_type));
926 }
927 }
928
destroy(iterator first,iterator last)929 void destroy(iterator first, iterator last)
930 {
931 if (first == last) {
932 return;
933 }
934 if constexpr (!__is_trivially_destructible(value_type)) {
935 pointer fPtr = first.ptr();
936 pointer lPtr = last.ptr();
937 for (pointer c = fPtr; c != lPtr; ++c) {
938 c->~T();
939 if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
940 // Poison the memory.
941 ClearToValue(c, sizeof(value_type), POISON, sizeof(value_type));
942 }
943 }
944 } else if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
945 // Poison the memory.
946 pointer fPtr = first.ptr();
947 pointer lPtr = last.ptr();
948 size_t todo = (uintptr_t)lPtr - (uintptr_t)fPtr;
949 ClearToValue(fPtr, todo, POISON, todo);
950 }
951 }
952
953 // Helpers for unitialized memory that select correct method based on T (value_type)
954 // Does memcpy if possible then move construct or finally a copy construct..
init_move(pointer dst,pointer src,size_type size)955 pointer init_move(pointer dst, pointer src, size_type size)
956 {
957 if (size == 0) {
958 return dst;
959 }
960 constexpr bool triviallyMovable = __is_trivially_constructible(value_type, value_type);
961 constexpr bool noExceptMove = EXCEPTION_GUARANTEE && __is_nothrow_constructible(value_type, value_type);
962 constexpr bool exceptMove = !EXCEPTION_GUARANTEE && __is_constructible(value_type, value_type);
963 constexpr bool copyable = __is_constructible(value_type, add_lvalue_reference_t<const value_type>);
964 constexpr bool noCopyExceptMove = EXCEPTION_GUARANTEE && __is_constructible(value_type, value_type);
965 if constexpr (triviallyMovable) {
966 // trivial move is valid.
967 CloneData(dst, size * sizeof(value_type), src, size * sizeof(value_type));
968 } else if constexpr (noExceptMove || exceptMove) {
969 // move constructor...
970 uninitialized_move(src, src + size, dst);
971 } else if constexpr (copyable) { // is_copy_constructable (ie. can construct from "const value_type&")
972 // copy constructor..
973 uninitialized_copy(src, src + size, dst);
974 } else if constexpr (noCopyExceptMove) { // is_move_constructable
975 // use move constructor even if it could throw.. (no copy constructor)
976 uninitialized_move(src, src + size, dst);
977 } else {
978 // Neither move nor copy is possible, but fail like std::vector.
979 uninitialized_copy(src, src + size, dst);
980 }
981 return dst + size;
982 }
983
init_value(pointer dst,size_t count)984 pointer init_value(pointer dst, size_t count)
985 {
986 if (count == 0) {
987 return dst;
988 }
989 // NOTE: verify correctness. (__is_trivially_constructable might not be enough)
990 if constexpr (!__is_trivially_constructible(value_type)) {
991 uninitialized_value_construct(dst, dst + count);
992 } else {
993 // trivial value initializing.. (ZERO)
994 const size_t size = count * sizeof(value_type);
995 ClearToValue(dst, size, 0, size);
996 }
997 return dst + count;
998 }
999
init_fill(pointer dst,size_t count,const_reference value)1000 pointer init_fill(pointer dst, size_t count, const_reference value)
1001 {
1002 if (count == 0) {
1003 return dst;
1004 }
1005 if constexpr (!__is_trivially_constructible(value_type)) {
1006 uninitialized_fill(dst, dst + count, value);
1007 } else {
1008 for (pointer p = dst; p != dst + count; p++) {
1009 CloneData(p, sizeof(value_type), &value, sizeof(value_type));
1010 }
1011 }
1012 return dst + count;
1013 }
1014
1015 // Does a memcpy if possible otherwise uses copy constructor.
init_copy(pointer dst,const_pointer src,size_type size)1016 pointer init_copy(pointer dst, const_pointer src, size_type size)
1017 {
1018 if (size == 0) {
1019 return dst;
1020 }
1021 if constexpr (__is_trivially_constructible(value_type, add_lvalue_reference_t<const value_type>)) {
1022 // is_trivially_copy_constructable
1023 CloneData(dst, size * sizeof(value_type), src, size * sizeof(value_type));
1024 } else if constexpr (__is_constructible(value_type, add_lvalue_reference_t<const value_type>)) {
1025 // is_copy_constructable
1026 uninitialized_copy(src, src + size, dst);
1027 } else {
1028 // FAIL..
1029 // fall back to copy initialize.. (which should fail to compile since no copy constructor, just like
1030 // std:vector)
1031 uninitialized_copy(src, src + size, dst);
1032 }
1033 return dst + size;
1034 }
1035
1036 // helper for range insert which may or may not need to convert values
1037 template<class InputIt>
init_copy(pointer pos,InputIt first,InputIt last)1038 pointer init_copy(pointer pos, InputIt first, InputIt last)
1039 {
1040 BASE_ASSERT(last >= first);
1041 constexpr auto matching_type =
1042 BASE_NS::is_same_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>, value_type>;
1043 constexpr auto is_random_access =
1044 has_iterator_category<InputIt>::value &&
1045 BASE_NS::is_same_v<typename has_iterator_category<InputIt>::category, BASE_NS::random_access_iterator_tag>;
1046 if constexpr (BASE_NS::is_pointer_v<InputIt> && matching_type) {
1047 const difference_type cnt = last - first;
1048 return init_copy(pos, first, cnt);
1049 } else if constexpr (is_random_access && has_ptr_method<InputIt>::value && matching_type) {
1050 const difference_type cnt = last.ptr() - first.ptr();
1051 return init_copy(pos, first.ptr(), cnt);
1052 } else {
1053 constexpr auto convertible_type =
1054 BASE_NS::is_convertible_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>,
1055 value_type>;
1056 static_assert(matching_type || convertible_type, "Invalid input type");
1057 for (InputIt cur = first; cur != last; ++cur) {
1058 if constexpr (matching_type) {
1059 // Same type so, no need to convert first.
1060 init_copy(pos, &*cur, 1);
1061 } else if constexpr (convertible_type) {
1062 value_type tmp = *cur;
1063 init_copy(pos, &tmp, 1);
1064 }
1065 ++pos;
1066 }
1067 return pos;
1068 }
1069 }
1070
allocate_if_needed(size_t newSize)1071 pointer allocate_if_needed(size_t newSize)
1072 {
1073 if (newSize > capacity_) {
1074 size_type newCapacity = capacity_ * 2u; // double the capacity
1075 if (newCapacity < newSize) {
1076 newCapacity = newSize;
1077 }
1078 return setup_storage(newCapacity);
1079 }
1080 return data_;
1081 }
1082
1083 // helpers to handle raw storage.
setup_storage(size_t count)1084 pointer setup_storage(size_t count)
1085 {
1086 pointer tmp = data_;
1087 if (count > capacity_) { // if requested capacity is greater than the current one..
1088 // .. allocate new storage.
1089 tmp = (pointer)allocator_.alloc(count * sizeof(value_type));
1090 BASE_ASSERT_MSG(tmp, "vector memory allocation failed.");
1091 // note: we update the capacity here, even though the actual storage has not been changed yet.
1092 capacity_ = count;
1093 }
1094 return tmp;
1095 }
1096
1097 // move constructs first "todo" objects from data_ to tmp and makes tmp the new data_.
finalize(pointer tmp,size_t todo)1098 void finalize(pointer tmp, size_t todo)
1099 {
1100 // Move old items to new storage.
1101 if (tmp != data_) {
1102 if (tmp && todo > 0) {
1103 // Move old items to new storage.
1104 init_move(tmp, data_, todo);
1105 destroy(begin(), begin() + todo); // Destroy the moved from items..
1106 }
1107 // free old storage
1108 allocator_.free(data_);
1109 // Use new storage.
1110 data_ = tmp;
1111 }
1112 }
1113
1114 template<class InputIt>
insert_impl(const_iterator pos,InputIt first,InputIt last)1115 iterator insert_impl(const_iterator pos, InputIt first, InputIt last)
1116 {
1117 if (first == last) {
1118 return iterator((pointer)pos.ptr());
1119 }
1120 pointer res = nullptr;
1121
1122 difference_type cnt = last - first;
1123 pointer tmp = allocate_if_needed(size_ + cnt);
1124 if (tmp != data_) {
1125 difference_type p = pos - cbegin();
1126 pointer begin = data_;
1127 pointer insrt = begin + p;
1128 pointer end = begin + size_;
1129 // Fill new storage
1130 res = init_move(tmp, begin, p); // move old objects from before pos
1131 pointer next = init_copy(res, first, last); // copy new objects
1132 init_move(next, insrt, size_ - p); // move old objects from after pos
1133 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
1134 // Free old storage
1135 allocator_.free(data_);
1136 data_ = tmp;
1137 } else {
1138 res = ((pointer)pos.ptr());
1139 if (cend() == pos) {
1140 res = init_copy(res, first, last);
1141 } else {
1142 pointer end = data_ + size_;
1143 reverse_move(end - 1, res, end + (cnt - 1));
1144 // copy over the existing items.
1145 const difference_type intializedSlots = end - res;
1146 const size_t cnt2 = (intializedSlots > cnt) ? cnt : intializedSlots;
1147 copy(res, first, first + cnt2);
1148 // init-copy over the uninitialized ones..
1149 init_copy(res + cnt2, first + cnt2, last);
1150 }
1151 }
1152 size_ += cnt;
1153
1154 return iterator(res);
1155 }
1156
1157 template<typename Iterator, typename = void>
1158 struct has_iterator_category : BASE_NS::false_type {
1159 using category = void;
1160 };
1161
1162 template<typename Iterator>
1163 struct has_iterator_category<Iterator, void_t<typename Iterator::iterator_category>> : BASE_NS::true_type {
1164 using category = typename Iterator::iterator_category;
1165 };
1166
1167 template<typename Iterator>
1168 using ptr_fn = decltype(BASE_NS::declval<Iterator>().ptr());
1169
1170 template<typename Iterator, typename = void>
1171 struct has_ptr_method : BASE_NS::false_type {};
1172
1173 template<typename Iterator>
1174 struct has_ptr_method<Iterator, BASE_NS::void_t<ptr_fn<Iterator>>> : BASE_NS::true_type {};
1175
1176 size_type size_ {}, capacity_ {};
1177 pointer data_ {};
1178
1179 // Wrapper to create a "re-seatable" reference.
1180 class Wrapper {
1181 public:
1182 inline Wrapper(allocator& a) : allocator_(&a) {}
1183
1184 inline Wrapper& operator=(allocator& a)
1185 {
1186 allocator_ = &a;
1187 return *this;
1188 }
1189
1190 inline void* alloc(size_type size)
1191 {
1192 if ((allocator_) && (allocator_->alloc)) {
1193 return allocator_->alloc(allocator_->instance, size);
1194 }
1195 return nullptr;
1196 }
1197
1198 inline void free(void* ptr)
1199 {
1200 if ((allocator_) && (allocator_->free)) {
1201 allocator_->free(allocator_->instance, ptr);
1202 }
1203 }
1204
1205 allocator& get()
1206 {
1207 BASE_ASSERT(allocator_ != nullptr);
1208 return *allocator_;
1209 }
1210
1211 const allocator& get() const
1212 {
1213 BASE_ASSERT(allocator_ != nullptr);
1214 return *allocator_;
1215 }
1216
1217 private:
1218 allocator* allocator_ { nullptr };
1219 } allocator_;
1220 };
1221 BASE_END_NAMESPACE()
1222
1223 #endif // API_BASE_CONTAINERS_VECTOR_H
1224