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