• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 PANDA_SMALL_VECTOR_H
17 #define PANDA_SMALL_VECTOR_H
18 
19 #include "utils/arch.h"
20 #include <algorithm>
21 #include <array>
22 #include <vector>
23 #if defined(__clang__)
24 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
25 #elif defined(__GNUC__)
26 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
27 #endif
28 
29 namespace panda {
30 
31 /**
32  * Helper class that provides main Panda's allocator interface: AdapterType, Adapter(), GetInstance().
33  */
34 class StdAllocatorStub {
35 public:
36     template <typename T>
37     using AdapterType = std::allocator<T>;
38 
Adapter()39     auto Adapter()
40     {
41         // We can't std::allocator<void> because it is removed in C++20
42         return std::allocator<uint8_t>();
43     }
44 
GetInstance()45     static StdAllocatorStub *GetInstance()
46     {
47         alignas(StdAllocatorStub *) static StdAllocatorStub instance;
48         return &instance;
49     }
50 };
51 
52 template <typename Allocator, typename T, bool use_allocator>
53 class AllocatorConfig {
54 public:
55     using Adapter = typename Allocator::template AdapterType<T>;
56 };
57 
58 template <typename Allocator, typename T>
59 class AllocatorConfig<Allocator, T, false> {
60 public:
61     using Adapter = Allocator;
62 };
63 
64 /**
65  * SmallVector stores `N` elements statically inside its static buffer. Static buffer shares memory with a std::vector
66  * that will be created once number of elements exceed size of the static buffer - `N`.
67  *
68  * @tparam T Type of elements to store
69  * @tparam N Number of elements to be stored statically
70  * @tparam Allocator Allocator that will be used to allocate memory for the dynamic storage
71  * @tparam use_allocator indicates type of Allocator:
72  * false - Allocator is adapter(e.g.AllocatorAdapter) for memory allocate/deallocate/construct/destry...
73  * true - Allocator is allocator(e.g.StdAllocatorStub) that implements Adapter() returning a adapter instance
74  */
75 template <typename T, size_t N, typename Allocator = std::allocator<T>, bool use_allocator = false>
76 class SmallVector {
77     // Least bit of the pointer should not be used in a memory addressing, because we pack `allocated` flag there
78     static_assert(alignof(Allocator *) > 1);
79     // When N is zero, then consider using std::vector directly
80     static_assert(N != 0);
81 
82     struct StaticBuffer {
83         uint32_t size {0};
84         std::array<T, N> data;
85     };
86 
87     using VectorType = std::vector<T, typename AllocatorConfig<Allocator, T, use_allocator>::Adapter>;
88 
89 public:
90     using value_type = typename VectorType::value_type;
91     using reference = typename VectorType::reference;
92     using const_reference = typename VectorType::const_reference;
93     using pointer = typename VectorType::pointer;
94     using const_pointer = typename VectorType::const_pointer;
95     using difference_type = typename VectorType::difference_type;
96 
97     template <typename IteratorType, bool reverse>
98     class Iterator
99         : public std::iterator<std::random_access_iterator_tag, IteratorType, int32_t, IteratorType *, IteratorType &> {
Add(difference_type v)100         IteratorType *Add(difference_type v)
101         {
102             if constexpr (reverse) {  // NOLINT
103                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
104                 return pointer_ -= v;
105             } else {  // NOLINT
106                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
107                 return pointer_ += v;
108             }
109         }
Sub(difference_type v)110         IteratorType *Sub(difference_type v)
111         {
112             if constexpr (reverse) {  // NOLINT
113                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
114                 return pointer_ + v;
115             } else {  // NOLINT
116                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
117                 return pointer_ - v;
118             }
119         }
120 
121     public:
Iterator(IteratorType * param_pointer)122         explicit Iterator(IteratorType *param_pointer) : pointer_(param_pointer) {}
123 
124         IteratorType *operator->()
125         {
126             return pointer_;
127         }
128         IteratorType &operator*()
129         {
130             return *pointer_;
131         }
132         Iterator &operator++()
133         {
134             pointer_ = Add(1);
135             return *this;
136         }
137         Iterator operator++(int)  // NOLINT(cert-dcl21-cpp)
138         {
139             Iterator it(pointer_);
140             pointer_ = Add(1);
141             return it;
142         }
143         Iterator &operator--()
144         {
145             pointer_ = Sub(1);
146             return *this;
147         }
148         Iterator operator--(int)  // NOLINT(cert-dcl21-cpp)
149         {
150             Iterator it(pointer_);
151             pointer_ = Sub(1);
152             return it;
153         }
154         Iterator &operator+=(difference_type n)
155         {
156             pointer_ = Add(n);
157             return *this;
158         }
159         Iterator &operator-=(difference_type n)
160         {
161             pointer_ = Sub(n);
162             return *this;
163         }
164         Iterator operator+(int32_t n) const
165         {
166             Iterator it(*this);
167             it.pointer_ = it.Add(n);
168             return it;
169         }
170         Iterator operator-(int32_t n) const
171         {
172             Iterator it(*this);
173             it.pointer_ = it.Sub(n);
174             return it;
175         }
176         difference_type operator-(const Iterator &rhs) const
177         {
178             if constexpr (reverse) {  // NOLINT
179                 return rhs.pointer_ - pointer_;
180             }
181             return pointer_ - rhs.pointer_;
182         }
183         bool operator==(const Iterator &rhs) const
184         {
185             return pointer_ == rhs.pointer_;
186         }
187         bool operator!=(const Iterator &rhs) const
188         {
189             return !(*this == rhs);
190         }
191 
192         ~Iterator() = default;
193 
194         DEFAULT_COPY_SEMANTIC(Iterator);
195         DEFAULT_NOEXCEPT_MOVE_SEMANTIC(Iterator);
196 
197     private:
198         IteratorType *pointer_;
199     };
200 
201     using iterator = Iterator<T, false>;
202     using const_iterator = Iterator<const T, false>;
203     using reverse_iterator = Iterator<T, true>;
204     using const_reverse_iterator = Iterator<const T, true>;
205 
SmallVector()206     SmallVector()
207     {
208         static_assert(!use_allocator);
209 
210         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
211         buffer_.size = 0;
212     }
213 
SmallVector(std::initializer_list<T> list)214     SmallVector(std::initializer_list<T> list)
215     {
216         static_assert(!use_allocator);
217 
218         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
219         buffer_.size = 0;
220 
221         for (auto it = list.begin(); it != list.end(); ++it) {
222             push_back(*it);
223         }
224     }
225 
SmallVector(Allocator * allocator)226     explicit SmallVector(Allocator *allocator) : allocator_(AddStaticFlag(allocator))
227     {
228         static_assert(use_allocator);
229         ASSERT(allocator != nullptr);
230 
231         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
232         buffer_.size = 0;
233     }
234 
SmallVector(const SmallVector & other)235     SmallVector(const SmallVector &other) : allocator_(other.allocator_)
236     {
237         if (other.IsStatic()) {
238             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
239             buffer_.size = other.buffer_.size;
240             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
241             for (uint32_t i = 0; i < buffer_.size; ++i) {
242                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
243                 new (&buffer_.data[i]) T(other.buffer_.data[i]);
244             }
245         } else {
246             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
247             new (&vector_) VectorType(other.vector_);
248         }
249     }
250 
SmallVector(SmallVector && other)251     SmallVector(SmallVector &&other) noexcept : allocator_(other.allocator_)
252     {
253         if (other.IsStatic()) {
254             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
255             buffer_.size = other.buffer_.size;
256             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
257             for (uint32_t i = 0; i < buffer_.size; ++i) {
258                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
259                 new (&buffer_.data[i]) T(std::move(other.buffer_.data[i]));
260             }
261         } else {
262             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
263             new (&vector_) VectorType(std::move(other.vector_));
264         }
265         other.ResetToStatic();
266     }
267 
~SmallVector()268     virtual ~SmallVector()
269     {
270         Destroy();
271     }
272 
273     // NOLINTNEXTLINE(bugprone-unhandled-self-assignment, cert-oop54-cpp)
274     SmallVector &operator=(const SmallVector &other)
275     {
276         if (&other == this) {
277             return *this;
278         }
279 
280         Destroy();
281         allocator_ = other.allocator_;
282         if (other.IsStatic()) {
283             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
284             buffer_.size = other.buffer_.size;
285             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
286             for (uint32_t i = 0; i < buffer_.size; ++i) {
287                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
288                 new (&buffer_.data[i]) T(other.buffer_.data[i]);
289             }
290         } else {
291             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
292             new (&vector_) VectorType(other.vector_);
293         }
294 
295         return *this;
296     }
297 
298     SmallVector &operator=(SmallVector &&other) noexcept
299     {
300         Destroy();
301         allocator_ = other.allocator_;
302         if (other.IsStatic()) {
303             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
304             buffer_.size = other.buffer_.size;
305             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
306             for (uint32_t i = 0; i < buffer_.size; ++i) {
307                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
308                 new (&buffer_.data[i]) T(std::move(other.buffer_.data[i]));
309             }
310         } else {
311             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
312             new (&vector_) VectorType(std::move(other.vector_));
313         }
314         other.ResetToStatic();
315         return *this;
316     }
317 
318     bool operator==(const SmallVector &other) const
319     {
320         if (this == &other) {
321             return true;
322         }
323 
324         if (size() != other.size()) {
325             return false;
326         }
327 
328         auto it1 = begin();
329         auto it2 = other.begin();
330         for (; it1 != end(); ++it1, ++it2) {
331             if (*it1 != *it2) {
332                 return false;
333             }
334         }
335         return true;
336     }
337 
338     bool operator!=(const SmallVector &other) const
339     {
340         return !operator==(other);
341     }
342 
begin()343     const_iterator begin() const
344     {
345         return const_iterator(&operator[](0));
346     }
begin()347     iterator begin()
348     {
349         return iterator(&operator[](0));
350     }
cbegin()351     const_iterator cbegin() const
352     {
353         return const_iterator(&operator[](0));
354     }
end()355     const_iterator end() const
356     {
357         return const_iterator(&operator[](size()));
358     }
end()359     iterator end()
360     {
361         return iterator(&operator[](size()));
362     }
cend()363     const_iterator cend() const
364     {
365         return const_iterator(&operator[](size()));
366     }
367 
rbegin()368     auto rbegin() const
369     {
370         return const_reverse_iterator(&operator[](size() - 1));
371     }
rbegin()372     auto rbegin()
373     {
374         return reverse_iterator(&operator[](size() - 1));
375     }
crbegin()376     auto crbegin() const
377     {
378         return const_reverse_iterator(&operator[](size() - 1));
379     }
rend()380     auto rend() const
381     {
382         return const_reverse_iterator(&operator[](0) - 1);
383     }
rend()384     auto rend()
385     {
386         return reverse_iterator(&operator[](0) - 1);
387     }
crend()388     auto crend() const
389     {
390         return const_reverse_iterator(&operator[](0) - 1);
391     }
392 
empty()393     bool empty() const
394     {
395         return size() == 0;
396     }
397 
data()398     const_pointer data() const
399     {
400         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
401         return IsStatic() ? buffer_.data.data() : vector_.data();
402     }
403 
size()404     size_t size() const
405     {
406         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
407         return IsStatic() ? buffer_.size : vector_.size();
408     }
409 
capacity()410     size_t capacity() const
411     {
412         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
413         return IsStatic() ? buffer_.data.size() : vector_.capacity();
414     }
415 
push_back(const value_type & value)416     void push_back(const value_type &value)
417     {
418         if (!EnsureStaticSpace(1)) {
419             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
420             vector_.push_back(value);
421         } else {
422             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
423             new (&buffer_.data[buffer_.size++]) T(value);
424         }
425     }
426 
push_back(value_type && value)427     void push_back(value_type &&value)
428     {
429         if (!EnsureStaticSpace(1)) {
430             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
431             vector_.push_back(std::move(value));
432         } else {
433             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
434             new (&buffer_.data[buffer_.size++]) T(std::move(value));
435         }
436     }
437 
438     template <typename... _Args>
emplace_back(_Args &&...values)439     reference emplace_back(_Args &&... values)
440     {
441         if (!EnsureStaticSpace(1)) {
442             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
443             return vector_.emplace_back(std::move(values)...);
444         }
445         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
446         return *(new (&buffer_.data[buffer_.size++]) T(std::move(values)...));
447     }
448 
reserve(size_t size)449     void reserve(size_t size)
450     {
451         if (size > capacity()) {
452             if (IsStatic()) {
453                 ASSERT(size > N);
454                 MoveToVector(size);
455             } else {
456                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
457                 vector_.reserve(size);
458             }
459         }
460     }
461 
resize(size_t size)462     void resize(size_t size)
463     {
464         if (size <= this->size()) {
465             if (IsStatic()) {
466                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
467                 std::for_each(buffer_.data.begin() + size, buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); });
468                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
469                 buffer_.size = size;
470             } else {
471                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
472                 vector_.resize(size);
473             }
474         } else {
475             if (EnsureStaticSpace(size - this->size())) {
476                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
477                 std::for_each(buffer_.data.begin() + buffer_.size, buffer_.data.begin() + size,
478                               [](T &v) { new (&v) T; });
479                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
480                 buffer_.size = size;
481             } else {
482                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
483                 vector_.resize(size);
484             }
485         }
486     }
487 
resize(size_t size,const value_type & val)488     void resize(size_t size, const value_type &val)
489     {
490         if (size <= this->size()) {
491             if (IsStatic()) {
492                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
493                 std::for_each(buffer_.data.begin() + size, buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); });
494                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
495                 buffer_.size = size;
496             } else {
497                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
498                 vector_.resize(size, val);
499             }
500         } else {
501             if (EnsureStaticSpace(size - this->size())) {
502                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
503                 std::for_each(buffer_.data.begin() + buffer_.size, buffer_.data.begin() + size,
504                               [&val](T &v) { new (&v) T(val); });
505                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
506                 buffer_.size = size;
507             } else {
508                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
509                 vector_.resize(size, val);
510             }
511         }
512     }
513 
clear()514     void clear()
515     {
516         if (IsStatic()) {
517             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
518             std::for_each(buffer_.data.begin(), buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); });
519             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
520             buffer_.size = 0;
521         } else {
522             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
523             vector_.clear();
524         }
525     }
526 
back()527     reference back()
528     {
529         ASSERT(size() > 0);
530 
531         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
532         return IsStatic() ? buffer_.data[buffer_.size - 1] : vector_.back();
533     }
534 
535     reference operator[](size_t i)
536     {
537         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
538         return IsStatic() ? buffer_.data[i] : vector_[i];
539     }
540     const_reference operator[](size_t i) const
541     {
542         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
543         return IsStatic() ? buffer_.data[i] : vector_[i];
544     }
545 
IsStatic()546     bool IsStatic() const
547     {
548         return (bit_cast<uintptr_t>(allocator_) & 1U) != 0;
549     }
550 
551 private:
EnsureStaticSpace(size_t size)552     bool EnsureStaticSpace(size_t size)
553     {
554         if (!IsStatic()) {
555             return false;
556         }
557         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
558         size_t size_required = buffer_.size + size;
559         if (size_required > N) {
560             MoveToVector(size_required);
561             return false;
562         }
563         return true;
564     }
565 
MoveStaticBufferData(VectorType & tmp_vector)566     void MoveStaticBufferData(VectorType &tmp_vector)
567     {
568         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
569         for (uint32_t i = 0; i < buffer_.size; ++i) {
570             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
571             tmp_vector.emplace_back(std::move(buffer_.data[i]));
572         }
573     }
574 
MoveToVector(size_t reserved_size)575     void MoveToVector(size_t reserved_size)
576     {
577         ASSERT(IsStatic());
578         allocator_ = reinterpret_cast<Allocator *>(bit_cast<uintptr_t>(allocator_) & ~1LLU);
579         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
580         if constexpr (use_allocator) {
581             ASSERT(allocator_ != nullptr);
582             VectorType tmp_vector(allocator_->Adapter());
583             tmp_vector.reserve(reserved_size);
584             MoveStaticBufferData(tmp_vector);
585             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
586             new (&vector_) VectorType(std::move(tmp_vector));
587             // NOLINTNEXTLINE(readability-misleading-indentation)
588         } else {
589             VectorType tmp_vector;
590             tmp_vector.reserve(reserved_size);
591             MoveStaticBufferData(tmp_vector);
592             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
593             new (&vector_) VectorType(std::move(tmp_vector));
594         }
595     }
596 
Destroy()597     void Destroy()
598     {
599         if (IsStatic()) {
600             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
601             std::for_each(buffer_.data.begin(), buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); });
602         } else {
603             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
604             vector_.~VectorType();
605         }
606     }
607 
ResetToStatic()608     void ResetToStatic()
609     {
610         allocator_ = AddStaticFlag(allocator_);
611         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
612         buffer_.size = 0;
613     }
614 
AddStaticFlag(Allocator * p)615     static Allocator *AddStaticFlag(Allocator *p)
616     {
617         return reinterpret_cast<Allocator *>((bit_cast<uintptr_t>(p) | 1U));
618     }
619 
620 private:
621     union {
622         StaticBuffer buffer_;
623         VectorType vector_;
624     };
625     Allocator *allocator_ {AddStaticFlag(nullptr)};
626 };
627 
628 }  // namespace panda
629 
630 #endif  // PANDA_SMALL_VECTOR_H
631