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