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 assert(index < size());
479 return data_[index];
480 }
481
at(size_type index)482 const_reference at(size_type index) const
483 {
484 // If index out-of-range, undefined behaviour
485 assert(index < size());
486 return data_[index];
487 }
488
489 reference operator[](size_type index)
490 {
491 // If index out-of-range, undefined behaviour
492 assert(index < size());
493 return data_[index];
494 }
495
496 const_reference operator[](size_type index) const
497 {
498 // If index out-of-range, undefined behaviour
499 assert(index < size());
500 return data_[index];
501 }
502
front()503 reference front()
504 {
505 // If container is empty, undefined behaviour
506 return *begin();
507 }
508
front()509 const_reference front() const
510 {
511 // If container is empty, undefined behaviour
512 return *begin();
513 }
514
back()515 reference back()
516 {
517 // If container is empty, undefined behaviour
518 return *(--end());
519 }
520
back()521 const_reference back() const
522 {
523 // If container is empty, undefined behaviour
524 return *(--end());
525 }
526
data()527 const_pointer data() const noexcept
528 {
529 return data_;
530 }
531
data()532 pointer data() noexcept
533 {
534 return data_;
535 }
536
537 // Modifiers
swap(vector & other)538 void swap(vector& other) noexcept
539 {
540 // NOTE: check how STD handles the allocator propagation (there IS a flag
541 // "std::allocator_traits<allocator_type>::propagate_on_container_swap::value") and see if it's usable for us?
542 // or do we want different semantics..
543 // swap allocators
544 const auto ta = allocator_;
545 allocator_ = other.allocator_;
546 other.allocator_ = ta;
547
548 const auto ts = size_;
549 size_ = other.size_;
550 other.size_ = ts;
551
552 const auto tc = capacity_;
553 capacity_ = other.capacity_;
554 other.capacity_ = tc;
555
556 const auto td = data_;
557 data_ = other.data_;
558 other.data_ = td;
559 }
560
insert(const_iterator pos,T && value)561 iterator insert(const_iterator pos, T&& value)
562 {
563 difference_type p = pos - cbegin();
564 pointer tmp = allocate_if_needed(size_ + 1U);
565 pointer res = nullptr;
566 if (data_ != tmp) {
567 pointer begin = data_;
568 pointer insrt = begin + p;
569 pointer end = begin + size_;
570 res = init_move(tmp, begin, static_cast<size_type>(p)); // move old objects from before pos
571 pointer next = init_move(res, &value, 1);
572 init_move(next, insrt, size_ - p); // move old objects from after pos
573 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
574 // free old storage
575 allocator_.free(data_);
576 data_ = tmp;
577 } else {
578 res = ((pointer)pos.ptr());
579 if (cend() == pos) {
580 init_move(res, &value, 1);
581 } else {
582 reverse_move(end().ptr() - 1, res, end().ptr());
583 *res = BASE_NS::move(value); // move in the new item.
584 }
585 }
586 size_++;
587 return iterator(res);
588 }
589
insert(const_iterator pos,const T & value)590 iterator insert(const_iterator pos, const T& value)
591 {
592 difference_type p = pos - cbegin();
593 pointer tmp = allocate_if_needed(size_ + 1U);
594 pointer res = nullptr;
595 if (tmp != data_) {
596 pointer begin = data_;
597 pointer insrt = begin + p;
598 pointer end = begin + size_;
599 // Use new storage.
600 res = init_move(tmp, begin, static_cast<size_type>(p)); // move old objects from before pos
601 pointer next = init_copy(res, &value, 1);
602 init_move(next, insrt, size_ - p); // move old objects from after pos
603 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
604 // free old storage
605 allocator_.free(data_);
606 data_ = tmp;
607 } else {
608 res = ((pointer)pos.ptr());
609 if (cend() == pos) {
610 init_copy(res, &value, 1);
611 } else {
612 reverse_move(end().ptr() - 1, res, end().ptr());
613 *res = value; // copy in the new item. (could inplace initialize?)
614 }
615 }
616 size_++;
617 return iterator(res);
618 }
619
insert(const_iterator pos,size_type count,const T & value)620 iterator insert(const_iterator pos, size_type count, const T& value)
621 {
622 // NOTE: do this properly
623 if (count == 0) {
624 return iterator((pointer)pos.ptr());
625 }
626 difference_type p = pos - cbegin();
627 pointer tmp = allocate_if_needed(size_ + count);
628 pointer res = nullptr;
629 if (tmp != data_) {
630 pointer begin = data_;
631 pointer insrt = begin + p;
632 pointer end = begin + size_;
633 // Use new storage.
634 res = init_move(tmp, begin, static_cast<size_type>(p)); // move old objects from before pos
635 pointer next = init_fill(res, count, value);
636 init_move(next, insrt, size_ - p); // move old objects from after pos
637 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
638 // free old storage
639 allocator_.free(data_);
640 data_ = tmp;
641 } else {
642 res = ((pointer)pos.ptr());
643 if (cend() != pos) {
644 reverse_move(end().ptr() - 1, res, end().ptr() + count - 1);
645 }
646 init_fill(res, count, value);
647 }
648 size_ += count;
649 return iterator(res);
650 }
651
insert(const_iterator pos,const_iterator first,const_iterator last)652 iterator insert(const_iterator pos, const_iterator first, const_iterator last)
653 {
654 return insert_impl(pos, first, last);
655 }
656
insert(const_iterator pos,iterator first,iterator last)657 iterator insert(const_iterator pos, iterator first, iterator last)
658 {
659 return insert_impl(pos, first, last);
660 }
661
662 template<class InputIt>
insert(const_iterator pos,InputIt first,InputIt last)663 iterator insert(const_iterator pos, InputIt first, InputIt last)
664 {
665 return insert_impl(pos, first, last);
666 }
667
append(size_type count,const T & value)668 void append(size_type count, const T& value)
669 {
670 if (count == 0) {
671 return;
672 }
673 pointer tmp = allocate_if_needed(static_cast<size_type>(size_ + count));
674 if (tmp != data_) {
675 pointer begin = data_;
676 size_type size = size_;
677 init_move(tmp, begin, size); // Move old objects
678 destroy(iterator(begin), iterator(begin + size)); // Destroy the moved from items..
679 // Free old storage
680 allocator_.free(data_);
681 data_ = tmp;
682 }
683 init_fill(tmp + size_, count, value);
684 size_ += count;
685 }
686
687 template<class InputIt,
688 enable_if_t<is_same<remove_const_t<typename InputIt::value_type>, value_type>::value, int> = 0>
append(InputIt first,InputIt last)689 void append(InputIt first, InputIt last)
690 {
691 if (first == last) {
692 return;
693 }
694 difference_type cnt = last - first;
695 if (cnt < 0) {
696 return;
697 }
698 const auto newSize = size_ + static_cast<size_type>(cnt);
699 pointer tmp = allocate_if_needed(newSize);
700 if (tmp != data_) {
701 pointer begin = data_;
702 size_type size = size_;
703 init_move(tmp, begin, size); // Move old objects
704 destroy(iterator(begin), iterator(begin + size)); // Destroy the moved from items..
705 // Free old storage
706 allocator_.free(data_);
707 data_ = tmp;
708 }
709 init_copy(tmp + size_, first, last); // Copy the new objects
710 size_ = newSize;
711 }
712
append(const_pointer first,const_pointer last)713 void append(const_pointer first, const_pointer last)
714 {
715 if (first == last) {
716 return;
717 }
718 difference_type cnt = last - first;
719 if (cnt < 0) {
720 return;
721 }
722 const auto newSize = size_ + static_cast<size_type>(cnt);
723 pointer tmp = allocate_if_needed(newSize);
724 if (tmp != data_) {
725 pointer begin = data_;
726 size_type size = size_;
727 init_move(tmp, begin, size); // Move old objects
728 destroy(iterator(begin), iterator(begin + size)); // Destroy the moved from items..
729 // Free old storage
730 allocator_.free(data_);
731 data_ = tmp;
732 }
733 init_copy(tmp + size_, first, cnt); // Copy the new objects
734 size_ = newSize;
735 }
736
erase(const_iterator pos)737 iterator erase(const_iterator pos)
738 {
739 BASE_ASSERT(pos >= cbegin());
740 BASE_ASSERT(pos <= cend());
741 if (pos == cend()) {
742 return end();
743 }
744 return erase(pos, pos + 1);
745 }
746
erase(const_iterator first,const_iterator last)747 iterator erase(const_iterator first, const_iterator last)
748 {
749 BASE_ASSERT(first >= cbegin());
750 BASE_ASSERT(first <= cend());
751 BASE_ASSERT(last >= cbegin());
752 BASE_ASSERT(last <= cend());
753 if (first == last) {
754 // handle the zero range erase..
755 if (last == cend()) {
756 return end();
757 }
758 } else {
759 // move from [last -> end] to first
760 if (last < cend()) {
761 move((pointer)last.ptr(), end().ptr(), (pointer)first.ptr());
762 }
763
764 // destroy the leftovers.
765 const auto newSize = static_cast<difference_type>(size_) - (last - first);
766 destroy(begin() + newSize, end());
767 size_ = static_cast<size_type>(newSize);
768 }
769 return iterator((pointer)first.ptr());
770 }
771
772 template<class... Args>
emplace(iterator pos,Args &&...args)773 iterator emplace(iterator pos, Args&&... args)
774 {
775 const difference_type p = pos - begin();
776 pointer tmp = allocate_if_needed(size_ + 1U);
777 pointer res = nullptr;
778 if (tmp != data_) {
779 pointer bgin = begin().ptr();
780 pointer insrt = pos.ptr();
781 pointer ed = end().ptr();
782 // Use new storage.
783 res = init_move(tmp, bgin, static_cast<size_type>(p)); // move old objects from before pos
784 pointer next = res + 1;
785 ::new (res) T(forward<Args>(args)...);
786 init_move(next, insrt, size_ - p); // move old objects from after pos
787 destroy(iterator(bgin), iterator(ed)); // Destroy the moved from items..
788 // free old storage
789 allocator_.free(data_);
790 data_ = tmp;
791 } else {
792 // move objects after pos..
793 res = pos.ptr();
794 if (pos != end()) {
795 reverse_move(end().ptr() - 1, res, end().ptr());
796 destroy_at(pos);
797 }
798 ::new (res) T(forward<Args>(args)...);
799 }
800 size_++;
801 return iterator(res);
802 }
803
804 template<class... Args>
emplace_back(Args &&...args)805 reference emplace_back(Args&&... args)
806 {
807 pointer tmp = allocate_if_needed(size_ + 1U);
808 pointer p = tmp + size_;
809 ::new (p) T(forward<Args>(args)...);
810 finalize(tmp, size_);
811 size_++;
812 return *p;
813 }
814
push_back(const value_type & val)815 void push_back(const value_type& val)
816 {
817 pointer tmp = allocate_if_needed(size_ + 1U);
818 init_copy(tmp + size_, &val, 1);
819 finalize(tmp, size_);
820 size_++;
821 }
822
push_back(value_type && val)823 void push_back(value_type&& val)
824 {
825 pointer tmp = allocate_if_needed(size_ + 1U);
826 init_move(tmp + size_, &val, 1);
827 finalize(tmp, size_);
828 size_++;
829 }
830
pop_back()831 void pop_back()
832 {
833 if (size_ > 0) {
834 destroy_at(end() - 1);
835 size_--;
836 }
837 }
838
clear()839 void clear()
840 {
841 destroy(begin(), end()); // destroy old objects
842 size_ = 0;
843 }
844
size_in_bytes()845 size_type size_in_bytes() const
846 {
847 return size_ * sizeof(value_type);
848 }
849
capacity_in_bytes()850 size_type capacity_in_bytes() const
851 {
852 return capacity_ * sizeof(value_type);
853 }
854
855 protected:
856 // Helpers for uninitialized memory.
uninitialized_default_construct(pointer first,pointer last)857 void uninitialized_default_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_value_construct(pointer first,pointer last)868 void uninitialized_value_construct(pointer first, pointer last)
869 {
870 if (first == last) {
871 return;
872 }
873 BASE_ASSERT(last > first);
874 for (; first < last; ++first) {
875 ::new (reinterpret_cast<void*>(first)) T();
876 }
877 }
878
uninitialized_copy(const_pointer first,const_pointer last,pointer d_first)879 void uninitialized_copy(const_pointer first, const_pointer last, pointer d_first)
880 {
881 if (first == last) {
882 return;
883 }
884 BASE_ASSERT(last > first);
885 // use copy constructor.
886 for (; first < last; ++first, ++d_first) {
887 ::new (reinterpret_cast<void*>(d_first)) T(*first);
888 }
889 }
890
uninitialized_fill(pointer first,const_pointer last,const_reference value)891 void uninitialized_fill(pointer first, const_pointer last, const_reference value)
892 {
893 if (first == last) {
894 return;
895 }
896 BASE_ASSERT(last > first);
897 // use copy constructor.
898 for (; first < last; ++first) {
899 ::new (first) T(value);
900 }
901 }
902
uninitialized_move(pointer first,const_pointer last,pointer d_first)903 void uninitialized_move(pointer first, const_pointer last, pointer d_first)
904 {
905 if (first == last) {
906 return;
907 }
908 BASE_ASSERT(last > first);
909 // use move constructor...
910 for (; first < last; ++first, ++d_first) {
911 ::new (d_first) T(BASE_NS::move(*first));
912 }
913 }
914
915 // helpers for initialized memory
916 // helper for range insert which may or may not need to convert values
917 template<class InputIt>
copy(pointer pos,InputIt first,InputIt last)918 void copy(pointer pos, InputIt first, InputIt last)
919 {
920 if (first == last) {
921 return;
922 }
923 BASE_ASSERT(last > first);
924 using TypeToInsert = BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>;
925 constexpr auto is_trivially_copy_constructable =
926 __is_trivially_constructible(value_type, add_lvalue_reference_t<const TypeToInsert>);
927 constexpr auto matching_type = BASE_NS::is_same_v<TypeToInsert, value_type>;
928 constexpr auto is_random_access =
929 has_iterator_category<InputIt>::value &&
930 BASE_NS::is_same_v<typename has_iterator_category<InputIt>::category, BASE_NS::random_access_iterator_tag>;
931 if constexpr (BASE_NS::is_pointer_v<InputIt> && matching_type && is_trivially_copy_constructable) {
932 const difference_type count = last - first;
933 CloneData(pos, count * sizeof(value_type), first, count * sizeof(TypeToInsert));
934 } else if constexpr (is_random_access && has_ptr_method<InputIt>::value && matching_type &&
935 is_trivially_copy_constructable) {
936 const difference_type count = last.ptr() - first.ptr();
937 CloneData(pos, count * sizeof(value_type), first.ptr(), count * sizeof(TypeToInsert));
938 } else {
939 constexpr auto convertible_type =
940 BASE_NS::is_convertible_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>,
941 value_type>;
942 static_assert(matching_type || convertible_type, "Invalid input type");
943 for (; first < last; ++first, ++pos) {
944 if constexpr (matching_type) {
945 // Same type so, no need to convert first.
946 *pos = *first;
947 } else if constexpr (convertible_type) {
948 *pos = value_type(*first);
949 }
950 }
951 }
952 }
953
move(pointer first,const_pointer last,pointer d_first)954 void move(pointer first, const_pointer last, pointer d_first) // last>first
955 {
956 if (first == last) {
957 return;
958 }
959 BASE_ASSERT(last > first);
960 for (; first < last; ++first, ++d_first) {
961 *d_first = BASE_NS::move(*first);
962 }
963 }
964
965 // 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)966 void reverse_move(pointer first, pointer last, pointer dst)
967 {
968 BASE_ASSERT(first >= last);
969 const auto endPtr = end().ptr();
970 for (; first >= last; first--, dst--) {
971 if (dst >= endPtr) {
972 // uninitialized storage, use move initialize.
973 init_move(dst, first, 1);
974 } else {
975 // Hit initialized storage, continue with move.
976 break;
977 }
978 }
979 // now move assign the rest..
980 for (; first >= last; first--, dst--) {
981 *dst = BASE_NS::move(*first);
982 }
983 }
984
destroy_at(iterator pos)985 void destroy_at(iterator pos)
986 {
987 pointer c = pos.ptr();
988 if constexpr (!__is_trivially_destructible(value_type)) {
989 c->~T();
990 }
991 if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
992 // Poison the memory.
993 ClearToValue(c, sizeof(value_type), POISON, sizeof(value_type));
994 }
995 }
996
destroy(iterator first,iterator last)997 void destroy(iterator first, iterator last)
998 {
999 if (first == last) {
1000 return;
1001 }
1002 if constexpr (!__is_trivially_destructible(value_type)) {
1003 pointer fPtr = first.ptr();
1004 pointer lPtr = last.ptr();
1005 for (pointer c = fPtr; c != lPtr; ++c) {
1006 c->~T();
1007 if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
1008 // Poison the memory.
1009 ClearToValue(c, sizeof(value_type), POISON, sizeof(value_type));
1010 }
1011 }
1012 } else if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
1013 // Poison the memory.
1014 pointer fPtr = first.ptr();
1015 pointer lPtr = last.ptr();
1016 size_t todo = (uintptr_t)lPtr - (uintptr_t)fPtr;
1017 ClearToValue(fPtr, todo, POISON, todo);
1018 }
1019 }
1020
1021 // Helpers for unitialized memory that select correct method based on T (value_type)
1022 // Does memcpy if possible then move construct or finally a copy construct..
init_move(pointer dst,pointer src,size_type size)1023 pointer init_move(pointer dst, pointer src, size_type size)
1024 {
1025 if (size == 0) {
1026 return dst;
1027 }
1028 constexpr bool triviallyMovable = __is_trivially_constructible(value_type, value_type);
1029 constexpr bool noExceptMove = EXCEPTION_GUARANTEE && __is_nothrow_constructible(value_type, value_type);
1030 constexpr bool exceptMove = !EXCEPTION_GUARANTEE && __is_constructible(value_type, value_type);
1031 constexpr bool copyable = __is_constructible(value_type, add_lvalue_reference_t<const value_type>);
1032 constexpr bool noCopyExceptMove = EXCEPTION_GUARANTEE && __is_constructible(value_type, value_type);
1033 if constexpr (triviallyMovable) {
1034 // trivial move is valid.
1035 CloneData(dst, size * sizeof(value_type), src, size * sizeof(value_type));
1036 } else if constexpr (noExceptMove || exceptMove) {
1037 // move constructor...
1038 uninitialized_move(src, src + size, dst);
1039 } else if constexpr (copyable) { // is_copy_constructable (ie. can construct from "const value_type&")
1040 // copy constructor..
1041 uninitialized_copy(src, src + size, dst);
1042 } else if constexpr (noCopyExceptMove) { // is_move_constructable
1043 // use move constructor even if it could throw.. (no copy constructor)
1044 uninitialized_move(src, src + size, dst);
1045 } else {
1046 // Neither move nor copy is possible, but fail like std::vector.
1047 uninitialized_copy(src, src + size, dst);
1048 }
1049 return dst + size;
1050 }
1051
init_value(pointer dst,size_t count)1052 pointer init_value(pointer dst, size_t count)
1053 {
1054 if (count == 0) {
1055 return dst;
1056 }
1057 // NOTE: verify correctness. (__is_trivially_constructable might not be enough)
1058 if constexpr (!__is_trivially_constructible(value_type)) {
1059 uninitialized_value_construct(dst, dst + count);
1060 } else {
1061 // trivial value initializing.. (ZERO)
1062 const size_t size = count * sizeof(value_type);
1063 ClearToValue(dst, size, 0, size);
1064 }
1065 return dst + count;
1066 }
1067
init_fill(pointer dst,size_t count,const_reference value)1068 pointer init_fill(pointer dst, size_t count, const_reference value)
1069 {
1070 if (count == 0) {
1071 return dst;
1072 }
1073 if constexpr (!__is_trivially_constructible(value_type)) {
1074 uninitialized_fill(dst, dst + count, value);
1075 } else {
1076 for (pointer p = dst; p != dst + count; p++) {
1077 CloneData(p, sizeof(value_type), &value, sizeof(value_type));
1078 }
1079 }
1080 return dst + count;
1081 }
1082
1083 // Does a memcpy if possible otherwise uses copy constructor.
init_copy(pointer dst,const_pointer src,size_type size)1084 pointer init_copy(pointer dst, const_pointer src, size_type size)
1085 {
1086 if (size == 0) {
1087 return dst;
1088 }
1089 if constexpr (__is_trivially_constructible(value_type, add_lvalue_reference_t<const value_type>)) {
1090 // is_trivially_copy_constructable
1091 CloneData(dst, size * sizeof(value_type), src, size * sizeof(value_type));
1092 } else if constexpr (__is_constructible(value_type, add_lvalue_reference_t<const value_type>)) {
1093 // is_copy_constructable
1094 uninitialized_copy(src, src + size, dst);
1095 } else {
1096 // FAIL..
1097 // fall back to copy initialize.. (which should fail to compile since no copy constructor, just like
1098 // std:vector)
1099 uninitialized_copy(src, src + size, dst);
1100 }
1101 return dst + size;
1102 }
1103
1104 // helper for range insert which may or may not need to convert values
1105 template<class InputIt>
init_copy(pointer pos,InputIt first,InputIt last)1106 pointer init_copy(pointer pos, InputIt first, InputIt last)
1107 {
1108 BASE_ASSERT(last >= first);
1109 constexpr auto matching_type =
1110 BASE_NS::is_same_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>, value_type>;
1111 constexpr auto is_random_access =
1112 has_iterator_category<InputIt>::value &&
1113 BASE_NS::is_same_v<typename has_iterator_category<InputIt>::category, BASE_NS::random_access_iterator_tag>;
1114 if constexpr (BASE_NS::is_pointer_v<InputIt> && matching_type) {
1115 const difference_type cnt = last - first;
1116 return init_copy(pos, first, static_cast<size_type>(cnt));
1117 } else if constexpr (is_random_access && has_ptr_method<InputIt>::value && matching_type) {
1118 const difference_type cnt = last.ptr() - first.ptr();
1119 return init_copy(pos, first.ptr(), static_cast<size_type>(cnt));
1120 } else {
1121 constexpr auto convertible_type =
1122 BASE_NS::is_convertible_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>,
1123 value_type>;
1124 static_assert(matching_type || convertible_type, "Invalid input type");
1125 for (InputIt cur = first; cur != last; ++cur) {
1126 if constexpr (matching_type) {
1127 // Same type so, no need to convert first.
1128 init_copy(pos, &*cur, 1U);
1129 } else if constexpr (convertible_type) {
1130 value_type tmp = static_cast<value_type>(*cur);
1131 init_copy(pos, &tmp, 1U);
1132 }
1133 ++pos;
1134 }
1135 return pos;
1136 }
1137 }
1138
allocate_if_needed(size_t newSize)1139 pointer allocate_if_needed(size_t newSize)
1140 {
1141 if (newSize > capacity_) {
1142 size_type newCapacity = capacity_ * 2u; // double the capacity
1143 if (newCapacity < newSize) {
1144 newCapacity = newSize;
1145 }
1146 return setup_storage(newCapacity);
1147 }
1148 return data_;
1149 }
1150
1151 // helpers to handle raw storage.
setup_storage(size_t count)1152 pointer setup_storage(size_t count)
1153 {
1154 pointer tmp = data_;
1155 if (count > capacity_) { // if requested capacity is greater than the current one..
1156 // .. allocate new storage.
1157 tmp = (pointer)allocator_.alloc(count * sizeof(value_type));
1158 BASE_ASSERT_MSG(tmp, "vector memory allocation failed.");
1159 // note: we update the capacity here, even though the actual storage has not been changed yet.
1160 capacity_ = count;
1161 }
1162 return tmp;
1163 }
1164
1165 // move constructs first "todo" objects from data_ to tmp and makes tmp the new data_.
finalize(pointer tmp,size_t todo)1166 void finalize(pointer tmp, size_t todo)
1167 {
1168 // Move old items to new storage.
1169 if (tmp != data_) {
1170 if (tmp && todo > 0) {
1171 // Move old items to new storage.
1172 init_move(tmp, data_, todo);
1173 destroy(begin(), begin() + static_cast<difference_type>(todo)); // Destroy the moved from items..
1174 }
1175 // free old storage
1176 allocator_.free(data_);
1177 // Use new storage.
1178 data_ = tmp;
1179 }
1180 }
1181
1182 template<class InputIt>
insert_impl(const_iterator pos,InputIt first,InputIt last)1183 iterator insert_impl(const_iterator pos, InputIt first, InputIt last)
1184 {
1185 if (first == last) {
1186 return iterator((pointer)pos.ptr());
1187 }
1188 pointer res = nullptr;
1189
1190 difference_type cnt = last - first;
1191 pointer tmp = allocate_if_needed(static_cast<size_type>(size_ + cnt));
1192 if (tmp != data_) {
1193 difference_type p = pos - cbegin();
1194 pointer begin = data_;
1195 pointer insrt = begin + p;
1196 pointer end = begin + size_;
1197 // Fill new storage
1198 res = init_move(tmp, begin, static_cast<size_type>(p)); // move old objects from before pos
1199 pointer next = init_copy(res, first, last); // copy new objects
1200 init_move(next, insrt, static_cast<size_type>(size_ - p)); // move old objects from after pos
1201 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
1202 // Free old storage
1203 allocator_.free(data_);
1204 data_ = tmp;
1205 } else {
1206 res = ((pointer)pos.ptr());
1207 if (cend() == pos) {
1208 res = init_copy(res, first, last);
1209 } else {
1210 pointer end = data_ + size_;
1211 reverse_move(end - 1, res, end + (cnt - 1));
1212 // copy over the existing items.
1213 const difference_type intializedSlots = end - res;
1214 const auto cnt2 = (intializedSlots > cnt) ? cnt : intializedSlots;
1215 copy(res, first, first + cnt2);
1216 // init-copy over the uninitialized ones..
1217 init_copy(res + cnt2, first + cnt2, last);
1218 }
1219 }
1220 size_ += cnt;
1221
1222 return iterator(res);
1223 }
1224
1225 template<typename Iterator, typename = void>
1226 struct has_iterator_category : BASE_NS::false_type {
1227 using category = void;
1228 };
1229
1230 template<typename Iterator>
1231 struct has_iterator_category<Iterator, void_t<typename Iterator::iterator_category>> : BASE_NS::true_type {
1232 using category = typename Iterator::iterator_category;
1233 };
1234
1235 template<typename Iterator>
1236 using ptr_fn = decltype(BASE_NS::declval<Iterator>().ptr());
1237
1238 template<typename Iterator, typename = void>
1239 struct has_ptr_method : BASE_NS::false_type {};
1240
1241 template<typename Iterator>
1242 struct has_ptr_method<Iterator, BASE_NS::void_t<ptr_fn<Iterator>>> : BASE_NS::true_type {};
1243
1244 size_type size_ {}, capacity_ {};
1245 pointer data_ {};
1246
1247 // Wrapper to create a "re-seatable" reference.
1248 class Wrapper {
1249 public:
1250 inline Wrapper(allocator& a) : allocator_(&a) {}
1251
1252 inline Wrapper& operator=(allocator& a)
1253 {
1254 allocator_ = &a;
1255 return *this;
1256 }
1257
1258 inline void* alloc(size_type size)
1259 {
1260 if ((allocator_) && (allocator_->alloc)) {
1261 return allocator_->alloc(allocator_->instance, size);
1262 }
1263 return nullptr;
1264 }
1265
1266 inline void free(void* ptr)
1267 {
1268 if ((allocator_) && (allocator_->free)) {
1269 allocator_->free(allocator_->instance, ptr);
1270 }
1271 }
1272
1273 allocator& get()
1274 {
1275 BASE_ASSERT(allocator_ != nullptr);
1276 return *allocator_;
1277 }
1278
1279 const allocator& get() const
1280 {
1281 BASE_ASSERT(allocator_ != nullptr);
1282 return *allocator_;
1283 }
1284
1285 private:
1286 allocator* allocator_ { nullptr };
1287 } allocator_;
1288 };
1289 BASE_END_NAMESPACE()
1290
1291 #endif // API_BASE_CONTAINERS_VECTOR_H
1292