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