• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef LIBPANDABASE_UTILS_SMALL_VECTOR_H
17 #define LIBPANDABASE_UTILS_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(data());
346     }
begin()347     iterator begin()
348     {
349         return iterator(data());
350     }
cbegin()351     const_iterator cbegin() const
352     {
353         return const_iterator(data());
354     }
end()355     const_iterator end() const
356     {
357         return begin() + size();
358     }
end()359     iterator end()
360     {
361         return begin() + size();
362     }
cend()363     const_iterator cend() const
364     {
365         return begin() + size();
366     }
367 
rbegin()368     auto rbegin() const
369     {
370         return const_reverse_iterator(data() + size() - 1);
371     }
rbegin()372     auto rbegin()
373     {
374         return reverse_iterator(data() + size() - 1);
375     }
crbegin()376     auto crbegin() const
377     {
378         return const_reverse_iterator(data() + size() - 1);
379     }
rend()380     auto rend() const
381     {
382         return const_reverse_iterator(data() - 1);
383     }
rend()384     auto rend()
385     {
386         return reverse_iterator(data() - 1);
387     }
crend()388     auto crend() const
389     {
390         return const_reverse_iterator(data() - 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 
data()404     pointer data()
405     {
406         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
407         return IsStatic() ? buffer_.data.data() : vector_.data();
408     }
409 
size()410     size_t size() const
411     {
412         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
413         return IsStatic() ? buffer_.size : vector_.size();
414     }
415 
capacity()416     size_t capacity() const
417     {
418         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
419         return IsStatic() ? buffer_.data.size() : vector_.capacity();
420     }
421 
push_back(const value_type & value)422     void push_back(const value_type &value)
423     {
424         if (!EnsureStaticSpace(1)) {
425             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
426             vector_.push_back(value);
427         } else {
428             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
429             new (&buffer_.data[buffer_.size++]) T(value);
430         }
431     }
432 
push_back(value_type && value)433     void push_back(value_type &&value)
434     {
435         if (!EnsureStaticSpace(1)) {
436             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
437             vector_.push_back(std::move(value));
438         } else {
439             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
440             new (&buffer_.data[buffer_.size++]) T(std::move(value));
441         }
442     }
443 
444     template <typename... _Args>
emplace_back(_Args &&...values)445     reference emplace_back(_Args &&... values)
446     {
447         if (!EnsureStaticSpace(1)) {
448             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
449             return vector_.emplace_back(std::move(values)...);
450         }
451         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
452         return *(new (&buffer_.data[buffer_.size++]) T(std::move(values)...));
453     }
454 
reserve(size_t size)455     void reserve(size_t size)
456     {
457         if (size > capacity()) {
458             if (IsStatic()) {
459                 ASSERT(size > N);
460                 MoveToVector(size);
461             } else {
462                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
463                 vector_.reserve(size);
464             }
465         }
466     }
467 
resize(size_t size)468     void resize(size_t size)
469     {
470         if (size <= this->size()) {
471             if (IsStatic()) {
472                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
473                 std::for_each(buffer_.data.begin() + size, buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); });
474                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
475                 buffer_.size = size;
476             } else {
477                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
478                 vector_.resize(size);
479             }
480         } else {
481             if (EnsureStaticSpace(size - this->size())) {
482                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
483                 std::for_each(buffer_.data.begin() + buffer_.size, buffer_.data.begin() + size,
484                               [](T &v) { new (&v) T; });
485                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
486                 buffer_.size = size;
487             } else {
488                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
489                 vector_.resize(size);
490             }
491         }
492     }
493 
resize(size_t size,const value_type & val)494     void resize(size_t size, const value_type &val)
495     {
496         if (size <= this->size()) {
497             if (IsStatic()) {
498                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
499                 std::for_each(buffer_.data.begin() + size, buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); });
500                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
501                 buffer_.size = size;
502             } else {
503                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
504                 vector_.resize(size, val);
505             }
506         } else {
507             if (EnsureStaticSpace(size - this->size())) {
508                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
509                 std::for_each(buffer_.data.begin() + buffer_.size, buffer_.data.begin() + size,
510                               [&val](T &v) { new (&v) T(val); });
511                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
512                 buffer_.size = size;
513             } else {
514                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
515                 vector_.resize(size, val);
516             }
517         }
518     }
519 
clear()520     void clear()
521     {
522         if (IsStatic()) {
523             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
524             std::for_each(buffer_.data.begin(), buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); });
525             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
526             buffer_.size = 0;
527         } else {
528             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
529             vector_.clear();
530         }
531     }
532 
back()533     reference back()
534     {
535         ASSERT(size() > 0);
536 
537         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
538         return IsStatic() ? buffer_.data[buffer_.size - 1] : vector_.back();
539     }
540 
541     reference operator[](size_t i)
542     {
543         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
544         return IsStatic() ? buffer_.data[i] : vector_[i];
545     }
546     const_reference operator[](size_t i) const
547     {
548         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
549         return IsStatic() ? buffer_.data[i] : vector_[i];
550     }
551 
IsStatic()552     bool IsStatic() const
553     {
554         return (bit_cast<uintptr_t>(allocator_) & 1U) != 0;
555     }
556 
557 private:
EnsureStaticSpace(size_t size)558     bool EnsureStaticSpace(size_t size)
559     {
560         if (!IsStatic()) {
561             return false;
562         }
563         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
564         size_t size_required = buffer_.size + size;
565         if (size_required > N) {
566             MoveToVector(size_required);
567             return false;
568         }
569         return true;
570     }
571 
MoveStaticBufferData(VectorType & tmp_vector)572     void MoveStaticBufferData(VectorType &tmp_vector)
573     {
574         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
575         for (uint32_t i = 0; i < buffer_.size; ++i) {
576             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
577             tmp_vector.emplace_back(std::move(buffer_.data[i]));
578         }
579     }
580 
MoveToVector(size_t reserved_size)581     void MoveToVector(size_t reserved_size)
582     {
583         ASSERT(IsStatic());
584         allocator_ = reinterpret_cast<Allocator *>(bit_cast<uintptr_t>(allocator_) & ~1LLU);
585         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
586         if constexpr (use_allocator) {
587             ASSERT(allocator_ != nullptr);
588             VectorType tmp_vector(allocator_->Adapter());
589             tmp_vector.reserve(reserved_size);
590             MoveStaticBufferData(tmp_vector);
591             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
592             new (&vector_) VectorType(std::move(tmp_vector));
593             // NOLINTNEXTLINE(readability-misleading-indentation)
594         } else {
595             VectorType tmp_vector;
596             tmp_vector.reserve(reserved_size);
597             MoveStaticBufferData(tmp_vector);
598             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
599             new (&vector_) VectorType(std::move(tmp_vector));
600         }
601     }
602 
Destroy()603     void Destroy()
604     {
605         if (IsStatic()) {
606             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
607             std::for_each(buffer_.data.begin(), buffer_.data.begin() + buffer_.size, [](T &v) { v.~T(); });
608         } else {
609             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
610             vector_.~VectorType();
611         }
612     }
613 
ResetToStatic()614     void ResetToStatic()
615     {
616         allocator_ = AddStaticFlag(allocator_);
617         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
618         buffer_.size = 0;
619     }
620 
AddStaticFlag(Allocator * p)621     static Allocator *AddStaticFlag(Allocator *p)
622     {
623         return reinterpret_cast<Allocator *>((bit_cast<uintptr_t>(p) | 1U));
624     }
625 
626 private:
627     union {
628         StaticBuffer buffer_;
629         VectorType vector_;
630     };
631     Allocator *allocator_ {AddStaticFlag(nullptr)};
632 };
633 
634 }  // namespace panda
635 
636 #endif  // LIBPANDABASE_UTILS_SMALL_VECTOR_H
637