1 /*
2 * Copyright (c) 2022 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 const 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 const 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(const_iterator pos,Args &&...args)773 iterator emplace(const_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 = bgin + p;
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 res = ((pointer)pos.ptr());
793 if (cend() == pos) {
794 ::new (res) T(forward<Args>(args)...);
795 } else {
796 // move objects after pos..
797 reverse_move(end().ptr() - 1, res, end().ptr());
798 *res = T(forward<Args>(args)...);
799 }
800 }
801 size_++;
802 return iterator(res);
803 }
804
805 template<class... Args>
emplace_back(Args &&...args)806 reference emplace_back(Args&&... args)
807 {
808 pointer tmp = allocate_if_needed(size_ + 1U);
809 pointer p = tmp + size_;
810 ::new (p) T(forward<Args>(args)...);
811 finalize(tmp, size_);
812 size_++;
813 return *p;
814 }
815
push_back(const value_type & val)816 void push_back(const value_type& val)
817 {
818 pointer tmp = allocate_if_needed(size_ + 1U);
819 init_copy(tmp + size_, &val, 1);
820 finalize(tmp, size_);
821 size_++;
822 }
823
push_back(value_type && val)824 void push_back(value_type&& val)
825 {
826 pointer tmp = allocate_if_needed(size_ + 1U);
827 init_move(tmp + size_, &val, 1);
828 finalize(tmp, size_);
829 size_++;
830 }
831
pop_back()832 void pop_back()
833 {
834 if (size_ > 0) {
835 destroy_at(end() - 1);
836 size_--;
837 }
838 }
839
clear()840 void clear()
841 {
842 destroy(begin(), end()); // destroy old objects
843 size_ = 0;
844 }
845
size_in_bytes()846 size_type size_in_bytes() const
847 {
848 return size_ * sizeof(value_type);
849 }
850
capacity_in_bytes()851 size_type capacity_in_bytes() const
852 {
853 return capacity_ * sizeof(value_type);
854 }
855
856 protected:
857 // Helpers for uninitialized memory.
uninitialized_default_construct(pointer first,pointer last)858 void uninitialized_default_construct(pointer first, pointer last)
859 {
860 if (first == last) {
861 return;
862 }
863 BASE_ASSERT(last > first);
864 for (; first < last; ++first) {
865 ::new (reinterpret_cast<void*>(first)) T;
866 }
867 }
868
uninitialized_value_construct(pointer first,pointer last)869 void uninitialized_value_construct(pointer first, pointer last)
870 {
871 if (first == last) {
872 return;
873 }
874 BASE_ASSERT(last > first);
875 for (; first < last; ++first) {
876 ::new (reinterpret_cast<void*>(first)) T();
877 }
878 }
879
uninitialized_copy(const_pointer first,const_pointer last,pointer d_first)880 void uninitialized_copy(const_pointer first, const_pointer last, pointer d_first)
881 {
882 if (first == last) {
883 return;
884 }
885 BASE_ASSERT(last > first);
886 // use copy constructor.
887 for (; first < last; ++first, ++d_first) {
888 ::new (reinterpret_cast<void*>(d_first)) T(*first);
889 }
890 }
891
uninitialized_fill(pointer first,const_pointer last,const_reference value)892 void uninitialized_fill(pointer first, const_pointer last, const_reference value)
893 {
894 if (first == last) {
895 return;
896 }
897 BASE_ASSERT(last > first);
898 // use copy constructor.
899 for (; first < last; ++first) {
900 ::new (first) T(value);
901 }
902 }
903
uninitialized_move(pointer first,const_pointer last,pointer d_first)904 void uninitialized_move(pointer first, const_pointer last, pointer d_first)
905 {
906 if (first == last) {
907 return;
908 }
909 BASE_ASSERT(last > first);
910 // use move constructor...
911 for (; first < last; ++first, ++d_first) {
912 ::new (d_first) T(BASE_NS::move(*first));
913 }
914 }
915
916 // helpers for initialized memory
917 // helper for range insert which may or may not need to convert values
918 template<class InputIt>
copy(pointer pos,InputIt first,InputIt last)919 void copy(pointer pos, InputIt first, InputIt last)
920 {
921 if (first == last) {
922 return;
923 }
924 BASE_ASSERT(last > first);
925 using TypeToInsert = BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>;
926 constexpr auto is_trivially_copy_constructable =
927 __is_trivially_constructible(value_type, add_lvalue_reference_t<const TypeToInsert>);
928 constexpr auto matching_type = BASE_NS::is_same_v<TypeToInsert, value_type>;
929 constexpr auto is_random_access =
930 has_iterator_category<InputIt>::value &&
931 BASE_NS::is_same_v<typename has_iterator_category<InputIt>::category, BASE_NS::random_access_iterator_tag>;
932 if constexpr (BASE_NS::is_pointer_v<InputIt> && matching_type && is_trivially_copy_constructable) {
933 const difference_type count = last - first;
934 CloneData(pos, count * sizeof(value_type), first, count * sizeof(TypeToInsert));
935 } else if constexpr (is_random_access && has_ptr_method<InputIt>::value && matching_type &&
936 is_trivially_copy_constructable) {
937 const difference_type count = last.ptr() - first.ptr();
938 CloneData(pos, count * sizeof(value_type), first.ptr(), count * sizeof(TypeToInsert));
939 } else {
940 constexpr auto convertible_type =
941 BASE_NS::is_convertible_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>,
942 value_type>;
943 static_assert(matching_type || convertible_type, "Invalid input type");
944 for (; first < last; ++first, ++pos) {
945 if constexpr (matching_type) {
946 // Same type so, no need to convert first.
947 *pos = *first;
948 } else if constexpr (convertible_type) {
949 *pos = value_type(*first);
950 }
951 }
952 }
953 }
954
move(pointer first,const_pointer last,pointer d_first)955 void move(pointer first, const_pointer last, pointer d_first) // last>first
956 {
957 if (first == last) {
958 return;
959 }
960 BASE_ASSERT(last > first);
961 for (; first < last; ++first, ++d_first) {
962 *d_first = BASE_NS::move(*first);
963 }
964 }
965
966 // 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)967 void reverse_move(pointer first, pointer last, pointer dst)
968 {
969 BASE_ASSERT(first >= last);
970 const auto endPtr = end().ptr();
971 for (; first >= last; first--, dst--) {
972 if (dst >= endPtr) {
973 // uninitialized storage, use move initialize.
974 init_move(dst, first, 1);
975 } else {
976 // Hit initialized storage, continue with move.
977 break;
978 }
979 }
980 // now move assign the rest..
981 for (; first >= last; first--, dst--) {
982 *dst = BASE_NS::move(*first);
983 }
984 }
985
destroy_at(iterator pos)986 void destroy_at(iterator pos)
987 {
988 pointer c = pos.ptr();
989 if constexpr (!__is_trivially_destructible(value_type)) {
990 c->~T();
991 }
992 if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
993 // Poison the memory.
994 ClearToValue(c, sizeof(value_type), POISON, sizeof(value_type));
995 }
996 }
997
destroy(iterator first,iterator last)998 void destroy(iterator first, iterator last)
999 {
1000 if (first == last) {
1001 return;
1002 }
1003 if constexpr (!__is_trivially_destructible(value_type)) {
1004 pointer fPtr = first.ptr();
1005 pointer lPtr = last.ptr();
1006 for (pointer c = fPtr; c != lPtr; ++c) {
1007 c->~T();
1008 if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
1009 // Poison the memory.
1010 ClearToValue(c, sizeof(value_type), POISON, sizeof(value_type));
1011 }
1012 }
1013 } else if constexpr (BASE_CONTAINERS_ENABLE_POISON) {
1014 // Poison the memory.
1015 pointer fPtr = first.ptr();
1016 pointer lPtr = last.ptr();
1017 size_t todo = (uintptr_t)lPtr - (uintptr_t)fPtr;
1018 ClearToValue(fPtr, todo, POISON, todo);
1019 }
1020 }
1021
1022 // Helpers for unitialized memory that select correct method based on T (value_type)
1023 // Does memcpy if possible then move construct or finally a copy construct..
init_move(pointer dst,pointer src,size_type size)1024 pointer init_move(pointer dst, pointer src, size_type size)
1025 {
1026 if (size == 0) {
1027 return dst;
1028 }
1029 constexpr bool triviallyMovable = __is_trivially_constructible(value_type, value_type);
1030 constexpr bool noExceptMove = EXCEPTION_GUARANTEE && __is_nothrow_constructible(value_type, value_type);
1031 constexpr bool exceptMove = !EXCEPTION_GUARANTEE && __is_constructible(value_type, value_type);
1032 constexpr bool copyable = __is_constructible(value_type, add_lvalue_reference_t<const value_type>);
1033 constexpr bool noCopyExceptMove = EXCEPTION_GUARANTEE && __is_constructible(value_type, value_type);
1034 if constexpr (triviallyMovable) {
1035 // trivial move is valid.
1036 CloneData(dst, size * sizeof(value_type), src, size * sizeof(value_type));
1037 } else if constexpr (noExceptMove || exceptMove) {
1038 // move constructor...
1039 uninitialized_move(src, src + size, dst);
1040 } else if constexpr (copyable) { // is_copy_constructable (ie. can construct from "const value_type&")
1041 // copy constructor..
1042 uninitialized_copy(src, src + size, dst);
1043 } else if constexpr (noCopyExceptMove) { // is_move_constructable
1044 // use move constructor even if it could throw.. (no copy constructor)
1045 uninitialized_move(src, src + size, dst);
1046 } else {
1047 // Neither move nor copy is possible, but fail like std::vector.
1048 uninitialized_copy(src, src + size, dst);
1049 }
1050 return dst + size;
1051 }
1052
init_value(pointer dst,size_t count)1053 pointer init_value(pointer dst, size_t count)
1054 {
1055 if (count == 0) {
1056 return dst;
1057 }
1058 // NOTE: verify correctness. (__is_trivially_constructable might not be enough)
1059 if constexpr (!__is_trivially_constructible(value_type)) {
1060 uninitialized_value_construct(dst, dst + count);
1061 } else {
1062 // trivial value initializing.. (ZERO)
1063 const size_t size = count * sizeof(value_type);
1064 ClearToValue(dst, size, 0, size);
1065 }
1066 return dst + count;
1067 }
1068
init_fill(pointer dst,size_t count,const_reference value)1069 pointer init_fill(pointer dst, size_t count, const_reference value)
1070 {
1071 if (count == 0) {
1072 return dst;
1073 }
1074 if constexpr (!__is_trivially_constructible(value_type)) {
1075 uninitialized_fill(dst, dst + count, value);
1076 } else {
1077 for (pointer p = dst; p != dst + count; p++) {
1078 CloneData(p, sizeof(value_type), &value, sizeof(value_type));
1079 }
1080 }
1081 return dst + count;
1082 }
1083
1084 // Does a memcpy if possible otherwise uses copy constructor.
init_copy(pointer dst,const_pointer src,size_type size)1085 pointer init_copy(pointer dst, const_pointer src, size_type size)
1086 {
1087 if (size == 0) {
1088 return dst;
1089 }
1090 if constexpr (__is_trivially_constructible(value_type, add_lvalue_reference_t<const value_type>)) {
1091 // is_trivially_copy_constructable
1092 CloneData(dst, size * sizeof(value_type), src, size * sizeof(value_type));
1093 } else if constexpr (__is_constructible(value_type, add_lvalue_reference_t<const value_type>)) {
1094 // is_copy_constructable
1095 uninitialized_copy(src, src + size, dst);
1096 } else {
1097 // FAIL..
1098 // fall back to copy initialize.. (which should fail to compile since no copy constructor, just like
1099 // std:vector)
1100 uninitialized_copy(src, src + size, dst);
1101 }
1102 return dst + size;
1103 }
1104
1105 // helper for range insert which may or may not need to convert values
1106 template<class InputIt>
init_copy(pointer pos,InputIt first,InputIt last)1107 pointer init_copy(pointer pos, InputIt first, InputIt last)
1108 {
1109 BASE_ASSERT(last >= first);
1110 constexpr auto matching_type =
1111 BASE_NS::is_same_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>, value_type>;
1112 constexpr auto is_random_access =
1113 has_iterator_category<InputIt>::value &&
1114 BASE_NS::is_same_v<typename has_iterator_category<InputIt>::category, BASE_NS::random_access_iterator_tag>;
1115 if constexpr (BASE_NS::is_pointer_v<InputIt> && matching_type) {
1116 const difference_type cnt = last - first;
1117 return init_copy(pos, first, static_cast<size_type>(cnt));
1118 } else if constexpr (is_random_access && has_ptr_method<InputIt>::value && matching_type) {
1119 const difference_type cnt = last.ptr() - first.ptr();
1120 return init_copy(pos, first.ptr(), static_cast<size_type>(cnt));
1121 } else {
1122 constexpr auto convertible_type =
1123 BASE_NS::is_convertible_v<BASE_NS::remove_const_t<BASE_NS::remove_reference_t<decltype(*first)>>,
1124 value_type>;
1125 static_assert(matching_type || convertible_type, "Invalid input type");
1126 for (InputIt cur = first; cur != last; ++cur) {
1127 if constexpr (matching_type) {
1128 // Same type so, no need to convert first.
1129 init_copy(pos, &*cur, 1U);
1130 } else if constexpr (convertible_type) {
1131 value_type tmp = static_cast<value_type>(*cur);
1132 init_copy(pos, &tmp, 1U);
1133 }
1134 ++pos;
1135 }
1136 return pos;
1137 }
1138 }
1139
allocate_if_needed(size_t newSize)1140 pointer allocate_if_needed(size_t newSize)
1141 {
1142 if (newSize > capacity_) {
1143 size_type newCapacity = capacity_ * 2u; // double the capacity
1144 if (newCapacity < newSize) {
1145 newCapacity = newSize;
1146 }
1147 return setup_storage(newCapacity);
1148 }
1149 return data_;
1150 }
1151
1152 // helpers to handle raw storage.
setup_storage(size_t count)1153 pointer setup_storage(size_t count)
1154 {
1155 pointer tmp = data_;
1156 if (count > capacity_) { // if requested capacity is greater than the current one..
1157 // .. allocate new storage.
1158 tmp = (pointer)allocator_.alloc(count * sizeof(value_type));
1159 BASE_ASSERT_MSG(tmp, "vector memory allocation failed.");
1160 // note: we update the capacity here, even though the actual storage has not been changed yet.
1161 capacity_ = count;
1162 }
1163 return tmp;
1164 }
1165
1166 // move constructs first "todo" objects from data_ to tmp and makes tmp the new data_.
finalize(pointer tmp,size_t todo)1167 void finalize(pointer tmp, size_t todo)
1168 {
1169 // Move old items to new storage.
1170 if (tmp != data_) {
1171 if (tmp && todo > 0) {
1172 // Move old items to new storage.
1173 init_move(tmp, data_, todo);
1174 destroy(begin(), begin() + static_cast<difference_type>(todo)); // Destroy the moved from items..
1175 }
1176 // free old storage
1177 allocator_.free(data_);
1178 // Use new storage.
1179 data_ = tmp;
1180 }
1181 }
1182
1183 template<class InputIt>
insert_impl(const_iterator pos,InputIt first,InputIt last)1184 iterator insert_impl(const_iterator pos, InputIt first, InputIt last)
1185 {
1186 if (first == last) {
1187 return iterator((pointer)pos.ptr());
1188 }
1189 pointer res = nullptr;
1190
1191 difference_type cnt = last - first;
1192 pointer tmp = allocate_if_needed(static_cast<size_type>(size_ + cnt));
1193 if (tmp != data_) {
1194 difference_type p = pos - cbegin();
1195 pointer begin = data_;
1196 pointer insrt = begin + p;
1197 pointer end = begin + size_;
1198 // Fill new storage
1199 res = init_move(tmp, begin, static_cast<size_type>(p)); // move old objects from before pos
1200 pointer next = init_copy(res, first, last); // copy new objects
1201 init_move(next, insrt, static_cast<size_type>(size_ - p)); // move old objects from after pos
1202 destroy(iterator(begin), iterator(end)); // Destroy the moved from items..
1203 // Free old storage
1204 allocator_.free(data_);
1205 data_ = tmp;
1206 } else {
1207 res = ((pointer)pos.ptr());
1208 if (cend() == pos) {
1209 res = init_copy(res, first, last);
1210 } else {
1211 pointer end = data_ + size_;
1212 reverse_move(end - 1, res, end + (cnt - 1));
1213 // copy over the existing items.
1214 const difference_type intializedSlots = end - res;
1215 const auto cnt2 = (intializedSlots > cnt) ? cnt : intializedSlots;
1216 copy(res, first, first + cnt2);
1217 // init-copy over the uninitialized ones..
1218 init_copy(res + cnt2, first + cnt2, last);
1219 }
1220 }
1221 size_ += cnt;
1222
1223 return iterator(res);
1224 }
1225
1226 template<typename Iterator, typename = void>
1227 struct has_iterator_category : BASE_NS::false_type {
1228 using category = void;
1229 };
1230
1231 template<typename Iterator>
1232 struct has_iterator_category<Iterator, void_t<typename Iterator::iterator_category>> : BASE_NS::true_type {
1233 using category = typename Iterator::iterator_category;
1234 };
1235
1236 template<typename Iterator>
1237 using ptr_fn = decltype(BASE_NS::declval<Iterator>().ptr());
1238
1239 template<typename Iterator, typename = void>
1240 struct has_ptr_method : BASE_NS::false_type {};
1241
1242 template<typename Iterator>
1243 struct has_ptr_method<Iterator, BASE_NS::void_t<ptr_fn<Iterator>>> : BASE_NS::true_type {};
1244
1245 size_type size_ {}, capacity_ {};
1246 pointer data_ {};
1247
1248 // Wrapper to create a "re-seatable" reference.
1249 class Wrapper {
1250 public:
1251 inline Wrapper(allocator& a) : allocator_(&a) {}
1252
1253 inline Wrapper& operator=(allocator& a)
1254 {
1255 allocator_ = &a;
1256 return *this;
1257 }
1258
1259 inline void* alloc(size_type size)
1260 {
1261 if ((allocator_) && (allocator_->alloc)) {
1262 return allocator_->alloc(allocator_->instance, size);
1263 }
1264 return nullptr;
1265 }
1266
1267 inline void free(void* ptr)
1268 {
1269 if ((allocator_) && (allocator_->free)) {
1270 allocator_->free(allocator_->instance, ptr);
1271 }
1272 }
1273
1274 allocator& get()
1275 {
1276 BASE_ASSERT(allocator_ != nullptr);
1277 return *allocator_;
1278 }
1279
1280 const allocator& get() const
1281 {
1282 BASE_ASSERT(allocator_ != nullptr);
1283 return *allocator_;
1284 }
1285
1286 private:
1287 allocator* allocator_ { nullptr };
1288 } allocator_;
1289 };
1290 BASE_END_NAMESPACE()
1291
1292 #endif // API_BASE_CONTAINERS_VECTOR_H
1293