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