• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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